Compare commits

..

629 Commits
v5.5.3 ... v

Author SHA1 Message Date
Greyson Parrelli
8004565c84 Bump version to 5.17.1 2021-07-16 15:55:54 -04:00
Greyson Parrelli
a101dc4fd1 Updated language translations. 2021-07-16 15:53:42 -04:00
Alex Hart
57f730d8ee Fix cursor issue for non-signal-contact searches. 2021-07-16 16:34:38 -03:00
Alex Hart
3543cc80ba Don't show SMS label for push groups. 2021-07-16 16:34:02 -03:00
Greyson Parrelli
71613d9db1 Ensure SQLCipher libraries are loaded. 2021-07-16 14:12:00 -04:00
Greyson Parrelli
4a0e6a3eb2 Improve logging around message sends. 2021-07-16 12:53:29 -04:00
Alex Hart
f1a87518e1 Fix contact search query returning outdated or bad recipients. 2021-07-16 13:53:17 -03:00
Alex Hart
61f880fd78 Hide all media for new conversations. 2021-07-16 13:20:30 -03:00
Greyson Parrelli
09904e7a16 Remove GIF button from attachment keyboard.
We've had it there for ~45 days for education purposes, but we can
remove it now.
2021-07-16 11:01:10 -04:00
Alex Hart
94658e9090 Fix bug where marquee text stopped scrolling. 2021-07-16 09:23:47 -03:00
Greyson Parrelli
a47448b6c6 Bump version to 5.17.0 2021-07-15 16:41:11 -04:00
Greyson Parrelli
7e4b9b685a Updated language translations. 2021-07-15 16:40:28 -04:00
lucio-signal
64922a8e51 Fix custom vibration settings. 2021-07-15 16:29:43 -04:00
Cody Henthorne
f65f4704c9 Improve routine around bulk attachment deletion. 2021-07-15 16:29:11 -04:00
Greyson Parrelli
b04ca202f6 Fix ApplicationMigrations UI. 2021-07-15 16:28:13 -04:00
Greyson Parrelli
83086a5a2b Make Sms/MmsDatabase ID's autoincrement. 2021-07-15 16:28:13 -04:00
Cody Henthorne
51a521594f Fix crash when deleting threads directly after backup restore. 2021-07-15 16:28:13 -04:00
Greyson Parrelli
0a7a7cf5a9 Fix envelope type conversion. 2021-07-15 16:28:13 -04:00
Greyson Parrelli
6bd689504c Make internal recipient details selectable. 2021-07-15 16:28:13 -04:00
Greyson Parrelli
efec40ff57 Fix crash with GV2 group repair during storage sync. 2021-07-15 16:28:13 -04:00
Greyson Parrelli
69716dde4a Fix navigation directly to the help screen. 2021-07-15 16:28:13 -04:00
Greyson Parrelli
e90fa05d60 Update recipient merging. 2021-07-15 16:28:13 -04:00
Greyson Parrelli
580c000bda Move distribution message processing into the decryption phase. 2021-07-15 16:28:13 -04:00
lucio-signal
2f3d04d3e8 Add EmojiFilter to SearchView input field. 2021-07-15 16:28:13 -04:00
Greyson Parrelli
bf37d412e9 Add message trimming info to the debug log. 2021-07-15 16:28:13 -04:00
Alex Hart
fd115ebb72 Drop voice notes that do not have a URI. 2021-07-15 16:28:13 -04:00
Greyson Parrelli
b9657208fe Make ThreadDatabase ID's autoincrement. 2021-07-15 16:28:13 -04:00
Cody Henthorne
5d6d78a51e Initial WebSocket refactor. 2021-07-15 16:28:13 -04:00
Cody Henthorne
916006e664 Tweak sizes and padding of various keyboard elements. 2021-07-15 16:28:13 -04:00
Cody Henthorne
55c69cd50a Add additional fallback logic for change dialog. 2021-07-15 16:28:13 -04:00
Cody Henthorne
14565b0864 Fix crash when building notification state for messages without threads. 2021-07-15 16:28:13 -04:00
Alex Hart
a157c1ae1d Refresh contact search views. 2021-07-15 16:28:13 -04:00
Greyson Parrelli
a4d458f969 Use current tag for nightly versionName. 2021-07-15 16:28:13 -04:00
Alex Hart
3f53abedab Migrate to new Share APIs. 2021-07-15 16:28:13 -04:00
Jordan Rose
68a2d5ed20 Reimplement ProfileCipherInputStream using libsignal-client.
libsignal-client provides an AES-GCM streaming interface that can
replace the implementation in AES-GCM-Provider. Using it from
ProfileCipherInputStream requires some knowledge about the tag size of
AES-GCM, but frees it from the JCE interface.

Note that it remains a serious error to not read the *entire* stream,
since the authentication tag is at the end!
2021-07-15 16:28:11 -04:00
Jordan Rose
35e9e31a7b Update to libsignal-client 0.8.3
This also fixes a misalignment where signal-client-android was on
0.8.0 but signal-client-java was 0.8.1, which was fortunately harmless
for this particular pair of versions.
2021-07-12 20:29:07 -04:00
Cody Henthorne
444d947743 Add RxJava. 2021-07-12 20:29:07 -04:00
Cody Henthorne
c427dbad08 Bump version to 5.16.3 2021-07-12 20:27:40 -04:00
Cody Henthorne
2cefe813e4 Updated language translations. 2021-07-12 20:17:43 -04:00
Alex Hart
123ffe42c3 Fix crash saving a FLAC file. 2021-07-12 13:37:59 -03:00
Alex Hart
da20e66ecd Fix issue where shared contact render would not hide audio view. 2021-07-12 13:24:04 -03:00
Alex Hart
901440017a Fix audio view width on very narrow screens. 2021-07-12 13:20:56 -03:00
Cody Henthorne
0be76a37fe Bump version to 5.16.2 2021-07-09 15:43:11 -04:00
Cody Henthorne
36dadc8777 Updated language translations. 2021-07-09 15:37:19 -04:00
Cody Henthorne
182749c101 Fix bug where some profile fetches would 400 over the websocket. 2021-07-09 15:30:08 -04:00
Alex Hart
d9228bd911 Fix issue where compose views still display under draft. 2021-07-09 15:30:08 -04:00
Greyson Parrelli
a361fcc8f3 Add additional logging to media send jobs. 2021-07-09 15:30:08 -04:00
Alex Hart
ff4f0b9f42 Stop voice note playback after user locks Signal. 2021-07-09 15:30:07 -04:00
Alex Hart
060dffc9cc Fix crash caused when quote draft left and re-entered. 2021-07-09 15:30:07 -04:00
Alex Hart
172cc302fc Add warning dialog for chat color deletion with no uses. 2021-07-09 15:30:07 -04:00
Alex Hart
416e62112f Refresh shared media screens. 2021-07-09 15:30:07 -04:00
Alex Hart
e584a90f81 Fix several voice note beta bugs.
* Sim label positioning
* Bad player state when navigating to and from conversations
* Scrolling date header placement
2021-07-09 15:29:40 -04:00
Cody Henthorne
9876ffb5e4 Bump version to 5.16.1 2021-07-08 17:52:50 -04:00
Cody Henthorne
53e10f2cad Updated language translations. 2021-07-08 17:43:16 -04:00
Cody Henthorne
cb79f75ac1 Fix bug when calling non-Signal contacts from Settings.
Fixes #11450
2021-07-08 17:36:14 -04:00
Cody Henthorne
5ec9c1cd90 Fix crash when saving media with octet stream content type. 2021-07-08 17:36:14 -04:00
Greyson Parrelli
1f28a30ace Add a nightly build type. 2021-07-08 17:36:14 -04:00
Alex Hart
7715917436 Fix issue where position would not update in draft. 2021-07-08 17:36:14 -04:00
Alex Hart
f79b445fdf Fix issue where drafts might not be properly deleted. 2021-07-08 17:36:14 -04:00
Alex Hart
14484deabe Implement count-down in inline player. 2021-07-08 17:36:14 -04:00
Alex Hart
3ac395d33e Fix row item size issue with huge fonts. 2021-07-08 17:36:14 -04:00
Alex Hart
f83b520ca9 Bump version to 5.16.0 2021-07-07 14:58:51 -03:00
Alex Hart
0123f9aa87 Updated language translations. 2021-07-07 14:58:51 -03:00
Alex Hart
06b64fe619 Add inline voice note player to conversation and conversation list. 2021-07-07 14:58:51 -03:00
Greyson Parrelli
1bb87834d8 Reduce recipient resolves in MessageContentProcessor. 2021-07-07 14:58:51 -03:00
Greyson Parrelli
ae4167ddae Write to RecipientIdCache on cache miss. 2021-07-07 14:58:51 -03:00
Greyson Parrelli
383beafdef Move 'you' to end of unnamed groups. 2021-07-07 14:58:51 -03:00
Greyson Parrelli
062e88b24f Rotate sender key flag. 2021-07-07 14:58:51 -03:00
Greyson Parrelli
8299d49042 Show an error for internal users for decryption failures. 2021-07-07 14:58:51 -03:00
Greyson Parrelli
4677883838 Improve mapping SignalServiceAddresses to Recipients. 2021-07-07 14:58:51 -03:00
Greyson Parrelli
7f0a0bef5a Incrementally insert MSL entries for legacy group sends. 2021-07-07 14:58:50 -03:00
Greyson Parrelli
acc825971b Handle additional places where MSL entries need to be deleted. 2021-07-07 14:58:50 -03:00
Greyson Parrelli
62040d06b4 Create a write-through cache for PendingRetryReceiptDatabase. 2021-07-07 14:58:50 -03:00
Greyson Parrelli
0921ebe5f1 Add read and viewed receipts to the MSL. 2021-07-07 14:58:50 -03:00
Greyson Parrelli
3d0e15e2b8 Add delivery receipts to the MSL. 2021-07-07 14:58:50 -03:00
Greyson Parrelli
5372f79c40 Allow for MSL entries to be associated with multiple messages. 2021-07-07 14:58:50 -03:00
Christian
92e8f9de0e Do not collapse list to hide only one entry. 2021-07-07 14:58:50 -03:00
Christian
c3cf846a10 Fix OutdatedBuildReminder duration.
Fixes #11438
2021-07-07 14:58:50 -03:00
Alex Hart
5826b0c068 Implement drafts for voice notes. 2021-07-07 14:58:50 -03:00
Alex Hart
2d7c043398 Implement a playback speed toggle for voice notes. 2021-07-07 14:58:50 -03:00
Alex Hart
e20d6b63cf Fix adaptive shortcut icon shapes. 2021-07-07 14:58:50 -03:00
Cody Henthorne
b85c5eb54a Make it more likely 8 emoji fit on a row, fix emoji search emoticons. 2021-07-07 14:58:50 -03:00
Greyson Parrelli
a1c8573fad Insert resent messages at the proper location. 2021-07-07 14:58:50 -03:00
Cody Henthorne
90a27d2227 Fix device transfer test dependent on native library. 2021-07-07 14:58:50 -03:00
Cody Henthorne
c54c6018b2 Remove dead keyboard code after refresh. 2021-06-30 16:13:42 -04:00
Rainer Matischek
7419570f94 Fix rotation not updated on phones using 'Legacy API'.
Fixes #10940
2021-06-30 16:13:42 -04:00
Jim Gustafson
8860f792c4 Update to RingRTC v2.10.6 2021-06-30 16:13:42 -04:00
Greyson Parrelli
e47db0d532 Ensure recipients added to the cache have an identifier. 2021-06-30 16:13:42 -04:00
Greyson Parrelli
ab5d3badc2 Enable WAL mode. 2021-06-30 16:13:42 -04:00
Greyson Parrelli
fce362960f Switch to LinkifyCompat.
We've seen some inconsistencies across OEMs with Linkify. Hopefully
LinkifyCompat will resolve them.
2021-06-30 16:13:42 -04:00
Greyson Parrelli
5bf23dcfb3 Bump version to 5.15.6 2021-06-30 16:12:58 -04:00
Greyson Parrelli
65c7dc6ca2 Updated language translations. 2021-06-30 16:12:36 -04:00
Greyson Parrelli
e30a8b6954 Use proper EmojiTextView in conversation settings toolbar. 2021-06-30 15:43:21 -04:00
Alex Hart
838e318200 Fix edit profile theming issue and mute until issue. 2021-06-30 11:11:35 -03:00
Greyson Parrelli
62ee411901 Bump version to 5.15.5 2021-06-29 14:16:06 -04:00
Greyson Parrelli
ceefd2d92f Updated language translations. 2021-06-29 14:15:28 -04:00
Cody Henthorne
e3870f5656 Fix Customize Reactions shadow. 2021-06-29 12:38:40 -04:00
Alex Hart
6e7022ab70 Fix custom notifications toggle and enable copy phone number on long press. 2021-06-29 11:19:51 -03:00
Alex Hart
031d1551e7 Prevent crash by ignoring call if view is null. 2021-06-29 11:02:57 -03:00
Greyson Parrelli
6755b25361 Bump version to 5.15.4 2021-06-28 18:07:36 -04:00
Greyson Parrelli
11d0a73675 Updated language translations. 2021-06-28 18:07:36 -04:00
Alex Hart
44119b6437 Do not crash if we try to access an item outside of the bounds of the conversation. 2021-06-28 18:07:36 -04:00
Cody Henthorne
d4a3b442f4 Add vertical scrolling to Sticker Keyboard. 2021-06-28 18:07:36 -04:00
Cody Henthorne
aba5774446 Fix share contact list updating improperly on selection change. 2021-06-28 18:07:36 -04:00
Alex Hart
911dd9efb1 Fix conversation media overview underline flicker. 2021-06-28 11:38:14 -03:00
Alex Hart
f2a490b07e Fix several conversation settings feedback issues.
* Mute icon in wrong location in RTL
* No exit animation when dismissing conversation settings
* Thumbnails flicker when you come back to conversation settings
* Rounded corners for mute dialog don't match other dialogs
* Mute button in note-to-self conversation settings
* Explore adding contact details to the contact bottom sheet
2021-06-28 11:11:57 -03:00
Cody Henthorne
5675f080f2 Fix text emoticons not showing up in recents. 2021-06-28 10:11:01 -04:00
Greyson Parrelli
f0dbe230b5 Bump version to 5.15.3 2021-06-25 17:41:53 -04:00
Greyson Parrelli
8b81800052 Updated language translations. 2021-06-25 17:41:53 -04:00
Greyson Parrelli
f598c14298 Update the sender key feature flag. 2021-06-25 17:41:53 -04:00
Greyson Parrelli
58b070e6e3 Fix job info log formatting. 2021-06-25 17:25:59 -04:00
Greyson Parrelli
71c92a1c90 Fix syncing group messages when you're the only member. 2021-06-25 17:00:14 -04:00
Greyson Parrelli
b86acb9773 Increase log size for internal users. 2021-06-25 16:55:15 -04:00
Greyson Parrelli
1b8758b657 Fix text wrapping issues in message details. 2021-06-25 16:49:48 -04:00
Cody Henthorne
ed4bab1b8b Add vertical scroll to Emoji Keyboard. 2021-06-25 16:39:04 -04:00
Greyson Parrelli
a71fe0fd75 Fix issue with group creation on linked devices. 2021-06-25 16:35:42 -04:00
Alan Evans
3d2a634aac Apply the ringer volume to the join/hangup sounds with 15% minimum. 2021-06-25 16:46:59 -03:00
Alex Hart
01047f0546 Apply style changes to shared media, color icon, and wallpaper previews. 2021-06-25 14:27:51 -03:00
Alex Hart
9dac5691f0 Fix issue where all content would be displayed if thread id was -1. 2021-06-25 11:28:28 -03:00
Alex Hart
3c489ad247 Check admin status in areContentsTheSame. 2021-06-25 11:16:53 -03:00
Alex Hart
7797351341 Fix see more icon tint and fix recipient bottom sheet scroll. 2021-06-25 11:09:49 -03:00
Alex Hart
f7212b9916 Update legacy text and fix small animation bug. 2021-06-25 10:57:58 -03:00
Alex Hart
93bb49dc16 Fix inconsistent toolbar animation state on back. 2021-06-25 10:35:07 -03:00
Alex Hart
e504c490c8 Prevent all menu invalidations if we have requested a conversation search. 2021-06-25 09:28:55 -03:00
Cody Henthorne
42e865813c Bump version to 5.15.2 2021-06-24 16:49:02 -04:00
Cody Henthorne
fc14d1d464 Updated language translations. 2021-06-24 16:45:50 -04:00
Cody Henthorne
2a1e5e4471 Add React With Any Search and update UX. 2021-06-24 16:36:13 -04:00
Alex Hart
da2ee33dff Refactor conversation settings screens into a single fragment with new UI. 2021-06-24 16:36:13 -04:00
Greyson Parrelli
f19033a7a2 Implement the message send log for sender key retries. 2021-06-24 16:36:13 -04:00
Greyson Parrelli
6502ef64ce Read the group history response as a stream. 2021-06-23 17:47:05 -04:00
Alan Evans
b3ebf778fd Group call server selection for internal users. 2021-06-23 17:50:59 -03:00
Cody Henthorne
1dca3698d2 Fix crash when adding person to an existing mms group. 2021-06-22 17:03:20 -04:00
Cody Henthorne
2bfe1198d1 Bump version to 5.15.1 2021-06-21 20:24:07 -04:00
Cody Henthorne
4f704670b1 Updated language translations. 2021-06-21 20:01:03 -04:00
Cody Henthorne
a1aafd7453 Fix incorrect mark as read behavior when leaving conversation. 2021-06-21 19:55:02 -04:00
Alex Hart
4932623937 Allow FABs to go as high as the bottom of the toolbar on the conversation list. 2021-06-21 19:55:02 -04:00
Alex Hart
b93568d9c6 Invoke onTick immediately in onResume. 2021-06-21 19:55:02 -04:00
Alex Hart
b3041ab6e0 Always update ViewOnceState before rendering hud. 2021-06-21 14:27:28 -03:00
Alex Hart
3a151b30ac Catch MediaCodecException in extractThumbnails for configuration crash. 2021-06-21 14:19:11 -03:00
Alex Hart
97b3d36433 Add support to MessageDetailsActivity for viewed reciepts. 2021-06-21 14:11:36 -03:00
Cody Henthorne
81e3252128 Do not apply universal timer to SMS chats. 2021-06-21 11:04:28 -04:00
Cody Henthorne
426c83c6cc Fix baby emoji in Help and Profile. 2021-06-21 10:54:55 -04:00
Greyson Parrelli
b427754a81 Fix quoted media rendering issue. 2021-06-21 10:31:14 -04:00
Cody Henthorne
08f023fb12 Revert "Fix ANR when leaving MediaPreviewActivity."
This reverts commit 8be659c1c8.
2021-06-21 09:55:40 -04:00
Greyson Parrelli
5f1454aeb8 Improve the performance of detecting duplicate messages.
To do this, we do two things:
- Make the index on DATE_SENT also include the other relevant fields:
the recipientId and threadId
- Use the most minimal projection possible
2021-06-21 09:51:51 -04:00
Greyson Parrelli
0d254e0724 Fix the mock data initializer.
Needed to ignore the emoji_data FTS tables.
2021-06-20 17:36:27 -04:00
Cody Henthorne
e882e6e111 Bump version to 5.15.0 2021-06-18 15:21:41 -04:00
Cody Henthorne
4b0811f9aa Revert "Temporarily block payments in all regions."
This reverts commit 4637e1b5d8.
2021-06-18 15:10:29 -04:00
Greyson Parrelli
817f1ee938 Add a feature flag to disable SMS megaphone.
As part of this work, we also make sure we fetch feature flags during
registration.
2021-06-18 15:10:16 -04:00
Cody Henthorne
2d93d74b9f Fix incorrect linting by preventing Github Actions from using Android S. 2021-06-18 15:10:16 -04:00
Greyson Parrelli
93f37ad70f Reduce fetches when you open a conversation. 2021-06-18 15:10:16 -04:00
Cody Henthorne
3c6bed90db Fix ANR by upgrading Firebase Messaging. 2021-06-18 15:10:16 -04:00
Greyson Parrelli
fa26fb6b8b Improve conversation query performance.
For the conversation query at least, we stopped joining on the
attachments tables, and instead get attachments on a page-by-page basis.
2021-06-18 15:10:15 -04:00
Cody Henthorne
263ddb0d1e Fix main thread recipient resolve in contact selection. 2021-06-18 15:10:15 -04:00
Cody Henthorne
8be659c1c8 Fix ANR when leaving MediaPreviewActivity. 2021-06-18 15:10:15 -04:00
Cody Henthorne
e5c9dddb5a Fix ANR when generating group message snippets. 2021-06-18 15:10:15 -04:00
Greyson Parrelli
6da72aad6d Log the build variant. 2021-06-18 15:10:15 -04:00
Greyson Parrelli
5dd5a024c9 Narrow locking in LiveRecipientCache.
This should make it so that we never hold a lock while accessing the
database.
2021-06-18 15:10:15 -04:00
Greyson Parrelli
c0eac5564c Clean up message processing locks. 2021-06-18 15:10:15 -04:00
Cody Henthorne
0d0ee753df Make portrait bubbled keyboard height dynamic based on bubble height. 2021-06-18 15:10:15 -04:00
Aaron Labiaga
908f952893 Update API for Activity in bubble check. 2021-06-18 15:10:15 -04:00
Cody Henthorne
1c80e65c5a Bump version to 5.14.5 2021-06-18 15:02:33 -04:00
Cody Henthorne
20b13a929b Updated language translations. 2021-06-18 14:55:16 -04:00
Alex Hart
4637e1b5d8 Temporarily block payments in all regions. 2021-06-18 14:47:32 -04:00
Greyson Parrelli
4b6cb79c75 Fix message exception handling. 2021-06-18 13:52:31 -04:00
Greyson Parrelli
feaf2a33a9 Bump version to 5.14.4 2021-06-17 17:39:30 -04:00
Greyson Parrelli
4c893a11fc Updated language translations. 2021-06-17 17:39:30 -04:00
Cody Henthorne
f4dd80c929 Switch logic order for detecting conversation channel changes. 2021-06-15 13:09:11 -04:00
Cody Henthorne
4af078007e Attempt to recover from encountering octet stream media. 2021-06-15 11:54:14 -04:00
Greyson Parrelli
be297120a1 Include 'you' in dynamic group name. 2021-06-15 11:37:28 -04:00
Cody Henthorne
a9741cadbf Fix logging around dialog flow. 2021-06-15 11:31:56 -04:00
Cody Henthorne
79200c82da Fix create bubble conversation notification. 2021-06-14 16:51:18 -04:00
Cody Henthorne
d9c9ae8dae Update MobileCoin dependency and add new configuration. 2021-06-14 13:25:50 -04:00
Greyson Parrelli
8ee96b40d0 Bump version to 5.14.3 2021-06-10 16:50:51 -04:00
Greyson Parrelli
67f0f45b67 Updated language translations. 2021-06-10 16:50:17 -04:00
Cody Henthorne
881ab90982 Add additional logging to dialog. 2021-06-10 16:06:32 -04:00
Alex Hart
6d7e09fec1 Fix bug preventing VIEWED receipts from being sent to group recipients. 2021-06-10 16:52:24 -03:00
Greyson Parrelli
c274ed6a96 Improve search performance. 2021-06-10 15:47:12 -04:00
Greyson Parrelli
53ffca964d Restrict group member names to 2 lines. 2021-06-10 11:08:45 -04:00
Greyson Parrelli
3da3367291 Ensure that multi-forwards have unique timestamps. 2021-06-10 11:03:07 -04:00
Cody Henthorne
412ee220ce Improve keyboard sizing in bubbled conversations. 2021-06-09 16:18:55 -04:00
Alex Hart
a3e3667dc2 Add 'tick' to update conversation bubble timestamps every 1m. 2021-06-09 16:35:36 -03:00
Greyson Parrelli
d5f63da9e4 Better database error handling. 2021-06-09 15:04:16 -04:00
Greyson Parrelli
f8d2044356 Bump version to 5.14.2 2021-06-09 11:16:19 -04:00
Greyson Parrelli
4d2dc61f5d Updated language translations. 2021-06-09 11:16:19 -04:00
Cody Henthorne
5492685df2 Fix fragment lifecycle crash in Edit Profile. 2021-06-09 11:16:19 -04:00
Alex Hart
ad8c6bc579 Hide 'remove from group' if not an admin of that group. 2021-06-09 11:16:19 -04:00
Alex Hart
fb08f8ae17 Fix issue preventing people blocking receipts from seeing incoming voice notes as viewed. 2021-06-09 11:16:10 -04:00
Greyson Parrelli
7833d7c99a Handle the sender key capability better. 2021-06-09 09:56:57 -04:00
Alex Hart
335ff61011 Fix several Gif MP4 UX issues. 2021-06-09 10:23:41 -03:00
Greyson Parrelli
2029ea378f Bump version to 5.14.1 2021-06-08 16:48:10 -04:00
Greyson Parrelli
cd7bc63cec Updated language translations. 2021-06-08 16:48:10 -04:00
Cody Henthorne
958331a8ea Fix bug with APNGParser over reading larger files and invalidating the stream. 2021-06-08 16:48:10 -04:00
Greyson Parrelli
2ba206b9db Rotate the mp4 gif feature flag. 2021-06-08 16:13:19 -04:00
Greyson Parrelli
9b90e371f9 Inline viewed receipt feature flags. 2021-06-08 16:10:34 -04:00
Alex Hart
ff1c298817 Allow video gifs to download as if they were images. 2021-06-08 17:00:07 -03:00
Alex Hart
dfe804dfa0 Increment GIF flag in AttachmentPointer to avoid android client bug. 2021-06-08 16:53:21 -03:00
Alex Hart
978c6f9349 Fix mp4 support and viewed dot coloring. 2021-06-08 16:10:08 -03:00
Alex Hart
c5c176a818 Remove use of transitionmanager to prevent sticky header flickering. 2021-06-08 14:02:05 -03:00
Cody Henthorne
9f2d57493d Hide quality selector when no images selected. 2021-06-08 12:53:14 -04:00
Greyson Parrelli
0972d8f1e1 Inline the GV1 forced migration flag. 2021-06-08 12:42:51 -04:00
Alex Hart
cf361334c4 Fix jank and decrease animation duration in share contact selection recycler. 2021-06-08 13:10:54 -03:00
Cody Henthorne
c72dd86fed Remove old notification system and notification rewrite feature flag. 2021-06-08 11:20:19 -04:00
Cody Henthorne
b6c653ff77 Remove Universal Expire Timer flag and fix bug with SMS. 2021-06-08 11:20:06 -04:00
Greyson Parrelli
5e3bbb0e64 Improve name rendering for nameless groups. 2021-06-08 11:18:08 -04:00
Greyson Parrelli
64124f6f4b Update strings from 'cellular' to 'mobile data'. 2021-06-08 08:16:02 -04:00
Cody Henthorne
6f6a6826d9 Restrict edit description to V2 and remove feature flag. 2021-06-07 20:07:49 -04:00
Greyson Parrelli
57c0b8fd0f Initial pre-alpha support for sender key. 2021-06-07 18:14:12 -04:00
Max Ullinger
c54f016213 Fix inconsistent text scaling in quotes.
Fixes #10188
2021-06-07 17:26:47 -04:00
Cody Henthorne
bece58d939 Improve notification channel consistency checks with Android Conversations. 2021-06-07 15:58:39 -04:00
Alex Hart
36443c59f9 Apply proximity wake lock in locked audio recording mode.
Fixes #10098
2021-06-07 16:55:26 -03:00
Cody Henthorne
02f0301f25 Change how we enable/disable vibration for notifications. 2021-06-07 15:44:38 -04:00
Alex Hart
334cf669ed Add support for multiple typing indicators in groups. 2021-06-07 15:35:19 -03:00
Greyson Parrelli
8442143818 Add support for the updated link device schema. 2021-06-07 11:19:06 -04:00
Greyson Parrelli
b25b8b90e4 Set last search index download time. 2021-06-07 10:32:18 -04:00
Alex Hart
06aec0b7d7 Move bubble rendering from onMeasure to onLayout. 2021-06-07 09:16:18 -03:00
Alex Hart
835d7f5ccb Bump version to 5.14.0 2021-06-04 16:36:16 -03:00
Alex Hart
ffd0b16753 Updated language translations. 2021-06-04 16:35:29 -03:00
Alex Hart
b351fb43e6 Revert "Temporarily block payments in all regions."
This reverts commit 1466875293.
2021-06-04 16:29:37 -03:00
Cody Henthorne
7da47c9586 Fix NPE in ThumbnailsTask.
The async task was being cancelled, but there was still a race condition
in how the thumbnails list was being managed. This attempts to fix that.
2021-06-04 16:29:23 -03:00
Alex Hart
e4755b298f Bump version to 5.13.8 2021-06-04 16:18:51 -03:00
Alex Hart
4a65487842 Updated language translations. 2021-06-04 16:18:09 -03:00
Alex Hart
1466875293 Temporarily block payments in all regions. 2021-06-04 16:18:09 -03:00
Alex Hart
fd1e552ad1 Update name colors palette. 2021-06-04 16:05:02 -03:00
Alex Hart
be3e89ac20 Utilize built in string id getter instead of using our own logic for name colors. 2021-06-04 16:05:02 -03:00
Alex Hart
b8f1b98c74 Use user avatar or avatar color for bubble on wallpaper fragment. 2021-06-04 16:05:02 -03:00
Alex Hart
4bdd07db16 Fix NPE if system ringtone name lookup returns null. 2021-06-04 09:09:34 -03:00
Greyson Parrelli
511b095647 Bump version to 5.13.7 2021-06-03 21:27:56 -04:00
Greyson Parrelli
23f4d30e57 Updated language translations. 2021-06-03 21:27:37 -04:00
Greyson Parrelli
45c587c5e4 Allow variation selection in emoji search results. 2021-06-03 21:18:18 -04:00
Greyson Parrelli
115e74d844 Use borderless ripple for keyboard category buttons. 2021-06-03 20:50:31 -04:00
Greyson Parrelli
1475a77260 Update "GIFs moved" education tooltip. 2021-06-03 20:38:36 -04:00
Greyson Parrelli
d0e2fbf8e7 Fix issues with emoji search backup/restore. 2021-06-03 20:30:44 -04:00
Greyson Parrelli
0f2f0450e3 Do not show chat color megaphone to new users.
They already have the onboarding variant.
2021-06-03 19:50:42 -04:00
Cody Henthorne
e57f24c062 Bump version to 5.13.6 2021-06-03 17:23:34 -04:00
Cody Henthorne
203d7de6a2 Updated language translations. 2021-06-03 17:20:43 -04:00
Cody Henthorne
6e5f2f50fb Fix padding issue with keyboard indicator in compose. 2021-06-03 17:11:07 -04:00
Cody Henthorne
875895524e Fix media keyboard sizing issue by trying two ways to find window insets. 2021-06-03 17:11:07 -04:00
Cody Henthorne
3a21a2a49e Fix bad sync of default timer to linked devices. 2021-06-03 17:11:07 -04:00
Alex Hart
262b4e7d62 Hide bottom bar on scroll for Emoji pager. 2021-06-03 17:11:07 -04:00
Cody Henthorne
c202f97088 Fix non-hiding bottom bar when not enough stickers. 2021-06-03 17:11:07 -04:00
Cody Henthorne
f96eac96f9 Update keyboard colors to improve consistency. 2021-06-03 17:11:07 -04:00
Alex Hart
c59006e06e Go back to emoji selection on keyboard close in search. 2021-06-03 14:21:56 -03:00
Cody Henthorne
84e27e7bff Remove GIFs from attachment keyboard. 2021-06-03 13:05:38 -04:00
Alex Hart
a3a4b10f83 Wrap emoji pages with coordinator layout. Fix issue with bubble coloring in wallpaper preview. 2021-06-03 14:02:37 -03:00
Cody Henthorne
a644c81736 Actually skip emoji searchd ata in backup restore. 2021-06-03 11:34:41 -04:00
Cody Henthorne
27b9fbe490 Add pull down search bar to stickers and auto hide when scrolling. 2021-06-03 11:33:06 -04:00
Alex Hart
2131c56513 Add unit testing for Recipient#getChatColors 2021-06-03 11:29:19 -03:00
Alex Hart
95dba15db8 Fix initial scroll position if there's not enough vertical space to hide search bar. 2021-06-03 11:26:49 -03:00
Cody Henthorne
c23215604d Bump version to 5.13.5 2021-06-03 10:18:32 -04:00
Cody Henthorne
8c9f274d5a Updated language translations. 2021-06-03 10:18:18 -04:00
Cody Henthorne
504a70f3ee Skip emoji search data in backup/restore. 2021-06-03 10:12:12 -04:00
Alex Hart
ad6f51901e Fix bad logic in chat color selection. 2021-06-03 10:12:12 -04:00
Cody Henthorne
52ef4c6235 Get more space on gif keyboard by hiding views. 2021-06-03 10:12:12 -04:00
Cody Henthorne
9ba4005433 Show keyboard when opening gif search. 2021-06-02 20:57:08 -04:00
Cody Henthorne
3cea3766ab Use correct GIF icon for dark theme. 2021-06-02 20:53:34 -04:00
Cody Henthorne
ede24e0e73 Bump version to 5.13.4 2021-06-02 18:08:02 -04:00
Cody Henthorne
df79bbc5aa Updated language translations. 2021-06-02 18:03:57 -04:00
Cody Henthorne
3c522c677b Fix crash when applying unknown fields. 2021-06-02 17:59:44 -04:00
Android Team
08e86b8c82 Add Emoji Search, Sticker Search, and GIF Keyboard.
Co-authored-by: Alex Hart <alex@signal.org>
Co-authored-by: Cody Henthorne <cody@signal.org>
Co-authored-by: ⁨Greyson Parrelli<greyson@signal.org>
2021-06-02 17:43:17 -04:00
Alex Hart
66c3b1388a Add new chat colors megaphone. 2021-06-02 16:52:21 -03:00
Alex Hart
8992f59c3b Update logic for color selection to match spec. 2021-06-02 16:51:23 -03:00
Alex Hart
1d6d27d46c Tweak name color palette and fix issue with non-present group members. 2021-06-02 16:31:55 -03:00
Alex Hart
625d36fb27 Start animation when megaphone is displayed. 2021-06-02 15:11:23 -03:00
Alex Hart
665ce14bb6 Fix RTL issue with thumbnail masking. 2021-06-02 14:49:26 -03:00
Alex Hart
6e4f002b6d Fix masking issue with multiselect highlighted items. 2021-06-02 14:34:34 -03:00
Cody Henthorne
39a7dbda94 Bump version to 5.13.3 2021-06-02 12:24:25 -04:00
Cody Henthorne
7ae8af4153 Updated language translations. 2021-06-02 12:22:17 -04:00
Alex Hart
fb817e0c3b Add Chat Colors onboarding. 2021-06-02 12:16:10 -04:00
Tomer Rosenfeld
1eae360470 Do not remove system contact badging during partial syncs.
Fixes #11236
2021-06-02 12:16:10 -04:00
Cody Henthorne
0314db0b58 Small UI tweaks for edit reactions. 2021-06-02 12:16:10 -04:00
Cody Henthorne
4598387187 Bump version to 5.13.2 2021-05-27 16:27:43 -04:00
Cody Henthorne
445c93a756 Updated language translations. 2021-05-27 16:23:09 -04:00
Alex Hart
6c168ec575 Fix revealable color. 2021-05-27 16:17:07 -04:00
Greyson Parrelli
1322f5bc08 Be more careful with unknown IDs during storage sync. 2021-05-27 16:17:07 -04:00
Alex Hart
1c40f2d167 Fix issue with disappearing colors upon group removal. 2021-05-27 16:17:07 -04:00
Alex Hart
18133e2a10 Fix several issues with chatcolors. 2021-05-27 16:17:07 -04:00
Cody Henthorne
e5b0941d30 Add ability to edit default reactions. 2021-05-27 16:17:07 -04:00
Cody Henthorne
811bef8c35 Bump version to 5.13.1 2021-05-26 20:07:20 -04:00
Cody Henthorne
057107ea7a Updated language translations. 2021-05-26 20:02:54 -04:00
Alex Hart
273e5f9168 Remove gradient support from api 19. 2021-05-26 19:56:20 -04:00
Alex Hart
35930fb23a Fix several ChatColors issues. 2021-05-26 20:06:57 -03:00
Alex Hart
c794b5c2e7 Only display edit pencil if custom color is selected. 2021-05-26 19:56:04 -03:00
Greyson Parrelli
e74d502ae6 Remove legacy session version.
Hasn't been used since the TextSecure days!
2021-05-26 17:46:58 -04:00
Greyson Parrelli
e5ce6e3e2e Fix internal preference. 2021-05-26 12:45:54 -04:00
Greyson Parrelli
65020dde1a Fix some missed cases for blocking unregistered sends. 2021-05-26 12:02:22 -04:00
Alex Hart
98f432d23c Fix advanced prefs dialog title. 2021-05-26 11:50:17 -03:00
Cody Henthorne
2651b789dd Fix some group description UX oddities. 2021-05-26 10:42:36 -04:00
Cody Henthorne
dbabac34b0 Fix video not showing until phone moved. 2021-05-26 10:25:58 -04:00
Alex Hart
6866b7a277 Fix chat color selection context menu positioning. 2021-05-26 11:13:25 -03:00
Alex Hart
03c19f54c2 Set background of typing indicator to match conversation. 2021-05-26 10:56:09 -03:00
Alex Hart
ba510ca77d Update chat pluralization. 2021-05-26 10:47:40 -03:00
Alex Hart
bb7409fd91 Add proper background color for quote preview. 2021-05-26 10:14:22 -03:00
Alex Hart
23e5da4d95 Fix issue where message sender was impacting bubble color in groups. 2021-05-26 09:41:20 -03:00
Greyson Parrelli
fb1b46b67e Bump version to 5.13.0 2021-05-26 00:45:32 -04:00
Greyson Parrelli
7a21e6b5f8 Updated language translations. 2021-05-26 00:45:06 -04:00
Greyson Parrelli
6342a45b4e Separate avatar colors from chat colors. 2021-05-26 00:39:59 -04:00
Alex Hart
bcc5d485ab Update chat colors. 2021-05-26 00:39:59 -04:00
Rainer Matischek
36fe150678 Increase maximum zoom level for large images. 2021-05-26 00:39:59 -04:00
Greyson Parrelli
54f92ae466 Do not send if unregistered. 2021-05-26 00:39:59 -04:00
Cody Henthorne
b9b2924939 Add screen share receive support and improve video calling rotation. 2021-05-26 00:39:59 -04:00
Greyson Parrelli
513e5b45c5 Show notifications for group creates. 2021-05-26 00:39:59 -04:00
Greyson Parrelli
1fad5e2c1e Add some extra preconditions to reaction processing. 2021-05-26 00:39:59 -04:00
Greyson Parrelli
5a28cf616d Do not allow bad QR data to crash. 2021-05-26 00:39:59 -04:00
Cody Henthorne
c08199659b Support pasting of images into input text. 2021-05-26 00:39:59 -04:00
Greyson Parrelli
ca508514a7 Updated flipper to 0.91.0 2021-05-26 00:39:59 -04:00
Greyson Parrelli
da2038dd46 Revert "Temporarily block payments in all regions."
This reverts commit 152cc27394.
2021-05-26 00:39:59 -04:00
Greyson Parrelli
f02e2d23d0 Bump version to 5.12.3 2021-05-26 00:31:33 -04:00
Greyson Parrelli
ef1c25c3d3 Updated language translations. 2021-05-26 00:31:02 -04:00
Alex Hart
152cc27394 Temporarily block payments in all regions. 2021-05-26 00:28:05 -04:00
Greyson Parrelli
c582aca465 Bump version to 5.12.2 2021-05-20 11:15:00 -04:00
Greyson Parrelli
80e85fb49a Updated language translations. 2021-05-20 11:14:01 -04:00
Greyson Parrelli
d660e22e61 Pull translations in parallel. 2021-05-20 11:10:16 -04:00
Cody Henthorne
51856c4f06 Add support back for Android Auto. 2021-05-20 10:42:06 -04:00
Cody Henthorne
fd37da42f9 Revert "Remove Android Auto support (for now)."
This reverts commit 6c2adfeec2.
2021-05-20 09:46:38 -04:00
Cody Henthorne
11df2bc51f Replace spongy with libsignal x509 generation for device transfer. 2021-05-19 17:29:48 -04:00
Cody Henthorne
6770d21cf7 Fix crash when processing invalid mentions. 2021-05-19 13:15:28 -04:00
Cody Henthorne
f490d1f6d2 Add long click copy for urls in group descriptions. 2021-05-19 12:29:34 -04:00
Cody Henthorne
f890ae8ddc Enforce two line limit on group description.
Sorry.
2021-05-19 11:57:53 -04:00
Cody Henthorne
5d5d61d8ed Pluralize units for custom timer dialog. 2021-05-19 09:40:20 -04:00
Cody Henthorne
75589f1b2d Use new expire timer dialog from overflow menu. 2021-05-18 20:23:59 -04:00
Greyson Parrelli
6225c676e2 Bump version to 5.12.1 2021-05-18 19:31:12 -04:00
Greyson Parrelli
9b18668f49 Updated language translations. 2021-05-18 19:30:53 -04:00
Greyson Parrelli
2f80e7f1ff Put the default message timer behind a feature flag. 2021-05-18 19:26:25 -04:00
Greyson Parrelli
790413680d Bump version to 5.12.0 2021-05-18 18:28:24 -04:00
Cody Henthorne
47e9a4ec29 Fix hard to see media send HUD. 2021-05-18 18:21:49 -04:00
Cody Henthorne
defd5e8047 Add universal disappearing messages. 2021-05-18 18:21:48 -04:00
Greyson Parrelli
8c6a88374b No longer use SignalServiceAddress legacy identifier.
We had to do this in the past because we previously didn't allow
UUID-only contacts back in the day. This hasn't been the case for some
time. We should be preferring the UUID in all cases.
2021-05-18 18:21:48 -04:00
Greyson Parrelli
7343613bea Revert "Temporarily block payments in all regions."
This reverts commit ec486d66f7.
2021-05-18 18:21:48 -04:00
Greyson Parrelli
155dda1fa4 Bump version to 5.11.5 2021-05-18 18:02:37 -04:00
Greyson Parrelli
3c74306c8d Updated language translations. 2021-05-18 18:02:18 -04:00
Alex Hart
13ecd9eee6 Temporarily block payments in all regions. 2021-05-18 17:56:38 -04:00
Alex Hart
c48f3b4582 Bump version to 5.11.4 2021-05-17 17:03:21 -03:00
Alex Hart
30c007194d Updated language translations. 2021-05-17 17:03:21 -03:00
Cody Henthorne
ef5b68eb35 Add report spam in message request state. 2021-05-17 17:03:21 -03:00
Cody Henthorne
c47dcd5720 Add code formatting styles. 2021-05-17 17:03:21 -03:00
Greyson Parrelli
ed3c5ab479 Do more to ensure that we have the latest self in StorageSyncJob. 2021-05-17 17:03:21 -03:00
Cody Henthorne
a697b6c3d4 Fix long text layout bug in media quality selector. 2021-05-17 17:03:21 -03:00
Alex Hart
3965df78c9 Fix several settings issues. 2021-05-17 17:03:21 -03:00
Alex Hart
64ebf20c1b Bump version to 5.11.3 2021-05-14 16:46:51 -03:00
Alex Hart
797bed6701 Updated language translations. 2021-05-14 16:46:08 -03:00
Alex Hart
e84e021127 Fix proxy settings navigation. 2021-05-14 16:46:08 -03:00
Greyson Parrelli
0b9515b58b Clean up another bad usage of self in StorageSyncJob. 2021-05-13 16:15:20 -04:00
Alex Hart
81ec9e96c7 Fix several settings issues. 2021-05-13 16:59:31 -03:00
Greyson Parrelli
ee09793ef2 Fix outage reminder in dark theme.
Fixes #11258
2021-05-13 13:21:30 -04:00
Greyson Parrelli
61a130e645 Handle ServerRejectedException in more jobs. 2021-05-13 13:16:11 -04:00
Greyson Parrelli
19d342749a Bump version to 5.11.2 2021-05-13 12:27:50 -04:00
Greyson Parrelli
94adcf04f5 Updated language translations. 2021-05-13 12:27:24 -04:00
Alex Hart
53e1da0f43 Fix bad preference class setting. 2021-05-13 12:20:08 -04:00
Greyson Parrelli
b41989de03 Be more consistent with 'self' in StorageSyncJob. 2021-05-13 12:20:08 -04:00
Greyson Parrelli
6c7848b750 Ensure we don't enqueue a ProfileKeySendJob to a v2 group. 2021-05-13 12:20:08 -04:00
Greyson Parrelli
07bd9ad840 Make debuglog submission slightly more discoverable. 2021-05-13 12:20:08 -04:00
Greyson Parrelli
14236d3062 Show About in AppSettings screen. 2021-05-13 12:00:24 -04:00
Cody Henthorne
6c4df30252 Fix flashing tap to view showing on conversation open. 2021-05-13 12:00:24 -04:00
Cody Henthorne
45218470af Update media send quality icons. 2021-05-13 12:00:24 -04:00
Greyson Parrelli
417ee1e047 Mark url as non-translatable. 2021-05-13 12:00:24 -04:00
Alex Hart
08a3bc457e Fix autodownload constraint. 2021-05-13 12:00:24 -04:00
Alex Hart
0cc2cba883 Clean up device fragments and utilize dsl toolbar. 2021-05-13 12:00:24 -04:00
Alex Hart
24d461c8b2 Fix settings crash and RTL bug. 2021-05-13 12:00:24 -04:00
Greyson Parrelli
4d472fccd2 Fix bugs with the bio preference in AppSettings.
- Always show the profile name.
- Pretty-print the phone number.
- Show the correct avatar when none is set.
2021-05-12 20:39:43 -04:00
Greyson Parrelli
45d010bdb6 Only update SMS setting if registration is complete.
Otherwise you could crash during registration if the user had previously
set Signal as the default.
2021-05-12 17:16:01 -04:00
Greyson Parrelli
70db617229 Fix some issues with avatar syncing.
- We weren't falling back to system avatars when no profile was present
- We weren't triggering a sync when the setting changed
2021-05-12 17:08:13 -04:00
Greyson Parrelli
d8256013a3 Bump version to 5.11.1 2021-05-12 15:48:46 -04:00
Greyson Parrelli
6d2c22addc Updated language translations. 2021-05-12 15:48:46 -04:00
Greyson Parrelli
9640f3f215 Do not allow profile given names to be empty when editing. 2021-05-12 15:48:37 -04:00
Greyson Parrelli
80c911e118 Sync whether or not our primary device can send SMS. 2021-05-12 14:58:19 -04:00
Alex Hart
f2d5ea0391 Refactor app settings. 2021-05-12 12:23:00 -04:00
Greyson Parrelli
a94d77d81e Ensure inbound messages mark recipients as registered. 2021-05-12 12:20:14 -04:00
Greyson Parrelli
2d2de1a652 Fix storage service record merge. 2021-05-12 10:56:34 -04:00
Greyson Parrelli
01f8823fb2 Show an error animation if you don't select a help category. 2021-05-12 00:05:41 -04:00
Greyson Parrelli
260575d139 Utilize RecipientIdCache during message processing. 2021-05-11 12:19:07 -04:00
Greyson Parrelli
1fb3290038 Be more direct with AccountRecord updates. 2021-05-11 10:05:13 -04:00
Greyson Parrelli
37596320e8 Bump version to 5.11.0 2021-05-10 19:37:20 -04:00
Greyson Parrelli
7a959c2c3e Updated language translations. 2021-05-10 19:36:52 -04:00
Greyson Parrelli
877c03e6a1 Fix issue where bulk-archive wasn't triggering a storage sync.
Also took the opportunity to consolidate our archive code to reduce
duplication.
2021-05-10 19:30:11 -04:00
Greyson Parrelli
d3431d227b Make selecting a help category mandatory. 2021-05-10 19:30:11 -04:00
Greyson Parrelli
fbf307bf01 Manually handle 6-digit short codes for UK.
Also cleans up some set usages.

Fixes #11274
2021-05-10 19:30:11 -04:00
Alex Hart
d672857e82 Fix layout designer deadlock. 2021-05-10 19:30:11 -04:00
Cody Henthorne
dd934e0095 Add photo media quality selector when sending images. 2021-05-10 19:30:11 -04:00
Cody Henthorne
8c9df8d3be Add support for Group V2 description field. 2021-05-10 19:30:10 -04:00
Chris Eager
b3aec58e69 Add additional test cases to VerificationCodeParserTest. 2021-05-10 19:30:10 -04:00
Greyson Parrelli
b4111cffef Guard against shared content not having proper permissions.
Fixes #11269
2021-05-10 19:30:10 -04:00
Greyson Parrelli
ecc8d1738e Respect system avatar preference when syncing with linked devices. 2021-05-10 19:30:10 -04:00
Greyson Parrelli
d1982cbc0a Do not fetch profiles when unregistered. 2021-05-10 19:30:10 -04:00
Greyson Parrelli
03b65ce6dc Update to libsignal-client 0.5.1 2021-05-10 19:30:10 -04:00
Greyson Parrelli
56ea11cdff Reactively share profiles to those who should already have it. 2021-05-10 19:30:10 -04:00
Greyson Parrelli
7a02404f7b Update SQLCipher to v4.4.3 2021-05-10 19:30:10 -04:00
Greyson Parrelli
b9a960a7c8 Revert "Temporarily block payments in all regions."
This reverts commit ec486d66f7.
2021-05-10 19:30:10 -04:00
Greyson Parrelli
02c87a4d7b Bump version to 5.10.8 2021-05-10 19:22:36 -04:00
Greyson Parrelli
0dd9cd82f8 Updated language translations. 2021-05-10 19:16:14 -04:00
Alex Hart
ec486d66f7 Temporarily block payments in all regions. 2021-05-10 19:16:14 -04:00
Alex Hart
69cd7eb449 Fix issue with sharing resizable media to MMS. 2021-05-10 13:56:54 -03:00
Greyson Parrelli
1427de7c65 Bump version to 5.10.7 2021-05-07 13:23:13 -04:00
Greyson Parrelli
8ad66e1e5e Updated language translations. 2021-05-07 13:11:57 -04:00
Greyson Parrelli
a2e31e97db Trim giphy queries. 2021-05-07 13:05:02 -04:00
Alex Hart
1f3e131690 Fix Emoji crashes when downloaded bitmap files cannot be found. 2021-05-07 13:56:14 -03:00
Greyson Parrelli
276b757e2d Fix the other possible NPE in database migration. 2021-05-07 10:43:45 -04:00
Cody Henthorne
093df70602 Bump version to 5.10.6 2021-05-06 18:06:50 -04:00
Cody Henthorne
fe9ab66f31 Updated language translations. 2021-05-06 18:06:26 -04:00
Alex Hart
138f9476ac Revert emoji cache to old pattern. 2021-05-06 17:47:36 -04:00
Alex Hart
cb9ab61b6b Fix issue where gifs would load as images. 2021-05-06 16:19:24 -03:00
Alex Hart
bcbd365326 Fix gif player audio. 2021-05-06 13:29:28 -03:00
Greyson Parrelli
afdf4e365f Fix possible NPE in database migration. 2021-05-05 17:56:06 -04:00
Greyson Parrelli
553b7522aa Bump version to 5.10.5 2021-05-05 16:52:51 -04:00
Greyson Parrelli
13f38dd594 Updated language translations. 2021-05-05 16:51:39 -04:00
Greyson Parrelli
31e1c6f7aa Handle 428 rate limiting. 2021-05-05 16:47:13 -04:00
Alex Hart
02d060ca0a Fix issue with gif search and emoji loading on lowmem devices. 2021-05-05 14:42:51 -04:00
Cody Henthorne
5e2a3ac644 Bump version to 5.10.4 2021-05-04 20:40:06 -04:00
Cody Henthorne
2fc461b85f Updated language translations. 2021-05-04 20:39:13 -04:00
Cody Henthorne
29a0b86411 Address API23 notification issues and update when conversation content changes. 2021-05-04 20:31:29 -04:00
Alex Hart
efc3e7b25d Fix emoji on odd densities and add internal pref to force built-in. 2021-05-04 12:20:18 -04:00
Greyson Parrelli
6c2adfeec2 Remove Android Auto support (for now). 2021-05-03 22:11:52 -04:00
Cody Henthorne
3124d6d43e Bump version to 5.10.3 2021-05-03 14:17:50 -04:00
Cody Henthorne
e5a6b7d47d Updated language translations. 2021-05-03 14:17:05 -04:00
Greyson Parrelli
add65cf592 Prevent crash when opening conversation with unregistered UUID-only recipient. 2021-05-03 14:02:58 -04:00
Alex Hart
129effd0ec Add lighter weight emoji. 2021-05-03 14:02:58 -04:00
Cody Henthorne
2aad00df85 Add ability to configure locale specific media quality settings.
Part 1 of improve media quality controls. User selection coming soon.
2021-05-03 14:02:58 -04:00
Alex Hart
85e0e74bc6 Add support for OTA emoji download. 2021-05-03 14:02:58 -04:00
Alex Hart
7fa200401c Fix content insets for API30+ devices. 2021-04-30 13:16:23 -03:00
Cody Henthorne
1a452efbb9 Bump version to 5.10.2 2021-04-30 10:37:03 -04:00
Cody Henthorne
eb4bdf1db2 Updated language translations. 2021-04-30 10:35:59 -04:00
Greyson Parrelli
d58f68cb44 Fix issue where we could give storageIds to MMS groups or emails.
Things like force-unread and mute could be applied to MMS groups or
unregistered users (the worst kind being email SMS contacts) that could
result in crashes down the line.

Includes a DB migration to clean up the bad stuff.
2021-04-30 00:19:48 -04:00
Greyson Parrelli
2f30d29351 Ensure we have a storageId for self. 2021-04-29 17:55:34 -04:00
Cody Henthorne
dc6dc192dc Bump version to 5.10.1 2021-04-28 20:56:28 -04:00
Cody Henthorne
751afadebd Fix notification issues introduced when adding lower API versions. 2021-04-28 20:49:36 -04:00
Alex Hart
ac71c02dfa Bump version to 5.10.0 2021-04-28 16:45:09 -03:00
Alex Hart
4567da193e Revert "Temporarily block payments in all regions."
This reverts commit f4ae39dd44.
2021-04-28 16:36:40 -03:00
Cody Henthorne
bd2a1d5574 Add support for lower APIs to new notification system. 2021-04-28 16:36:11 -03:00
Alex Hart
ab44d608d2 Add support for sending and syncing viewed receipts behind a feature flag. 2021-04-28 16:36:11 -03:00
Greyson Parrelli
cdc7f1565e Further simplify storage service syncing. 2021-04-28 16:36:11 -03:00
Jim Gustafson
1493581a4d Update to RingRTC v2.9.6 2021-04-28 16:36:11 -03:00
Greyson Parrelli
4461d6cf7f Rename StorageSyncJobV2 -> StorageSyncJob. 2021-04-28 16:36:11 -03:00
Greyson Parrelli
38e64b1f75 Remove old Storage Service V1 code. 2021-04-28 16:36:10 -03:00
Alex Hart
eb1daf4a20 Pass exception to thrown AssertionError in shortuct icon generation. 2021-04-28 16:36:10 -03:00
Alex Hart
e0c38f7c72 Bump version to 5.9.5 2021-04-28 16:27:31 -03:00
Alex Hart
5638ff4a3a Updated language translations. 2021-04-28 16:26:26 -03:00
Alex Hart
f4ae39dd44 Temporarily block payments in all regions. 2021-04-28 16:16:04 -03:00
Cody Henthorne
d235125138 Fix empty conversation banner view tap to unblur bug. 2021-04-28 14:08:30 -04:00
Cody Henthorne
78d7759197 Update witness file. 2021-04-28 12:09:57 -04:00
Cody Henthorne
eddaad3b05 Bump version to 5.9.4 2021-04-26 16:44:26 -04:00
Cody Henthorne
f2c80c800c Updated language translations. 2021-04-26 16:44:26 -04:00
Greyson Parrelli
a0e787e424 Disable additional storage service validations for internal users. 2021-04-26 16:44:26 -04:00
Cody Henthorne
c9d1fb8533 Fix reaction notification data inconsistencies.
Co-authored-by: Greyson Parrelli <greyson@signal.org>
2021-04-26 16:44:26 -04:00
Greyson Parrelli
006eebb09e Remove storageIds on rows that have no other identifier. 2021-04-26 16:44:26 -04:00
Alex Hart
4aec824bfd Retain minimum width for link previews. 2021-04-26 16:44:26 -04:00
Alex Hart
56f7564ce4 Re-enable internal sharing to SMS users. 2021-04-26 15:45:25 -03:00
Alex Hart
f89daefd43 Hide and show video players as content changes. 2021-04-26 15:03:06 -03:00
Cody Henthorne
8572a2d262 Bump version to 5.9.3 2021-04-24 15:06:34 -04:00
Cody Henthorne
d6e41be4b4 Updated language translations. 2021-04-24 15:05:44 -04:00
Cody Henthorne
5e715ffcce Fix crash when including self in contact search projection. 2021-04-24 14:55:00 -04:00
Cody Henthorne
5100341e60 Fix leaked receiver when call does not connect. 2021-04-24 14:34:16 -04:00
Cody Henthorne
5ca4db6ea5 Fix crashes and issues with blurred avatars.
- Tinting on Android 5/6 caused NPE deep in Android
- Invite group flow can have zero members
- Missed spot to blur avatar in old notification flow
2021-04-24 14:32:02 -04:00
Alex Hart
e02e07ae7a Bump version to 5.9.2 2021-04-23 16:43:45 -03:00
Alex Hart
f3caedc045 Updated language translations. 2021-04-23 16:38:23 -03:00
Cody Henthorne
59c49254e7 Insert temporary warning update message during message request state. 2021-04-23 15:29:59 -04:00
Cody Henthorne
ad81b310e3 Blur avatar photos from unknown senders when in message request state. 2021-04-23 14:42:51 -04:00
Alex Hart
bf124b87fa Fix bad flag parsing on attachment pointers. 2021-04-22 14:46:20 -03:00
Alex Hart
a4868602b5 Add better handling for non-existent response bodies or empty responses from giphy. 2021-04-22 14:33:56 -03:00
Greyson Parrelli
763aeabddd Bump version to 5.9.1 2021-04-21 16:43:59 -04:00
Greyson Parrelli
71fc5af320 Updated language translations. 2021-04-21 16:43:38 -04:00
Greyson Parrelli
d28f0e3544 Relax GIF size restrictions. 2021-04-21 16:43:38 -04:00
Greyson Parrelli
b4d0dde129 Always get the storage manifest for internal users.
This will hopefully help us track down some of the validation issues
when writing local changes.
2021-04-21 16:43:38 -04:00
Alex Hart
281630e751 Add support for inline video playback of gifs in Conversation. 2021-04-21 16:43:38 -04:00
Karalix
32d79ead15 Add unicode wildcards for equivalent latin characters in contact search. 2021-04-21 16:43:38 -04:00
Martin d'Allens
5a91c7e84a Remove seconds from screen lock timeout input for coherence
Configure the TimeDurationPickerDialog to hide seconds.
Seconds were already ignored below 1min. This avoids the user expecting it to work.

Feature regression: after this change, seconds above 1min will also be impossible to input (ex: 1m30s).
But it makes little sense anyway to allow it: they are even less useful for longer durations.

Another possibility to reach a point where eveything is coherent would have been to just remove the Math.max(..., 60) that ignored seconds.

The duration will be displayed as "xx:xx:00" to make it clear that xx:xx represents minutes.

Fixes #10788.
2021-04-21 16:43:38 -04:00
Alex Hart
e2e1200c89 Improvments to MP4 giphy fragment behaviour. 2021-04-21 16:43:38 -04:00
Cody Henthorne
ed1be76606 Scrub domains from debug logs. 2021-04-19 20:28:54 -04:00
Greyson Parrelli
a64de91781 Bump version to 5.9.0 2021-04-19 18:13:05 -04:00
Greyson Parrelli
e802c8b8cc Updated language translations. 2021-04-19 18:11:59 -04:00
Alex Hart
86f2cf0ac4 Remove support for linear gif flow. 2021-04-19 18:11:56 -04:00
Alex Hart
a844a6b6c1 Revert "Temporarily block payments in all regions."
This reverts commit 1169331462.
2021-04-19 18:11:56 -04:00
Alex Hart
c31146e902 Render gifs in gif search as MP4s. 2021-04-19 18:11:56 -04:00
Greyson Parrelli
fcc5db2fe6 Bump version to 5.8.10 2021-04-19 17:58:08 -04:00
Greyson Parrelli
9fdd3ae1be Updated language translations. 2021-04-19 17:56:33 -04:00
Greyson Parrelli
c99509a967 Temporarily disable some storage service validations. 2021-04-19 10:26:32 -04:00
Alex Hart
1169331462 Temporarily block payments in all regions. 2021-04-19 11:20:41 -03:00
Greyson Parrelli
0978822939 Bump version to 5.8.9 2021-04-18 11:27:59 -04:00
Greyson Parrelli
8bf8ecf7fa Reset manifest version to zero after account restore. 2021-04-18 11:27:59 -04:00
Greyson Parrelli
f55db6a5d7 Bump version to 5.8.8 2021-04-18 11:02:30 -04:00
Greyson Parrelli
e82d6cf91d Updated language translations. 2021-04-18 10:45:34 -04:00
Greyson Parrelli
65a1d165ac Fix issue where storage dirty state wasn't cleared. 2021-04-18 10:36:40 -04:00
Greyson Parrelli
e3b27bd39c Bump version to 5.8.7 2021-04-17 13:03:16 -04:00
Greyson Parrelli
07dde01c3b Updated language translations. 2021-04-17 13:03:16 -04:00
Greyson Parrelli
11a2e8686c Simplify local changes written to storage service. 2021-04-17 13:03:11 -04:00
Greyson Parrelli
daeeb17142 Restore storage manifest version during StorageAccountRestoreJob. 2021-04-17 10:31:54 -04:00
Greyson Parrelli
35ab2f6704 Rename 'key' to 'id' where appropriate for storage service. 2021-04-17 10:14:59 -04:00
Greyson Parrelli
9c428f6db7 Bump version to 5.8.6 2021-04-16 19:04:08 -04:00
Greyson Parrelli
a76b067b96 Updated language translations. 2021-04-16 19:03:44 -04:00
Cody Henthorne
a843619c5b Fix various notification display issues and properly support reply. 2021-04-16 18:59:21 -04:00
Greyson Parrelli
5bbc4aea95 Default storage sync feature flag to enabled. 2021-04-16 18:59:21 -04:00
Greyson Parrelli
4676043826 Simplify storage sync write construction.
Instead of trying to keep track of changes as we go and hope that lines
up with reality, now we just write all of our changes and do another
diff at the end to build our insert/delete set. Nice and simple.
2021-04-16 18:58:36 -04:00
Greyson Parrelli
64a841487f Fix storage sync validation error when re-registering with a new number. 2021-04-16 13:22:01 -04:00
Alex Hart
cfd69f2da8 Fix issue where we do not display initials for contacts we have names for. 2021-04-16 10:37:23 -03:00
Alex Hart
e97a14f617 Prevent crash when thumbnail decoder cannot stop, log instead. 2021-04-16 09:29:58 -03:00
Cody Henthorne
b7118b6bd8 Fix view once media in notifications. 2021-04-15 23:36:15 -04:00
Greyson Parrelli
742da4ccb8 Bump version to 5.8.5 2021-04-15 18:13:11 -04:00
Cody Henthorne
97296ca7d7 Fix bugs with preference and lock state changes. 2021-04-15 15:08:27 -04:00
Greyson Parrelli
1486a9ae1b Bump version to 5.8.4 2021-04-15 12:30:39 -04:00
Greyson Parrelli
a847e385cb Updated language translations. 2021-04-15 12:30:13 -04:00
Greyson Parrelli
134284723b Fix storage service crash when matching a local GV2 group without a master key. 2021-04-15 12:07:00 -04:00
Greyson Parrelli
a6eb44ba95 Log when there's local storage inserts. 2021-04-15 11:45:24 -04:00
Cody Henthorne
1457738905 Fix bug with notification privacy and bubbles. 2021-04-15 11:35:42 -04:00
Greyson Parrelli
3dd0a60555 Rotate storage service V2 feature flag. 2021-04-15 11:08:29 -04:00
Greyson Parrelli
1eef18dcd3 Add a failsafe for deleting storageIds. 2021-04-15 11:08:29 -04:00
Greyson Parrelli
c86ee33371 Skip new validations in old storage sync job. 2021-04-15 11:08:29 -04:00
Greyson Parrelli
60690208de Fix storage service crash when matching a local contact without an ID.
It's possible that we could match a local contact that doesn't have a
storageId, which would crash when we tried to make a model from it for
merging. This isn't an impossible case -- it could be that the manifest
has record of a user that is newly registered (or just registered at
some point and never deleted) and so we need to give our local record a
storageId for merging.
2021-04-15 11:08:29 -04:00
Cody Henthorne
69ebee3eeb Fix notification thumbnail being shown when content should be hidden. 2021-04-15 10:24:06 -04:00
Cody Henthorne
4bdb367c19 Fix thumbnail being shown for reaction notifications. 2021-04-15 10:02:53 -04:00
Greyson Parrelli
c817a3097d Bump version to 5.8.3 2021-04-14 18:50:48 -04:00
Greyson Parrelli
b3aa25ad59 Updated language translations. 2021-04-14 18:50:15 -04:00
Greyson Parrelli
cdddfd37d2 Dynamically respond to notificationsV2 feature flag.
We were only reading it once, possibly before the flags were
initialized. This lets us be more responsive to the flag changing within
an application cycle.
2021-04-14 18:50:15 -04:00
Cody Henthorne
2547db2a8e Revert "Prevent changes to conversations while device is unregistered."
This reverts commit ed8edb5aee.
2021-04-14 18:02:57 -04:00
Cody Henthorne
9363f0ebb4 Prevent NPE in safety number change dialog. 2021-04-14 17:28:07 -04:00
Cody Henthorne
8d6d8019fe Fix NPE in ComposeText. 2021-04-14 16:33:01 -04:00
Greyson Parrelli
e27089157d Bump version to 5.8.2 2021-04-14 15:56:00 -04:00
Greyson Parrelli
99fc75eeda Updated language translations. 2021-04-14 15:55:17 -04:00
Cody Henthorne
ec63dd704a Mark previous messages in thread as read when receiving a read sync message. 2021-04-14 15:50:40 -04:00
Cody Henthorne
d46a9f6d1d Rename Messages notification group and channel. 2021-04-14 15:50:40 -04:00
Christian
4b7d87c6bc Display GroupV1MigrationEvent as ActionMessage in conversation MenuState 2021-04-14 15:50:40 -04:00
Cody Henthorne
9c5a0ba7eb Hide message in notification when pending message request approval. 2021-04-14 15:50:40 -04:00
Greyson Parrelli
e461625da4 Add Log.internal() 2021-04-14 15:50:40 -04:00
Cody Henthorne
c393cd655d Fix bad notification state with in-thread reaction notifications. 2021-04-14 15:50:40 -04:00
Cody Henthorne
ed8edb5aee Prevent changes to conversations while device is unregistered. 2021-04-14 15:50:40 -04:00
Cody Henthorne
5df1fa3c65 Move contact join events to separate channel. 2021-04-14 15:50:40 -04:00
Cody Henthorne
e796968d19 Add new notification system. 2021-04-14 15:50:40 -04:00
Greyson Parrelli
c8f17e2ab0 Bump version to 5.8.1 2021-04-13 15:07:54 -04:00
Greyson Parrelli
69870eb229 Updated language translations. 2021-04-13 15:07:21 -04:00
Greyson Parrelli
670aed2074 Move ViewOnceMessageManager and ExpiringMessageManager to AppDependencies. 2021-04-13 14:49:32 -04:00
Greyson Parrelli
0020c7c6dc Clear storageIDs from recipient rows with bad data.
TBH this shouldn't affect external users. I believe this bad data was
only experienced internally a long time ago. But we want to make sure we
don't continue to sync that bad data, so we're just stripping the
storageID's from it.
2021-04-13 12:13:05 -04:00
Greyson Parrelli
20b98122c1 Send an empty group sync when linking devices if no groups exist. 2021-04-13 11:35:42 -04:00
Greyson Parrelli
35c102aa98 Fix issues with StorageSyncV2 bookkeeping.
1. I screwed up the comparators in the record processor. Pretty bad, glad this was caught.
2. Previously I was sort of keeping track of which local-only records were accounted for while I was merging, and then hoping everything worked out in the end. Now I just very directly take some set differences and retrieve the appropriate records, so it's clear that we should never fail certain validations.
3. Rev's the feature flag so we don't turn on something broken.
2021-04-13 11:32:24 -04:00
Cody Henthorne
fb316a22c6 Remove noisy log statement from DirectoryHelper. 2021-04-13 09:34:34 -04:00
Greyson Parrelli
5342af60cb Set archived when merging contact records. 2021-04-13 00:53:23 -04:00
Cody Henthorne
3d01bd7c57 Update gradle config to use 3g of ram. 2021-04-12 16:02:58 -04:00
Greyson Parrelli
1b3ac83876 Bump version to 5.8.0 2021-04-12 12:32:35 -04:00
Greyson Parrelli
4ba3104bf3 Updated language translations. 2021-04-12 12:32:11 -04:00
Cody Henthorne
eda2b87a57 Prevent race condition when closing/reopening message receiver. 2021-04-12 12:25:09 -04:00
Greyson Parrelli
ac0216d916 Only allow emojis as reactions. 2021-04-12 12:25:09 -04:00
Greyson Parrelli
d0986383ad Make some assets smaller. 2021-04-12 12:25:09 -04:00
Alex Hart
ab7f507b03 Add custom lint for AlertDialog.Builder usage. 2021-04-12 12:25:09 -04:00
Greyson Parrelli
e096ba27ce Sync mute status via storage service. 2021-04-12 12:25:09 -04:00
Greyson Parrelli
25ce2a649a Write additional storage validations based on previous manifest. 2021-04-12 12:25:09 -04:00
Greyson Parrelli
0e200b1fb6 Rewrite storage service change processing. 2021-04-12 12:25:09 -04:00
Alex Hart
552b19cbb0 Revert "Temporarily block payments for all regions."
This reverts commit 06ed124057.
2021-04-12 12:25:09 -04:00
Greyson Parrelli
3f0377a840 Bump version to 5.7.6 2021-04-12 11:49:43 -04:00
Greyson Parrelli
d9147874dd Updated language translations. 2021-04-12 11:49:10 -04:00
Alex Hart
06ed124057 Temporarily block payments for all regions. 2021-04-12 09:46:35 -03:00
Alan Evans
68aa97a676 Bump version to 5.7.5 2021-04-09 17:01:16 -03:00
Alan Evans
20bb07e829 Updated language translations. 2021-04-09 16:59:21 -03:00
Alan Evans
53b6cc21b1 Make call to desugared classes from ApplicationContext indirect for API19.
Fixes #11185
2021-04-09 16:48:49 -03:00
Alan Evans
96a80f0ed2 Bump version to 5.7.4 2021-04-09 15:34:19 -03:00
Alan Evans
9ebb150b68 Updated language translations. 2021-04-09 15:19:02 -03:00
Alan Evans
4ae7312c7f Use correct currency position for all locales. 2021-04-09 12:33:16 -03:00
Alan Evans
113393de8f Enforce upper bound on MobileCoin/fiat entry. 2021-04-09 11:17:50 -03:00
Greyson Parrelli
5daa027c10 Disallow SMS/MMS sends to UUID-only recipients. 2021-04-09 10:13:00 -04:00
Alan Evans
7394b4ac27 Bump version to 5.7.3 2021-04-08 19:58:30 -03:00
Alan Evans
1259da01a5 Updated language translations. 2021-04-08 19:27:38 -03:00
Alex Hart
86a8cd29e5 Fix issue preventing user from entering fiat value. 2021-04-08 12:00:41 -03:00
Alex Hart
bfc84d50dd Always show scrollbar on create payment screen if there is scrollable content. 2021-04-08 11:39:31 -03:00
Alan Evans
af060f52e1 Place fee after estimated fiat. 2021-04-08 09:45:29 -03:00
Alex Hart
f87fc1d639 Fix activate getting cut off in activation dialog. 2021-04-08 09:13:09 -03:00
Alan Evans
6ddfbcb945 Bump version to 5.7.2 2021-04-07 19:33:39 -03:00
Alan Evans
d4f11867a8 Updated language translations. 2021-04-07 19:31:28 -03:00
Alan Evans
759f30244a Remove unnecessary sorts. 2021-04-07 18:02:55 -03:00
Greyson Parrelli
fcc49ae7b6 Limit the directory refresh in response to system contact changes.
Previously, we would do a full directory/CDS refresh in response to any
change in system contacts. That can be expensive.

This changes the behavior to look at how many new contacts there after
being notified of a contact change.

- If there aren't any, we just sync names and stuff.
- If we just have a few new contacts, we'll sync just those specifically.
- If we have a lot, we'll do a full sync.
2021-04-07 17:45:51 -03:00
Alex Hart
1aa8e9753d Fix aspect ratio with info icon on API 21 devices. 2021-04-07 17:45:51 -03:00
Alex Hart
f400504898 Fix issue where formatted mnemonic with newlines would fail even though it looks correct. 2021-04-07 15:32:38 -03:00
Alex Hart
41e6097ac5 Fix issue resulting in crash when re-entering Recovery Phrase screen. 2021-04-07 15:10:50 -03:00
Alex Hart
8e4b08b493 Do not modify amount immediately after toggle. 2021-04-07 14:59:07 -03:00
Alex Hart
df948179d8 Wrap add_money layout in ScrollView. 2021-04-07 14:00:22 -03:00
Greyson Parrelli
7b3aa43217 Fix issue with GV1 deprecation and rotate feature flag. 2021-04-07 10:29:36 -04:00
Alex Hart
e42fe5349b Add proper payments icon in AttachmentKeyboard 2021-04-07 09:43:45 -03:00
Alan Evans
1f578ebd2c Bump version to 5.7.1 2021-04-06 20:21:56 -03:00
Alan Evans
b80875773f Updated language translations. 2021-04-06 20:17:22 -03:00
Alan Evans
3caebb8613 Enable Java8 core library desugaring. 2021-04-06 19:54:18 -03:00
Alan Evans
24ac705fe8 Bump version to 5.7.0 2021-04-06 17:09:00 -03:00
Alan Evans
57acdd4b21 Updated language translations. 2021-04-06 17:07:19 -03:00
Android Team
fddba2906a Payments.
Co-authored-by: Alan Evans <alan@signal.org>
Co-authored-by: Alex Hart <alex@signal.org>
Co-authored-by: Cody Henthorne <cody@signal.org>
2021-04-06 17:07:19 -03:00
Android Team
c42023855b Service support for Payments.
Co-authored-by: Alan Evans <alan@signal.org>
Co-authored-by: Alex Hart <alex@signal.org>
Co-authored-by: Cody Henthorne <cody@signal.org>
2021-04-06 15:27:23 -03:00
Hugo Kiehl
dd38dd9cae Fixing notification image preview. 2021-04-06 15:27:23 -03:00
Cody Henthorne
5f0341cd53 Fix calling PIP stuck when calls ends on device lock screen.
Fixes #11090
2021-04-06 11:34:20 -04:00
Cody Henthorne
e3d3129e6d Bump version to 5.6.3 2021-04-05 16:11:03 -04:00
Cody Henthorne
ed8b6c6bc9 Updated language translations. 2021-04-05 16:09:10 -04:00
Cody Henthorne
2218fc0d41 Start call service as foreground for notification actions. 2021-04-05 16:09:04 -04:00
Greyson Parrelli
b8cbcfe986 Prevent crash when reading cursor in DirectoryHelper.
The cause of the crash is very unclear. Our best guess at present is
that the cursor is unable to fit a single row within the 2mb allocation
window, and therefore can't read anything. In this case, the best we can
do is catch the exception and develop some future fallback. Logging the
exception will also mean that maybe we'll be able to get the actual
exception message.
2021-04-05 15:52:30 -04:00
Cody Henthorne
dbc5f5bfcc Fix bluetooth ringing for outgoing calls. 2021-04-05 15:44:58 -04:00
Greyson Parrelli
33cb02b9e4 Disallow GV1 group edits when GV1 is disabled. 2021-04-05 15:25:41 -04:00
Cody Henthorne
8783d150e8 Fix potential OOM when updating shortcuts. 2021-04-05 14:21:32 -04:00
Cody Henthorne
449ea9375e Fix old device locked dialog crash. 2021-04-05 14:10:54 -04:00
Cody Henthorne
d5a73a3380 Bump version to 5.6.2 2021-04-02 15:29:01 -04:00
Cody Henthorne
173dd180d9 Updated language translations. 2021-04-02 15:29:01 -04:00
Cody Henthorne
f332cbf1bc Fix state exception by always starting call service in foreground. 2021-04-02 15:29:01 -04:00
Cody Henthorne
c4d317b33e Fix crash when bluetooth is unavailable for calling. 2021-04-02 13:09:06 -04:00
Cody Henthorne
9c59d6a69b Use english help categories for email filter. 2021-04-02 12:42:10 -04:00
Alex Hart
61bdd4e027 Bump version to 5.6.1 2021-04-01 16:58:59 -03:00
Alex Hart
ab2efe78b1 Updated language translations. 2021-04-01 16:58:59 -03:00
Cody Henthorne
c3af3e4740 Fix rounded corners for messages with a quote and small link preview. 2021-04-01 16:58:59 -03:00
Alex Hart
6418eac658 Prevent crash in forwarding captioned audio message to multiple recipients. 2021-04-01 10:33:40 -03:00
Alex Hart
d74e9f7410 Bump version to 5.6.0 2021-03-31 16:02:32 -03:00
Alex Hart
569c83d90e Updated language translations. 2021-03-31 16:02:32 -03:00
Cody Henthorne
1dc3cf7824 Move calling management out of service. 2021-03-31 16:02:32 -03:00
Cody Henthorne
d8dead82b6 Improve Call Notification UX when things don't go as planned. 2021-03-31 16:02:32 -03:00
Greyson Parrelli
b053fbc4a7 Use Log.tag where appropriate. 2021-03-31 16:02:32 -03:00
Cody Henthorne
2144dc3b67 Fix call ringtone not playing on some custom ROMs and Samsung Android 11 devices. 2021-03-31 16:02:32 -03:00
Alex Hart
243b4b9414 Refactor ContactsCursorLoader to implement factory pattern.
Utilization of the factory pattern will enable us to more easily change what contacts we present to the user for a specific screen in the future instead of continuing to modify and potentially introduce bugs to this screen.
2021-03-31 16:02:32 -03:00
Greyson Parrelli
e068fde8f2 Improve efficiency of bulk receipt processing.
If there were N receipts for a single thread, we were previously
updating that thread N times.

This change bundles updates together so we will only update each thread
once after all receipts in a bundle are processed.
2021-03-31 16:02:32 -03:00
Greyson Parrelli
3162f04937 Update mute options.
2 hours -> 8 hours
1 year -> Always

These options should make way more sense, and existing mute settings
will continue to be respected.
2021-03-31 16:02:32 -03:00
Greyson Parrelli
58a32c11ec Disable inline message processing for internal users. 2021-03-31 16:02:32 -03:00
Fumiaki Yoshimatsu
f06817f00d Account for grapheme cluster when trimming to fit a specific length.
Fixes #10076
2021-03-31 16:02:32 -03:00
Greyson Parrelli
da4be5c1cf Allow recipient cache to refresh inline while in a transaction. 2021-03-31 16:02:32 -03:00
Cody Henthorne
a59f5d953a Fix bug during registration when self exists already without an e164. 2021-03-31 16:02:32 -03:00
Cody Henthorne
815a988587 Prevent crash when trying to save conversation viewing position. 2021-03-31 16:02:32 -03:00
Cody Henthorne
b65d9ffaed Fix KitKat crash when showing cell service warning during registration. 2021-03-31 16:02:32 -03:00
Cody Henthorne
0fc73c3a6f Remove Group Calling feature flag. 2021-03-31 16:02:31 -03:00
Cody Henthorne
57fdc1b223 Include pin reminder preference in settings backup. 2021-03-23 10:34:56 -04:00
Fumiaki Yoshimatsu
22d5fc6cba Fix shape of message bubbles in RTL langauges.
Fixes #9894
2021-03-23 08:58:33 -04:00
Sgn-32
c36f9646f9 Fix bugs with RTL languages and showing/entering backup code.
Fixes #10193
Fixes #10195
2021-03-22 15:52:05 -04:00
Fumiaki Yoshimatsu
45e11f6291 Fix continue arrow icon for RTL.
Fixes #10914.
2021-03-22 15:50:43 -04:00
Fumiaki Yoshimatsu
2893c3dc0e Fix RTL bug in lockscreen timeout dialog.
Fixes #9892
2021-03-22 15:50:09 -04:00
Cody Henthorne
116022b01d Install gradle as part of docker image build. 2021-03-22 15:49:46 -04:00
Chris Eager
09cba8774d Add support for verification codes without an internal hyphen 2021-03-19 14:38:30 -05:00
Greyson Parrelli
41129f7c50 Add tracing to a few critical jobs. 2021-03-19 09:28:04 -04:00
Greyson Parrelli
44d014c445 Bump version to 5.5.5 2021-03-18 11:24:29 -04:00
Greyson Parrelli
51e086b20e Updated language translations. 2021-03-18 11:24:09 -04:00
Greyson Parrelli
d71a5c99f4 Fix crash in migration job retries. 2021-03-18 11:18:08 -04:00
Greyson Parrelli
fb0243a029 Fix issue with websocket connection after reregistering.
Big shoutout to @jonahbeckford for the investigation here. Thanks!

Fixes #10939
Fixes #11095
2021-03-18 11:07:34 -04:00
Greyson Parrelli
713441d9cb Bump version to 5.5.4 2021-03-17 19:11:15 -04:00
Greyson Parrelli
451f0fd12b Updated language translations. 2021-03-17 19:10:53 -04:00
Cody Henthorne
5a84fa5a80 Fix device transfer stall if screen is locked during transfer. 2021-03-17 16:46:25 -04:00
1961 changed files with 112870 additions and 33356 deletions

4
.editorconfig Normal file
View File

@@ -0,0 +1,4 @@
root = true
[*.kt]
indent_size = 2

View File

@@ -24,6 +24,9 @@ jobs:
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
- name: Remove Android S
run: $ANDROID_HOME/tools/bin/sdkmanager --uninstall "platforms;android-S"
- name: Build with Gradle
run: ./gradlew qa

193
.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,193 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="RIGHT_MARGIN" value="240" />
<option name="FORMATTER_TAGS_ENABLED" value="true" />
<option name="SOFT_MARGINS" value="160" />
<JavaCodeStyleSettings>
<option name="GENERATE_FINAL_LOCALS" value="true" />
<option name="DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION" value="true" />
<option name="ALIGN_MULTILINE_ANNOTATION_PARAMETERS" value="true" />
<option name="ALIGN_MULTILINE_TEXT_BLOCKS" value="true" />
</JavaCodeStyleSettings>
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value />
</option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="JAVA">
<option name="BRACE_STYLE" value="5" />
<option name="CLASS_BRACE_STYLE" value="5" />
<option name="METHOD_BRACE_STYLE" value="5" />
<option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" />
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
<option name="ALIGN_MULTILINE_ASSIGNMENT" value="true" />
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
<option name="ALIGN_MULTILINE_THROWS_LIST" value="true" />
<option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" />
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
<option name="ALIGN_GROUP_FIELD_DECLARATIONS" value="true" />
<option name="ALIGN_CONSECUTIVE_VARIABLE_DECLARATIONS" value="true" />
<option name="ALIGN_CONSECUTIVE_ASSIGNMENTS" value="true" />
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" />
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
<option name="WRAP_FIRST_METHOD_IN_CALL_CHAIN" value="true" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
<option name="KEEP_SIMPLE_BLOCKS_IN_ONE_LINE" value="true" />
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
<option name="KEEP_SIMPLE_LAMBDAS_IN_ONE_LINE" value="true" />
<option name="KEEP_MULTIPLE_EXPRESSIONS_IN_ONE_LINE" value="true" />
<option name="METHOD_ANNOTATION_WRAP" value="0" />
<option name="CLASS_ANNOTATION_WRAP" value="0" />
<option name="FIELD_ANNOTATION_WRAP" value="0" />
<option name="ENUM_CONSTANTS_WRAP" value="5" />
<option name="WRAP_ON_TYPING" value="0" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
<arrangement>
<groups>
<group>
<type>GETTERS_AND_SETTERS</type>
<order>KEEP</order>
</group>
<group>
<type>OVERRIDDEN_METHODS</type>
<order>KEEP</order>
</group>
<group>
<type>DEPENDENT_METHODS</type>
<order>BREADTH_FIRST</order>
</group>
</groups>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="XML">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@@ -3,11 +3,16 @@ import org.signal.signing.ApkSignerUtil
import java.security.MessageDigest
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'com.google.protobuf'
apply plugin: 'androidx.navigation.safeargs'
apply plugin: 'witness'
apply plugin: 'org.jlleitschuh.gradle.ktlint'
apply from: 'translations.gradle'
apply from: 'witness-verifications.gradle'
apply plugin: 'org.jetbrains.kotlin.android'
apply plugin: 'app.cash.exhaustive'
repositories {
maven {
@@ -22,12 +27,6 @@ repositories {
includeGroupByRegex "com\\.github\\.dmytrodanylyk\\.circular-progress-button\\.*"
}
}
maven {
url "https://raw.github.com/signalapp/maven/master/sqlcipher/release/"
content {
includeGroupByRegex "org\\.signal.*"
}
}
maven { // textdrawable
url 'https://dl.bintray.com/amulyakhare/maven'
content {
@@ -38,6 +37,9 @@ repositories {
mavenCentral()
jcenter()
mavenLocal()
maven {
url "https://dl.cloudsmith.io/qxAgwaeEE1vN8aLU/mobilecoin/mobilecoin/maven/"
}
}
protobuf {
@@ -55,8 +57,8 @@ protobuf {
}
}
def canonicalVersionCode = 802
def canonicalVersionName = "5.5.3"
def canonicalVersionCode = 879
def canonicalVersionName = "5.17.1"
def postFixSize = 100
def abiPostFix = ['universal' : 0,
@@ -74,6 +76,11 @@ android {
flavorDimensions 'distribution', 'environment'
useLibrary 'org.apache.http.legacy'
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs = ["-Xallow-result-return-type"]
}
dexOptions {
javaMaxHeapSize "4g"
}
@@ -111,6 +118,8 @@ android {
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_SFU_INTERNAL_NAMES", "new String[]{\"Test\", \"Staging\"}"
buildConfigField "String[]", "SIGNAL_SFU_INTERNAL_URLS", "new String[]{\"https://sfu.test.voip.signal.org\", \"https://sfu.staging.voip.signal.org\"}"
buildConfigField "String", "CONTENT_PROXY_HOST", "\"contentproxy.signal.org\""
buildConfigField "int", "CONTENT_PROXY_PORT", "443"
buildConfigField "String", "SIGNAL_AGENT", "\"OWA\""
@@ -123,6 +132,14 @@ android {
buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X0=\""
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 "int[]", "MOBILE_COIN_REGIONS", "new int[]{44}"
buildConfigField "String", "GIPHY_API_KEY", "\"3o6ZsYH6U6Eri53TXy\""
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\""
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
@@ -143,6 +160,7 @@ android {
}
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JAVA_VERSION
targetCompatibility JAVA_VERSION
}
@@ -187,28 +205,34 @@ android {
'proguard/proguard.cfg'
testProguardFiles 'proguard/proguard-automation.pro',
'proguard/proguard.cfg'
buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Debug\""
}
flipper {
initWith debug
isDefault false
minifyEnabled false
matchingFallbacks = ['debug']
buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Flipper\""
}
release {
minifyEnabled true
proguardFiles = buildTypes.debug.proguardFiles
buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Release\""
}
perf {
initWith debug
isDefault false
debuggable false
matchingFallbacks = ['debug']
buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Perf\""
}
mock {
initWith debug
isDefault false
minifyEnabled false
matchingFallbacks = ['debug']
buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Mock\""
}
}
@@ -219,6 +243,7 @@ android {
ext.websiteUpdateUrl = "null"
buildConfigField "boolean", "PLAY_STORE_DISABLED", "false"
buildConfigField "String", "NOPLAY_UPDATE_URL", "$ext.websiteUpdateUrl"
buildConfigField "String", "BUILD_DISTRIBUTION_TYPE", "\"play\""
}
website {
@@ -226,6 +251,7 @@ android {
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\""
}
internal {
@@ -233,6 +259,16 @@ android {
ext.websiteUpdateUrl = "null"
buildConfigField "boolean", "PLAY_STORE_DISABLED", "false"
buildConfigField "String", "NOPLAY_UPDATE_URL", "$ext.websiteUpdateUrl"
buildConfigField "String", "BUILD_DISTRIBUTION_TYPE", "\"internal\""
}
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", "\"internal\""
}
study {
@@ -242,12 +278,16 @@ android {
ext.websiteUpdateUrl = "null"
buildConfigField "boolean", "PLAY_STORE_DISABLED", "false"
buildConfigField "String", "NOPLAY_UPDATE_URL", "$ext.websiteUpdateUrl"
buildConfigField "String", "BUILD_DISTRIBUTION_TYPE", "\"study\""
}
prod {
dimension 'environment'
isDefault true
buildConfigField "String", "MOBILE_COIN_ENVIRONMENT", "\"mainnet\""
buildConfigField "String", "BUILD_ENVIRONMENT_TYPE", "\"Prod\""
}
staging {
@@ -268,18 +308,30 @@ android {
buildConfigField "KbsEnclave[]", "KBS_FALLBACKS", "new KbsEnclave[0]"
buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx\""
buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdls=\""
buildConfigField "String", "MOBILE_COIN_ENVIRONMENT", "\"testnet\""
buildConfigField "String", "RECAPTCHA_PROOF_URL", "\"https://signalcaptchas.org/staging/challenge/generate.html\""
buildConfigField "String", "BUILD_ENVIRONMENT_TYPE", "\"Staging\""
}
}
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
output.outputFileName = output.outputFileName.replace(".apk", "-${variant.versionName}.apk")
def abiName = output.getFilter("ABI") ?: 'universal'
def postFix = abiPostFix.get(abiName, 0)
if (output.baseName.contains('nightly')) {
output.versionCodeOverride = canonicalVersionCode * postFixSize + 5
def tag = getCurrentGitTag()
if (tag != null && tag.length() > 0) {
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")
if (postFix >= postFixSize) throw new AssertionError("postFix is too large")
output.versionCodeOverride = canonicalVersionCode * postFixSize + postFix
output.versionCodeOverride = canonicalVersionCode * postFixSize + postFix
}
}
}
@@ -292,6 +344,12 @@ android {
variant.setIgnore(true)
} else if (distribution != 'study' && buildType == 'mock') {
variant.setIgnore(true)
} else if (distribution == 'internal' && buildType != 'flipper' && buildType != 'perf' && buildType != 'release') {
variant.setIgnore(true)
} else if (distribution == 'nightly' && environment != 'prod') {
variant.setIgnore(true)
} else if (distribution == 'nightly' && buildType != 'flipper' && buildType != 'perf' && buildType != 'release') {
variant.setIgnore(true)
}
}
@@ -309,13 +367,17 @@ android {
}
dependencies {
implementation 'androidx.core:core-ktx:1.5.0'
implementation 'androidx.fragment:fragment-ktx:1.2.5'
lintChecks project(':lintchecks')
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation ('androidx.appcompat:appcompat:1.2.0') {
force = true
}
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.legacy:legacy-support-v13:1.0.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.preference:preference:1.0.0'
@@ -336,8 +398,9 @@ dependencies {
implementation "androidx.concurrent:concurrent-futures:1.0.0"
implementation "androidx.autofill:autofill:1.0.0"
implementation "androidx.biometric:biometric:1.1.0"
implementation "androidx.sharetarget:sharetarget:1.1.0"
implementation ('com.google.firebase:firebase-messaging:20.2.0') {
implementation ('com.google.firebase:firebase-messaging:22.0.0') {
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'
@@ -360,11 +423,16 @@ dependencies {
implementation project(':device-transfer')
implementation 'org.signal:zkgroup-android:0.7.0'
implementation 'org.whispersystems:signal-client-android:0.1.7'
implementation 'org.whispersystems:signal-client-android:0.8.3'
implementation 'com.google.protobuf:protobuf-javalite:3.10.0'
implementation('com.mobilecoin:android-sdk:1.1.0') {
exclude group: 'com.google.protobuf'
}
implementation 'org.signal:argon2:13.1@aar'
implementation 'org.signal:ringrtc-android:2.9.4'
implementation 'org.signal:ringrtc-android:2.10.6'
implementation "me.leolin:ShortcutBadger:1.1.22"
implementation 'se.emilsjolander:stickylistheaders:2.7.0'
@@ -372,8 +440,8 @@ dependencies {
implementation 'org.apache.httpcomponents:httpclient-android:4.3.5'
implementation 'com.github.chrisbanes:PhotoView:2.1.3'
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
annotationProcessor 'androidx.annotation:annotation:1.1.0'
kapt 'com.github.bumptech.glide:compiler:4.11.0'
kapt 'androidx.annotation:annotation:1.1.0'
implementation 'com.makeramen:roundedimageview:2.1.0'
implementation 'com.pnikosis:materialish-progress:1.5'
implementation 'org.greenrobot:eventbus:3.0.0'
@@ -406,15 +474,18 @@ dependencies {
implementation 'com.codewaves.stickyheadergrid:stickyheadergrid:0.9.4'
implementation 'com.github.dmytrodanylyk.circular-progress-button:library:1.1.3-S2'
implementation 'org.signal:android-database-sqlcipher:3.5.9-S3'
implementation "net.zetetic:android-database-sqlcipher:4.4.3"
implementation "androidx.sqlite:sqlite:2.1.0"
implementation ('com.googlecode.ez-vcard:ez-vcard:0.9.11') {
exclude group: 'com.fasterxml.jackson.core'
exclude group: 'org.freemarker'
}
implementation 'dnsjava:dnsjava:2.1.9'
flipperImplementation 'com.facebook.flipper:flipper:0.32.2'
flipperImplementation 'com.facebook.soloader:soloader:0.8.2'
flipperImplementation 'com.facebook.flipper:flipper:0.91.0'
flipperImplementation 'com.facebook.soloader:soloader:0.10.1'
testImplementation 'junit:junit:4.12'
testImplementation 'org.assertj:assertj-core:3.11.1'
@@ -431,8 +502,16 @@ dependencies {
testImplementation 'org.robolectric:shadows-multidex:4.4'
testImplementation 'org.hamcrest:hamcrest:2.2'
testImplementation(testFixtures(project(":libsignal-service")))
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.12.0"
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
implementation 'io.reactivex.rxjava3:rxkotlin:3.0.1'
}
dependencyVerification {
@@ -501,6 +580,10 @@ task signProductionWebsiteRelease {
}
def getLastCommitTimestamp() {
if (!(new File('.git').exists())) {
return System.currentTimeMillis().toString()
}
new ByteArrayOutputStream().withStream { os ->
def result = exec {
executable = 'git'
@@ -513,6 +596,10 @@ def getLastCommitTimestamp() {
}
def getGitHash() {
if (!(new File('.git').exists())) {
return "abcd1234"
}
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-parse', '--short', 'HEAD'
@@ -521,6 +608,26 @@ def getGitHash() {
return stdout.toString().trim()
}
def getCurrentGitTag() {
if (!(new File('.git').exists())) {
return ''
}
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'tag', '--points-at', 'HEAD'
standardOutput = stdout
}
def output = stdout.toString().trim()
if (output != null && output.size() > 0) {
return output.split('\n')[0];
} else {
return null
}
}
tasks.withType(Test) {
testLogging {
events "failed"
@@ -541,3 +648,9 @@ def loadKeystoreProperties(filename) {
return null;
}
}
def getDateSuffix() {
def date = new Date()
def formattedDate = date.format('yyyy-MM-dd-HH:mm')
return formattedDate
}

View File

@@ -31,6 +31,8 @@
<issue id="LogNotAppSignal" severity="error" />
<issue id="LogTagInlined" severity="error" />
<issue id="AlertDialogBuilderUsage" severity="warning" />
<issue id="RestrictedApi" severity="error">
<ignore path="*/org/thoughtcrime/securesms/mediasend/camerax/VideoCapture.java" />
<ignore path="*/org/thoughtcrime/securesms/mediasend/camerax/CameraXModule.java" />

View File

@@ -17,6 +17,7 @@ import net.sqlcipher.database.SQLiteStatement;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.util.Hex;
import java.lang.reflect.Field;
import java.util.ArrayList;
@@ -237,7 +238,12 @@ public class FlipperSqlCipherAdapter extends DatabaseDriver<FlipperSqlCipherAdap
case Cursor.FIELD_TYPE_FLOAT:
return cursor.getDouble(column);
case Cursor.FIELD_TYPE_BLOB:
return cursor.getBlob(column);
byte[] blob = cursor.getBlob(column);
String bytes = blob != null ? "(blob) " + Hex.toStringCondensed(Arrays.copyOf(blob, Math.min(blob.length, 32))) : null;
if (bytes != null && bytes.length() == 32 && blob.length > 32) {
bytes += "...";
}
return bytes;
case Cursor.FIELD_TYPE_STRING:
default:
return cursor.getString(column);

View File

@@ -153,6 +153,14 @@
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="tsdevice"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="sgnl"
android:host="linkdevice"/>
</intent-filter>
</activity>
<activity android:name=".preferences.MmsPreferencesActivity"
@@ -190,7 +198,7 @@
<meta-data
android:name="android.service.chooser.chooser_target_service"
android:value=".service.DirectShareService" />
android:value="androidx.sharetarget.ChooserTargetServiceCompat" />
</activity>
@@ -300,13 +308,10 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"
android:theme="@style/Theme.Signal.DayNight.NoActionBar" />
<activity android:name=".groups.ui.managegroup.ManageGroupActivity"
android:windowSoftInputMode="stateAlwaysHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".recipients.ui.managerecipient.ManageRecipientActivity"
android:windowSoftInputMode="stateAlwaysHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".recipients.ui.disappearingmessages.RecipientDisappearingMessagesActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:theme="@style/Signal.DayNight.NoActionBar"
android:windowSoftInputMode="adjustResize"/>
<activity android:name=".DatabaseMigrationActivity"
android:theme="@style/NoAnimation.Theme.AppCompat.Light.DarkActionBar"
@@ -314,7 +319,7 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".migrations.ApplicationMigrationActivity"
android:theme="@style/NoAnimation.Theme.AppCompat.Light.DarkActionBar"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
@@ -358,8 +363,9 @@
<activity android:name=".VerifyIdentityActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ApplicationPreferencesActivity"
<activity android:name=".components.settings.app.AppSettingsActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:theme="@style/Signal.DayNight.NoActionBar"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -367,6 +373,13 @@
</intent-filter>
</activity>
<activity android:name=".components.settings.conversation.ConversationSettingsActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:theme="@style/Signal.DayNight.ConversationSettings"
android:windowSoftInputMode="stateAlwaysHidden">
</activity>
<activity android:name=".wallpaper.ChatWallpaperActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:windowSoftInputMode="stateAlwaysHidden">
@@ -486,7 +499,7 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".scribbles.ImageEditorStickerSelectActivity"
android:theme="@style/TextSecure.DarkTheme"
android:theme="@style/Signal.DayNight.NoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".profiles.edit.EditProfileActivity"
@@ -497,6 +510,10 @@
android:theme="@style/TextSecure.LightTheme"
android:windowSoftInputMode="stateVisible|adjustResize" />
<activity android:name=".payments.preferences.PaymentsActivity"
android:theme="@style/TextSecure.LightRegistrationTheme"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".lock.v2.CreateKbsPinActivity"
android:theme="@style/TextSecure.LightRegistrationTheme"
android:windowSoftInputMode="adjustResize"
@@ -572,6 +589,10 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"
android:launchMode="singleTask" />
<activity android:name=".ratelimit.RecaptchaProofActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode" />
<activity android:name=".wallpaper.crop.WallpaperImageSelectionActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:theme="@style/TextSecure.FullScreenMedia" />
@@ -581,7 +602,11 @@
android:screenOrientation="portrait"
android:theme="@style/Theme.Signal.WallpaperCropper" />
<service android:enabled="true" android:name=".service.WebRtcCallService"/>
<activity android:name=".reactions.edit.EditReactionsActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<service android:enabled="true" android:name=".service.webrtc.WebRtcCallService"/>
<service android:enabled="true" android:name=".service.ApplicationMigrationService"/>
<service android:enabled="true" android:exported="false" android:name=".service.KeyCachingService"/>
<service android:enabled="true" android:name=".messages.IncomingMessageObserver$ForegroundService"/>
@@ -626,13 +651,6 @@
<meta-data android:name="android.provider.CONTACTS_STRUCTURE" android:resource="@xml/contactsformat" />
</service>
<service android:name=".service.DirectShareService"
android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
<intent-filter>
<action android:name="android.service.chooser.ChooserTargetService" />
</intent-filter>
</service>
<service android:name=".service.GenericForegroundService"/>
<service android:name=".gcm.FcmFetchService" />
@@ -692,26 +710,16 @@
</intent-filter>
</receiver>
<receiver android:name=".notifications.AndroidAutoHeardReceiver"
android:exported="false">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.notifications.ANDROID_AUTO_HEARD"/>
</intent-filter>
</receiver>
<receiver android:name=".notifications.AndroidAutoReplyReceiver"
android:exported="false">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.notifications.ANDROID_AUTO_REPLY"/>
</intent-filter>
</receiver>
<receiver android:name=".service.ExpirationListener" />
<receiver android:name=".revealable.ViewOnceMessageManager$ViewOnceAlarm" />
<receiver android:name=".service.PendingRetryReceiptManager$PendingRetryReceiptAlarm" />
<receiver android:name=".service.TrimThreadsByDateManager$TrimThreadsByDateAlarm" />
<receiver android:name=".payments.backup.phrase.ClearClipboardAlarmReceiver" />
<provider android:name=".providers.PartProvider"
android:grantUriPermissions="true"
android:exported="false"
@@ -740,10 +748,6 @@
android:authorities="${applicationId}.database.conversation"
android:exported="false" />
<provider android:name=".database.DatabaseContentProviders$ConversationList"
android:authorities="${applicationId}.database.conversationlist"
android:exported="false" />
<provider android:name=".database.DatabaseContentProviders$Attachment"
android:authorities="${applicationId}.database.attachment"
android:exported="false" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 421 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 365 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 KiB

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 664 KiB

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 608 KiB

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 552 KiB

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 631 KiB

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 653 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 652 KiB

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 531 KiB

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 685 KiB

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 603 KiB

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 391 KiB

After

Width:  |  Height:  |  Size: 93 KiB

File diff suppressed because one or more lines are too long

View File

@@ -65,6 +65,8 @@ import androidx.lifecycle.LiveData;
import com.google.common.util.concurrent.ListenableFuture;
import org.signal.core.util.logging.Log;
import java.io.File;
import java.util.concurrent.Executor;
@@ -82,7 +84,7 @@ import java.util.concurrent.Executor;
@RequiresApi(21)
@SuppressLint("RestrictedApi")
public final class SignalCameraView extends FrameLayout {
static final String TAG = SignalCameraView.class.getSimpleName();
static final String TAG = Log.tag(SignalCameraView.class);
static final int INDEFINITE_VIDEO_DURATION = -1;
static final int INDEFINITE_VIDEO_SIZE = -1;

View File

@@ -512,7 +512,12 @@ final class SignalCameraXModule {
return rotationDegrees;
}
@SuppressLint("UnsafeExperimentalUsageError")
public void invalidateView() {
if (mPreview != null) {
mPreview.setTargetRotation(getDisplaySurfaceRotation()); // Fixes issue #10940 (rotation not updated on phones using "Legacy API")
}
updateViewInfo();
}

View File

@@ -32,7 +32,7 @@ import java.util.List;
*/
public class APNGDecoder extends FrameSeqDecoder<APNGReader, APNGWriter> {
private static final String TAG = APNGDecoder.class.getSimpleName();
private static final String TAG = Log.tag(APNGDecoder.class);
private APNGWriter apngWriter;
private int mLoopCount;

View File

@@ -35,7 +35,7 @@ import java.util.Set;
* @CreateDate: 2019/3/27
*/
public abstract class FrameAnimationDrawable<Decoder extends FrameSeqDecoder> extends Drawable implements Animatable2Compat, FrameSeqDecoder.RenderListener {
private static final String TAG = FrameAnimationDrawable.class.getSimpleName();
private static final String TAG = Log.tag(FrameAnimationDrawable.class);
private final Paint paint = new Paint();
private final Decoder frameSeqDecoder;
private DrawFilter drawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);

View File

@@ -39,7 +39,7 @@ import java.util.concurrent.locks.LockSupport;
* @CreateDate: 2019/3/27
*/
public abstract class FrameSeqDecoder<R extends Reader, W extends Writer> {
private static final String TAG = FrameSeqDecoder.class.getSimpleName();
private static final String TAG = Log.tag(FrameSeqDecoder.class);
private final int taskId;
private final Loader mLoader;

View File

@@ -17,6 +17,6 @@ public final class AppCapabilities {
* asking if the user has set a Signal PIN or not.
*/
public static AccountAttributes.Capabilities getCapabilities(boolean storageCapable) {
return new AccountAttributes.Capabilities(UUID_CAPABLE, GV2_CAPABLE, storageCapable, GV1_MIGRATION);
return new AccountAttributes.Capabilities(UUID_CAPABLE, GV2_CAPABLE, storageCapable, GV1_MIGRATION, FeatureFlags.senderKey());
}
}

View File

@@ -8,6 +8,7 @@ import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.insights.InsightsOptOut;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobs.EmojiSearchIndexDownloadJob;
import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.migrations.ApplicationMigrations;
@@ -60,6 +61,7 @@ public final class AppInitialization {
ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.DAY_BY_DAY.getPackId(), BlessedPacks.DAY_BY_DAY.getPackKey(), false));
ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forReference(BlessedPacks.SWOON_HANDS.getPackId(), BlessedPacks.SWOON_HANDS.getPackKey()));
ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forReference(BlessedPacks.SWOON_FACES.getPackId(), BlessedPacks.SWOON_FACES.getPackKey()));
EmojiSearchIndexDownloadJob.scheduleImmediately();
}
/**

View File

@@ -27,6 +27,8 @@ import androidx.multidex.MultiDexApplication;
import com.google.android.gms.security.ProviderInstaller;
import net.sqlcipher.database.SQLiteDatabase;
import org.conscrypt.Conscrypt;
import org.signal.aesgcmprovider.AesGcmProvider;
import org.signal.core.util.concurrent.SignalExecutors;
@@ -37,11 +39,15 @@ import org.signal.core.util.tracing.Tracer;
import org.signal.glide.SignalGlideCodecs;
import org.signal.ringrtc.CallManager;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.SqlCipherLibraryLoader;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencyProvider;
import org.thoughtcrime.securesms.emoji.EmojiSource;
import org.thoughtcrime.securesms.gcm.FcmJobService;
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
import org.thoughtcrime.securesms.jobs.DownloadLatestEmojiDataJob;
import org.thoughtcrime.securesms.jobs.EmojiSearchIndexDownloadJob;
import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
import org.thoughtcrime.securesms.jobs.GroupV1MigrationJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
@@ -56,11 +62,10 @@ import org.thoughtcrime.securesms.migrations.ApplicationMigrations;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.ratelimit.RateLimitUtil;
import org.thoughtcrime.securesms.registration.RegistrationUtil;
import org.thoughtcrime.securesms.revealable.ViewOnceMessageManager;
import org.thoughtcrime.securesms.ringrtc.RingRtcLogger;
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.service.LocalBackupListener;
import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
@@ -69,6 +74,7 @@ import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.AppStartup;
import org.thoughtcrime.securesms.util.ByteUnit;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.SignalUncaughtExceptionHandler;
@@ -81,10 +87,11 @@ import org.webrtc.voiceengine.WebRtcAudioUtils;
import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider;
import java.security.Security;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import io.reactivex.rxjava3.plugins.RxJavaPlugins;
import io.reactivex.rxjava3.schedulers.Schedulers;
/**
* Will be called once when the TextSecure process is created.
*
@@ -95,11 +102,9 @@ import java.util.concurrent.TimeUnit;
*/
public class ApplicationContext extends MultiDexApplication implements AppForegroundObserver.Listener {
private static final String TAG = ApplicationContext.class.getSimpleName();
private static final String TAG = Log.tag(ApplicationContext.class);
private ExpiringMessageManager expiringMessageManager;
private ViewOnceMessageManager viewOnceMessageManager;
private PersistentLogger persistentLogger;
private PersistentLogger persistentLogger;
public static ApplicationContext getInstance(Context context) {
return (ApplicationContext)context.getApplicationContext();
@@ -120,11 +125,15 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
AppStartup.getInstance().addBlocking("security-provider", this::initializeSecurityProvider)
.addBlocking("logging", () -> {
initializeLogging();
Log.i(TAG, "onCreate()");
initializeLogging();
Log.i(TAG, "onCreate()");
})
.addBlocking("crash-handling", this::initializeCrashHandling)
.addBlocking("eat-db", () -> DatabaseFactory.getInstance(this))
.addBlocking("sqlcipher-init", () -> SqlCipherLibraryLoader.load(this))
.addBlocking("rx-init", () -> {
RxJavaPlugins.setInitIoSchedulerHandler(schedulerSupplier -> Schedulers.from(SignalExecutors.BOUNDED_IO, true, false));
RxJavaPlugins.setInitComputationSchedulerHandler(schedulerSupplier -> Schedulers.from(SignalExecutors.BOUNDED, true, false));
})
.addBlocking("app-dependencies", this::initializeAppDependencies)
.addBlocking("notification-channels", () -> NotificationChannels.create(this))
.addBlocking("first-launch", this::initializeFirstEverAppLaunch)
@@ -146,7 +155,9 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
}
})
.addBlocking("blob-provider", this::initializeBlobProvider)
.addBlocking("feature-flags", FeatureFlags::init)
.addNonBlocking(this::initializeRevealableMessageManager)
.addNonBlocking(this::initializePendingRetryReceiptManager)
.addNonBlocking(this::initializeGcmCheck)
.addNonBlocking(this::initializeSignedPreKeyCheck)
.addNonBlocking(this::initializePeriodicTasks)
@@ -154,11 +165,16 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
.addNonBlocking(this::initializePendingMessages)
.addNonBlocking(this::initializeCleanup)
.addNonBlocking(this::initializeGlideCodecs)
.addNonBlocking(FeatureFlags::init)
.addNonBlocking(RefreshPreKeysJob::scheduleIfNecessary)
.addNonBlocking(StorageSyncHelper::scheduleRoutineSync)
.addNonBlocking(() -> ApplicationDependencies.getJobManager().beginJobLoop())
.addNonBlocking(EmojiSource::refresh)
.addPostRender(() -> RateLimitUtil.retryAllRateLimitedMessages(this))
.addPostRender(this::initializeExpiringMessageManager)
.addPostRender(() -> SignalStore.settings().setDefaultSms(Util.isDefaultSmsProvider(this)))
.addPostRender(() -> DownloadLatestEmojiDataJob.scheduleIfNecessary(this))
.addPostRender(EmojiSearchIndexDownloadJob::scheduleIfNecessary)
.addPostRender(() -> DatabaseFactory.getMessageLogDatabase(this).trimOldMessages(System.currentTimeMillis(), FeatureFlags.retryRespondMaxAge()))
.execute();
Log.d(TAG, "onCreate() took " + (System.currentTimeMillis() - startTime) + " ms");
@@ -196,17 +212,6 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
ApplicationDependencies.getShakeToReport().disable();
}
public ExpiringMessageManager getExpiringMessageManager() {
if (expiringMessageManager == null) {
initializeExpiringMessageManager();
}
return expiringMessageManager;
}
public ViewOnceMessageManager getViewOnceMessageManager() {
return viewOnceMessageManager;
}
public PersistentLogger getPersistentLogger() {
return persistentLogger;
}
@@ -243,8 +248,8 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
}
private void initializeLogging() {
persistentLogger = new PersistentLogger(this, LogSecretProvider.getOrCreateAttachmentSecret(this), BuildConfig.VERSION_NAME);
org.signal.core.util.logging.Log.initialize(new AndroidLogger(), persistentLogger);
persistentLogger = new PersistentLogger(this, LogSecretProvider.getOrCreateAttachmentSecret(this), BuildConfig.VERSION_NAME, FeatureFlags.internalUser() ? 15 : 7, ByteUnit.KILOBYTES.toBytes(300));
org.signal.core.util.logging.Log.initialize(FeatureFlags::internalUser, new AndroidLogger(), persistentLogger);
SignalProtocolLoggerProvider.setProvider(new CustomSignalProtocolLogger());
}
@@ -301,11 +306,15 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
}
private void initializeExpiringMessageManager() {
this.expiringMessageManager = new ExpiringMessageManager(this);
ApplicationDependencies.getExpiringMessageManager().checkSchedule();
}
private void initializeRevealableMessageManager() {
this.viewOnceMessageManager = new ViewOnceMessageManager(this);
ApplicationDependencies.getViewOnceMessageManager().scheduleIfNecessary();
}
private void initializePendingRetryReceiptManager() {
ApplicationDependencies.getPendingRetryReceiptManager().scheduleIfNecessary();
}
private void initializePeriodicTasks() {
@@ -322,31 +331,11 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
private void initializeRingRtc() {
try {
Set<String> HARDWARE_AEC_BLACKLIST = new HashSet<String>() {{
add("Pixel");
add("Pixel XL");
add("Moto G5");
add("Moto G (5S) Plus");
add("Moto G4");
add("TA-1053");
add("Mi A1");
add("Mi A2");
add("E5823"); // Sony z5 compact
add("Redmi Note 5");
add("FP2"); // Fairphone FP2
add("MI 5");
}};
Set<String> OPEN_SL_ES_WHITELIST = new HashSet<String>() {{
add("Pixel");
add("Pixel XL");
}};
if (HARDWARE_AEC_BLACKLIST.contains(Build.MODEL)) {
if (RtcDeviceLists.hardwareAECBlocked()) {
WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true);
}
if (!OPEN_SL_ES_WHITELIST.contains(Build.MODEL)) {
if (!RtcDeviceLists.openSLESAllowed()) {
WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true);
}

View File

@@ -1,370 +0,0 @@
/*
* Copyright (C) 2011 Whisper Systems
* Copyright (C) 2013-2017 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.PorterDuff;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import org.thoughtcrime.securesms.help.HelpFragment;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.preferences.AdvancedPreferenceFragment;
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
import org.thoughtcrime.securesms.preferences.AppearancePreferenceFragment;
import org.thoughtcrime.securesms.preferences.BackupsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.ChatsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment;
import org.thoughtcrime.securesms.preferences.DataAndStoragePreferenceFragment;
import org.thoughtcrime.securesms.preferences.EditProxyFragment;
import org.thoughtcrime.securesms.preferences.NotificationsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.SmsMmsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.widgets.ProfilePreference;
import org.thoughtcrime.securesms.preferences.widgets.UsernamePreference;
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity;
import org.thoughtcrime.securesms.profiles.manage.ManageProfileActivity;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.CachedInflater;
import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
/**
* The Activity for application preference display and management.
*
* @author Moxie Marlinspike
*
*/
public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
implements SharedPreferences.OnSharedPreferenceChangeListener
{
public static final String LAUNCH_TO_BACKUPS_FRAGMENT = "launch.to.backups.fragment";
public static final String LAUNCH_TO_HELP_FRAGMENT = "launch.to.help.fragment";
public static final String LAUNCH_TO_PROXY_FRAGMENT = "launch.to.proxy.fragment";
public static final String LAUNCH_TO_NOTIFICATIONS_FRAGMENT = "launch.to.notifications.fragment";
@SuppressWarnings("unused")
private static final String TAG = ApplicationPreferencesActivity.class.getSimpleName();
private static final String PREFERENCE_CATEGORY_PROFILE = "preference_category_profile";
private static final String PREFERENCE_CATEGORY_USERNAME = "preference_category_username";
private static final String PREFERENCE_CATEGORY_SMS_MMS = "preference_category_sms_mms";
private static final String PREFERENCE_CATEGORY_NOTIFICATIONS = "preference_category_notifications";
private static final String PREFERENCE_CATEGORY_APP_PROTECTION = "preference_category_app_protection";
private static final String PREFERENCE_CATEGORY_APPEARANCE = "preference_category_appearance";
private static final String PREFERENCE_CATEGORY_CHATS = "preference_category_chats";
private static final String PREFERENCE_CATEGORY_STORAGE = "preference_category_storage";
private static final String PREFERENCE_CATEGORY_DEVICES = "preference_category_devices";
private static final String PREFERENCE_CATEGORY_HELP = "preference_category_help";
private static final String PREFERENCE_CATEGORY_ADVANCED = "preference_category_advanced";
private static final String PREFERENCE_CATEGORY_DONATE = "preference_category_donate";
private static final String WAS_CONFIGURATION_UPDATED = "was_configuration_updated";
private final DynamicTheme dynamicTheme = new DynamicTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private boolean wasConfigurationUpdated = false;
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
}
@Override
protected void onCreate(Bundle icicle, boolean ready) {
//noinspection ConstantConditions
this.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (getIntent() != null && getIntent().getCategories() != null && getIntent().getCategories().contains("android.intent.category.NOTIFICATION_PREFERENCES")) {
initFragment(android.R.id.content, new NotificationsPreferenceFragment());
} else if (getIntent() != null && getIntent().getBooleanExtra(LAUNCH_TO_BACKUPS_FRAGMENT, false)) {
initFragment(android.R.id.content, new BackupsPreferenceFragment());
} else if (getIntent() != null && getIntent().getBooleanExtra(LAUNCH_TO_HELP_FRAGMENT, false)) {
initFragment(android.R.id.content, new HelpFragment());
} else if (getIntent() != null && getIntent().getBooleanExtra(LAUNCH_TO_PROXY_FRAGMENT, false)) {
initFragment(android.R.id.content, EditProxyFragment.newInstance());
} else if (getIntent() != null && getIntent().getBooleanExtra(LAUNCH_TO_NOTIFICATIONS_FRAGMENT, false)) {
initFragment(android.R.id.content, new NotificationsPreferenceFragment());
} else if (icicle == null) {
initFragment(android.R.id.content, new ApplicationPreferenceFragment());
} else {
wasConfigurationUpdated = icicle.getBoolean(WAS_CONFIGURATION_UPDATED);
}
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBoolean(WAS_CONFIGURATION_UPDATED, wasConfigurationUpdated);
super.onSaveInstanceState(outState);
}
@Override
public void onResume() {
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
Fragment fragment = getSupportFragmentManager().findFragmentById(android.R.id.content);
fragment.onActivityResult(requestCode, resultCode, data);
}
@Override
public boolean onSupportNavigateUp() {
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStack();
} else {
if (wasConfigurationUpdated) {
setResult(MainActivity.RESULT_CONFIG_CHANGED);
} else {
setResult(RESULT_OK);
}
finish();
}
return true;
}
@Override
public void onBackPressed() {
onSupportNavigateUp();
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(TextSecurePreferences.THEME_PREF)) {
DynamicTheme.setDefaultDayNightMode(this);
recreate();
} else if (key.equals(TextSecurePreferences.LANGUAGE_PREF)) {
CachedInflater.from(this).clear();
wasConfigurationUpdated = true;
recreate();
Intent intent = new Intent(this, KeyCachingService.class);
intent.setAction(KeyCachingService.LOCALE_CHANGE_EVENT);
startService(intent);
}
}
public void pushFragment(@NonNull Fragment fragment) {
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_from_end, R.anim.slide_to_start, R.anim.slide_from_start, R.anim.slide_to_end)
.replace(android.R.id.content, fragment)
.addToBackStack(null)
.commit();
}
public static class ApplicationPreferenceFragment extends CorrectedPreferenceFragment {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
this.findPreference(PREFERENCE_CATEGORY_PROFILE)
.setOnPreferenceClickListener(new ProfileClickListener());
this.findPreference(PREFERENCE_CATEGORY_USERNAME)
.setOnPreferenceClickListener(new UsernameClickListener());
this.findPreference(PREFERENCE_CATEGORY_SMS_MMS)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_SMS_MMS));
this.findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_NOTIFICATIONS));
this.findPreference(PREFERENCE_CATEGORY_APP_PROTECTION)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_APP_PROTECTION));
this.findPreference(PREFERENCE_CATEGORY_APPEARANCE)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_APPEARANCE));
this.findPreference(PREFERENCE_CATEGORY_CHATS)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_CHATS));
this.findPreference(PREFERENCE_CATEGORY_STORAGE)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_STORAGE));
this.findPreference(PREFERENCE_CATEGORY_DEVICES)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_DEVICES));
this.findPreference(PREFERENCE_CATEGORY_HELP)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_HELP));
this.findPreference(PREFERENCE_CATEGORY_ADVANCED)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_ADVANCED));
this.findPreference(PREFERENCE_CATEGORY_DONATE)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_DONATE));
tintIcons();
}
private void tintIcons() {
if (Build.VERSION.SDK_INT >= 21) return;
Preference preference = this.findPreference(PREFERENCE_CATEGORY_SMS_MMS);
preference.getIcon().setColorFilter(ContextCompat.getColor(requireContext(), R.color.signal_icon_tint_primary), PorterDuff.Mode.SRC_IN);
}
@Override
public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.preferences);
if (FeatureFlags.usernames()) {
UsernamePreference pref = (UsernamePreference) findPreference(PREFERENCE_CATEGORY_USERNAME);
pref.setVisible(shouldDisplayUsernameReminder());
pref.setOnLongClickListener(v -> {
new AlertDialog.Builder(requireContext())
.setMessage(R.string.ApplicationPreferencesActivity_hide_reminder)
.setPositiveButton(R.string.ApplicationPreferencesActivity_hide, (dialog, which) -> {
dialog.dismiss();
SignalStore.misc().hideUsernameReminder();
findPreference(PREFERENCE_CATEGORY_USERNAME).setVisible(false);
})
.setNegativeButton(android.R.string.cancel, ((dialog, which) -> dialog.dismiss()))
.setCancelable(true)
.show();
return true;
});
}
}
@Override
public void onResume() {
super.onResume();
//noinspection ConstantConditions
((ApplicationPreferencesActivity) getActivity()).getSupportActionBar().setTitle(R.string.text_secure_normal__menu_settings);
setCategorySummaries();
setCategoryVisibility();
}
private void setCategorySummaries() {
((ProfilePreference)this.findPreference(PREFERENCE_CATEGORY_PROFILE)).refresh();
if (FeatureFlags.usernames()) {
this.findPreference(PREFERENCE_CATEGORY_USERNAME)
.setVisible(shouldDisplayUsernameReminder());
}
this.findPreference(PREFERENCE_CATEGORY_SMS_MMS)
.setSummary(SmsMmsPreferenceFragment.getSummary(getActivity()));
this.findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS)
.setSummary(NotificationsPreferenceFragment.getSummary(getActivity()));
this.findPreference(PREFERENCE_CATEGORY_APP_PROTECTION)
.setSummary(AppProtectionPreferenceFragment.getSummary(getActivity()));
this.findPreference(PREFERENCE_CATEGORY_APPEARANCE)
.setSummary(AppearancePreferenceFragment.getSummary(getActivity()));
this.findPreference(PREFERENCE_CATEGORY_CHATS)
.setSummary(ChatsPreferenceFragment.getSummary(getActivity()));
}
private void setCategoryVisibility() {
Preference devicePreference = this.findPreference(PREFERENCE_CATEGORY_DEVICES);
if (devicePreference != null && !TextSecurePreferences.isPushRegistered(getActivity())) {
getPreferenceScreen().removePreference(devicePreference);
}
}
private static boolean shouldDisplayUsernameReminder() {
return FeatureFlags.usernames() && !Recipient.self().getUsername().isPresent() && SignalStore.misc().shouldShowUsernameReminder();
}
private class CategoryClickListener implements Preference.OnPreferenceClickListener {
private String category;
CategoryClickListener(String category) {
this.category = category;
}
@Override
public boolean onPreferenceClick(Preference preference) {
Fragment fragment = null;
switch (category) {
case PREFERENCE_CATEGORY_SMS_MMS:
fragment = new SmsMmsPreferenceFragment();
break;
case PREFERENCE_CATEGORY_NOTIFICATIONS:
fragment = new NotificationsPreferenceFragment();
break;
case PREFERENCE_CATEGORY_APP_PROTECTION:
fragment = new AppProtectionPreferenceFragment();
break;
case PREFERENCE_CATEGORY_APPEARANCE:
fragment = new AppearancePreferenceFragment();
break;
case PREFERENCE_CATEGORY_CHATS:
fragment = new ChatsPreferenceFragment();
break;
case PREFERENCE_CATEGORY_STORAGE:
fragment = new DataAndStoragePreferenceFragment();
break;
case PREFERENCE_CATEGORY_DEVICES:
Intent intent = new Intent(getActivity(), DeviceActivity.class);
startActivity(intent);
break;
case PREFERENCE_CATEGORY_ADVANCED:
fragment = new AdvancedPreferenceFragment();
break;
case PREFERENCE_CATEGORY_HELP:
fragment = new HelpFragment();
break;
case PREFERENCE_CATEGORY_DONATE:
CommunicationActions.openBrowserLink(requireContext(), getString(R.string.donate_url));
break;
default:
throw new AssertionError();
}
if (fragment != null) {
Bundle args = new Bundle();
fragment.setArguments(args);
((ApplicationPreferencesActivity) requireActivity()).pushFragment(fragment);
}
return true;
}
}
private class ProfileClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
requireActivity().startActivity(ManageProfileActivity.getIntent(requireActivity()));
return true;
}
}
private class UsernameClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
requireActivity().startActivity(ManageProfileActivity.getIntentForUsernameEdit(preference.getContext()));
return true;
}
}
}
}

View File

@@ -11,8 +11,12 @@ import androidx.lifecycle.Observer;
import org.thoughtcrime.securesms.components.voice.VoiceNotePlaybackState;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.conversation.ConversationMessage;
import org.thoughtcrime.securesms.conversation.colors.Colorizable;
import org.thoughtcrime.securesms.conversation.colors.Colorizer;
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.giph.mp4.GiphyMp4Playable;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
@@ -20,13 +24,14 @@ import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.stickers.StickerLocator;
import org.thoughtcrime.securesms.video.exo.AttachmentMediaSourceFactory;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.List;
import java.util.Locale;
import java.util.Set;
public interface BindableConversationItem extends Unbindable {
public interface BindableConversationItem extends Unbindable, GiphyMp4Playable, Colorizable {
void bind(@NonNull LifecycleOwner lifecycleOwner,
@NonNull ConversationMessage messageRecord,
@NonNull Optional<MessageRecord> previousMessageRecord,
@@ -38,12 +43,19 @@ public interface BindableConversationItem extends Unbindable {
@Nullable String searchQuery,
boolean pulseMention,
boolean hasWallpaper,
boolean isMessageRequestAccepted);
boolean isMessageRequestAccepted,
@NonNull AttachmentMediaSourceFactory attachmentMediaSourceFactory,
boolean canPlayInline,
@NonNull Colorizer colorizer);
ConversationMessage getConversationMessage();
void setEventListener(@Nullable EventListener listener);
default void updateTimestamps() {
// Intentionally Blank.
}
interface EventListener {
void onQuoteClicked(MmsMessageRecord messageRecord);
void onLinkPreviewClicked(@NonNull LinkPreview linkPreview);
@@ -57,16 +69,24 @@ public interface BindableConversationItem extends Unbindable {
void onReactionClicked(@NonNull View reactionTarget, long messageId, boolean isMms);
void onGroupMemberClicked(@NonNull RecipientId recipientId, @NonNull GroupId groupId);
void onMessageWithErrorClicked(@NonNull MessageRecord messageRecord);
void onMessageWithRecaptchaNeededClicked(@NonNull MessageRecord messageRecord);
void onIncomingIdentityMismatchClicked(@NonNull RecipientId recipientId);
void onRegisterVoiceNoteCallbacks(@NonNull Observer<VoiceNotePlaybackState> onPlaybackStartObserver);
void onUnregisterVoiceNoteCallbacks(@NonNull Observer<VoiceNotePlaybackState> onPlaybackStartObserver);
void onVoiceNotePause(@NonNull Uri uri);
void onVoiceNotePlay(@NonNull Uri uri, long messageId, double position);
void onVoiceNoteSeekTo(@NonNull Uri uri, double position);
void onVoiceNotePlaybackSpeedChanged(@NonNull Uri uri, float speed);
void onGroupMigrationLearnMoreClicked(@NonNull GroupMigrationMembershipChange membershipChange);
void onDecryptionFailedLearnMoreClicked();
void onChatSessionRefreshLearnMoreClicked();
void onBadDecryptLearnMoreClicked(@NonNull RecipientId author);
void onSafetyNumberLearnMoreClicked(@NonNull Recipient recipient);
void onJoinGroupCallClicked();
void onInviteFriendsToGroupClicked(@NonNull GroupId.V2 groupId);
void onEnableCallNotificationsClicked();
void onPlayInlineContent(ConversationMessage conversationMessage);
void onInMemoryMessageClicked(@NonNull InMemoryMessageRecord messageRecord);
void onViewGroupDescriptionChange(@Nullable GroupId groupId, @NonNull String description, boolean isMessageRequestAccepted);
/** @return true if handled, false if you want to let the normal url handling continue */
boolean onUrlClicked(@NonNull String url);

View File

@@ -9,6 +9,8 @@ import androidx.annotation.WorkerThread;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.Lifecycle;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
@@ -30,15 +32,15 @@ public final class BlockUnblockDialog {
AlertDialog.Builder::show);
}
public static void showBlockAndDeleteFor(@NonNull Context context,
@NonNull Lifecycle lifecycle,
@NonNull Recipient recipient,
@NonNull Runnable onBlock,
@NonNull Runnable onBlockAndDelete)
public static void showBlockAndReportSpamFor(@NonNull Context context,
@NonNull Lifecycle lifecycle,
@NonNull Recipient recipient,
@NonNull Runnable onBlock,
@NonNull Runnable onBlockAndReportSpam)
{
SimpleTask.run(lifecycle,
() -> buildBlockFor(context, recipient, onBlock, onBlockAndDelete),
AlertDialog.Builder::show);
() -> buildBlockFor(context, recipient, onBlock, onBlockAndReportSpam),
AlertDialog.Builder::show);
}
public static void showUnblockFor(@NonNull Context context,
@@ -55,11 +57,11 @@ public final class BlockUnblockDialog {
private static AlertDialog.Builder buildBlockFor(@NonNull Context context,
@NonNull Recipient recipient,
@NonNull Runnable onBlock,
@Nullable Runnable onBlockAndDelete)
@Nullable Runnable onBlockAndReportSpam)
{
recipient = recipient.resolve();
AlertDialog.Builder builder = new AlertDialog.Builder(context);
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(context);
Resources resources = context.getResources();
if (recipient.isGroup()) {
@@ -78,10 +80,10 @@ public final class BlockUnblockDialog {
builder.setTitle(resources.getString(R.string.BlockUnblockDialog_block_s, recipient.getDisplayName(context)));
builder.setMessage(R.string.BlockUnblockDialog_blocked_people_wont_be_able_to_call_you_or_send_you_messages);
if (onBlockAndDelete != null) {
if (onBlockAndReportSpam != null) {
builder.setNeutralButton(android.R.string.cancel, null);
builder.setPositiveButton(R.string.BlockUnblockDialog_block_and_delete, (d, w) -> onBlockAndDelete.run());
builder.setNegativeButton(R.string.BlockUnblockDialog_block, (d, w) -> onBlock.run());
builder.setNegativeButton(R.string.BlockUnblockDialog_report_spam_and_block, (d, w) -> onBlockAndReportSpam.run());
builder.setPositiveButton(R.string.BlockUnblockDialog_block, (d, w) -> onBlock.run());
} else {
builder.setPositiveButton(R.string.BlockUnblockDialog_block, ((dialog, which) -> onBlock.run()));
builder.setNegativeButton(android.R.string.cancel, null);
@@ -98,7 +100,7 @@ public final class BlockUnblockDialog {
{
recipient = recipient.resolve();
AlertDialog.Builder builder = new AlertDialog.Builder(context);
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(context);
Resources resources = context.getResources();
if (recipient.isGroup()) {

View File

@@ -1,176 +0,0 @@
package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.PushDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.PushDecryptMessageJob;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.VerifySpan;
import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalSessionLock;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import java.io.IOException;
public class ConfirmIdentityDialog extends AlertDialog {
@SuppressWarnings("unused")
private static final String TAG = ConfirmIdentityDialog.class.getSimpleName();
private OnClickListener callback;
public ConfirmIdentityDialog(Context context,
MessageRecord messageRecord,
IdentityKeyMismatch mismatch)
{
super(context);
Recipient recipient = Recipient.resolved(mismatch.getRecipientId(context));
String name = recipient.getDisplayName(context);
String introduction = context.getString(R.string.ConfirmIdentityDialog_your_safety_number_with_s_has_changed, name, name);
SpannableString spannableString = new SpannableString(introduction + " " +
context.getString(R.string.ConfirmIdentityDialog_you_may_wish_to_verify_your_safety_number_with_this_contact));
spannableString.setSpan(new VerifySpan(context, mismatch),
introduction.length()+1, spannableString.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
setTitle(name);
setMessage(spannableString);
setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ConfirmIdentityDialog_accept), new AcceptListener(messageRecord, mismatch, recipient.getId()));
setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), new CancelListener());
}
@Override
public void show() {
super.show();
((TextView)this.findViewById(android.R.id.message))
.setMovementMethod(LinkMovementMethod.getInstance());
}
public void setCallback(OnClickListener callback) {
this.callback = callback;
}
private class AcceptListener implements OnClickListener {
private final MessageRecord messageRecord;
private final IdentityKeyMismatch mismatch;
private final RecipientId recipientId;
private AcceptListener(MessageRecord messageRecord, IdentityKeyMismatch mismatch, RecipientId recipientId) {
this.messageRecord = messageRecord;
this.mismatch = mismatch;
this.recipientId = recipientId;
}
@SuppressLint("StaticFieldLeak")
@Override
public void onClick(DialogInterface dialog, int which) {
new AsyncTask<Void, Void, Void>()
{
@Override
protected Void doInBackground(Void... params) {
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(Recipient.resolved(recipientId).requireServiceId(), 1);
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext());
identityKeyStore.saveIdentity(mismatchAddress, mismatch.getIdentityKey(), true);
}
processMessageRecord(messageRecord);
return null;
}
private void processMessageRecord(MessageRecord messageRecord) {
if (messageRecord.isOutgoing()) processOutgoingMessageRecord(messageRecord);
else processIncomingMessageRecord(messageRecord);
}
private void processOutgoingMessageRecord(MessageRecord messageRecord) {
MessageDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext());
MessageDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(getContext());
if (messageRecord.isMms()) {
mmsDatabase.removeMismatchedIdentity(messageRecord.getId(),
mismatch.getRecipientId(getContext()),
mismatch.getIdentityKey());
if (messageRecord.getRecipient().isPushGroup()) {
MessageSender.resendGroupMessage(getContext(), messageRecord, Recipient.resolved(mismatch.getRecipientId(getContext())).getId());
} else {
MessageSender.resend(getContext(), messageRecord);
}
} else {
smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
mismatch.getRecipientId(getContext()),
mismatch.getIdentityKey());
MessageSender.resend(getContext(), messageRecord);
}
}
private void processIncomingMessageRecord(MessageRecord messageRecord) {
try {
MessageDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext());
smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
mismatch.getRecipientId(getContext()),
mismatch.getIdentityKey());
boolean legacy = !messageRecord.isContentBundleKeyExchange();
SignalServiceEnvelope envelope = new SignalServiceEnvelope(SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE,
Optional.of(RecipientUtil.toSignalServiceAddress(getContext(), messageRecord.getIndividualRecipient())),
messageRecord.getRecipientDeviceId(),
messageRecord.getDateSent(),
legacy ? Base64.decode(messageRecord.getBody()) : null,
!legacy ? Base64.decode(messageRecord.getBody()) : null,
0,
0,
null);
ApplicationDependencies.getJobManager().add(new PushDecryptMessageJob(getContext(), envelope, messageRecord.getId()));
} catch (IOException e) {
throw new AssertionError(e);
}
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
if (callback != null) callback.onClick(null, 0);
}
}
private class CancelListener implements OnClickListener {
@Override
public void onClick(DialogInterface dialog, int which) {
if (callback != null) callback.onClick(null, 0);
}
}
}

View File

@@ -20,17 +20,18 @@ import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.appcompat.widget.Toolbar;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
import org.thoughtcrime.securesms.components.ContactFilterView;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
@@ -47,7 +48,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActivit
ContactSelectionListFragment.OnContactSelectedListener,
ContactSelectionListFragment.ScrollCallback
{
private static final String TAG = ContactSelectionActivity.class.getSimpleName();
private static final String TAG = Log.tag(ContactSelectionActivity.class);
public static final String EXTRA_LAYOUT_RES_ID = "layout_res_id";
@@ -55,7 +56,8 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActivit
protected ContactSelectionListFragment contactsFragment;
private ContactFilterToolbar toolbar;
private Toolbar toolbar;
private ContactFilterView contactFilterView;
@Override
protected void onPreCreate() {
@@ -65,13 +67,14 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActivit
@Override
protected void onCreate(Bundle icicle, boolean ready) {
if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) {
int displayMode = TextSecurePreferences.isSmsEnabled(this) ? DisplayMode.FLAG_ALL
: DisplayMode.FLAG_PUSH | DisplayMode.FLAG_ACTIVE_GROUPS | DisplayMode.FLAG_INACTIVE_GROUPS | DisplayMode.FLAG_SELF;
int displayMode = Util.isDefaultSmsProvider(this) ? DisplayMode.FLAG_ALL
: DisplayMode.FLAG_PUSH | DisplayMode.FLAG_ACTIVE_GROUPS | DisplayMode.FLAG_INACTIVE_GROUPS | DisplayMode.FLAG_SELF;
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, displayMode);
}
setContentView(getIntent().getIntExtra(EXTRA_LAYOUT_RES_ID, R.layout.contact_selection_activity));
initializeContactFilterView();
initializeToolbar();
initializeResources();
initializeSearch();
@@ -83,16 +86,23 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActivit
dynamicTheme.onResume(this);
}
protected ContactFilterToolbar getToolbar() {
protected Toolbar getToolbar() {
return toolbar;
}
protected ContactFilterView getContactFilterView() {
return contactFilterView;
}
private void initializeContactFilterView() {
this.contactFilterView = findViewById(R.id.contact_filter_edit_text);
}
private void initializeToolbar() {
this.toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
getSupportActionBar().setDisplayShowTitleEnabled(false);
getSupportActionBar().setIcon(null);
getSupportActionBar().setLogo(null);
}
@@ -103,7 +113,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActivit
}
private void initializeSearch() {
toolbar.setOnFilterChangedListener(filter -> contactsFragment.setQueryFilter(filter));
contactFilterView.setOnFilterChangedListener(filter -> contactsFragment.setQueryFilter(filter));
}
@Override
@@ -154,7 +164,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActivit
ContactSelectionActivity activity = this.activity.get();
if (activity != null && !activity.isFinishing()) {
activity.toolbar.clear();
activity.contactFilterView.clear();
activity.contactsFragment.resetQueryFilter();
}
}

View File

@@ -57,12 +57,14 @@ import com.pnikosis.materialishprogress.ProgressWheel;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.components.RecyclerViewFastScroller;
import org.thoughtcrime.securesms.components.emoji.WarningTextView;
import org.thoughtcrime.securesms.components.recyclerview.ToolbarShadowAnimationHelper;
import org.thoughtcrime.securesms.contacts.AbstractContactsCursorLoader;
import org.thoughtcrime.securesms.contacts.ContactChip;
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter;
import org.thoughtcrime.securesms.contacts.ContactSelectionListItem;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.LetterHeaderDecoration;
import org.thoughtcrime.securesms.contacts.SelectedContact;
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
import org.thoughtcrime.securesms.groups.SelectionLimits;
@@ -73,7 +75,6 @@ import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.UsernameUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
@@ -113,23 +114,27 @@ public final class ContactSelectionListFragment extends LoggingFragment
public static final String HIDE_COUNT = "hide_count";
public static final String CAN_SELECT_SELF = "can_select_self";
public static final String DISPLAY_CHIPS = "display_chips";
public static final String RV_PADDING_BOTTOM = "recycler_view_padding_bottom";
public static final String RV_CLIP = "recycler_view_clipping";
private ConstraintLayout constraintLayout;
private TextView emptyText;
private OnContactSelectedListener onContactSelectedListener;
private SwipeRefreshLayout swipeRefresh;
private View showContactsLayout;
private Button showContactsButton;
private TextView showContactsDescription;
private ProgressWheel showContactsProgress;
private String cursorFilter;
private RecyclerView recyclerView;
private RecyclerViewFastScroller fastScroller;
private ContactSelectionListAdapter cursorRecyclerViewAdapter;
private ChipGroup chipGroup;
private HorizontalScrollView chipGroupScrollContainer;
private WarningTextView groupLimit;
private OnSelectionLimitReachedListener onSelectionLimitReachedListener;
private ConstraintLayout constraintLayout;
private TextView emptyText;
private OnContactSelectedListener onContactSelectedListener;
private SwipeRefreshLayout swipeRefresh;
private View showContactsLayout;
private Button showContactsButton;
private TextView showContactsDescription;
private ProgressWheel showContactsProgress;
private String cursorFilter;
private RecyclerView recyclerView;
private RecyclerViewFastScroller fastScroller;
private ContactSelectionListAdapter cursorRecyclerViewAdapter;
private ChipGroup chipGroup;
private HorizontalScrollView chipGroupScrollContainer;
private OnSelectionLimitReachedListener onSelectionLimitReachedListener;
private AbstractContactsCursorLoaderFactoryProvider cursorFactoryProvider;
private View shadowView;
private ToolbarShadowAnimationHelper toolbarShadowAnimationHelper;
@Nullable private FixedViewsAdapter headerAdapter;
@@ -151,10 +156,18 @@ public final class ContactSelectionListFragment extends LoggingFragment
listCallback = (ListCallback) context;
}
if (getParentFragment() instanceof ScrollCallback) {
scrollCallback = (ScrollCallback) getParentFragment();
}
if (context instanceof ScrollCallback) {
scrollCallback = (ScrollCallback) context;
}
if (getParentFragment() instanceof OnContactSelectedListener) {
onContactSelectedListener = (OnContactSelectedListener) getParentFragment();
}
if (context instanceof OnContactSelectedListener) {
onContactSelectedListener = (OnContactSelectedListener) context;
}
@@ -162,6 +175,14 @@ public final class ContactSelectionListFragment extends LoggingFragment
if (context instanceof OnSelectionLimitReachedListener) {
onSelectionLimitReachedListener = (OnSelectionLimitReachedListener) context;
}
if (context instanceof AbstractContactsCursorLoaderFactoryProvider) {
cursorFactoryProvider = (AbstractContactsCursorLoaderFactoryProvider) context;
}
if (getParentFragment() instanceof AbstractContactsCursorLoaderFactoryProvider) {
cursorFactoryProvider = (AbstractContactsCursorLoaderFactoryProvider) context;
}
}
@Override
@@ -190,7 +211,7 @@ public final class ContactSelectionListFragment extends LoggingFragment
activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
if (activity.getIntent().getBooleanExtra(RECENTS, false)) {
if (safeArguments().getBoolean(RECENTS, activity.getIntent().getBooleanExtra(RECENTS, false))) {
LoaderManager.getInstance(this).initLoader(0, null, ContactSelectionListFragment.this);
} else {
initializeNoContactsPermission();
@@ -213,9 +234,12 @@ public final class ContactSelectionListFragment extends LoggingFragment
showContactsProgress = view.findViewById(R.id.progress);
chipGroup = view.findViewById(R.id.chipGroup);
chipGroupScrollContainer = view.findViewById(R.id.chipGroupScrollContainer);
groupLimit = view.findViewById(R.id.group_limit);
constraintLayout = view.findViewById(R.id.container);
shadowView = view.findViewById(R.id.toolbar_shadow);
toolbarShadowAnimationHelper = new ToolbarShadowAnimationHelper(shadowView);
recyclerView.addOnScrollListener(toolbarShadowAnimationHelper);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setItemAnimator(new DefaultItemAnimator() {
@Override
@@ -224,14 +248,27 @@ public final class ContactSelectionListFragment extends LoggingFragment
}
});
Intent intent = requireActivity().getIntent();
Intent intent = requireActivity().getIntent();
Bundle arguments = safeArguments();
swipeRefresh.setEnabled(intent.getBooleanExtra(REFRESHABLE, true));
int recyclerViewPadBottom = arguments.getInt(RV_PADDING_BOTTOM, intent.getIntExtra(RV_PADDING_BOTTOM, -1));
boolean recyclerViewClipping = arguments.getBoolean(RV_CLIP, intent.getBooleanExtra(RV_CLIP, true));
hideCount = intent.getBooleanExtra(HIDE_COUNT, false);
selectionLimit = intent.getParcelableExtra(SELECTION_LIMITS);
isMulti = selectionLimit != null;
canSelectSelf = intent.getBooleanExtra(CAN_SELECT_SELF, !isMulti);
if (recyclerViewPadBottom != -1) {
ViewUtil.setPaddingBottom(recyclerView, recyclerViewPadBottom);
}
recyclerView.setClipToPadding(recyclerViewClipping);
swipeRefresh.setEnabled(arguments.getBoolean(REFRESHABLE, intent.getBooleanExtra(REFRESHABLE, true)));
hideCount = arguments.getBoolean(HIDE_COUNT, intent.getBooleanExtra(HIDE_COUNT, false));
selectionLimit = arguments.getParcelable(SELECTION_LIMITS);
if (selectionLimit == null) {
selectionLimit = intent.getParcelableExtra(SELECTION_LIMITS);
}
isMulti = selectionLimit != null;
canSelectSelf = arguments.getBoolean(CAN_SELECT_SELF, intent.getBooleanExtra(CAN_SELECT_SELF, !isMulti));
if (!isMulti) {
selectionLimit = SelectionLimits.NO_LIMITS;
@@ -239,16 +276,11 @@ public final class ContactSelectionListFragment extends LoggingFragment
currentSelection = getCurrentSelection();
updateGroupLimit(getChipCount());
return view;
}
private void updateGroupLimit(int chipCount) {
int members = currentSelection.size() + chipCount;
groupLimit.setText(getResources().getQuantityString(R.plurals.ContactSelectionListFragment_d_members, members, members));
groupLimit.setVisibility(isMulti && !hideCount ? View.VISIBLE : View.GONE);
groupLimit.setWarning(selectionWarningLimitExceeded());
private @NonNull Bundle safeArguments() {
return getArguments() != null ? getArguments() : new Bundle();
}
@Override
@@ -272,8 +304,19 @@ public final class ContactSelectionListFragment extends LoggingFragment
return cursorRecyclerViewAdapter.getSelectedContactsCount();
}
public int getTotalMemberCount() {
if (cursorRecyclerViewAdapter == null) {
return 0;
}
return cursorRecyclerViewAdapter.getSelectedContactsCount() + cursorRecyclerViewAdapter.getCurrentContactsCount();
}
private Set<RecipientId> getCurrentSelection() {
List<RecipientId> currentSelection = requireActivity().getIntent().getParcelableArrayListExtra(CURRENT_SELECTION);
List<RecipientId> currentSelection = safeArguments().getParcelableArrayList(CURRENT_SELECTION);
if (currentSelection == null) {
currentSelection = requireActivity().getIntent().getParcelableArrayListExtra(CURRENT_SELECTION);
}
return currentSelection == null ? Collections.emptySet()
: Collections.unmodifiableSet(Stream.of(currentSelection).collect(Collectors.toSet()));
@@ -309,8 +352,8 @@ public final class ContactSelectionListFragment extends LoggingFragment
concatenateAdapter.addAdapter(footerAdapter);
}
recyclerView.addItemDecoration(new LetterHeaderDecoration(requireContext(), this::hideLetterHeaders));
recyclerView.setAdapter(concatenateAdapter);
recyclerView.addItemDecoration(new StickyHeaderDecoration(concatenateAdapter, true, true, 0));
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
@@ -321,6 +364,14 @@ public final class ContactSelectionListFragment extends LoggingFragment
}
}
});
if (onContactSelectedListener != null) {
onContactSelectedListener.onSelectionChanged();
}
}
private boolean hideLetterHeaders() {
return hasQueryFilter() || shouldDisplayRecents();
}
private View createInviteActionView(@NonNull ListCallback listCallback) {
@@ -387,10 +438,15 @@ public final class ContactSelectionListFragment extends LoggingFragment
@Override
public @NonNull Loader<Cursor> onCreateLoader(int id, Bundle args) {
FragmentActivity activity = requireActivity();
return new ContactsCursorLoader(activity,
activity.getIntent().getIntExtra(DISPLAY_MODE, DisplayMode.FLAG_ALL),
cursorFilter, activity.getIntent().getBooleanExtra(RECENTS, false));
FragmentActivity activity = requireActivity();
int displayMode = safeArguments().getInt(DISPLAY_MODE, activity.getIntent().getIntExtra(DISPLAY_MODE, DisplayMode.FLAG_ALL));
boolean displayRecents = shouldDisplayRecents();
if (cursorFactoryProvider != null) {
return cursorFactoryProvider.get().create();
} else {
return new ContactsCursorLoader.Factory(activity, displayMode, cursorFilter, displayRecents).create();
}
}
@Override
@@ -430,6 +486,10 @@ public final class ContactSelectionListFragment extends LoggingFragment
fastScroller.setVisibility(View.GONE);
}
private boolean shouldDisplayRecents() {
return safeArguments().getBoolean(RECENTS, requireActivity().getIntent().getBooleanExtra(RECENTS, false));
}
@SuppressLint("StaticFieldLeak")
private void handleContactPermissionGranted() {
final Context context = requireContext();
@@ -561,12 +621,19 @@ public final class ContactSelectionListFragment extends LoggingFragment
if (isMulti) {
addChipForSelectedContact(selectedContact);
}
if (onContactSelectedListener != null) {
onContactSelectedListener.onSelectionChanged();
}
}
private void markContactUnselected(@NonNull SelectedContact selectedContact) {
cursorRecyclerViewAdapter.removeFromSelectedContacts(selectedContact);
cursorRecyclerViewAdapter.notifyItemRangeChanged(0, cursorRecyclerViewAdapter.getItemCount(), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
removeChipForContact(selectedContact);
if (onContactSelectedListener != null) {
onContactSelectedListener.onSelectionChanged();
}
}
private void removeChipForContact(@NonNull SelectedContact contact) {
@@ -577,8 +644,6 @@ public final class ContactSelectionListFragment extends LoggingFragment
}
}
updateGroupLimit(getChipCount());
if (getChipCount() == 0) {
setChipGroupVisibility(ConstraintSet.GONE);
}
@@ -628,7 +693,6 @@ public final class ContactSelectionListFragment extends LoggingFragment
private void addChip(@NonNull ContactChip chip) {
chipGroup.addView(chip);
updateGroupLimit(getChipCount());
if (selectionWarningLimitReachedExactly()) {
if (onSelectionLimitReachedListener != null) {
onSelectionLimitReachedListener.onSuggestedLimitReached(selectionLimit.getRecommendedLimit());
@@ -656,7 +720,7 @@ public final class ContactSelectionListFragment extends LoggingFragment
}
private void setChipGroupVisibility(int visibility) {
if (!requireActivity().getIntent().getBooleanExtra(DISPLAY_CHIPS, true)) {
if (!safeArguments().getBoolean(DISPLAY_CHIPS, requireActivity().getIntent().getBooleanExtra(DISPLAY_CHIPS, true))) {
return;
}
@@ -681,6 +745,7 @@ public final class ContactSelectionListFragment extends LoggingFragment
/** @return True if the contact is allowed to be selected, otherwise false. */
boolean onBeforeContactSelected(Optional<RecipientId> recipientId, String number);
void onContactDeselected(Optional<RecipientId> recipientId, String number);
void onSelectionChanged();
}
public interface OnSelectionLimitReachedListener {
@@ -696,4 +761,8 @@ public final class ContactSelectionListFragment extends LoggingFragment
public interface ScrollCallback {
void onBeginScroll();
}
public interface AbstractContactsCursorLoaderFactoryProvider {
@NonNull AbstractContactsCursorLoader.Factory get();
}
}

View File

@@ -16,6 +16,7 @@ import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import org.signal.core.util.ThreadUtil;
@@ -27,6 +28,7 @@ import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.qr.ScanListener;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
@@ -45,9 +47,9 @@ public class DeviceActivity extends PassphraseRequiredActivity
implements Button.OnClickListener, ScanListener, DeviceLinkFragment.LinkClickedListener
{
private static final String TAG = DeviceActivity.class.getSimpleName();
private static final String TAG = Log.tag(DeviceActivity.class);
private final DynamicTheme dynamicTheme = new DynamicTheme();
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private DeviceAddFragment deviceAddFragment;
@@ -62,9 +64,14 @@ public class DeviceActivity extends PassphraseRequiredActivity
@Override
public void onCreate(Bundle bundle, boolean ready) {
getSupportActionBar().setHomeAsUpIndicator(ContextCompat.getDrawable(this, R.drawable.ic_arrow_left_24));
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle(R.string.AndroidManifest__linked_devices);
setContentView(R.layout.device_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
requireSupportActionBar().setDisplayHomeAsUpEnabled(true);
requireSupportActionBar().setTitle(R.string.AndroidManifest__linked_devices);
this.deviceAddFragment = new DeviceAddFragment();
this.deviceListFragment = new DeviceListFragment();
this.deviceLinkFragment = new DeviceLinkFragment();
@@ -73,20 +80,10 @@ public class DeviceActivity extends PassphraseRequiredActivity
this.deviceAddFragment.setScanListener(this);
if (getIntent().getBooleanExtra("add", false)) {
initFragment(android.R.id.content, deviceAddFragment, dynamicLanguage.getCurrentLocale());
initFragment(R.id.fragment_container, deviceAddFragment, dynamicLanguage.getCurrentLocale());
} else {
initFragment(android.R.id.content, deviceListFragment, dynamicLanguage.getCurrentLocale());
initFragment(R.id.fragment_container, deviceListFragment, dynamicLanguage.getCurrentLocale());
}
overridePendingTransition(R.anim.slide_from_end, R.anim.slide_to_start);
}
@Override
protected void onPause() {
if (isFinishing()) {
overridePendingTransition(R.anim.slide_from_start, R.anim.slide_to_end);
}
super.onPause();
}
@Override
@@ -98,8 +95,9 @@ public class DeviceActivity extends PassphraseRequiredActivity
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home: finish(); return true;
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return false;
@@ -113,7 +111,7 @@ public class DeviceActivity extends PassphraseRequiredActivity
.withPermanentDenialDialog(getString(R.string.DeviceActivity_signal_needs_the_camera_permission_in_order_to_scan_a_qr_code))
.onAllGranted(() -> {
getSupportFragmentManager().beginTransaction()
.replace(android.R.id.content, deviceAddFragment)
.replace(R.id.fragment_container, deviceAddFragment)
.addToBackStack(null)
.commitAllowingStateLoss();
})
@@ -128,7 +126,7 @@ public class DeviceActivity extends PassphraseRequiredActivity
Uri uri = Uri.parse(data);
deviceLinkFragment.setLinkClickedListener(uri, DeviceActivity.this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (Build.VERSION.SDK_INT >= 21) {
deviceAddFragment.setSharedElementReturnTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(R.transition.fragment_shared));
deviceAddFragment.setExitTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(android.R.transition.fade));
@@ -138,14 +136,14 @@ public class DeviceActivity extends PassphraseRequiredActivity
getSupportFragmentManager().beginTransaction()
.addToBackStack(null)
.addSharedElement(deviceAddFragment.getDevicesImage(), "devices")
.replace(android.R.id.content, deviceLinkFragment)
.replace(R.id.fragment_container, deviceLinkFragment)
.commit();
} else {
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_from_bottom, R.anim.slide_to_bottom,
R.anim.slide_from_bottom, R.anim.slide_to_bottom)
.replace(android.R.id.content, deviceLinkFragment)
.replace(R.id.fragment_container, deviceLinkFragment)
.addToBackStack(null)
.commit();
}

View File

@@ -42,9 +42,9 @@ public class DeviceAddFragment extends LoggingFragment {
this.overlay.setOrientation(LinearLayout.VERTICAL);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (Build.VERSION.SDK_INT >= 21) {
this.container.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@TargetApi(21)
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom)
@@ -80,7 +80,7 @@ public class DeviceAddFragment extends LoggingFragment {
}
@Override
public void onConfigurationChanged(Configuration newConfiguration) {
public void onConfigurationChanged(@NonNull Configuration newConfiguration) {
super.onConfigurationChanged(newConfiguration);
this.scannerView.onPause();
@@ -107,6 +107,4 @@ public class DeviceAddFragment extends LoggingFragment {
this.scanningThread.setScanListener(scanListener);
}
}
}

View File

@@ -32,7 +32,7 @@ public class DeviceLinkFragment extends Fragment implements View.OnClickListener
}
@Override
public void onConfigurationChanged(Configuration newConfiguration) {
public void onConfigurationChanged(@NonNull Configuration newConfiguration) {
super.onConfigurationChanged(newConfiguration);
if (newConfiguration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
container.setOrientation(LinearLayout.HORIZONTAL);

View File

@@ -21,6 +21,7 @@ import androidx.fragment.app.ListFragment;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.melnykov.fab.FloatingActionButton;
import org.signal.core.util.logging.Log;
@@ -40,7 +41,7 @@ public class DeviceListFragment extends ListFragment
ListView.OnItemClickListener, Button.OnClickListener
{
private static final String TAG = DeviceListFragment.class.getSimpleName();
private static final String TAG = Log.tag(DeviceListFragment.class);
private SignalServiceAccountManager accountManager;
private Locale locale;
@@ -52,12 +53,12 @@ public class DeviceListFragment extends ListFragment
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.locale = (Locale) getArguments().getSerializable(PassphraseRequiredActivity.LOCALE_EXTRA);
this.locale = (Locale) requireArguments().getSerializable(PassphraseRequiredActivity.LOCALE_EXTRA);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
public void onAttach(@NonNull Context context) {
super.onAttach(context);
this.accountManager = ApplicationDependencies.getSignalServiceAccountManager();
}
@@ -121,42 +122,22 @@ public class DeviceListFragment extends ListFragment
final String deviceName = ((DeviceListItem)view).getDeviceName();
final long deviceId = ((DeviceListItem)view).getDeviceId();
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(getActivity().getString(R.string.DeviceListActivity_unlink_s, deviceName));
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(requireActivity());
builder.setTitle(getString(R.string.DeviceListActivity_unlink_s, deviceName));
builder.setMessage(R.string.DeviceListActivity_by_unlinking_this_device_it_will_no_longer_be_able_to_send_or_receive);
builder.setNegativeButton(android.R.string.cancel, null);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
handleDisconnectDevice(deviceId);
}
});
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> handleDisconnectDevice(deviceId));
builder.show();
}
private void handleLoaderFailed() {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(requireActivity());
builder.setMessage(R.string.DeviceListActivity_network_connection_failed);
builder.setPositiveButton(R.string.DeviceListActivity_try_again,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
getLoaderManager().restartLoader(0, null, DeviceListFragment.this);
}
});
(dialog, which) -> getLoaderManager().restartLoader(0, null, DeviceListFragment.this));
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
DeviceListFragment.this.getActivity().onBackPressed();
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
DeviceListFragment.this.getActivity().onBackPressed();
}
});
builder.setNegativeButton(android.R.string.cancel, (dialog, which) -> requireActivity().onBackPressed());
builder.setOnCancelListener(dialog -> requireActivity().onBackPressed());
builder.show();
}

View File

@@ -6,10 +6,12 @@ import android.view.Window;
import androidx.appcompat.app.AlertDialog;
import org.signal.core.util.logging.Log;
public class DeviceProvisioningActivity extends PassphraseRequiredActivity {
@SuppressWarnings("unused")
private static final String TAG = DeviceProvisioningActivity.class.getSimpleName();
private static final String TAG = Log.tag(DeviceProvisioningActivity.class);
@Override
protected void onPreCreate() {

View File

@@ -1,103 +0,0 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import java.util.Arrays;
import cn.carbswang.android.numberpickerview.library.NumberPickerView;
public class ExpirationDialog extends AlertDialog {
protected ExpirationDialog(Context context) {
super(context);
}
protected ExpirationDialog(Context context, int theme) {
super(context, theme);
}
protected ExpirationDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
}
public static void show(final Context context,
final int currentExpiration,
final @NonNull OnClickListener listener)
{
final View view = createNumberPickerView(context, currentExpiration);
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(context.getString(R.string.ExpirationDialog_disappearing_messages));
builder.setView(view);
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> {
int selected = ((NumberPickerView)view.findViewById(R.id.expiration_number_picker)).getValue();
listener.onClick(getExpirationTimes(context, currentExpiration)[selected]);
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
}
private static View createNumberPickerView(final Context context, final int currentExpiration) {
final LayoutInflater inflater = LayoutInflater.from(context);
final View view = inflater.inflate(R.layout.expiration_dialog, null);
final NumberPickerView numberPickerView = view.findViewById(R.id.expiration_number_picker);
final TextView textView = view.findViewById(R.id.expiration_details);
final int[] expirationTimes = getExpirationTimes(context, currentExpiration);
final String[] expirationDisplayValues = new String[expirationTimes.length];
int selectedIndex = expirationTimes.length - 1;
for (int i=0;i<expirationTimes.length;i++) {
expirationDisplayValues[i] = ExpirationUtil.getExpirationDisplayValue(context, expirationTimes[i]);
if ((currentExpiration >= expirationTimes[i]) &&
(i == expirationTimes.length -1 || currentExpiration < expirationTimes[i+1])) {
selectedIndex = i;
}
}
numberPickerView.setDisplayedValues(expirationDisplayValues);
numberPickerView.setMinValue(0);
numberPickerView.setMaxValue(expirationTimes.length-1);
NumberPickerView.OnValueChangeListener listener = (picker, oldVal, newVal) -> {
if (newVal == 0) {
textView.setText(R.string.ExpirationDialog_your_messages_will_not_expire);
} else {
textView.setText(context.getString(R.string.ExpirationDialog_your_messages_will_disappear_s_after_they_have_been_seen, picker.getDisplayedValues()[newVal]));
}
};
numberPickerView.setOnValueChangedListener(listener);
numberPickerView.setValue(selectedIndex);
listener.onValueChange(numberPickerView, selectedIndex, selectedIndex);
return view;
}
private static int[] getExpirationTimes(Context context, int currentExpiration) {
int[] expirationTimes = context.getResources().getIntArray(R.array.expiration_times);
int location = Arrays.binarySearch(expirationTimes, currentExpiration);
if (location < 0) {
int[] temp = Arrays.copyOf(expirationTimes, expirationTimes.length + 1);
temp[temp.length - 1] = currentExpiration;
Arrays.sort(temp);
expirationTimes = temp;
}
return expirationTimes;
}
public interface OnClickListener {
public void onClick(int expirationTime);
}
}

View File

@@ -7,8 +7,6 @@ import android.graphics.PorterDuff;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
@@ -24,8 +22,8 @@ import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener;
import org.thoughtcrime.securesms.components.ContactFilterView;
import org.thoughtcrime.securesms.components.ContactFilterView.OnFilterChangedListener;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.SelectedContact;
import org.thoughtcrime.securesms.database.DatabaseFactory;
@@ -100,7 +98,8 @@ public class InviteActivity extends PassphraseRequiredActivity implements Contac
View shareButton = findViewById(R.id.share_button);
Button smsButton = findViewById(R.id.sms_button);
Button smsCancelButton = findViewById(R.id.cancel_sms_button);
ContactFilterToolbar contactFilter = findViewById(R.id.contact_filter);
Toolbar smsToolbar = findViewById(R.id.sms_send_frame_toolbar);
ContactFilterView contactFilter = findViewById(R.id.contact_filter_edit_text);
inviteText = findViewById(R.id.invite_text);
smsSendFrame = findViewById(R.id.sms_send_frame);
@@ -121,7 +120,7 @@ public class InviteActivity extends PassphraseRequiredActivity implements Contac
smsCancelButton.setOnClickListener(new SmsCancelClickListener());
smsSendButton.setOnClickListener(new SmsSendClickListener());
contactFilter.setOnFilterChangedListener(new ContactFilterChangedListener());
contactFilter.setNavigationIcon(R.drawable.ic_search_conversation_24);
smsToolbar.setNavigationIcon(R.drawable.ic_search_conversation_24);
if (Util.isDefaultSmsProvider(this)) {
shareButton.setOnClickListener(new ShareClickListener());
@@ -150,6 +149,10 @@ public class InviteActivity extends PassphraseRequiredActivity implements Contac
updateSmsButtonText(contactsFragment.getSelectedContacts().size());
}
@Override
public void onSelectionChanged() {
}
private void sendSmsInvites() {
new SendSmsInvitesAsyncTask(this, inviteText.getText().toString())
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,

View File

@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms;
import android.os.Bundle;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
@@ -17,7 +18,7 @@ public abstract class LoggingFragment extends Fragment {
public LoggingFragment() { }
public LoggingFragment(int contentLayoutId) {
public LoggingFragment(@LayoutRes int contentLayoutId) {
super(contentLayoutId);
}

View File

@@ -9,6 +9,8 @@ import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController;
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner;
import org.thoughtcrime.securesms.devicetransfer.olddevice.OldDeviceTransferLockedDialog;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.util.AppStartup;
@@ -17,13 +19,15 @@ import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
public class MainActivity extends PassphraseRequiredActivity {
public class MainActivity extends PassphraseRequiredActivity implements VoiceNoteMediaControllerOwner {
public static final int RESULT_CONFIG_CHANGED = Activity.RESULT_FIRST_USER + 901;
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
private final MainNavigator navigator = new MainNavigator(this);
private VoiceNoteMediaController mediaController;
public static @NonNull Intent clearTop(@NonNull Context context) {
Intent intent = new Intent(context, MainActivity.class);
@@ -40,6 +44,7 @@ public class MainActivity extends PassphraseRequiredActivity {
super.onCreate(savedInstanceState, ready);
setContentView(R.layout.main_activity);
mediaController = new VoiceNoteMediaController(this);
navigator.onCreate(savedInstanceState);
handleGroupLinkInIntent(getIntent());
@@ -109,4 +114,9 @@ public class MainActivity extends PassphraseRequiredActivity {
CommunicationActions.handlePotentialProxyLinkUrl(this, data.toString());
}
}
@Override
public @NonNull VoiceNoteMediaController getVoiceNoteMediaController() {
return mediaController;
}
}

View File

@@ -9,6 +9,8 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import org.thoughtcrime.securesms.components.settings.DSLSettingsActivity;
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity;
import org.thoughtcrime.securesms.conversation.ConversationIntents;
import org.thoughtcrime.securesms.conversationlist.ConversationListArchiveFragment;
import org.thoughtcrime.securesms.conversationlist.ConversationListFragment;
@@ -69,8 +71,7 @@ public class MainNavigator {
}
public void goToAppSettings() {
Intent intent = new Intent(activity, ApplicationPreferencesActivity.class);
activity.startActivityForResult(intent, REQUEST_CONFIG_CHANGES);
activity.startActivityForResult(AppSettingsActivity.home(activity), REQUEST_CONFIG_CHANGES);
}
public void goToArchiveList() {

View File

@@ -91,7 +91,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
MediaPreviewFragment.Events
{
private final static String TAG = MediaPreviewActivity.class.getSimpleName();
private final static String TAG = Log.tag(MediaPreviewActivity.class);
private static final int NOT_IN_A_THREAD = -2;
@@ -103,6 +103,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
public static final String HIDE_ALL_MEDIA_EXTRA = "came_from_all_media";
public static final String SHOW_THREAD_EXTRA = "show_thread";
public static final String SORTING_EXTRA = "sorting";
public static final String IS_VIDEO_GIF = "is_video_gif";
private ViewPager mediaPager;
private View detailsContainer;
@@ -115,6 +116,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
private String initialMediaType;
private long initialMediaSize;
private String initialCaption;
private boolean initialMediaIsVideoGif;
private boolean leftIsRecent;
private MediaPreviewViewModel viewModel;
private ViewPagerListener viewPagerListener;
@@ -139,6 +141,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, attachment.getSize());
intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, attachment.getCaption());
intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, leftIsRecent);
intent.putExtra(MediaPreviewActivity.IS_VIDEO_GIF, attachment.isVideoGif());
intent.setDataAndType(attachment.getUri(), mediaRecord.getContentType());
return intent;
}
@@ -296,12 +299,13 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
showThread = intent.getBooleanExtra(SHOW_THREAD_EXTRA, false);
sorting = MediaDatabase.Sorting.values()[intent.getIntExtra(SORTING_EXTRA, 0)];
initialMediaUri = intent.getData();
initialMediaType = intent.getType();
initialMediaSize = intent.getLongExtra(SIZE_EXTRA, 0);
initialCaption = intent.getStringExtra(CAPTION_EXTRA);
leftIsRecent = intent.getBooleanExtra(LEFT_IS_RECENT_EXTRA, false);
restartItem = -1;
initialMediaUri = intent.getData();
initialMediaType = intent.getType();
initialMediaSize = intent.getLongExtra(SIZE_EXTRA, 0);
initialCaption = intent.getStringExtra(CAPTION_EXTRA);
leftIsRecent = intent.getBooleanExtra(LEFT_IS_RECENT_EXTRA, false);
initialMediaIsVideoGif = intent.getBooleanExtra(IS_VIDEO_GIF, false);
restartItem = -1;
}
private void initializeObservers() {
@@ -354,7 +358,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
if (isMediaInDb()) {
LoaderManager.getInstance(this).restartLoader(0, null, this);
} else {
mediaPager.setAdapter(new SingleItemPagerAdapter(getSupportFragmentManager(), initialMediaUri, initialMediaType, initialMediaSize));
mediaPager.setAdapter(new SingleItemPagerAdapter(getSupportFragmentManager(), initialMediaUri, initialMediaType, initialMediaSize, initialMediaIsVideoGif));
if (initialCaption != null) {
detailsContainer.setVisibility(View.VISIBLE);
@@ -632,21 +636,24 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
private static class SingleItemPagerAdapter extends FragmentStatePagerAdapter implements MediaItemAdapter {
private final Uri uri;
private final String mediaType;
private final long size;
private final Uri uri;
private final String mediaType;
private final long size;
private final boolean isVideoGif;
private MediaPreviewFragment mediaPreviewFragment;
SingleItemPagerAdapter(@NonNull FragmentManager fragmentManager,
@NonNull Uri uri,
@NonNull String mediaType,
long size)
long size,
boolean isVideoGif)
{
super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
this.uri = uri;
this.mediaType = mediaType;
this.size = size;
this.uri = uri;
this.mediaType = mediaType;
this.size = size;
this.isVideoGif = isVideoGif;
}
@Override
@@ -657,7 +664,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
@NonNull
@Override
public Fragment getItem(int position) {
mediaPreviewFragment = MediaPreviewFragment.newInstance(uri, mediaType, size, true);
mediaPreviewFragment = MediaPreviewFragment.newInstance(uri, mediaType, size, true, isVideoGif);
return mediaPreviewFragment;
}

View File

@@ -7,6 +7,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.concurrent.TimeUnit;
public class MuteDialog extends AlertDialog {
@@ -29,7 +31,7 @@ public class MuteDialog extends AlertDialog {
}
public static void show(final Context context, final @NonNull MuteSelectionListener listener, @Nullable Runnable cancelListener) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(context);
builder.setTitle(R.string.MuteDialog_mute_notifications);
builder.setItems(R.array.mute_durations, new DialogInterface.OnClickListener() {
@Override
@@ -38,10 +40,10 @@ public class MuteDialog extends AlertDialog {
switch (which) {
case 0: muteUntil = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1); break;
case 1: muteUntil = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(2); break;
case 1: muteUntil = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(8); break;
case 2: muteUntil = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1); break;
case 3: muteUntil = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(7); break;
case 4: muteUntil = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(365); break;
case 4: muteUntil = Long.MAX_VALUE; break;
default: muteUntil = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1); break;
}

View File

@@ -49,13 +49,14 @@ public class NewConversationActivity extends ContactSelectionActivity
{
@SuppressWarnings("unused")
private static final String TAG = NewConversationActivity.class.getSimpleName();
private static final String TAG = Log.tag(NewConversationActivity.class);
@Override
public void onCreate(Bundle bundle, boolean ready) {
super.onCreate(bundle, ready);
assert getSupportActionBar() != null;
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle(R.string.NewConversationActivity__new_message);
}
@Override
@@ -96,6 +97,10 @@ public class NewConversationActivity extends ContactSelectionActivity
return true;
}
@Override
public void onSelectionChanged() {
}
private void launch(Recipient recipient) {
long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient.getId());
Intent intent = ConversationIntents.createBuilder(this, recipient.getId(), existingThread)

View File

@@ -34,7 +34,7 @@ import org.thoughtcrime.securesms.service.KeyCachingService;
*/
public abstract class PassphraseActivity extends BaseActivity {
private static final String TAG = PassphraseActivity.class.getSimpleName();
private static final String TAG = Log.tag(PassphraseActivity.class);
private KeyCachingService keyCachingService;
private MasterSecret masterSecret;

View File

@@ -35,7 +35,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.util.Locale;
public abstract class PassphraseRequiredActivity extends BaseActivity implements MasterSecretListener {
private static final String TAG = PassphraseRequiredActivity.class.getSimpleName();
private static final String TAG = Log.tag(PassphraseRequiredActivity.class);
public static final String LOCALE_EXTRA = "locale_extra";
public static final String NEXT_INTENT_EXTRA = "next_intent";
@@ -166,7 +166,7 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
return STATE_UI_BLOCKING_UPGRADE;
} else if (!TextSecurePreferences.hasPromptedPushRegistration(this)) {
return STATE_WELCOME_PUSH_SCREEN;
} else if (SignalStore.storageServiceValues().needsAccountRestore()) {
} else if (SignalStore.storageService().needsAccountRestore()) {
return STATE_ENTER_SIGNAL_PIN;
} else if (userMustSetProfileName()) {
return STATE_CREATE_PROFILE_NAME;

View File

@@ -21,6 +21,7 @@ import android.os.Bundle;
import com.annimon.stream.Stream;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.contacts.SelectedContact;
import org.thoughtcrime.securesms.recipients.RecipientId;
@@ -38,7 +39,7 @@ public class PushContactSelectionActivity extends ContactSelectionActivity {
public static final String KEY_SELECTED_RECIPIENTS = "recipients";
@SuppressWarnings("unused")
private final static String TAG = PushContactSelectionActivity.class.getSimpleName();
private final static String TAG = Log.tag(PushContactSelectionActivity.class);
@Override
protected void onCreate(Bundle icicle, boolean ready) {
@@ -64,4 +65,8 @@ public class PushContactSelectionActivity extends ContactSelectionActivity {
setResult(RESULT_OK, resultIntent);
finish();
}
@Override
public void onSelectionChanged() {
}
}

View File

@@ -0,0 +1,48 @@
package org.thoughtcrime.securesms;
import android.os.Build;
import java.util.HashSet;
import java.util.Set;
/**
* Device hardware capability lists.
* <p>
* Moved outside of ApplicationContext as the indirection was important for API19 support with desugaring: https://issuetracker.google.com/issues/183419297
*/
final class RtcDeviceLists {
private RtcDeviceLists() {}
static Set<String> hardwareAECBlockList() {
return new HashSet<String>() {{
add("Pixel");
add("Pixel XL");
add("Moto G5");
add("Moto G (5S) Plus");
add("Moto G4");
add("TA-1053");
add("Mi A1");
add("Mi A2");
add("E5823"); // Sony z5 compact
add("Redmi Note 5");
add("FP2"); // Fairphone FP2
add("MI 5");
}};
}
static Set<String> openSlEsAllowList() {
return new HashSet<String>() {{
add("Pixel");
add("Pixel XL");
}};
}
static boolean hardwareAECBlocked() {
return hardwareAECBlockList().contains(Build.MODEL);
}
static boolean openSLESAllowed() {
return openSlEsAllowList().contains(Build.MODEL);
}
}

View File

@@ -20,7 +20,7 @@ import java.net.URISyntaxException;
public class SmsSendtoActivity extends Activity {
private static final String TAG = SmsSendtoActivity.class.getSimpleName();
private static final String TAG = Log.tag(SmsSendtoActivity.class);
@Override
protected void onCreate(Bundle savedInstanceState) {

View File

@@ -6,6 +6,7 @@ import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.util.CharacterCalculator;
import org.thoughtcrime.securesms.util.MmsCharacterCalculator;
@@ -25,7 +26,7 @@ import static org.thoughtcrime.securesms.TransportOption.Type;
public class TransportOptions {
private static final String TAG = TransportOptions.class.getSimpleName();
private static final String TAG = Log.tag(TransportOptions.class);
private final List<OnTransportChangedListener> listeners = new LinkedList<>();
private final Context context;

View File

@@ -29,7 +29,6 @@ import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Vibrator;
@@ -63,9 +62,8 @@ import androidx.fragment.app.FragmentTransaction;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.components.camera.CameraView;
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
import org.thoughtcrime.securesms.crypto.ReentrantSessionLock;
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
@@ -87,16 +85,13 @@ import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.WindowUtil;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.fingerprint.Fingerprint;
import org.whispersystems.libsignal.fingerprint.FingerprintParsingException;
import org.whispersystems.libsignal.fingerprint.FingerprintVersionMismatchException;
import org.whispersystems.libsignal.fingerprint.NumericFingerprintGenerator;
import org.whispersystems.signalservice.api.SignalSessionLock;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Locale;
@@ -218,12 +213,6 @@ public class VerifyIdentityActivity extends PassphraseRequiredActivity implement
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}
private void setActionBarNotificationBarColor(MaterialColor color) {
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(color.toActionBarColor(this)));
WindowUtil.setStatusBarColor(getWindow(), color.toStatusBarColor(this));
}
public static class VerifyDisplayFragment extends Fragment implements CompoundButton.OnCheckedChangeListener {
public static final String RECIPIENT_ID = "recipient_id";
@@ -420,11 +409,9 @@ public class VerifyIdentityActivity extends PassphraseRequiredActivity implement
} else {
Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_your_contact_is_running_an_old_version_of_signal, Toast.LENGTH_LONG).show();
}
} catch (FingerprintParsingException e) {
} catch (Exception e) {
Log.w(TAG, e);
Toast.makeText(getActivity(), R.string.VerifyIdentityActivity_the_scanned_qr_code_is_not_a_correctly_formatted_safety_number, Toast.LENGTH_LONG).show();
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
}
@@ -623,7 +610,7 @@ public class VerifyIdentityActivity extends PassphraseRequiredActivity implement
final RecipientId recipientId = recipient.getId();
SignalExecutors.BOUNDED.execute(() -> {
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
try (SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) {
if (isChecked) {
Log.i(TAG, "Saving identity: " + recipientId);
DatabaseFactory.getIdentityDatabase(getActivity())

View File

@@ -41,33 +41,37 @@ import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.components.TooltipPopup;
import org.thoughtcrime.securesms.components.sensors.DeviceOrientationMonitor;
import org.thoughtcrime.securesms.components.webrtc.CallParticipantsListUpdatePopupWindow;
import org.thoughtcrime.securesms.components.webrtc.CallParticipantsState;
import org.thoughtcrime.securesms.components.sensors.DeviceOrientationMonitor;
import org.thoughtcrime.securesms.components.webrtc.CallToastPopupWindow;
import org.thoughtcrime.securesms.components.webrtc.GroupCallSafetyNumberChangeNotificationUtil;
import org.thoughtcrime.securesms.components.webrtc.WebRtcAudioOutput;
import org.thoughtcrime.securesms.components.webrtc.WebRtcCallView;
import org.thoughtcrime.securesms.components.webrtc.WebRtcCallViewModel;
import org.thoughtcrime.securesms.components.webrtc.participantslist.CallParticipantsListDialog;
import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.messagerequests.CalleeMustAcceptMessageRequestActivity;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.ringrtc.RemotePeer;
import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.service.webrtc.SignalCallManager;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.EllapsedTimeFormatter;
import org.thoughtcrime.securesms.util.FullscreenHelper;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.signalservice.api.messages.calls.HangupMessage;
import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
import java.util.List;
import static org.thoughtcrime.securesms.components.sensors.Orientation.PORTRAIT_BOTTOM_EDGE;
public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChangeDialog.Callback {
private static final String TAG = Log.tag(WebRtcCallActivity.class);
@@ -141,7 +145,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
Log.i(TAG, "onPause");
super.onPause();
if (!isInPipMode()) {
if (!isInPipMode() || isFinishing()) {
EventBus.getDefault().unregister(this);
}
@@ -158,18 +162,24 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
Log.i(TAG, "onStop");
super.onStop();
EventBus.getDefault().unregister(this);
if (!isInPipMode() || isFinishing()) {
EventBus.getDefault().unregister(this);
}
if (!viewModel.isCallStarting()) {
CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
if (state != null && state.getCallState().isPreJoinOrNetworkUnavailable()) {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_CANCEL_PRE_JOIN_CALL);
startService(intent);
ApplicationDependencies.getSignalCallManager().cancelPreJoin();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
@@ -248,7 +258,8 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
viewModel.getWebRtcControls().observe(this, callScreen::setWebRtcControls);
viewModel.getEvents().observe(this, this::handleViewModelEvent);
viewModel.getCallTime().observe(this, this::handleCallTime);
viewModel.getCallParticipantsState().observe(this, callScreen::updateCallParticipants);
LiveDataUtil.combineLatest(viewModel.getCallParticipantsState(), viewModel.getOrientation(), (s, o) -> new Pair<>(s, o == PORTRAIT_BOTTOM_EDGE))
.observe(this, p -> callScreen.updateCallParticipants(p.first(), p.second()));
viewModel.getCallParticipantListUpdate().observe(this, participantUpdateWindow::addCallParticipantListUpdate);
viewModel.getSafetyNumberChangeEvent().observe(this, this::handleSafetyNumberChangeEvent);
viewModel.getGroupMembers().observe(this, unused -> updateGroupMembersForGroupCall());
@@ -258,19 +269,13 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
if (state != null) {
if (state.needsNewRequestSizes()) {
Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_GROUP_UPDATE_RENDERED_RESOLUTIONS);
startService(intent);
ApplicationDependencies.getSignalCallManager().updateRenderedResolutions();
}
}
});
viewModel.getOrientation().observe(this, orientation -> {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_ORIENTATION_CHANGED)
.putExtra(WebRtcCallService.EXTRA_ORIENTATION_DEGREES, orientation.getDegrees());
startService(intent);
ApplicationDependencies.getSignalCallManager().orientationChanged(orientation.getDegrees());
switch (orientation) {
case LANDSCAPE_LEFT_EDGE:
@@ -292,6 +297,12 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
} else if (event instanceof WebRtcCallViewModel.Event.ShowGroupCallSafetyNumberChange) {
SafetyNumberChangeDialog.showForGroupCall(getSupportFragmentManager(), ((WebRtcCallViewModel.Event.ShowGroupCallSafetyNumberChange) event).getIdentityRecords());
return;
} else if (event instanceof WebRtcCallViewModel.Event.SwitchToSpeaker) {
callScreen.switchToSpeakerView();
return;
} else if (event instanceof WebRtcCallViewModel.Event.ShowSwipeToSpeakerHint) {
CallToastPopupWindow.show(callScreen);
return;
}
if (isInPipMode()) {
@@ -329,30 +340,19 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
}
private void handleSetAudioHandset() {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_SET_AUDIO_SPEAKER);
startService(intent);
ApplicationDependencies.getSignalCallManager().setAudioSpeaker(false);
}
private void handleSetAudioSpeaker() {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_SET_AUDIO_SPEAKER);
intent.putExtra(WebRtcCallService.EXTRA_SPEAKER, true);
startService(intent);
ApplicationDependencies.getSignalCallManager().setAudioSpeaker(true);
}
private void handleSetAudioBluetooth() {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_SET_AUDIO_BLUETOOTH);
intent.putExtra(WebRtcCallService.EXTRA_BLUETOOTH, true);
startService(intent);
ApplicationDependencies.getSignalCallManager().setAudioBluetooth(true);
}
private void handleSetMuteAudio(boolean enabled) {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_SET_MUTE_AUDIO);
intent.putExtra(WebRtcCallService.EXTRA_MUTE, enabled);
startService(intent);
ApplicationDependencies.getSignalCallManager().setMuteAudio(enabled);
}
private void handleSetMuteVideo(boolean muted) {
@@ -366,20 +366,13 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
.ifNecessary()
.withRationaleDialog(getString(R.string.WebRtcCallActivity__to_call_s_signal_needs_access_to_your_camera, recipientDisplayName), R.drawable.ic_video_solid_24_tinted)
.withPermanentDenialDialog(getString(R.string.WebRtcCallActivity__to_call_s_signal_needs_access_to_your_camera, recipientDisplayName))
.onAllGranted(() -> {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_SET_ENABLE_VIDEO);
intent.putExtra(WebRtcCallService.EXTRA_ENABLE, !muted);
startService(intent);
})
.onAllGranted(() -> ApplicationDependencies.getSignalCallManager().setMuteVideo(!muted))
.execute();
}
}
private void handleFlipCamera() {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_FLIP_CAMERA);
startService(intent);
ApplicationDependencies.getSignalCallManager().flipCamera();
}
private void handleAnswerWithAudio() {
@@ -396,9 +389,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
callScreen.setRecipient(recipient);
callScreen.setStatus(getString(R.string.RedPhone_answering));
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_ACCEPT_CALL);
startService(intent);
ApplicationDependencies.getSignalCallManager().acceptCall(false);
})
.onAnyDenied(this::handleDenyCall)
.execute();
@@ -419,10 +410,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
callScreen.setRecipient(recipient);
callScreen.setStatus(getString(R.string.RedPhone_answering));
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_ACCEPT_CALL);
intent.putExtra(WebRtcCallService.EXTRA_ANSWER_WITH_VIDEO, true);
startService(intent);
ApplicationDependencies.getSignalCallManager().acceptCall(true);
handleSetMuteVideo(false);
})
@@ -435,9 +423,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
Recipient recipient = viewModel.getRecipient().get();
if (!recipient.equals(Recipient.UNKNOWN)) {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_DENY_CALL);
startService(intent);
ApplicationDependencies.getSignalCallManager().denyCall();
callScreen.setRecipient(recipient);
callScreen.setStatus(getString(R.string.RedPhone_ending_call));
@@ -447,9 +433,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
private void handleEndCall() {
Log.i(TAG, "Hangup pressed, handling termination now...");
Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_LOCAL_HANGUP);
startService(intent);
ApplicationDependencies.getSignalCallManager().localHangup();
}
private void handleOutgoingCall(@NonNull WebRtcViewModel event) {
@@ -480,7 +464,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
private void handleCallBusy() {
EventBus.getDefault().removeStickyEvent(WebRtcViewModel.class);
callScreen.setStatus(getString(R.string.RedPhone_busy));
delayedFinish(WebRtcCallService.BUSY_TONE_LENGTH);
delayedFinish(SignalCallManager.BUSY_TONE_LENGTH);
}
private void handleCallConnected(@NonNull WebRtcViewModel event) {
@@ -537,7 +521,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
}
private void updateGroupMembersForGroupCall() {
startService(new Intent(this, WebRtcCallService.class).setAction(WebRtcCallService.ACTION_GROUP_REQUEST_UPDATE_MEMBERS));
ApplicationDependencies.getSignalCallManager().requestUpdateGroupMembers();
}
private void updateSpeakerHint(boolean showSpeakerHint) {
@@ -551,11 +535,13 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
@Override
public void onSendAnywayAfterSafetyNumberChange(@NonNull List<RecipientId> changedRecipients) {
CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
if (state == null) {
return;
}
if (state.getGroupCallState().isConnected()) {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_GROUP_APPROVE_SAFETY_CHANGE)
.putExtra(WebRtcCallService.EXTRA_RECIPIENT_IDS, RecipientId.toSerializedList(changedRecipients));
startService(intent);
ApplicationDependencies.getSignalCallManager().groupApproveSafetyChange(changedRecipients);
} else {
viewModel.startCall(state.getLocalParticipant().isVideoEnabled());
}
@@ -569,9 +555,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
if (state != null && state.getGroupCallState().isNotIdle()) {
if (state.getCallState().isPreJoinOrNetworkUnavailable()) {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_CANCEL_PRE_JOIN_CALL);
startService(intent);
ApplicationDependencies.getSignalCallManager().cancelPreJoin();
finish();
} else {
handleEndCall();
@@ -637,11 +621,11 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
private void startCall(boolean isVideoCall) {
enableVideoIfAvailable = isVideoCall;
Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL)
.putExtra(WebRtcCallService.EXTRA_REMOTE_PEER, new RemotePeer(viewModel.getRecipient().getId()))
.putExtra(WebRtcCallService.EXTRA_OFFER_TYPE, (isVideoCall ? OfferMessage.Type.VIDEO_CALL : OfferMessage.Type.AUDIO_CALL).getCode());
startService(intent);
if (isVideoCall) {
ApplicationDependencies.getSignalCallManager().startOutgoingVideoCall(viewModel.getRecipient().get());
} else {
ApplicationDependencies.getSignalCallManager().startOutgoingAudioCall(viewModel.getRecipient().get());
}
MessageSender.onMessageSent();
}

View File

@@ -0,0 +1,44 @@
package org.thoughtcrime.securesms.animation.transitions
import android.animation.Animator
import android.animation.ObjectAnimator
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.transition.Transition
import androidx.transition.TransitionValues
private const val ALPHA = "signal.alpha_transition.alpha"
/**
* Alpha transition that can be used with [ConstraintLayout]
*/
class AlphaTransition : Transition() {
override fun captureStartValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
override fun captureEndValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
private fun captureValues(transitionValues: TransitionValues) {
val view: View = transitionValues.view
if (view !is ConstraintLayout) {
transitionValues.values[ALPHA] = view.alpha
}
}
override fun createAnimator(sceneRoot: ViewGroup, startValues: TransitionValues?, endValues: TransitionValues?): Animator? {
if (startValues == null || endValues == null) {
return null
}
val view: View = endValues.view
val startAlpha: Float = startValues.values[ALPHA] as? Float ?: view.alpha
val endAlpha: Float = endValues.values[ALPHA] as? Float ?: view.alpha
return ObjectAnimator.ofFloat(view, "alpha", startAlpha, endAlpha)
}
}

View File

@@ -0,0 +1,97 @@
package org.thoughtcrime.securesms.animation.transitions
import android.animation.Animator
import android.animation.ObjectAnimator
import android.animation.PropertyValuesHolder
import android.animation.TypeEvaluator
import android.content.Context
import android.transition.Transition
import android.transition.TransitionValues
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator
import android.view.animation.Interpolator
import androidx.annotation.RequiresApi
import org.thoughtcrime.securesms.components.AvatarImageView
private const val POSITION_ON_SCREEN = "signal.circleavatartransition.positiononscreen"
private const val WIDTH = "signal.circleavatartransition.width"
private const val HEIGHT = "signal.circleavatartransition.height"
/**
* Custom transition for Circular avatars, because once you have multiple things animating stuff was getting broken and weird.
*/
@RequiresApi(21)
class CircleAvatarTransition(context: Context, attrs: AttributeSet?) : Transition(context, attrs) {
override fun captureStartValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
override fun captureEndValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
private fun captureValues(transitionValues: TransitionValues) {
val view: View = transitionValues.view
if (view is AvatarImageView) {
val topLeft = intArrayOf(0, 0)
view.getLocationOnScreen(topLeft)
transitionValues.values[POSITION_ON_SCREEN] = topLeft
transitionValues.values[WIDTH] = view.measuredWidth
transitionValues.values[HEIGHT] = view.measuredHeight
}
}
override fun createAnimator(sceneRoot: ViewGroup, startValues: TransitionValues?, endValues: TransitionValues?): Animator? {
if (startValues == null || endValues == null) {
return null
}
val view: View = endValues.view
if (view !is AvatarImageView || view.transitionName != "avatar") {
return null
}
val startCoords: IntArray = startValues.values[POSITION_ON_SCREEN] as? IntArray ?: intArrayOf(0, 0).apply { view.getLocationOnScreen(this) }
val endCoords: IntArray = endValues.values[POSITION_ON_SCREEN] as? IntArray ?: intArrayOf(0, 0).apply { view.getLocationOnScreen(this) }
val startWidth: Int = startValues.values[WIDTH] as? Int ?: view.measuredWidth
val endWidth: Int = endValues.values[WIDTH] as? Int ?: view.measuredWidth
val startHeight: Int = startValues.values[HEIGHT] as? Int ?: view.measuredHeight
val endHeight: Int = endValues.values[HEIGHT] as? Int ?: view.measuredHeight
val startHeightOffset = (endHeight - startHeight) / 2f
val startWidthOffset = (endWidth - startWidth) / 2f
val translateXHolder = PropertyValuesHolder.ofFloat("translationX", startCoords[0] - endCoords[0] - startWidthOffset, 0f).apply {
setEvaluator(FloatInterpolatorEvaluator(DecelerateInterpolator()))
}
val translateYHolder = PropertyValuesHolder.ofFloat("translationY", startCoords[1] - endCoords[1] - startHeightOffset, 0f).apply {
setEvaluator(FloatInterpolatorEvaluator(AccelerateInterpolator()))
}
val widthRatio = startWidth.toFloat() / endWidth
val scaleXHolder = PropertyValuesHolder.ofFloat("scaleX", widthRatio, 1f)
val heightRatio = startHeight.toFloat() / endHeight
val scaleYHolder = PropertyValuesHolder.ofFloat("scaleY", heightRatio, 1f)
return ObjectAnimator.ofPropertyValuesHolder(view, translateXHolder, translateYHolder, scaleXHolder, scaleYHolder)
}
private class FloatInterpolatorEvaluator(
private val interpolator: Interpolator
) : TypeEvaluator<Float> {
override fun evaluate(fraction: Float, startValue: Float, endValue: Float): Float {
val interpolatedFraction = interpolator.getInterpolation(fraction)
val delta = endValue - startValue
return delta * interpolatedFraction + startValue
}
}
}

View File

@@ -0,0 +1,63 @@
package org.thoughtcrime.securesms.animation.transitions
import android.animation.Animator
import android.animation.ObjectAnimator
import android.animation.RectEvaluator
import android.content.Context
import android.graphics.Rect
import android.transition.Transition
import android.transition.TransitionValues
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import androidx.annotation.RequiresApi
import androidx.core.animation.addListener
import androidx.fragment.app.FragmentContainerView
private const val BOUNDS = "signal.wipedowntransition.bottom"
/**
* WipeDownTransition will animate the bottom position of a view such that it "wipes" down the screen to a final position.
*/
@RequiresApi(21)
class WipeDownTransition(context: Context, attrs: AttributeSet?) : Transition(context, attrs) {
override fun captureStartValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
override fun captureEndValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
private fun captureValues(transitionValues: TransitionValues) {
val view: View = transitionValues.view
if (view is ViewGroup) {
val rect = Rect()
view.getLocalVisibleRect(rect)
transitionValues.values[BOUNDS] = rect
}
}
override fun createAnimator(sceneRoot: ViewGroup, startValues: TransitionValues?, endValues: TransitionValues?): Animator? {
if (startValues == null || endValues == null) {
return null
}
val view: View = endValues.view
if (view !is FragmentContainerView) {
return null
}
val startBottom: Rect = startValues.values[BOUNDS] as? Rect ?: Rect().apply { view.getLocalVisibleRect(this) }
val endBottom: Rect = endValues.values[BOUNDS] as? Rect ?: Rect().apply { view.getLocalVisibleRect(this) }
return ObjectAnimator.ofObject(view, "clipBounds", RectEvaluator(), startBottom, endBottom).apply {
addListener(
onEnd = {
view.clipBounds = null
}
)
}
}
}

View File

@@ -40,6 +40,7 @@ public abstract class Attachment {
private final boolean voiceNote;
private final boolean borderless;
private final boolean videoGif;
private final int width;
private final int height;
private final boolean quote;
@@ -72,6 +73,7 @@ public abstract class Attachment {
@Nullable String fastPreflightId,
boolean voiceNote,
boolean borderless,
boolean videoGif,
int width,
int height,
boolean quote,
@@ -94,6 +96,7 @@ public abstract class Attachment {
this.fastPreflightId = fastPreflightId;
this.voiceNote = voiceNote;
this.borderless = borderless;
this.videoGif = videoGif;
this.width = width;
this.height = height;
this.quote = quote;
@@ -108,6 +111,8 @@ public abstract class Attachment {
@Nullable
public abstract Uri getUri();
public abstract @Nullable Uri getPublicUri();
public int getTransferState() {
return transferState;
}
@@ -168,6 +173,10 @@ public abstract class Attachment {
return borderless;
}
public boolean isVideoGif() {
return videoGif;
}
public int getWidth() {
return width;
}

View File

@@ -36,6 +36,7 @@ public class DatabaseAttachment extends Attachment {
String fastPreflightId,
boolean voiceNote,
boolean borderless,
boolean videoGif,
int width,
int height,
boolean quote,
@@ -47,7 +48,7 @@ public class DatabaseAttachment extends Attachment {
int displayOrder,
long uploadTimestamp)
{
super(contentType, transferProgress, size, fileName, cdnNumber, location, key, relay, digest, fastPreflightId, voiceNote, borderless, width, height, quote, uploadTimestamp, caption, stickerLocator, blurHash, audioHash, transformProperties);
super(contentType, transferProgress, size, fileName, cdnNumber, location, key, relay, digest, fastPreflightId, voiceNote, borderless, videoGif, width, height, quote, uploadTimestamp, caption, stickerLocator, blurHash, audioHash, transformProperties);
this.attachmentId = attachmentId;
this.hasData = hasData;
this.hasThumbnail = hasThumbnail;
@@ -65,6 +66,15 @@ public class DatabaseAttachment extends Attachment {
}
}
@Override
public @Nullable Uri getPublicUri() {
if (hasData) {
return PartAuthority.getAttachmentPublicUri(getUri());
} else {
return null;
}
}
public AttachmentId getAttachmentId() {
return attachmentId;
}

View File

@@ -11,7 +11,7 @@ import org.thoughtcrime.securesms.database.MmsDatabase;
public class MmsNotificationAttachment extends Attachment {
public MmsNotificationAttachment(int status, long size) {
super("application/mms", getTransferStateFromStatus(status), size, null, 0, null, null, null, null, null, false, false, 0, 0, false, 0, null, null, null, null, null);
super("application/mms", getTransferStateFromStatus(status), size, null, 0, null, null, null, null, null, false, false, false, 0, 0, false, 0, null, null, null, null, null);
}
@Nullable
@@ -20,6 +20,11 @@ public class MmsNotificationAttachment extends Attachment {
return null;
}
@Override
public @Nullable Uri getPublicUri() {
return null;
}
private static int getTransferStateFromStatus(int status) {
if (status == MmsDatabase.Status.DOWNLOAD_INITIALIZED ||
status == MmsDatabase.Status.DOWNLOAD_NO_CONNECTIVITY)

View File

@@ -30,6 +30,7 @@ public class PointerAttachment extends Attachment {
@Nullable String fastPreflightId,
boolean voiceNote,
boolean borderless,
boolean videoGif,
int width,
int height,
long uploadTimestamp,
@@ -37,7 +38,7 @@ public class PointerAttachment extends Attachment {
@Nullable StickerLocator stickerLocator,
@Nullable BlurHash blurHash)
{
super(contentType, transferState, size, fileName, cdnNumber, location, key, relay, digest, fastPreflightId, voiceNote, borderless, width, height, false, uploadTimestamp, caption, stickerLocator, blurHash, null, null);
super(contentType, transferState, size, fileName, cdnNumber, location, key, relay, digest, fastPreflightId, voiceNote, borderless, videoGif, width, height, false, uploadTimestamp, caption, stickerLocator, blurHash, null, null);
}
@Nullable
@@ -46,6 +47,11 @@ public class PointerAttachment extends Attachment {
return null;
}
@Override
public @Nullable Uri getPublicUri() {
return null;
}
public static List<Attachment> forPointers(Optional<List<SignalServiceAttachment>> pointers) {
List<Attachment> results = new LinkedList<>();
@@ -106,6 +112,7 @@ public class PointerAttachment extends Attachment {
fastPreflightId,
pointer.get().asPointer().getVoiceNote(),
pointer.get().asPointer().isBorderless(),
pointer.get().asPointer().isGif(),
pointer.get().asPointer().getWidth(),
pointer.get().asPointer().getHeight(),
pointer.get().asPointer().getUploadTimestamp(),
@@ -130,6 +137,7 @@ public class PointerAttachment extends Attachment {
null,
false,
false,
false,
thumbnail != null ? thumbnail.asPointer().getWidth() : 0,
thumbnail != null ? thumbnail.asPointer().getHeight() : 0,
thumbnail != null ? thumbnail.asPointer().getUploadTimestamp() : 0,

View File

@@ -16,11 +16,16 @@ import org.thoughtcrime.securesms.database.AttachmentDatabase;
public class TombstoneAttachment extends Attachment {
public TombstoneAttachment(@NonNull String contentType, boolean quote) {
super(contentType, AttachmentDatabase.TRANSFER_PROGRESS_DONE, 0, null, 0, null, null, null, null, null, false, false, 0, 0, quote, 0, null, null, null, null, null);
super(contentType, AttachmentDatabase.TRANSFER_PROGRESS_DONE, 0, null, 0, null, null, null, null, null, false, false, false, 0, 0, quote, 0, null, null, null, null, null);
}
@Override
public @Nullable Uri getUri() {
return null;
}
@Override
public @Nullable Uri getPublicUri() {
return null;
}
}

View File

@@ -21,6 +21,7 @@ public class UriAttachment extends Attachment {
@Nullable String fileName,
boolean voiceNote,
boolean borderless,
boolean videoGif,
boolean quote,
@Nullable String caption,
@Nullable StickerLocator stickerLocator,
@@ -28,7 +29,7 @@ public class UriAttachment extends Attachment {
@Nullable AudioHash audioHash,
@Nullable TransformProperties transformProperties)
{
this(uri, contentType, transferState, size, 0, 0, fileName, null, voiceNote, borderless, quote, caption, stickerLocator, blurHash, audioHash, transformProperties);
this(uri, contentType, transferState, size, 0, 0, fileName, null, voiceNote, borderless, videoGif, quote, caption, stickerLocator, blurHash, audioHash, transformProperties);
}
public UriAttachment(@NonNull Uri dataUri,
@@ -41,6 +42,7 @@ public class UriAttachment extends Attachment {
@Nullable String fastPreflightId,
boolean voiceNote,
boolean borderless,
boolean videoGif,
boolean quote,
@Nullable String caption,
@Nullable StickerLocator stickerLocator,
@@ -48,7 +50,7 @@ public class UriAttachment extends Attachment {
@Nullable AudioHash audioHash,
@Nullable TransformProperties transformProperties)
{
super(contentType, transferState, size, fileName, 0, null, null, null, null, fastPreflightId, voiceNote, borderless, width, height, quote, 0, caption, stickerLocator, blurHash, audioHash, transformProperties);
super(contentType, transferState, size, fileName, 0, null, null, null, null, fastPreflightId, voiceNote, borderless, videoGif, width, height, quote, 0, caption, stickerLocator, blurHash, audioHash, transformProperties);
this.dataUri = dataUri;
}
@@ -58,6 +60,11 @@ public class UriAttachment extends Attachment {
return dataUri;
}
@Override
public @Nullable Uri getPublicUri() {
return null;
}
@Override
public boolean equals(Object other) {
return other != null && other instanceof UriAttachment && ((UriAttachment) other).dataUri.equals(this.dataUri);

View File

@@ -20,7 +20,7 @@ import java.nio.ByteBuffer;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class AudioCodec {
private static final String TAG = AudioCodec.class.getSimpleName();
private static final String TAG = Log.tag(AudioCodec.class);
private static final int SAMPLE_RATE = 44100;
private static final int SAMPLE_RATE_INDEX = 4;

View File

@@ -11,11 +11,11 @@ import androidx.annotation.NonNull;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.components.voice.VoiceNoteDraft;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.whispersystems.libsignal.util.Pair;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
@@ -23,7 +23,7 @@ import java.util.concurrent.ExecutorService;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class AudioRecorder {
private static final String TAG = AudioRecorder.class.getSimpleName();
private static final String TAG = Log.tag(AudioRecorder.class);
private static final ExecutorService executor = SignalExecutors.newCachedSingleThreadExecutor("signal-AudioRecorder");
@@ -51,7 +51,7 @@ public class AudioRecorder {
captureUri = BlobProvider.getInstance()
.forData(new ParcelFileDescriptor.AutoCloseInputStream(fds[0]), 0)
.withMimeType(MediaUtil.AUDIO_AAC)
.createForSingleSessionOnDiskAsync(context, () -> Log.i(TAG, "Write successful."), e -> Log.w(TAG, "Error during recording", e));
.createForDraftAttachmentAsync(context, () -> Log.i(TAG, "Write successful."), e -> Log.w(TAG, "Error during recording", e));
audioCodec = new AudioCodec();
audioCodec.start(new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]));
@@ -61,10 +61,10 @@ public class AudioRecorder {
});
}
public @NonNull ListenableFuture<Pair<Uri, Long>> stopRecording() {
public @NonNull ListenableFuture<VoiceNoteDraft> stopRecording() {
Log.i(TAG, "stopRecording()");
final SettableFuture<Pair<Uri, Long>> future = new SettableFuture<>();
final SettableFuture<VoiceNoteDraft> future = new SettableFuture<>();
executor.execute(() -> {
if (audioCodec == null) {
@@ -76,7 +76,7 @@ public class AudioRecorder {
try {
long size = MediaUtil.getMediaSize(context, captureUri);
sendToFuture(future, new Pair<>(captureUri, size));
sendToFuture(future, new VoiceNoteDraft(captureUri, size));
} catch (IOException ioe) {
Log.w(TAG, ioe);
sendToFuture(future, ioe);

View File

@@ -65,12 +65,6 @@ public final class AudioWaveForm {
return;
}
if (!(attachment instanceof DatabaseAttachment)) {
Log.i(TAG, "Not yet in database");
ThreadUtil.runOnMain(onFailure);
return;
}
String cacheKey = uri.toString();
AudioFileInfo cached = WAVE_FORM_CACHE.get(cacheKey);
if (cached != null) {
@@ -104,26 +98,46 @@ public final class AudioWaveForm {
}
}
try {
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
DatabaseAttachment dbAttachment = (DatabaseAttachment) attachment;
long startTime = System.currentTimeMillis();
if (attachment instanceof DatabaseAttachment) {
try {
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
DatabaseAttachment dbAttachment = (DatabaseAttachment) attachment;
long startTime = System.currentTimeMillis();
attachmentDatabase.writeAudioHash(dbAttachment.getAttachmentId(), AudioWaveFormData.getDefaultInstance());
attachmentDatabase.writeAudioHash(dbAttachment.getAttachmentId(), AudioWaveFormData.getDefaultInstance());
Log.i(TAG, String.format("Starting wave form generation (%s)", cacheKey));
Log.i(TAG, String.format("Starting wave form generation (%s)", cacheKey));
AudioFileInfo fileInfo = generateWaveForm(uri);
AudioFileInfo fileInfo = generateWaveForm(uri);
Log.i(TAG, String.format(Locale.US, "Audio wave form generation time %d ms (%s)", System.currentTimeMillis() - startTime, cacheKey));
Log.i(TAG, String.format(Locale.US, "Audio wave form generation time %d ms (%s)", System.currentTimeMillis() - startTime, cacheKey));
attachmentDatabase.writeAudioHash(dbAttachment.getAttachmentId(), fileInfo.toDatabaseProtobuf());
attachmentDatabase.writeAudioHash(dbAttachment.getAttachmentId(), fileInfo.toDatabaseProtobuf());
WAVE_FORM_CACHE.put(cacheKey, fileInfo);
ThreadUtil.runOnMain(() -> onSuccess.accept(fileInfo));
} catch (Throwable e) {
Log.w(TAG, "Failed to create audio wave form for " + cacheKey, e);
ThreadUtil.runOnMain(onFailure);
WAVE_FORM_CACHE.put(cacheKey, fileInfo);
ThreadUtil.runOnMain(() -> onSuccess.accept(fileInfo));
} catch (Throwable e) {
Log.w(TAG, "Failed to create audio wave form for " + cacheKey, e);
ThreadUtil.runOnMain(onFailure);
}
} else {
try {
Log.i(TAG, "Not in database and not cached. Generating wave form on-the-fly.");
long startTime = System.currentTimeMillis();
Log.i(TAG, String.format("Starting wave form generation (%s)", cacheKey));
AudioFileInfo fileInfo = generateWaveForm(uri);
Log.i(TAG, String.format(Locale.US, "Audio wave form generation time %d ms (%s)", System.currentTimeMillis() - startTime, cacheKey));
WAVE_FORM_CACHE.put(cacheKey, fileInfo);
ThreadUtil.runOnMain(() -> onSuccess.accept(fileInfo));
} catch (IOException e) {
Log.w(TAG, "Failed to create audio wave form for " + cacheKey, e);
ThreadUtil.runOnMain(onFailure);
}
}
});
}

View File

@@ -74,7 +74,7 @@ public class BackupDialog {
BackupPassphrase.set(context, Util.join(password, " "));
TextSecurePreferences.setNextBackupTime(context, 0);
TextSecurePreferences.setBackupEnabled(context, true);
SignalStore.settings().setBackupEnabled(true);
LocalBackupListener.schedule(context);
onBackupsEnabled.run();

View File

@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.backup;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -11,8 +10,8 @@ import androidx.annotation.StringRes;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity;
import org.thoughtcrime.securesms.notifications.NotificationCancellationHelper;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
@@ -39,11 +38,7 @@ public enum BackupFileIOError {
}
public void postNotification(@NonNull Context context) {
Intent intent = new Intent(context, ApplicationPreferencesActivity.class);
intent.putExtra(ApplicationPreferencesActivity.LAUNCH_TO_BACKUPS_FRAGMENT, true);
PendingIntent pendingIntent = PendingIntent.getActivity(context, -1, intent, 0);
PendingIntent pendingIntent = PendingIntent.getActivity(context, -1, AppSettingsActivity.backups(context), 0);
Notification backupFailedNotification = new NotificationCompat.Builder(context, NotificationChannels.FAILURES)
.setSmallIcon(R.drawable.ic_signal_backup)
.setContentTitle(context.getString(titleId))

View File

@@ -18,7 +18,7 @@ public final class BackupPassphrase {
private BackupPassphrase() {
}
private static final String TAG = BackupPassphrase.class.getSimpleName();
private static final String TAG = Log.tag(BackupPassphrase.class);
public static @Nullable String get(@NonNull Context context) {
String passphrase = TextSecurePreferences.getBackupPassphrase(context);

View File

@@ -5,6 +5,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.greenrobot.eventbus.EventBus;
import org.signal.core.util.logging.Log;
import org.whispersystems.libsignal.util.ByteUtil;
import java.security.MessageDigest;
@@ -13,7 +14,7 @@ import java.security.NoSuchAlgorithmException;
public abstract class FullBackupBase {
@SuppressWarnings("unused")
private static final String TAG = FullBackupBase.class.getSimpleName();
private static final String TAG = Log.tag(FullBackupBase.class);
static class BackupStream {
static @NonNull byte[] getBackupKey(@NonNull String passphrase, @Nullable byte[] salt) {

View File

@@ -24,12 +24,16 @@ import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.EmojiSearchDatabase;
import org.thoughtcrime.securesms.database.GroupReceiptDatabase;
import org.thoughtcrime.securesms.database.KeyValueDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.OneTimePreKeyDatabase;
import org.thoughtcrime.securesms.database.PendingRetryReceiptDatabase;
import org.thoughtcrime.securesms.database.SearchDatabase;
import org.thoughtcrime.securesms.database.SenderKeyDatabase;
import org.thoughtcrime.securesms.database.SenderKeySharedDatabase;
import org.thoughtcrime.securesms.database.SessionDatabase;
import org.thoughtcrime.securesms.database.SignedPreKeyDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
@@ -38,6 +42,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.keyvalue.KeyValueDataSet;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.service.PendingRetryReceiptManager;
import org.thoughtcrime.securesms.util.SetUtil;
import org.thoughtcrime.securesms.util.Stopwatch;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -68,14 +73,18 @@ import javax.crypto.spec.SecretKeySpec;
public class FullBackupExporter extends FullBackupBase {
private static final String TAG = FullBackupExporter.class.getSimpleName();
private static final String TAG = Log.tag(FullBackupExporter.class);
private static final Set<String> BLACKLISTED_TABLES = SetUtil.newHashSet(
SignedPreKeyDatabase.TABLE_NAME,
OneTimePreKeyDatabase.TABLE_NAME,
SessionDatabase.TABLE_NAME,
SearchDatabase.SMS_FTS_TABLE_NAME,
SearchDatabase.MMS_FTS_TABLE_NAME
SearchDatabase.MMS_FTS_TABLE_NAME,
EmojiSearchDatabase.TABLE_NAME,
SenderKeyDatabase.TABLE_NAME,
SenderKeySharedDatabase.TABLE_NAME,
PendingRetryReceiptDatabase.TABLE_NAME
);
public static void export(@NonNull Context context,
@@ -210,11 +219,11 @@ public class FullBackupExporter extends FullBackupBase {
String type = cursor.getString(2);
if (sql != null) {
boolean isSmsFtsSecretTable = name != null && !name.equals(SearchDatabase.SMS_FTS_TABLE_NAME) && name.startsWith(SearchDatabase.SMS_FTS_TABLE_NAME);
boolean isMmsFtsSecretTable = name != null && !name.equals(SearchDatabase.MMS_FTS_TABLE_NAME) && name.startsWith(SearchDatabase.MMS_FTS_TABLE_NAME);
boolean isEmojiFtsSecretTable = name != null && !name.equals(EmojiSearchDatabase.TABLE_NAME) && name.startsWith(EmojiSearchDatabase.TABLE_NAME);
boolean isSmsFtsSecretTable = name != null && !name.equals(SearchDatabase.SMS_FTS_TABLE_NAME) && name.startsWith(SearchDatabase.SMS_FTS_TABLE_NAME);
boolean isMmsFtsSecretTable = name != null && !name.equals(SearchDatabase.MMS_FTS_TABLE_NAME) && name.startsWith(SearchDatabase.MMS_FTS_TABLE_NAME);
if (!isSmsFtsSecretTable && !isMmsFtsSecretTable) {
if (!isSmsFtsSecretTable && !isMmsFtsSecretTable && !isEmojiFtsSecretTable) {
if ("table".equals(type)) {
tables.add(name);
}

View File

@@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.backup.BackupProtos.Sticker;
import org.thoughtcrime.securesms.crypto.AttachmentSecret;
import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.EmojiSearchDatabase;
import org.thoughtcrime.securesms.database.KeyValueDatabase;
import org.thoughtcrime.securesms.database.SearchDatabase;
import org.thoughtcrime.securesms.database.StickerDatabase;
@@ -65,7 +66,7 @@ import javax.crypto.spec.SecretKeySpec;
public class FullBackupImporter extends FullBackupBase {
@SuppressWarnings("unused")
private static final String TAG = FullBackupImporter.class.getSimpleName();
private static final String TAG = Log.tag(FullBackupImporter.class);
public static void importFile(@NonNull Context context, @NonNull AttachmentSecret attachmentSecret,
@NonNull SQLiteDatabase db, @NonNull Uri uri, @NonNull String passphrase)
@@ -136,9 +137,10 @@ public class FullBackupImporter extends FullBackupBase {
private static void processStatement(@NonNull SQLiteDatabase db, SqlStatement statement) {
boolean isForSmsFtsSecretTable = statement.getStatement().contains(SearchDatabase.SMS_FTS_TABLE_NAME + "_");
boolean isForMmsFtsSecretTable = statement.getStatement().contains(SearchDatabase.MMS_FTS_TABLE_NAME + "_");
boolean isForEmojiSecretTable = statement.getStatement().contains(EmojiSearchDatabase.TABLE_NAME + "_");
boolean isForSqliteSecretTable = statement.getStatement().toLowerCase().startsWith("create table sqlite_");
if (isForSmsFtsSecretTable || isForMmsFtsSecretTable || isForSqliteSecretTable) {
if (isForSmsFtsSecretTable || isForMmsFtsSecretTable || isForEmojiSecretTable || isForSqliteSecretTable) {
Log.i(TAG, "Ignoring import for statement: " + statement.getStatement());
return;
}

View File

@@ -5,7 +5,6 @@ import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.ViewSwitcher;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
@@ -18,7 +17,7 @@ import com.google.android.material.snackbar.Snackbar;
import org.thoughtcrime.securesms.ContactSelectionListFragment;
import org.thoughtcrime.securesms.PassphraseRequiredActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
import org.thoughtcrime.securesms.components.ContactFilterView;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
@@ -47,27 +46,26 @@ public class BlockedUsersActivity extends PassphraseRequiredActivity implements
viewModel = ViewModelProviders.of(this, factory).get(BlockedUsersViewModel.class);
ViewSwitcher viewSwitcher = findViewById(R.id.toolbar_switcher);
Toolbar toolbar = findViewById(R.id.toolbar);
ContactFilterToolbar contactFilterToolbar = findViewById(R.id.filter_toolbar);
View container = findViewById(R.id.fragment_container);
Toolbar toolbar = findViewById(R.id.toolbar);
ContactFilterView contactFilterView = findViewById(R.id.contact_filter_edit_text);
View container = findViewById(R.id.fragment_container);
toolbar.setNavigationOnClickListener(unused -> onBackPressed());
contactFilterToolbar.setNavigationOnClickListener(unused -> onBackPressed());
contactFilterToolbar.setOnFilterChangedListener(query -> {
contactFilterView.setOnFilterChangedListener(query -> {
Fragment fragment = getSupportFragmentManager().findFragmentByTag(CONTACT_SELECTION_FRAGMENT);
if (fragment != null) {
((ContactSelectionListFragment) fragment).setQueryFilter(query);
}
});
contactFilterToolbar.setHint(R.string.BlockedUsersActivity__add_blocked_user);
contactFilterView.setHint(R.string.BlockedUsersActivity__add_blocked_user);
//noinspection CodeBlock2Expr
getSupportFragmentManager().addOnBackStackChangedListener(() -> {
viewSwitcher.setDisplayedChild(getSupportFragmentManager().getBackStackEntryCount());
if (getSupportFragmentManager().getBackStackEntryCount() == 1) {
contactFilterToolbar.focusAndShowKeyboard();
contactFilterView.setVisibility(View.VISIBLE);
contactFilterView.focusAndShowKeyboard();
} else {
contactFilterView.setVisibility(View.GONE);
}
});
@@ -119,6 +117,10 @@ public class BlockedUsersActivity extends PassphraseRequiredActivity implements
}
@Override
public void onSelectionChanged() {
}
@Override
public void handleAddUserToBlockedList() {
ContactSelectionListFragment fragment = new ContactSelectionListFragment();
@@ -164,6 +166,6 @@ public class BlockedUsersActivity extends PassphraseRequiredActivity implements
throw new IllegalArgumentException("Unsupported event type " + event);
}
Snackbar.make(view, getString(messageResId, displayName), Snackbar.LENGTH_SHORT).show();
Snackbar.make(view, getString(messageResId, displayName), Snackbar.LENGTH_SHORT).setTextColor(Color.WHITE).show();
}
}

View File

@@ -91,14 +91,14 @@ public enum MaterialColor {
}
public @ColorRes int toQuoteBarColorResource(@NonNull Context context, boolean outgoing) {
if (outgoing) {
if (!outgoing) {
return isDarkTheme(context) ? tintColor : shadeColor ;
}
return R.color.core_white;
}
public @ColorInt int toQuoteBackgroundColor(@NonNull Context context, boolean outgoing) {
if (outgoing) {
if (!outgoing) {
int color = toConversationColor(context);
int alpha = isDarkTheme(context) ? (int) (0.2 * 255) : (int) (0.4 * 255);
return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
@@ -108,7 +108,7 @@ public enum MaterialColor {
}
public @ColorInt int toQuoteFooterColor(@NonNull Context context, boolean outgoing) {
if (outgoing) {
if (!outgoing) {
int color = toConversationColor(context);
int alpha = isDarkTheme(context) ? (int) (0.4 * 255) : (int) (0.6 * 255);
return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));

View File

@@ -9,11 +9,12 @@ import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
public class AlertView extends LinearLayout {
private static final String TAG = AlertView.class.getSimpleName();
private static final String TAG = Log.tag(AlertView.class);
private ImageView approvalIndicator;
private ImageView failedIndicator;
@@ -68,4 +69,10 @@ public class AlertView extends LinearLayout {
approvalIndicator.setVisibility(View.GONE);
failedIndicator.setVisibility(View.VISIBLE);
}
public void setRateLimited() {
this.setVisibility(View.VISIBLE);
approvalIndicator.setVisibility(View.VISIBLE);
failedIndicator.setVisibility(View.GONE);
}
}

View File

@@ -29,6 +29,7 @@ import com.pnikosis.materialishprogress.ProgressWheel;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.audio.AudioWaveForm;
import org.thoughtcrime.securesms.components.voice.VoiceNotePlaybackState;
@@ -42,19 +43,23 @@ import java.util.concurrent.TimeUnit;
public final class AudioView extends FrameLayout {
private static final String TAG = AudioView.class.getSimpleName();
private static final String TAG = Log.tag(AudioView.class);
private static final int MODE_NORMAL = 0;
private static final int MODE_SMALL = 1;
private static final int MODE_DRAFT = 2;
private static final int FORWARDS = 1;
private static final int REVERSE = -1;
@NonNull private final AnimatingToggle controlToggle;
@NonNull private final View progressAndPlay;
@NonNull private final LottieAnimationView playPauseButton;
@NonNull private final ImageView downloadButton;
@NonNull private final ProgressWheel circleProgress;
@NonNull private final SeekBar seekBar;
private final boolean smallView;
private final boolean autoRewind;
@NonNull private final AnimatingToggle controlToggle;
@NonNull private final View progressAndPlay;
@NonNull private final LottieAnimationView playPauseButton;
@NonNull private final ImageView downloadButton;
@Nullable private final ProgressWheel circleProgress;
@NonNull private final SeekBar seekBar;
private final boolean smallView;
private final boolean autoRewind;
@Nullable private final TextView duration;
@@ -86,10 +91,23 @@ public final class AudioView extends FrameLayout {
try {
typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.AudioView, 0, 0);
smallView = typedArray.getBoolean(R.styleable.AudioView_small, false);
int mode = typedArray.getInteger(R.styleable.AudioView_audioView_mode, MODE_NORMAL);
smallView = mode == MODE_SMALL;
autoRewind = typedArray.getBoolean(R.styleable.AudioView_autoRewind, false);
inflate(context, smallView ? R.layout.audio_view_small : R.layout.audio_view, this);
switch (mode) {
case MODE_NORMAL:
inflate(context, R.layout.audio_view, this);
break;
case MODE_SMALL:
inflate(context, R.layout.audio_view_small, this);
break;
case MODE_DRAFT:
inflate(context, R.layout.audio_view_draft, this);
break;
default:
throw new IllegalStateException("Unsupported mode: " + mode);
}
this.controlToggle = findViewById(R.id.control_toggle);
this.playPauseButton = findViewById(R.id.play);
@@ -109,7 +127,7 @@ public final class AudioView extends FrameLayout {
this.waveFormUnplayedBarsColor = typedArray.getColor(R.styleable.AudioView_waveformUnplayedBarsColor, Color.WHITE);
this.waveFormThumbTint = typedArray.getColor(R.styleable.AudioView_waveformThumbTint, Color.WHITE);
progressAndPlay.getBackground().setColorFilter(typedArray.getColor(R.styleable.AudioView_progressAndPlayTint, Color.BLACK), PorterDuff.Mode.SRC_IN);
setProgressAndPlayBackgroundTint(typedArray.getColor(R.styleable.AudioView_progressAndPlayTint, Color.BLACK));
} finally {
if (typedArray != null) {
typedArray.recycle();
@@ -129,6 +147,10 @@ public final class AudioView extends FrameLayout {
EventBus.getDefault().unregister(this);
}
public void setProgressAndPlayBackgroundTint(@ColorInt int color) {
progressAndPlay.getBackground().setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
public Observer<VoiceNotePlaybackState> getPlaybackStateObserver() {
return playbackStateObserver;
}
@@ -157,16 +179,20 @@ public final class AudioView extends FrameLayout {
controlToggle.displayQuick(downloadButton);
seekBar.setEnabled(false);
downloadButton.setOnClickListener(new DownloadClickedListener(audio));
if (circleProgress.isSpinning()) circleProgress.stopSpinning();
circleProgress.setVisibility(View.GONE);
if (circleProgress != null) {
if (circleProgress.isSpinning()) circleProgress.stopSpinning();
circleProgress.setVisibility(View.GONE);
}
} else if (showControls && audio.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_STARTED) {
controlToggle.displayQuick(progressAndPlay);
seekBar.setEnabled(false);
circleProgress.setVisibility(View.VISIBLE);
circleProgress.spin();
if (circleProgress != null) {
circleProgress.setVisibility(View.VISIBLE);
circleProgress.spin();
}
} else {
seekBar.setEnabled(true);
if (circleProgress.isSpinning()) circleProgress.stopSpinning();
if (circleProgress != null && circleProgress.isSpinning()) circleProgress.stopSpinning();
showPlayButton();
}
@@ -210,10 +236,11 @@ public final class AudioView extends FrameLayout {
private void onPlaybackState(@NonNull VoiceNotePlaybackState voiceNotePlaybackState) {
onDuration(voiceNotePlaybackState.getUri(), voiceNotePlaybackState.getTrackDuration());
onStart(voiceNotePlaybackState.getUri(), voiceNotePlaybackState.isAutoReset());
onProgress(voiceNotePlaybackState.getUri(),
(double) voiceNotePlaybackState.getPlayheadPositionMillis() / voiceNotePlaybackState.getTrackDuration(),
voiceNotePlaybackState.getPlayheadPositionMillis());
onSpeedChanged(voiceNotePlaybackState.getUri(), voiceNotePlaybackState.getSpeed());
onStart(voiceNotePlaybackState.getUri(), voiceNotePlaybackState.isPlaying(), voiceNotePlaybackState.isAutoReset());
}
private void onDuration(@NonNull Uri uri, long durationMillis) {
@@ -222,8 +249,8 @@ public final class AudioView extends FrameLayout {
}
}
private void onStart(@NonNull Uri uri, boolean autoReset) {
if (!isTarget(uri)) {
private void onStart(@NonNull Uri uri, boolean statePlaying, boolean autoReset) {
if (!isTarget(uri) || !statePlaying) {
if (hasAudioUri()) {
onStop(audioSlide.getUri(), autoReset);
}
@@ -273,6 +300,12 @@ public final class AudioView extends FrameLayout {
}
}
private void onSpeedChanged(@NonNull Uri uri, float speed) {
if (callbacks != null) {
callbacks.onSpeedChanged(speed, isTarget(uri));
}
}
private boolean isTarget(@NonNull Uri uri) {
return hasAudioUri() && Objects.equals(uri, audioSlide.getUri());
}
@@ -317,7 +350,7 @@ public final class AudioView extends FrameLayout {
duration.setText(getResources().getString(R.string.AudioView_duration, remainingSecs / 60, remainingSecs % 60));
}
if (smallView) {
if (smallView && circleProgress != null) {
circleProgress.setInstantProgress(seekBar.getProgress() == 0 ? 1 : progress);
}
}
@@ -328,7 +361,10 @@ public final class AudioView extends FrameLayout {
new LottieValueCallback<>(new SimpleColorFilter(foregroundTint))));
this.downloadButton.setColorFilter(foregroundTint, PorterDuff.Mode.SRC_IN);
this.circleProgress.setBarColor(foregroundTint);
if (circleProgress != null) {
this.circleProgress.setBarColor(foregroundTint);
}
if (this.duration != null) {
this.duration.setTextColor(foregroundTint);
@@ -371,11 +407,14 @@ public final class AudioView extends FrameLayout {
}
private void showPlayButton() {
if (!smallView) {
circleProgress.setVisibility(GONE);
} else if (seekBar.getProgress() == 0) {
circleProgress.setInstantProgress(1);
if (circleProgress != null) {
if (!smallView) {
circleProgress.setVisibility(GONE);
} else if (seekBar.getProgress() == 0) {
circleProgress.setInstantProgress(1);
}
}
playPauseButton.setVisibility(VISIBLE);
controlToggle.displayQuick(progressAndPlay);
}
@@ -450,6 +489,8 @@ public final class AudioView extends FrameLayout {
if (callbacks != null) {
if (wasPlaying) {
callbacks.onSeekTo(audioSlide.getUri(), getProgress());
} else {
callbacks.onProgressUpdated(durationMillis, Math.round(durationMillis * getProgress()));
}
}
}
@@ -464,7 +505,7 @@ public final class AudioView extends FrameLayout {
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventAsync(final PartProgressEvent event) {
if (audioSlide != null && event.attachment.equals(audioSlide.asAttachment())) {
if (audioSlide != null && circleProgress != null && event.attachment.equals(audioSlide.asAttachment())) {
circleProgress.setInstantProgress(((float) event.progress) / event.total);
}
}
@@ -474,6 +515,7 @@ public final class AudioView extends FrameLayout {
void onPause(@NonNull Uri audioUri);
void onSeekTo(@NonNull Uri audioUri, double progress);
void onStopAndReset(@NonNull Uri audioUri);
void onSpeedChanged(float speed, boolean isPlaying);
void onProgressUpdated(long durationMillis, long playheadMillis);
}
}

View File

@@ -2,35 +2,51 @@ package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.fragment.app.FragmentActivity;
import com.bumptech.glide.load.MultiTransformation;
import com.bumptech.glide.load.Transformation;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.bitmap.CircleCrop;
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy;
import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.transition.Transition;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.components.settings.conversation.ConversationSettingsActivity;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
import org.thoughtcrime.securesms.groups.ui.managegroup.ManageGroupActivity;
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
import org.thoughtcrime.securesms.conversation.colors.ChatColors;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequest;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.ui.bottomsheet.RecipientBottomSheetDialogFragment;
import org.thoughtcrime.securesms.recipients.ui.managerecipient.ManageRecipientActivity;
import org.thoughtcrime.securesms.util.AvatarUtil;
import org.thoughtcrime.securesms.util.BlurTransformation;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.Util;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public final class AvatarImageView extends AppCompatImageView {
@@ -39,7 +55,7 @@ public final class AvatarImageView extends AppCompatImageView {
private static final int SIZE_SMALL = 2;
@SuppressWarnings("unused")
private static final String TAG = AvatarImageView.class.getSimpleName();
private static final String TAG = Log.tag(AvatarImageView.class);
private static final Paint LIGHT_THEME_OUTLINE_PAINT = new Paint();
private static final Paint DARK_THEME_OUTLINE_PAINT = new Paint();
@@ -61,6 +77,9 @@ public final class AvatarImageView extends AppCompatImageView {
private Paint outlinePaint;
private OnClickListener listener;
private Recipient.FallbackPhotoProvider fallbackPhotoProvider;
private boolean blurred;
private ChatColors chatColors;
private FixedSizeTarget fixedSizeTarget;
private @Nullable RecipientContactPhoto recipientContactPhoto;
private @NonNull Drawable unknownRecipientDrawable;
@@ -80,23 +99,30 @@ public final class AvatarImageView extends AppCompatImageView {
if (attrs != null) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.AvatarImageView, 0, 0);
inverted = typedArray.getBoolean(R.styleable.AvatarImageView_inverted, false);
size = typedArray.getInt(R.styleable.AvatarImageView_fallbackImageSize, SIZE_LARGE);
inverted = typedArray.getBoolean(R.styleable.AvatarImageView_inverted, false);
size = typedArray.getInt(R.styleable.AvatarImageView_fallbackImageSize, SIZE_LARGE);
typedArray.recycle();
}
outlinePaint = ThemeUtil.isDarkTheme(getContext()) ? DARK_THEME_OUTLINE_PAINT : LIGHT_THEME_OUTLINE_PAINT;
outlinePaint = ThemeUtil.isDarkTheme(context) ? DARK_THEME_OUTLINE_PAINT : LIGHT_THEME_OUTLINE_PAINT;
unknownRecipientDrawable = new ResourceContactPhoto(R.drawable.ic_profile_outline_40, R.drawable.ic_profile_outline_20).asDrawable(getContext(), ContactColors.UNKNOWN_COLOR.toConversationColor(getContext()), inverted);
unknownRecipientDrawable = new ResourceContactPhoto(R.drawable.ic_profile_outline_40, R.drawable.ic_profile_outline_20).asDrawable(context, AvatarColor.UNKNOWN.colorInt(), inverted);
blurred = false;
chatColors = null;
}
@Override
public void setClipBounds(Rect clipBounds) {
super.setClipBounds(clipBounds);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float width = getWidth() - getPaddingRight() - getPaddingLeft();
float width = getWidth() - getPaddingRight() - getPaddingLeft();
float height = getHeight() - getPaddingBottom() - getPaddingTop();
float cx = width / 2f;
float cx = width / 2f;
float cy = height / 2f;
float radius = Math.min(cx, cy) - (outlinePaint.getStrokeWidth() / 2f);
@@ -118,14 +144,25 @@ public final class AvatarImageView extends AppCompatImageView {
* Shows self as the actual profile picture.
*/
public void setRecipient(@NonNull Recipient recipient) {
setRecipient(recipient, false);
}
/**
* Shows self as the actual profile picture.
*/
public void setRecipient(@NonNull Recipient recipient, boolean quickContactEnabled) {
if (recipient.isSelf()) {
setAvatar(GlideApp.with(this), null, false);
setAvatar(GlideApp.with(this), null, quickContactEnabled);
AvatarUtil.loadIconIntoImageView(recipient, this);
} else {
setAvatar(GlideApp.with(this), recipient, false);
setAvatar(GlideApp.with(this), recipient, quickContactEnabled);
}
}
public AvatarOptions.Builder buildOptions() {
return new AvatarOptions.Builder(this);
}
/**
* Shows self as the note to self icon.
*/
@@ -145,44 +182,78 @@ public final class AvatarImageView extends AppCompatImageView {
}
public void setAvatar(@NonNull GlideRequests requestManager, @Nullable Recipient recipient, boolean quickContactEnabled, boolean useSelfProfileAvatar) {
if (recipient != null) {
RecipientContactPhoto photo = (recipient.isSelf() && useSelfProfileAvatar) ? new RecipientContactPhoto(recipient,
new ProfileContactPhoto(Recipient.self(),
Recipient.self().getProfileAvatar()))
: new RecipientContactPhoto(recipient);
setAvatar(requestManager, recipient, new AvatarOptions.Builder(this)
.withUseSelfProfileAvatar(useSelfProfileAvatar)
.withQuickContactEnabled(quickContactEnabled)
.build());
}
if (!photo.equals(recipientContactPhoto)) {
private void setAvatar(@Nullable Recipient recipient, @NonNull AvatarOptions avatarOptions) {
setAvatar(GlideApp.with(this), recipient, avatarOptions);
}
private void setAvatar(@NonNull GlideRequests requestManager, @Nullable Recipient recipient, @NonNull AvatarOptions avatarOptions) {
if (recipient != null) {
RecipientContactPhoto photo = (recipient.isSelf() && avatarOptions.useSelfProfileAvatar) ? new RecipientContactPhoto(recipient,
new ProfileContactPhoto(Recipient.self(),
Recipient.self().getProfileAvatar()))
: new RecipientContactPhoto(recipient);
boolean shouldBlur = recipient.shouldBlurAvatar();
ChatColors chatColors = recipient.getChatColors();
if (!photo.equals(recipientContactPhoto) || shouldBlur != blurred || !Objects.equals(chatColors, this.chatColors)) {
requestManager.clear(this);
this.chatColors = chatColors;
recipientContactPhoto = photo;
Drawable fallbackContactPhotoDrawable = size == SIZE_SMALL
? photo.recipient.getSmallFallbackContactPhotoDrawable(getContext(), inverted, fallbackPhotoProvider)
: photo.recipient.getFallbackContactPhotoDrawable(getContext(), inverted, fallbackPhotoProvider);
Drawable fallbackContactPhotoDrawable = size == SIZE_SMALL ? photo.recipient.getSmallFallbackContactPhotoDrawable(getContext(), inverted, fallbackPhotoProvider)
: photo.recipient.getFallbackContactPhotoDrawable(getContext(), inverted, fallbackPhotoProvider);
if (fixedSizeTarget != null) {
requestManager.clear(fixedSizeTarget);
}
if (photo.contactPhoto != null) {
requestManager.load(photo.contactPhoto)
.fallback(fallbackContactPhotoDrawable)
.error(fallbackContactPhotoDrawable)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.circleCrop()
.into(this);
List<Transformation<Bitmap>> transforms = new ArrayList<>();
if (shouldBlur) {
transforms.add(new BlurTransformation(ApplicationDependencies.getApplication(), 0.25f, BlurTransformation.MAX_RADIUS));
}
transforms.add(new CircleCrop());
blurred = shouldBlur;
GlideRequest<Drawable> request = requestManager.load(photo.contactPhoto)
.fallback(fallbackContactPhotoDrawable)
.error(fallbackContactPhotoDrawable)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.downsample(DownsampleStrategy.CENTER_INSIDE)
.transform(new MultiTransformation<>(transforms));
if (avatarOptions.fixedSize > 0) {
fixedSizeTarget = new FixedSizeTarget(avatarOptions.fixedSize);
request.into(fixedSizeTarget);
} else {
request.into(this);
}
} else {
setImageDrawable(fallbackContactPhotoDrawable);
}
}
setAvatarClickHandler(recipient, quickContactEnabled);
setAvatarClickHandler(recipient, avatarOptions.quickContactEnabled);
} else {
recipientContactPhoto = null;
requestManager.clear(this);
if (fallbackPhotoProvider != null) {
setImageDrawable(fallbackPhotoProvider.getPhotoForRecipientWithoutName()
.asDrawable(getContext(), MaterialColor.STEEL.toAvatarColor(getContext()), inverted));
.asDrawable(getContext(), AvatarColor.UNKNOWN.colorInt(), inverted));
} else {
setImageDrawable(unknownRecipientDrawable);
}
super.setOnClickListener(listener);
disableQuickContact();
}
}
@@ -191,31 +262,30 @@ public final class AvatarImageView extends AppCompatImageView {
super.setOnClickListener(v -> {
Context context = getContext();
if (recipient.isPushGroup()) {
context.startActivity(ManageGroupActivity.newIntent(context, recipient.requireGroupId().requirePush()),
ManageGroupActivity.createTransitionBundle(context, this));
context.startActivity(ConversationSettingsActivity.forGroup(context, recipient.requireGroupId().requirePush()),
ConversationSettingsActivity.createTransitionBundle(context, this));
} else {
if (context instanceof FragmentActivity) {
RecipientBottomSheetDialogFragment.create(recipient.getId(), null)
.show(((FragmentActivity) context).getSupportFragmentManager(), "BOTTOM");
} else {
context.startActivity(ManageRecipientActivity.newIntent(context, recipient.getId()),
ManageRecipientActivity.createTransitionBundle(context, this));
context.startActivity(ConversationSettingsActivity.forRecipient(context, recipient.getId()),
ConversationSettingsActivity.createTransitionBundle(context, this));
}
}
});
} else {
super.setOnClickListener(listener);
setClickable(listener != null);
disableQuickContact();
}
}
public void setImageBytesForGroup(@Nullable byte[] avatarBytes,
@Nullable Recipient.FallbackPhotoProvider fallbackPhotoProvider,
@NonNull MaterialColor color)
@NonNull AvatarColor color)
{
Drawable fallback = Util.firstNonNull(fallbackPhotoProvider, Recipient.DEFAULT_FALLBACK_PHOTO_PROVIDER)
.getPhotoForGroup()
.asDrawable(getContext(), color.toAvatarColor(getContext()));
.asDrawable(getContext(), color.colorInt());
GlideApp.with(this)
.load(avatarBytes)
@@ -226,6 +296,16 @@ public final class AvatarImageView extends AppCompatImageView {
.into(this);
}
public void setNonAvatarImageResource(@DrawableRes int imageResource) {
recipientContactPhoto = null;
setImageResource(imageResource);
}
public void disableQuickContact() {
super.setOnClickListener(listener);
setClickable(listener != null);
}
private static class RecipientContactPhoto {
private final @NonNull Recipient recipient;
@@ -246,9 +326,70 @@ public final class AvatarImageView extends AppCompatImageView {
if (other == null) return false;
return other.recipient.equals(recipient) &&
other.recipient.getColor().equals(recipient.getColor()) &&
other.recipient.getChatColors().equals(recipient.getChatColors()) &&
other.ready == ready &&
Objects.equals(other.contactPhoto, contactPhoto);
}
}
private final class FixedSizeTarget extends SimpleTarget<Drawable> {
FixedSizeTarget(int size) {
super(size, size);
}
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
setImageDrawable(resource);
}
}
public static final class AvatarOptions {
private final boolean quickContactEnabled;
private final boolean useSelfProfileAvatar;
private final int fixedSize;
private AvatarOptions(@NonNull Builder builder) {
this.quickContactEnabled = builder.quickContactEnabled;
this.useSelfProfileAvatar = builder.useSelfProfileAvatar;
this.fixedSize = builder.fixedSize;
}
public static final class Builder {
private final AvatarImageView avatarImageView;
private boolean quickContactEnabled = false;
private boolean useSelfProfileAvatar = false;
private int fixedSize = -1;
private Builder(@NonNull AvatarImageView avatarImageView) {
this.avatarImageView = avatarImageView;
}
public @NonNull Builder withQuickContactEnabled(boolean quickContactEnabled) {
this.quickContactEnabled = quickContactEnabled;
return this;
}
public @NonNull Builder withUseSelfProfileAvatar(boolean useSelfProfileAvatar) {
this.useSelfProfileAvatar = useSelfProfileAvatar;
return this;
}
public @NonNull Builder withFixedSize(@Px @IntRange(from = 1) int fixedSize) {
this.fixedSize = fixedSize;
return this;
}
public AvatarOptions build() {
return new AvatarOptions(this);
}
public void load(@Nullable Recipient recipient) {
avatarImageView.setAvatar(recipient, build());
}
}
}
}

View File

@@ -35,11 +35,13 @@ import org.thoughtcrime.securesms.components.mention.MentionDeleter;
import org.thoughtcrime.securesms.components.mention.MentionRendererDelegate;
import org.thoughtcrime.securesms.components.mention.MentionValidatorWatcher;
import org.thoughtcrime.securesms.database.model.Mention;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.StringUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.util.List;
import java.util.Objects;
import static org.thoughtcrime.securesms.database.MentionUtil.MENTION_STARTER;
@@ -84,13 +86,13 @@ public class ComposeText extends EmojiEditText {
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (!TextUtils.isEmpty(hint)) {
if (getLayout() != null && !TextUtils.isEmpty(hint)) {
if (!TextUtils.isEmpty(subHint)) {
setHint(new SpannableStringBuilder().append(ellipsizeToWidth(hint))
.append("\n")
.append(ellipsizeToWidth(subHint)));
setHintWithChecks(new SpannableStringBuilder().append(ellipsizeToWidth(hint))
.append("\n")
.append(ellipsizeToWidth(subHint)));
} else {
setHint(ellipsizeToWidth(hint));
setHintWithChecks(ellipsizeToWidth(hint));
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@@ -160,14 +162,14 @@ public class ComposeText extends EmojiEditText {
}
if (this.subHint != null) {
super.setHint(new SpannableStringBuilder().append(ellipsizeToWidth(this.hint))
.append("\n")
.append(ellipsizeToWidth(this.subHint)));
setHintWithChecks(new SpannableStringBuilder().append(ellipsizeToWidth(this.hint))
.append("\n")
.append(ellipsizeToWidth(this.subHint)));
} else {
super.setHint(ellipsizeToWidth(this.hint));
setHintWithChecks(ellipsizeToWidth(this.hint));
}
super.setHint(hint);
setHintWithChecks(hint);
}
public void appendInvite(String invite) {
@@ -200,7 +202,7 @@ public class ComposeText extends EmojiEditText {
}
public void setTransport(TransportOption transport) {
final boolean useSystemEmoji = TextSecurePreferences.isSystemEmojiPreferred(getContext());
final boolean useSystemEmoji = SignalStore.settings().isPreferSystemEmoji();
int imeOptions = (getImeOptions() & ~EditorInfo.IME_MASK_ACTION) | EditorInfo.IME_ACTION_SEND;
int inputType = getInputType();
@@ -224,7 +226,7 @@ public class ComposeText extends EmojiEditText {
public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
InputConnection inputConnection = super.onCreateInputConnection(editorInfo);
if(TextSecurePreferences.isEnterSendsEnabled(getContext())) {
if(SignalStore.settings().isEnterKeySends()) {
editorInfo.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
}
@@ -264,6 +266,14 @@ public class ComposeText extends EmojiEditText {
addTextChangedListener(mentionValidatorWatcher);
}
private void setHintWithChecks(@Nullable CharSequence newHint) {
if (getLayout() == null || Objects.equals(getHint(), newHint)) {
return;
}
setHint(newHint);
}
private boolean changeSelectionForPartialMentions(@NonNull Spanned spanned, int selectionStart, int selectionEnd) {
Annotation[] annotations = spanned.getSpans(0, spanned.length(), Annotation.class);
for (Annotation annotation : annotations) {
@@ -365,7 +375,7 @@ public class ComposeText extends EmojiEditText {
private static class CommitContentListener implements InputConnectionCompat.OnCommitContentListener {
private static final String TAG = CommitContentListener.class.getSimpleName();
private static final String TAG = Log.tag(CommitContentListener.class);
private final InputPanel.MediaListener mediaListener;

View File

@@ -10,6 +10,7 @@ import android.util.AttributeSet;
import android.view.TouchDelegate;
import android.view.View;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -20,9 +21,8 @@ import androidx.core.widget.TextViewCompat;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.views.DarkOverflowToolbar;
public final class ContactFilterToolbar extends DarkOverflowToolbar {
public final class ContactFilterView extends FrameLayout {
private OnFilterChangedListener listener;
private final EditText searchText;
@@ -32,17 +32,17 @@ public final class ContactFilterToolbar extends DarkOverflowToolbar {
private final ImageView clearToggle;
private final LinearLayout toggleContainer;
public ContactFilterToolbar(Context context) {
public ContactFilterView(Context context) {
this(context, null);
}
public ContactFilterToolbar(Context context, AttributeSet attrs) {
public ContactFilterView(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.toolbarStyle);
}
public ContactFilterToolbar(Context context, AttributeSet attrs, int defStyleAttr) {
public ContactFilterView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
inflate(context, R.layout.contact_filter_toolbar, this);
inflate(context, R.layout.contact_filter_view, this);
this.searchText = findViewById(R.id.search_view);
this.toggle = findViewById(R.id.button_toggle);
@@ -99,8 +99,6 @@ public final class ContactFilterToolbar extends DarkOverflowToolbar {
}
});
setLogo(null);
setContentInsetStartWithNavigation(0);
expandTapArea(toggleContainer, dialpadToggle);
applyAttributes(searchText, context, attrs, defStyleAttr);
searchText.requestFocus();

View File

@@ -4,6 +4,9 @@ import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.tabs.TabLayout;
import java.util.List;
@@ -15,6 +18,8 @@ public class ControllableTabLayout extends TabLayout {
private List<View> touchables;
private NewTabListener newTabListener;
public ControllableTabLayout(Context context) {
super(context);
}
@@ -39,4 +44,28 @@ public class ControllableTabLayout extends TabLayout {
super.setEnabled(enabled);
}
public void setNewTabListener(@Nullable NewTabListener newTabListener) {
this.newTabListener = newTabListener;
}
@Override
public @NonNull Tab newTab() {
Tab tab = super.newTab();
if (newTabListener != null) {
newTabListener.onNewTab(tab);
}
return tab;
}
/**
* Allows implementor to modify tabs when they are created, before they are added to the tab layout.
* This is useful for loading custom views, to ensure that time is not spent inflating these views
* as the user is switching between pages.
*/
public interface NewTabListener {
void onNewTab(@NonNull Tab tab);
}
}

View File

@@ -1,35 +1,41 @@
package org.thoughtcrime.securesms.components;
import android.Manifest;
import android.animation.Animator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieProperty;
import com.airbnb.lottie.model.KeyPath;
import org.signal.core.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.ApplicationContext;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.Projection;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.dualsim.SubscriptionInfoCompat;
import org.thoughtcrime.securesms.util.dualsim.SubscriptionManagerCompat;
@@ -38,17 +44,24 @@ import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
public class ConversationItemFooter extends LinearLayout {
public class ConversationItemFooter extends ConstraintLayout {
private TextView dateView;
private TextView simView;
private ExpirationTimerView timerView;
private ImageView insecureIndicatorView;
private DeliveryStatusView deliveryStatusView;
private boolean onlyShowSendingStatus;
private View audioSpace;
private TextView audioDuration;
private LottieAnimationView revealDot;
private TextView dateView;
private TextView simView;
private ExpirationTimerView timerView;
private ImageView insecureIndicatorView;
private DeliveryStatusView deliveryStatusView;
private boolean onlyShowSendingStatus;
private TextView audioDuration;
private LottieAnimationView revealDot;
private PlaybackSpeedToggleTextView playbackSpeedToggleTextView;
private boolean isOutgoing;
private boolean hasShrunkDate;
private OnTouchDelegateChangedListener onTouchDelegateChangedListener;
private final Rect speedToggleHitRect = new Rect();
private final int touchTargetSize = ViewUtil.dpToPx(48);
public ConversationItemFooter(Context context) {
super(context);
@@ -66,24 +79,55 @@ public class ConversationItemFooter extends LinearLayout {
}
private void init(@Nullable AttributeSet attrs) {
inflate(getContext(), R.layout.conversation_item_footer, this);
dateView = findViewById(R.id.footer_date);
simView = findViewById(R.id.footer_sim_info);
timerView = findViewById(R.id.footer_expiration_timer);
insecureIndicatorView = findViewById(R.id.footer_insecure_indicator);
deliveryStatusView = findViewById(R.id.footer_delivery_status);
audioDuration = findViewById(R.id.footer_audio_duration);
audioSpace = findViewById(R.id.footer_audio_duration_space);
revealDot = findViewById(R.id.footer_revealed_dot);
final TypedArray typedArray;
if (attrs != null) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.ConversationItemFooter, 0, 0);
typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.ConversationItemFooter, 0, 0);
} else {
typedArray = null;
}
final @LayoutRes int contentId;
if (typedArray != null) {
int mode = typedArray.getInt(R.styleable.ConversationItemFooter_footer_mode, 0);
isOutgoing = mode == 0;
if (isOutgoing) {
contentId = R.layout.conversation_item_footer_outgoing;
} else {
contentId = R.layout.conversation_item_footer_incoming;
}
} else {
contentId = R.layout.conversation_item_footer_outgoing;
isOutgoing = true;
}
inflate(getContext(), contentId, this);
dateView = findViewById(R.id.footer_date);
simView = findViewById(R.id.footer_sim_info);
timerView = findViewById(R.id.footer_expiration_timer);
insecureIndicatorView = findViewById(R.id.footer_insecure_indicator);
deliveryStatusView = findViewById(R.id.footer_delivery_status);
audioDuration = findViewById(R.id.footer_audio_duration);
revealDot = findViewById(R.id.footer_revealed_dot);
playbackSpeedToggleTextView = findViewById(R.id.footer_audio_playback_speed_toggle);
if (typedArray != null) {
setTextColor(typedArray.getInt(R.styleable.ConversationItemFooter_footer_text_color, getResources().getColor(R.color.core_white)));
setIconColor(typedArray.getInt(R.styleable.ConversationItemFooter_footer_icon_color, getResources().getColor(R.color.core_white)));
setRevealDotColor(typedArray.getInt(R.styleable.ConversationItemFooter_footer_reveal_dot_color, getResources().getColor(R.color.core_white)));
typedArray.recycle();
}
dateView.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
if (oldLeft != left || oldRight != right) {
notifyTouchDelegateChanged(getPlaybackSpeedToggleTouchDelegateRect(), playbackSpeedToggleTextView);
}
});
}
public void setOnTouchDelegateChangedListener(@Nullable OnTouchDelegateChangedListener onTouchDelegateChangedListener) {
this.onTouchDelegateChangedListener = onTouchDelegateChangedListener;
}
@Override
@@ -98,7 +142,7 @@ public class ConversationItemFooter extends LinearLayout {
presentTimer(messageRecord);
presentInsecureIndicator(messageRecord);
presentDeliveryStatus(messageRecord);
hideAudioDurationViews();
presentAudioDuration(messageRecord);
}
public void setAudioDuration(long totalDurationMillis, long currentPostionMillis) {
@@ -106,6 +150,20 @@ public class ConversationItemFooter extends LinearLayout {
audioDuration.setText(getResources().getString(R.string.AudioView_duration, remainingSecs / 60, remainingSecs % 60));
}
public void setPlaybackSpeedListener(@Nullable PlaybackSpeedToggleTextView.PlaybackSpeedListener playbackSpeedListener) {
playbackSpeedToggleTextView.setPlaybackSpeedListener(playbackSpeedListener);
}
public void setAudioPlaybackSpeed(float playbackSpeed, boolean isPlaying) {
if (isPlaying) {
showPlaybackSpeedToggle();
} else {
hidePlaybackSpeedToggle();
}
playbackSpeedToggleTextView.setCurrentSpeed(playbackSpeed);
}
public void setTextColor(int color) {
dateView.setTextColor(color);
simView.setTextColor(color);
@@ -122,7 +180,7 @@ public class ConversationItemFooter extends LinearLayout {
revealDot.addValueCallback(
new KeyPath("**"),
LottieProperty.COLOR_FILTER,
frameInfo -> new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)
frameInfo -> new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)
);
}
@@ -145,6 +203,92 @@ public class ConversationItemFooter extends LinearLayout {
setBackground(null);
}
public @Nullable Projection getProjection() {
if (getVisibility() == VISIBLE) {
return Projection.relativeToViewRoot(this, new Projection.Corners(ViewUtil.dpToPx(11)));
} else {
return null;
}
}
private void notifyTouchDelegateChanged(@NonNull Rect rect, @NonNull View touchDelegate) {
if (onTouchDelegateChangedListener != null) {
onTouchDelegateChangedListener.onTouchDelegateChanged(rect, touchDelegate);
}
}
private void showPlaybackSpeedToggle() {
if (hasShrunkDate) {
return;
}
hasShrunkDate = true;
playbackSpeedToggleTextView.animate()
.alpha(1f)
.scaleX(1f)
.scaleY(1f)
.setDuration(150L)
.setListener(new AnimationCompleteListener() {
@Override
public void onAnimationEnd(Animator animation) {
playbackSpeedToggleTextView.setClickable(true);
}
});
if (isOutgoing) {
dateView.setMaxWidth(ViewUtil.dpToPx(28));
} else {
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(this);
constraintSet.constrainMaxWidth(R.id.date_and_expiry_wrapper, ViewUtil.dpToPx(40));
constraintSet.applyTo(this);
}
}
private void hidePlaybackSpeedToggle() {
if (!hasShrunkDate) {
return;
}
hasShrunkDate = false;
playbackSpeedToggleTextView.animate()
.alpha(0f)
.scaleX(0.5f)
.scaleY(0.5f)
.setDuration(150L).setListener(new AnimationCompleteListener() {
@Override
public void onAnimationEnd(Animator animation) {
playbackSpeedToggleTextView.setClickable(false);
playbackSpeedToggleTextView.clearRequestedSpeed();
}
});
if (isOutgoing) {
dateView.setMaxWidth(Integer.MAX_VALUE);
} else {
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(this);
constraintSet.constrainMaxWidth(R.id.date_and_expiry_wrapper, -1);
constraintSet.applyTo(this);
}
}
private @NonNull Rect getPlaybackSpeedToggleTouchDelegateRect() {
playbackSpeedToggleTextView.getHitRect(speedToggleHitRect);
int widthOffset = (touchTargetSize - speedToggleHitRect.width()) / 2;
int heightOffset = (touchTargetSize - speedToggleHitRect.height()) / 2;
speedToggleHitRect.top -= heightOffset;
speedToggleHitRect.left -= widthOffset;
speedToggleHitRect.right += widthOffset;
speedToggleHitRect.bottom += heightOffset;
return speedToggleHitRect;
}
private void presentDate(@NonNull MessageRecord messageRecord, @NonNull Locale locale) {
dateView.forceLayout();
if (messageRecord.isFailed()) {
@@ -160,6 +304,8 @@ public class ConversationItemFooter extends LinearLayout {
dateView.setText(errorMsg);
} else if (messageRecord.isPendingInsecureSmsFallback()) {
dateView.setText(R.string.ConversationItem_click_to_approve_unencrypted);
} else if (messageRecord.isRateLimited()) {
dateView.setText(R.string.ConversationItem_send_paused);
} else {
dateView.setText(DateUtils.getExtendedRelativeTimeSpanString(getContext(), locale, messageRecord.getTimestamp()));
}
@@ -177,7 +323,7 @@ public class ConversationItemFooter extends LinearLayout {
simView.setText(getContext().getString(R.string.ConversationItem_from_s, subscriptionInfo.get().getDisplayName()));
simView.setVisibility(View.VISIBLE);
} else if (subscriptionInfo.isPresent()) {
simView.setText(getContext().getString(R.string.ConversationItem_to_s, subscriptionInfo.get().getDisplayName()));
simView.setText(getContext().getString(R.string.ConversationItem_to_s, subscriptionInfo.get().getDisplayName()));
simView.setVisibility(View.VISIBLE);
} else {
simView.setVisibility(View.GONE);
@@ -197,16 +343,16 @@ public class ConversationItemFooter extends LinearLayout {
this.timerView.startAnimation();
if (messageRecord.getExpireStarted() + messageRecord.getExpiresIn() <= System.currentTimeMillis()) {
ApplicationContext.getInstance(getContext()).getExpiringMessageManager().checkSchedule();
ApplicationDependencies.getExpiringMessageManager().checkSchedule();
}
} else if (!messageRecord.isOutgoing() && !messageRecord.isMediaPending()) {
SignalExecutors.BOUNDED.execute(() -> {
ExpiringMessageManager expirationManager = ApplicationContext.getInstance(getContext()).getExpiringMessageManager();
ExpiringMessageManager expirationManager = ApplicationDependencies.getExpiringMessageManager();
long id = messageRecord.getId();
boolean mms = messageRecord.isMms();
if (mms) DatabaseFactory.getMmsDatabase(getContext()).markExpireStarted(id);
else DatabaseFactory.getSmsDatabase(getContext()).markExpireStarted(id);
else DatabaseFactory.getSmsDatabase(getContext()).markExpireStarted(id);
expirationManager.scheduleDeletion(id, mms, messageRecord.getExpiresIn());
});
@@ -233,7 +379,7 @@ public class ConversationItemFooter extends LinearLayout {
deliveryStatusView.setNone();
}
} else {
if (!messageRecord.isOutgoing()) {
if (!messageRecord.isOutgoing()) {
deliveryStatusView.setNone();
} else if (messageRecord.isPending()) {
deliveryStatusView.setPending();
@@ -252,12 +398,13 @@ public class ConversationItemFooter extends LinearLayout {
MmsMessageRecord mmsMessageRecord = (MmsMessageRecord) messageRecord;
if (mmsMessageRecord.getSlideDeck().getAudioSlide() != null) {
if (messageRecord.isOutgoing()) {
moveAudioViewsForOutgoing();
} else {
moveAudioViewsForIncoming();
}
showAudioDurationViews();
if (messageRecord.getViewedReceiptCount() > 0) {
revealDot.setProgress(1f);
} else {
revealDot.setProgress(0f);
}
} else {
hideAudioDurationViews();
}
@@ -266,44 +413,19 @@ public class ConversationItemFooter extends LinearLayout {
}
}
private void moveAudioViewsForOutgoing() {
removeView(audioSpace);
removeView(audioDuration);
removeView(revealDot);
addView(audioSpace, 0);
addView(revealDot, 0);
addView(audioDuration, 0);
int padStart = ViewUtil.dpToPx(60);
int padLeft = ViewUtil.isLtr(this) ? padStart : 0;
int padRight = ViewUtil.isRtl(this) ? padStart : 0;
audioDuration.setPadding(padLeft, 0, padRight, 0);
}
private void moveAudioViewsForIncoming() {
removeView(audioSpace);
removeView(audioDuration);
removeView(revealDot);
addView(audioSpace);
addView(revealDot);
addView(audioDuration);
audioDuration.setPadding(0, 0, 0, 0);
}
private void showAudioDurationViews() {
audioSpace.setVisibility(View.VISIBLE);
audioDuration.setVisibility(View.VISIBLE);
if (FeatureFlags.viewedReceipts()) {
revealDot.setVisibility(View.VISIBLE);
}
revealDot.setVisibility(View.VISIBLE);
playbackSpeedToggleTextView.setVisibility(View.VISIBLE);
}
private void hideAudioDurationViews() {
audioSpace.setVisibility(View.GONE);
audioDuration.setVisibility(View.GONE);
revealDot.setVisibility(View.GONE);
playbackSpeedToggleTextView.setVisibility(View.GONE);
}
public interface OnTouchDelegateChangedListener {
void onTouchDelegateChanged(@NonNull Rect delegateRect, @NonNull View delegateView);
}
}

View File

@@ -19,6 +19,8 @@ import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideClickListener;
import org.thoughtcrime.securesms.mms.SlidesClickedListener;
import org.thoughtcrime.securesms.util.Projection;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.List;
@@ -32,6 +34,9 @@ public class ConversationItemThumbnail extends FrameLayout {
private Outliner outliner;
private Outliner pulseOutliner;
private boolean borderless;
private int[] normalBounds;
private int[] gifBounds;
private int minimumThumbnailWidth;
public ConversationItemThumbnail(Context context) {
super(context);
@@ -60,14 +65,30 @@ public class ConversationItemThumbnail extends FrameLayout {
outliner.setColor(ContextCompat.getColor(getContext(), R.color.signal_inverse_transparent_20));
int gifWidth = ViewUtil.dpToPx(260);
if (attrs != null) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.ConversationItemThumbnail, 0, 0);
thumbnail.setBounds(typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_minWidth, 0),
typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_maxWidth, 0),
typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_minHeight, 0),
typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_maxHeight, 0));
normalBounds = new int[]{
typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_minWidth, 0),
typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_maxWidth, 0),
typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_minHeight, 0),
typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_maxHeight, 0)
};
gifWidth = typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_gifWidth, gifWidth);
typedArray.recycle();
} else {
normalBounds = new int[]{0, 0, 0, 0};
}
gifBounds = new int[]{
gifWidth,
gifWidth,
1,
Integer.MAX_VALUE
};
minimumThumbnailWidth = -1;
}
@SuppressWarnings("SuspiciousNameCombination")
@@ -88,6 +109,18 @@ public class ConversationItemThumbnail extends FrameLayout {
}
}
public void hideThumbnailView() {
thumbnail.setAlpha(0f);
}
public void showThumbnailView() {
thumbnail.setAlpha(1f);
}
public @NonNull Projection.Corners getCorners() {
return new Projection.Corners(cornerMask.getRadii());
}
public void setPulseOutliner(@NonNull Outliner outliner) {
this.pulseOutliner = outliner;
}
@@ -121,6 +154,7 @@ public class ConversationItemThumbnail extends FrameLayout {
}
public void setMinimumThumbnailWidth(int width) {
minimumThumbnailWidth = width;
thumbnail.setMinimumThumbnailWidth(width);
}
@@ -137,6 +171,17 @@ public class ConversationItemThumbnail extends FrameLayout {
boolean showControls, boolean isPreview)
{
if (slides.size() == 1) {
Slide slide = slides.get(0);
if (slide.isVideoGif()) {
setThumbnailBounds(gifBounds);
} else {
setThumbnailBounds(normalBounds);
if (minimumThumbnailWidth != -1) {
thumbnail.setMinimumThumbnailWidth(minimumThumbnailWidth);
}
}
thumbnail.setVisibility(VISIBLE);
album.setVisibility(GONE);
@@ -167,4 +212,8 @@ public class ConversationItemThumbnail extends FrameLayout {
thumbnail.setDownloadClickListener(listener);
album.setDownloadClickListener(listener);
}
private void setThumbnailBounds(@NonNull int[] bounds) {
thumbnail.setBounds(bounds[0], bounds[1], bounds[2], bounds[3]);
}
}

View File

@@ -4,22 +4,31 @@ import android.content.Context;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.ContextCompat;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.libsignal.util.Pair;
import java.util.LinkedList;
import java.util.List;
public class ConversationTypingView extends LinearLayout {
public class ConversationTypingView extends ConstraintLayout {
private AvatarImageView avatar;
private AvatarImageView avatar1;
private AvatarImageView avatar2;
private AvatarImageView avatar3;
private View bubble;
private TypingIndicatorView indicator;
private TextView typistCount;
public ConversationTypingView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -29,27 +38,58 @@ public class ConversationTypingView extends LinearLayout {
protected void onFinishInflate() {
super.onFinishInflate();
avatar = findViewById(R.id.typing_avatar);
bubble = findViewById(R.id.typing_bubble);
indicator = findViewById(R.id.typing_indicator);
avatar1 = findViewById(R.id.typing_avatar_1);
avatar2 = findViewById(R.id.typing_avatar_2);
avatar3 = findViewById(R.id.typing_avatar_3);
typistCount = findViewById(R.id.typing_count);
bubble = findViewById(R.id.typing_bubble);
indicator = findViewById(R.id.typing_indicator);
}
public void setTypists(@NonNull GlideRequests glideRequests, @NonNull List<Recipient> typists, boolean isGroupThread) {
public void setTypists(@NonNull GlideRequests glideRequests, @NonNull List<Recipient> typists, boolean isGroupThread, boolean hasWallpaper) {
if (typists.isEmpty()) {
indicator.stopAnimation();
return;
}
Recipient typist = typists.get(0);
bubble.getBackground().setColorFilter(typist.getColor().toConversationColor(getContext()), PorterDuff.Mode.MULTIPLY);
avatar1.setVisibility(GONE);
avatar2.setVisibility(GONE);
avatar3.setVisibility(GONE);
typistCount.setVisibility(GONE);
if (isGroupThread) {
avatar.setAvatar(glideRequests, typist, true);
avatar.setVisibility(VISIBLE);
presentGroupThreadAvatars(glideRequests, typists);
}
if (hasWallpaper) {
bubble.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.conversation_item_wallpaper_bubble_color));
typistCount.getBackground().setColorFilter(ContextCompat.getColor(getContext(), R.color.conversation_item_wallpaper_bubble_color), PorterDuff.Mode.SRC_IN);
} else {
avatar.setVisibility(GONE);
bubble.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.signal_background_secondary));
typistCount.getBackground().setColorFilter(ContextCompat.getColor(getContext(), R.color.signal_background_secondary), PorterDuff.Mode.SRC_IN);
}
indicator.startAnimation();
}
private void presentGroupThreadAvatars(@NonNull GlideRequests glideRequests, @NonNull List<Recipient> typists) {
avatar1.setAvatar(glideRequests, typists.get(0), typists.size() == 1);
avatar1.setVisibility(VISIBLE);
if (typists.size() > 1) {
avatar2.setAvatar(glideRequests, typists.get(1), false);
avatar2.setVisibility(VISIBLE);
}
if (typists.size() == 3) {
avatar3.setAvatar(glideRequests, typists.get(2), false);
avatar3.setVisibility(VISIBLE);
}
if (typists.size() > 3) {
typistCount.setText(getResources().getString(R.string.ConversationTypingView__plus_d, typists.size() - 2));
typistCount.setVisibility(VISIBLE);
}
}
}

View File

@@ -7,9 +7,11 @@ import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.drawable.shapes.RoundRectShape;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class CornerMask {
@@ -29,10 +31,7 @@ public class CornerMask {
}
public void mask(Canvas canvas) {
bounds.left = 0;
bounds.top = 0;
bounds.right = canvas.getWidth();
bounds.bottom = canvas.getHeight();
bounds.set(canvas.getClipBounds());
corners.reset();
corners.addRoundRect(bounds, radii, Path.Direction.CW);
@@ -57,6 +56,13 @@ public class CornerMask {
radii[6] = radii[7] = bottomLeft;
}
public void setRadii(float topLeft, float topRight, float bottomRight, float bottomLeft) {
radii[0] = radii[1] = topLeft;
radii[2] = radii[3] = topRight;
radii[4] = radii[5] = bottomRight;
radii[6] = radii[7] = bottomLeft;
}
public void setTopLeftRadius(int radius) {
radii[0] = radii[1] = radius;
}
@@ -72,4 +78,8 @@ public class CornerMask {
public void setBottomLeftRadius(int radius) {
radii[6] = radii[7] = radius;
}
public float[] getRadii() {
return radii;
}
}

View File

@@ -31,7 +31,7 @@ import java.net.URISyntaxException;
public class CustomDefaultPreference extends DialogPreference {
private static final String TAG = CustomDefaultPreference.class.getSimpleName();
private static final String TAG = Log.tag(CustomDefaultPreference.class);
private final int inputType;
private final String customPreference;

View File

@@ -1,32 +0,0 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.EditText;
import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import org.thoughtcrime.securesms.R;
/**
* Custom styled search view that we can insert into ActionBar menus
*/
public class DarkSearchView extends androidx.appcompat.widget.SearchView {
public DarkSearchView(@NonNull Context context) {
this(context, null);
}
public DarkSearchView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, R.attr.search_view_style_dark);
}
public DarkSearchView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
EditText searchText = findViewById(androidx.appcompat.R.id.search_src_text);
searchText.setTextColor(ContextCompat.getColor(context, R.color.signal_text_toolbar_subtitle));
}
}

View File

@@ -10,11 +10,12 @@ import android.view.animation.RotateAnimation;
import android.widget.FrameLayout;
import android.widget.ImageView;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
public class DeliveryStatusView extends FrameLayout {
private static final String TAG = DeliveryStatusView.class.getSimpleName();
private static final String TAG = Log.tag(DeliveryStatusView.class);
private static final RotateAnimation ROTATION_ANIMATION = new RotateAnimation(0, 360f,
Animation.RELATIVE_TO_SELF, 0.5f,

View File

@@ -20,6 +20,7 @@ import com.pnikosis.materialishprogress.ProgressWheel;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.events.PartProgressEvent;
@@ -29,7 +30,7 @@ import org.thoughtcrime.securesms.util.Util;
public class DocumentView extends FrameLayout {
private static final String TAG = DocumentView.class.getSimpleName();
private static final String TAG = Log.tag(DocumentView.class);
private final @NonNull AnimatingToggle controlToggle;
private final @NonNull ImageView downloadButton;

View File

@@ -1,7 +1,10 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
@@ -9,14 +12,19 @@ import android.text.style.StyleSpan;
import android.util.AttributeSet;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.Objects;
public class FromTextView extends EmojiTextView {
private static final String TAG = FromTextView.class.getSimpleName();
private static final String TAG = Log.tag(FromTextView.class);
public FromTextView(Context context) {
super(context);
@@ -64,10 +72,17 @@ public class FromTextView extends EmojiTextView {
setText(builder);
if (recipient.isBlocked()) setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_block_grey600_18dp, 0, 0, 0);
else if (recipient.isMuted()) setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_volume_off_grey600_18dp, 0, 0, 0);
if (recipient.isBlocked()) setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_block_grey600_18dp, 0, 0, 0);
else if (recipient.isMuted()) setCompoundDrawablesRelativeWithIntrinsicBounds(getMuted(), null, null, null);
else setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
private Drawable getMuted() {
Drawable mutedDrawable = Objects.requireNonNull(ContextCompat.getDrawable(getContext(), R.drawable.ic_bell_disabled_16));
mutedDrawable.setBounds(0, 0, ViewUtil.dpToPx(18), ViewUtil.dpToPx(18));
mutedDrawable.setColorFilter(new PorterDuffColorFilter(ContextCompat.getColor(getContext(), R.color.signal_icon_tint_secondary), PorterDuff.Mode.SRC_IN));
return mutedDrawable;
}
}

View File

@@ -26,8 +26,8 @@ public class InputAwareLayout extends KeyboardAwareLinearLayout implements OnKey
addOnKeyboardShownListener(this);
}
@Override public void onKeyboardShown() {
hideAttachedInput(true);
@Override
public void onKeyboardShown() {
}
public void show(@NonNull final EditText imeTarget, @NonNull final InputView input) {

View File

@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.components;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.text.format.DateUtils;
import android.util.AttributeSet;
@@ -22,6 +23,8 @@ import androidx.annotation.DimenRes;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.Observer;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -29,11 +32,16 @@ import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider;
import org.thoughtcrime.securesms.components.emoji.EmojiEventListener;
import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
import org.thoughtcrime.securesms.components.voice.VoiceNotePlaybackState;
import org.thoughtcrime.securesms.conversation.ConversationStickerSuggestionAdapter;
import org.thoughtcrime.securesms.conversation.VoiceNoteDraftView;
import org.thoughtcrime.securesms.database.DraftDatabase;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.keyboard.KeyboardPage;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
import org.thoughtcrime.securesms.mms.GlideApp;
@@ -41,24 +49,25 @@ import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.QuoteModel;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
public class InputPanel extends LinearLayout
implements MicrophoneRecorderView.Listener,
KeyboardAwareLinearLayout.OnKeyboardShownListener,
EmojiKeyboardProvider.EmojiEventListener,
EmojiEventListener,
ConversationStickerSuggestionAdapter.EventListener
{
private static final String TAG = InputPanel.class.getSimpleName();
private static final String TAG = Log.tag(InputPanel.class);
private static final long QUOTE_REVEAL_DURATION_MILLIS = 150;
private static final int FADE_TIME = 150;
@@ -73,12 +82,13 @@ public class InputPanel extends LinearLayout
private View buttonToggle;
private View recordingContainer;
private View recordLockCancel;
private View composeContainer;
private ViewGroup composeContainer;
private MicrophoneRecorderView microphoneRecorderView;
private SlideToCancel slideToCancel;
private RecordTime recordTime;
private ValueAnimator quoteAnimator;
private VoiceNoteDraftView voiceNoteDraftView;
private @Nullable Listener listener;
private boolean emojiVisible;
@@ -114,6 +124,7 @@ public class InputPanel extends LinearLayout
this.buttonToggle = findViewById(R.id.button_toggle);
this.recordingContainer = findViewById(R.id.recording_container);
this.recordLockCancel = findViewById(R.id.record_cancel);
this.voiceNoteDraftView = findViewById(R.id.voice_note_draft_view);
this.slideToCancel = new SlideToCancel(findViewById(R.id.slide_to_cancel));
this.microphoneRecorderView = findViewById(R.id.recorder_view);
this.microphoneRecorderView.setListener(this);
@@ -124,7 +135,7 @@ public class InputPanel extends LinearLayout
this.recordLockCancel.setOnClickListener(v -> microphoneRecorderView.cancelAction());
if (TextSecurePreferences.isSystemEmojiPreferred(getContext())) {
if (SignalStore.settings().isPreferSystemEmoji()) {
mediaKeyboard.setVisibility(View.GONE);
emojiVisible = false;
} else {
@@ -150,6 +161,7 @@ public class InputPanel extends LinearLayout
this.listener = listener;
mediaKeyboard.setOnClickListener(v -> listener.onEmojiToggle());
voiceNoteDraftView.setListener(listener);
}
public void setMediaListener(@NonNull MediaListener listener) {
@@ -162,7 +174,7 @@ public class InputPanel extends LinearLayout
@NonNull CharSequence body,
@NonNull SlideDeck attachments)
{
this.quoteView.setQuote(glideRequests, id, author, body, false, attachments);
this.quoteView.setQuote(glideRequests, id, author, body, false, attachments, null);
int originalHeight = this.quoteView.getVisibility() == VISIBLE ? this.quoteView.getMeasuredHeight()
: 0;
@@ -225,6 +237,10 @@ public class InputPanel extends LinearLayout
return animator;
}
public boolean hasSaveableContent() {
return getQuote().isPresent() || voiceNoteDraftView.getDraft() != null;
}
public Optional<QuoteModel> getQuote() {
if (quoteView.getQuoteId() > 0 && quoteView.getVisibility() == View.VISIBLE) {
return Optional.of(new QuoteModel(quoteView.getQuoteId(), quoteView.getAuthor().getId(), quoteView.getBody().toString(), false, quoteView.getAttachments(), quoteView.getMentions()));
@@ -275,8 +291,8 @@ public class InputPanel extends LinearLayout
mediaKeyboard.setVisibility(show ? View.VISIBLE : GONE);
}
public void setMediaKeyboardToggleMode(boolean isSticker) {
mediaKeyboard.setStickerMode(isSticker);
public void setMediaKeyboardToggleMode(@NonNull KeyboardPage page) {
mediaKeyboard.setStickerMode(page);
}
public boolean isStickerMode() {
@@ -287,13 +303,17 @@ public class InputPanel extends LinearLayout
return mediaKeyboard;
}
public MediaKeyboard.MediaKeyboardListener getMediaKeyboardListener() {
return mediaKeyboard;
}
public void setWallpaperEnabled(boolean enabled) {
if (enabled) {
setBackgroundColor(getContext().getResources().getColor(R.color.wallpaper_compose_background));
composeContainer.setBackgroundResource(R.drawable.compose_background_wallpaper);
setBackground(new ColorDrawable(getContext().getResources().getColor(R.color.wallpaper_compose_background)));
composeContainer.setBackground(Objects.requireNonNull(ContextCompat.getDrawable(getContext(), R.drawable.compose_background_wallpaper)));
} else {
setBackgroundColor(getResources().getColor(R.color.signal_background_primary));
composeContainer.setBackgroundResource(R.drawable.compose_background);
setBackground(new ColorDrawable(getContext().getResources().getColor(R.color.signal_background_primary)));
composeContainer.setBackground(Objects.requireNonNull(ContextCompat.getDrawable(getContext(), R.drawable.compose_background)));
}
}
@@ -308,7 +328,10 @@ public class InputPanel extends LinearLayout
recordTime.display();
slideToCancel.display();
if (emojiVisible) ViewUtil.fadeOut(mediaKeyboard, FADE_TIME, View.INVISIBLE);
if (emojiVisible) {
ViewUtil.fadeOut(mediaKeyboard, FADE_TIME, View.INVISIBLE);
}
ViewUtil.fadeOut(composeText, FADE_TIME, View.INVISIBLE);
ViewUtil.fadeOut(quickCameraToggle, FADE_TIME, View.INVISIBLE);
ViewUtil.fadeOut(quickAudioToggle, FADE_TIME, View.INVISIBLE);
@@ -361,6 +384,10 @@ public class InputPanel extends LinearLayout
this.microphoneRecorderView.cancelAction();
}
public @NonNull Observer<VoiceNotePlaybackState> getPlaybackStateObserver() {
return voiceNoteDraftView.getPlaybackStateObserver();
}
public void setEnabled(boolean enabled) {
composeText.setEnabled(enabled);
mediaKeyboard.setEnabled(enabled);
@@ -377,11 +404,7 @@ public class InputPanel extends LinearLayout
future.addListener(new AssertedSuccessListener<Void>() {
@Override
public void onSuccess(Void result) {
if (emojiVisible) ViewUtil.fadeIn(mediaKeyboard, FADE_TIME);
ViewUtil.fadeIn(composeText, FADE_TIME);
ViewUtil.fadeIn(quickCameraToggle, FADE_TIME);
ViewUtil.fadeIn(quickAudioToggle, FADE_TIME);
buttonToggle.animate().alpha(1).setDuration(FADE_TIME).start();
fadeInNormalComposeViews();
}
});
@@ -422,7 +445,57 @@ public class InputPanel extends LinearLayout
microphoneRecorderView.unlockAction();
}
public interface Listener {
public void setVoiceNoteDraft(@Nullable DraftDatabase.Draft voiceNoteDraft) {
if (voiceNoteDraft != null) {
voiceNoteDraftView.setDraft(voiceNoteDraft);
voiceNoteDraftView.setVisibility(VISIBLE);
hideNormalComposeViews();
} else {
voiceNoteDraftView.clearDraft();
ViewUtil.fadeOut(voiceNoteDraftView, FADE_TIME);
fadeInNormalComposeViews();
}
}
public @Nullable DraftDatabase.Draft getVoiceNoteDraft() {
return voiceNoteDraftView.getDraft();
}
private void hideNormalComposeViews() {
if (emojiVisible) {
Animation animation = mediaKeyboard.getAnimation();
if (animation != null) {
animation.cancel();
}
mediaKeyboard.setVisibility(View.INVISIBLE);
}
for (Animation animation : Arrays.asList(composeText.getAnimation(), quickCameraToggle.getAnimation(), quickAudioToggle.getAnimation())) {
if (animation != null) {
animation.cancel();
}
}
buttonToggle.animate().cancel();
composeText.setVisibility(View.INVISIBLE);
quickCameraToggle.setVisibility(View.INVISIBLE);
quickAudioToggle.setVisibility(View.INVISIBLE);
}
private void fadeInNormalComposeViews() {
if (emojiVisible) {
ViewUtil.fadeIn(mediaKeyboard, FADE_TIME);
}
ViewUtil.fadeIn(composeText, FADE_TIME);
ViewUtil.fadeIn(quickCameraToggle, FADE_TIME);
ViewUtil.fadeIn(quickAudioToggle, FADE_TIME);
buttonToggle.animate().alpha(1).setDuration(FADE_TIME).start();
}
public interface Listener extends VoiceNoteDraftView.Listener {
void onRecorderStarted();
void onRecorderLocked();
void onRecorderFinished();

View File

@@ -1,14 +1,19 @@
package org.thoughtcrime.securesms.components;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.WindowInsets;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Guideline;
import org.signal.glide.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.ViewUtil;
@@ -26,12 +31,31 @@ public class InsetAwareConstraintLayout extends ConstraintLayout {
super(context, attrs, defStyleAttr);
}
public InsetAwareConstraintLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
@Override
@TargetApi(20)
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
if (Build.VERSION.SDK_INT < 30) {
return super.onApplyWindowInsets(insets);
}
Insets windowInsets = insets.getInsets(WindowInsets.Type.systemBars() | WindowInsets.Type.ime() | WindowInsets.Type.displayCutout());
applyInsets(new Rect(windowInsets.left, windowInsets.top, windowInsets.right, windowInsets.bottom));
return super.onApplyWindowInsets(insets);
}
@Override
protected boolean fitSystemWindows(Rect insets) {
if (Build.VERSION.SDK_INT >= 30) {
return true;
}
applyInsets(insets);
return true;
}
private void applyInsets(@NonNull Rect insets) {
Guideline statusBarGuideline = findViewById(R.id.status_bar_guideline);
Guideline navigationBarGuideline = findViewById(R.id.navigation_bar_guideline);
Guideline parentStartGuideline = findViewById(R.id.parent_start_guideline);
@@ -60,7 +84,5 @@ public class InsetAwareConstraintLayout extends ConstraintLayout {
parentEndGuideline.setGuidelineEnd(insets.left);
}
}
return true;
}
}

View File

@@ -1,16 +1,16 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* <p>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* <p>
* 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 General Public License for more details.
*
* <p>
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -23,8 +23,10 @@ import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Surface;
import android.view.View;
import android.view.WindowInsets;
import androidx.appcompat.widget.LinearLayoutCompat;
@@ -43,23 +45,27 @@ import java.util.Set;
* has been opened and what its height would be.
*/
public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
private static final String TAG = KeyboardAwareLinearLayout.class.getSimpleName();
private static final String TAG = Log.tag(KeyboardAwareLinearLayout.class);
private final Rect rect = new Rect();
private final Set<OnKeyboardHiddenListener> hiddenListeners = new HashSet<>();
private final Set<OnKeyboardShownListener> shownListeners = new HashSet<>();
private final int minKeyboardSize;
private final int minCustomKeyboardSize;
private final int defaultCustomKeyboardSize;
private final int minCustomKeyboardTopMarginPortrait;
private final int minCustomKeyboardTopMarginLandscape;
private final int statusBarHeight;
private final Rect rect = new Rect();
private final Set<OnKeyboardHiddenListener> hiddenListeners = new HashSet<>();
private final Set<OnKeyboardShownListener> shownListeners = new HashSet<>();
private final DisplayMetrics displayMetrics = new DisplayMetrics();
private final int minKeyboardSize;
private final int minCustomKeyboardSize;
private final int defaultCustomKeyboardSize;
private final int minCustomKeyboardTopMarginPortrait;
private final int minCustomKeyboardTopMarginLandscape;
private final int minCustomKeyboardTopMarginLandscapeBubble;
private final int statusBarHeight;
private int viewInset;
private boolean keyboardOpen = false;
private int rotation = -1;
private boolean isFullscreen = false;
private boolean isBubble = false;
public KeyboardAwareLinearLayout(Context context) {
this(context, null);
@@ -71,13 +77,14 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
public KeyboardAwareLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
minKeyboardSize = getResources().getDimensionPixelSize(R.dimen.min_keyboard_size);
minCustomKeyboardSize = getResources().getDimensionPixelSize(R.dimen.min_custom_keyboard_size);
defaultCustomKeyboardSize = getResources().getDimensionPixelSize(R.dimen.default_custom_keyboard_size);
minCustomKeyboardTopMarginPortrait = getResources().getDimensionPixelSize(R.dimen.min_custom_keyboard_top_margin_portrait);
minCustomKeyboardTopMarginLandscape = getResources().getDimensionPixelSize(R.dimen.min_custom_keyboard_top_margin_portrait);
statusBarHeight = ViewUtil.getStatusBarHeight(this);
viewInset = getViewInset();
minKeyboardSize = getResources().getDimensionPixelSize(R.dimen.min_keyboard_size);
minCustomKeyboardSize = getResources().getDimensionPixelSize(R.dimen.min_custom_keyboard_size);
defaultCustomKeyboardSize = getResources().getDimensionPixelSize(R.dimen.default_custom_keyboard_size);
minCustomKeyboardTopMarginPortrait = getResources().getDimensionPixelSize(R.dimen.min_custom_keyboard_top_margin_portrait);
minCustomKeyboardTopMarginLandscape = getResources().getDimensionPixelSize(R.dimen.min_custom_keyboard_top_margin_portrait);
minCustomKeyboardTopMarginLandscapeBubble = getResources().getDimensionPixelSize(R.dimen.min_custom_keyboard_top_margin_landscape_bubble);
statusBarHeight = ViewUtil.getStatusBarHeight(this);
viewInset = getViewInset();
}
@Override
@@ -87,6 +94,10 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public void setIsBubble(boolean isBubble) {
this.isBubble = isBubble;
}
private void updateRotation() {
int oldRotation = rotation;
rotation = getDeviceRotation();
@@ -120,6 +131,25 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (Build.VERSION.SDK_INT >= 23 && getRootWindowInsets() != null) {
int bottomInset;
WindowInsets windowInsets = getRootWindowInsets();
if (Build.VERSION.SDK_INT >= 30) {
bottomInset = windowInsets.getInsets(WindowInsets.Type.navigationBars()).bottom;
} else {
bottomInset = windowInsets.getStableInsetBottom();
}
if (bottomInset != 0 && (viewInset == 0 || viewInset == statusBarHeight)) {
Log.i(TAG, "Updating view inset based on WindowInsets. viewInset: " + viewInset + " windowInset: " + bottomInset);
viewInset = bottomInset;
}
}
}
@TargetApi(VERSION_CODES.LOLLIPOP)
private int getViewInset() {
try {
@@ -129,7 +159,7 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
if (attachInfo != null) {
Field stableInsetsField = attachInfo.getClass().getDeclaredField("mStableInsets");
stableInsetsField.setAccessible(true);
Rect insets = (Rect)stableInsetsField.get(attachInfo);
Rect insets = (Rect) stableInsetsField.get(attachInfo);
if (insets != null) {
return insets.bottom;
}
@@ -177,28 +207,51 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
int rotation = getDeviceRotation();
return rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270;
}
private int getDeviceRotation() {
return ServiceUtil.getWindowManager(getContext()).getDefaultDisplay().getRotation();
if (Build.VERSION.SDK_INT >= 30) {
getContext().getDisplay().getRealMetrics(displayMetrics);
} else {
ServiceUtil.getWindowManager(getContext()).getDefaultDisplay().getRealMetrics(displayMetrics);
}
return displayMetrics.widthPixels > displayMetrics.heightPixels ? Surface.ROTATION_90 : Surface.ROTATION_0;
}
private int getKeyboardLandscapeHeight() {
if (isBubble) {
return getRootView().getHeight() - minCustomKeyboardTopMarginLandscapeBubble;
}
int keyboardHeight = PreferenceManager.getDefaultSharedPreferences(getContext())
.getInt("keyboard_height_landscape", defaultCustomKeyboardSize);
return Util.clamp(keyboardHeight, minCustomKeyboardSize, getRootView().getHeight() - minCustomKeyboardTopMarginLandscape);
}
private int getKeyboardPortraitHeight() {
if (isBubble) {
int height = getRootView().getHeight();
return height - (int)(height * 0.45);
}
int keyboardHeight = PreferenceManager.getDefaultSharedPreferences(getContext())
.getInt("keyboard_height_portrait", defaultCustomKeyboardSize);
return Util.clamp(keyboardHeight, minCustomKeyboardSize, getRootView().getHeight() - minCustomKeyboardTopMarginPortrait);
}
private void setKeyboardPortraitHeight(int height) {
if (isBubble) {
return;
}
PreferenceManager.getDefaultSharedPreferences(getContext())
.edit().putInt("keyboard_height_portrait", height).apply();
}
private void setKeyboardLandscapeHeight(int height) {
if (isBubble) {
return;
}
PreferenceManager.getDefaultSharedPreferences(getContext())
.edit().putInt("keyboard_height_landscape", height).apply();
}

View File

@@ -28,6 +28,7 @@ import java.text.SimpleDateFormat;
import java.util.Locale;
import okhttp3.HttpUrl;
import org.thoughtcrime.securesms.util.ViewUtil;
/**
* The view shown in the compose box or conversation that represents the state of the link preview.
@@ -181,10 +182,16 @@ public class LinkPreviewView extends FrameLayout {
}
}
public void setCorners(int topLeft, int topRight) {
cornerMask.setRadii(topLeft, topRight, 0, 0);
outliner.setRadii(topLeft, topRight, 0, 0);
thumbnail.setCorners(topLeft, defaultRadius, defaultRadius, defaultRadius);
public void setCorners(int topStart, int topEnd) {
if (ViewUtil.isRtl(this)) {
cornerMask.setRadii(topEnd, topStart, 0, 0);
outliner.setRadii(topEnd, topStart, 0, 0);
thumbnail.setCorners(defaultRadius, topEnd, defaultRadius, defaultRadius);
} else {
cornerMask.setRadii(topStart, topEnd, 0, 0);
outliner.setRadii(topStart, topEnd, 0, 0);
thumbnail.setCorners(topStart, defaultRadius, defaultRadius, defaultRadius);
}
postInvalidate();
}

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