Compare commits

...

314 Commits

Author SHA1 Message Date
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
Greyson Parrelli
751ba8d1c2 Bump version to 5.5.3 2021-03-17 14:12:40 -04:00
Greyson Parrelli
974ed439a4 Updated language translations. 2021-03-17 14:12:40 -04:00
Greyson Parrelli
0172c1e385 Prevent crashing on duplicate gv1 storage records. 2021-03-17 14:12:40 -04:00
Cody Henthorne
faa19acf81 Include additional settings in backup. 2021-03-17 14:12:40 -04:00
Cody Henthorne
1f9afb6c6e Use new Signal logo for more notifications. 2021-03-17 14:12:40 -04:00
Cody Henthorne
5d96bc2d3a Allow choose backup restore flow to work on pre-API29 devices. 2021-03-17 14:12:40 -04:00
Cody Henthorne
9366596f5f Fix discrepancy in message counting between export and import backups. 2021-03-17 14:12:40 -04:00
Cody Henthorne
cb6e3ade15 Fix bug where transfer continues when stopped from new device. 2021-03-17 14:12:40 -04:00
Cody Henthorne
45178b3eb3 Keep old device inactive after a successful transfer. 2021-03-17 14:12:40 -04:00
Greyson Parrelli
31e3e37c9b Improve logging for remapped recipients. 2021-03-16 15:57:14 -04:00
Alan Evans
e1489bb407 Exclude junit brought in by spongycastle. 2021-03-16 14:25:09 -03:00
Greyson Parrelli
8b50d8645a Add back private PlayStoreUtil constructor.
It was taken out in a public PR, thought I added it back in, but
apparently didn't.
2021-03-16 11:35:18 -04:00
Greyson Parrelli
796fdb1cf6 Bump version to 5.5.2 2021-03-16 10:52:56 -04:00
Greyson Parrelli
5203d40804 Updated language translations. 2021-03-16 10:45:43 -04:00
Yannick Verdie
21252aad0f Request storage permission when adding additional media. 2021-03-16 10:45:31 -04:00
Alex Hart
0c535904fc Restart activity in onNewIntent if activity is not yet initialized. 2021-03-16 11:37:40 -03:00
Cody Henthorne
490944a02a Improve UI/UX around device transfer. 2021-03-16 10:18:02 -04:00
Greyson Parrelli
ace85df9b7 Use US locale for log dates. 2021-03-16 10:00:04 -04:00
Alex Hart
9e56441d4a Fix several issues with contact name syncing. 2021-03-16 10:52:59 -03:00
Alan Evans
d83c3d35eb Ensure Job factories pass the parameters to their created Jobs. 2021-03-16 10:07:21 -03:00
Alan Evans
8f26d63d6f System name split migration. 2021-03-16 09:55:57 -03:00
Alan Evans
75c520097a Gradle witness checksums. 2021-03-15 19:54:12 -03:00
Leptopoda
38375982dd Don't ask for Play Store rating in website builds. 2021-03-15 12:35:57 -04:00
Greyson Parrelli
d24a71bbd2 Bump version to 5.5.1 2021-03-13 11:56:33 -05:00
Greyson Parrelli
7964c9fca7 Updated language translations. 2021-03-13 11:55:57 -05:00
Greyson Parrelli
ec07e4b233 Avoid possibility of walking outside a contact cursor. 2021-03-13 11:50:10 -05:00
Greyson Parrelli
b4266b8575 Fix contact sync issues where structured names are absent. 2021-03-13 11:29:57 -05:00
Greyson Parrelli
07201203b2 Add a category dropdown in the help fragment. 2021-03-12 22:56:56 -05:00
Greyson Parrelli
e7c5eb93dd Default OkHttp feature flag to 'true'. 2021-03-12 19:22:18 -05:00
Greyson Parrelli
e70229c672 Bump version to 5.5.0 2021-03-12 16:08:22 -05:00
Greyson Parrelli
86c5b28562 Updated language translations. 2021-03-12 16:08:22 -05:00
Cody Henthorne
a9149c5dc0 Stop backup jobs from continuing to run if backups become disabled.
Fixes #10819
2021-03-12 16:08:22 -05:00
Alan Evans
a64430c65f Make the foreground delay configurable. Remove short initial delay. 2021-03-12 16:08:22 -05:00
Cody Henthorne
75aab4c031 Add Device to Device Transfer UI. 2021-03-12 16:08:22 -05:00
Greyson Parrelli
6f8be3260c Do not mark dirty when updating profile key during storage sync. 2021-03-12 10:30:05 -05:00
Cody Henthorne
e74460bd91 Enable TLS connection and SAS verification between device transfer server and client. 2021-03-12 10:30:05 -05:00
Greyson Parrelli
c25250cb05 Include background restriction status in the logs. 2021-03-12 10:30:05 -05:00
Alan Evans
42c3cc5296 Do not insert empty group updates for non-internal users. 2021-03-12 10:30:05 -05:00
Greyson Parrelli
e4b3f90457 Log total time for conversation to render. 2021-03-12 10:30:05 -05:00
Greyson Parrelli
992b04f8c5 Add more logging around threads in message sends. 2021-03-12 10:30:05 -05:00
Alan Evans
d1e0f3646a Remove unused gradle file. 2021-03-08 13:50:34 -04:00
Fumiaki Yoshimatsu
b4ba565923 Purge view cache when layout direction is changed. 2021-03-08 12:13:32 -05:00
Fumiaki Yoshimatsu
006e7dc736 Fix reply and forward icons in RTL.
These vector drawables are `autoMirrored=true` but the attribute does
not work correctly due to [a known Android bug](https://issuetracker.google.com/issues/37138973).
This fix is to work around the bug.

Fixes #11006.
2021-03-08 12:12:48 -05:00
Greyson Parrelli
5ed6407ea3 No longer use a lock for RecipientCache.getSelf()
First, the only lock we can use for the time being is the database lock,
because if we use some other lock we could deadlock.

That said, it seems like we could avoid using a lock at all. The purpose
of the lock was to eliminate double-lookups, but if we have to acquire
the database lock to check if we need to do the lookup, we've lost the
advantage of doing so at all.

We *could* just do a traditional check-lock-check pattern to get the
lock far less often, but given that we're likely going to acquire it
during startup, even a single access has the possibility of really
gumming up the works.
2021-03-08 09:48:28 -05:00
Jon Chambers
faf6b5a4e4 Use a new serviceID for the re-deployed KBS setup in staging. 2021-03-05 14:11:50 -05:00
Greyson Parrelli
f92891895e Add foundation for automated performance tests. 2021-03-05 13:54:57 -05:00
Jonah Beckford
d8cc3c86b4 Correct check for null test. 2021-03-04 10:54:33 -04:00
Cody Henthorne
e7f233db5b Add Device Transfer via WiFi Direct groundwork. 2021-03-03 16:03:49 -05:00
Alex Hart
fd9c420dc8 Split system names into first / last. 2021-03-03 09:37:30 -04:00
Jim Gustafson
dc9fceb8cf Update to RingRTC v2.9.4 2021-03-02 11:09:55 -08:00
Cody Henthorne
dc9b8169c0 Make thread related utility methods available for use in all modules. 2021-03-02 11:07:04 -05:00
Greyson Parrelli
38caf1e2b7 Update ShortcutBadger to 1.1.22 2021-03-02 11:07:04 -05:00
Greyson Parrelli
4b3e7c8858 Remove SELF_LOCK from LiveRecipientCache.
Had the potential to deadlock if accessed inside of a database
transaction.
2021-03-02 11:07:04 -05:00
Alan Evans
2be3068675 Bump version to 5.4.12 2021-03-02 11:29:35 -04:00
Alan Evans
682e47c7b3 Updated language translations. 2021-03-02 11:29:07 -04:00
Greyson Parrelli
8c90c3ad81 Update notification megaphone behavior.
Detect when they've blocked the channel group, and also don't snooze the
megaphone unless they hit 'not now'.
2021-03-02 10:08:02 -05:00
Alan Evans
d5afcc4aec Bump version to 5.4.11 2021-03-01 10:54:29 -04:00
Alan Evans
14ed4201c0 Updated language translations. 2021-03-01 10:54:29 -04:00
Greyson Parrelli
1b9efeb049 Go back to using a reentrant lock for store operations. 2021-03-01 09:38:33 -05:00
Greyson Parrelli
4b862cf4c7 Bump version to 5.4.10 2021-02-26 15:24:41 -05:00
Greyson Parrelli
4e7683961e Updated language translations. 2021-02-26 15:24:04 -05:00
Greyson Parrelli
ce9d44d010 Move back to async message processing. 2021-02-26 15:24:04 -05:00
Greyson Parrelli
32d052259f Fix MessageContentProcessor logging. 2021-02-26 15:24:04 -05:00
Greyson Parrelli
fb98874948 Fix initialization of BlobProvider. 2021-02-26 15:24:04 -05:00
Cody Henthorne
55a62ead05 Avoid API 10 issues with androidx.biometric usage. 2021-02-26 15:24:03 -05:00
Alan Evans
f81f50646e Add share hint for sharing group link. 2021-02-25 18:09:36 -04:00
Alan Evans
f707c1d02c Bump version to 5.4.9 2021-02-25 16:03:26 -04:00
Alan Evans
417c04bf1c Updated language translations. 2021-02-25 16:02:16 -04:00
Greyson Parrelli
8f7f836598 Add logging around the message processing lock. 2021-02-25 15:56:16 -04:00
Surith Thekkiam
32aea8d154 Recommend Docker have 5GB memory available.
Co-authored-by: Alan Evans <alan@signal.org>
2021-02-25 14:16:59 -04:00
Alan Evans
3f6c8cb622 Prevent showing notification megaphone if not translated. 2021-02-25 14:16:59 -04:00
Greyson Parrelli
8f6ff215aa Show a megaphone when notifications are disabled. 2021-02-25 12:54:23 -05:00
Greyson Parrelli
4f01bacb49 Add recipient protections and logging to media send flow. 2021-02-25 12:19:58 -05:00
Alan Evans
e6f4b0976f Prevent double tap send on camera first flow.
Defensive array list copies where used in builders and Intent#putParcelableArrayListExtra.

Spelling.
2021-02-25 12:33:39 -04:00
Cody Henthorne
e0d9c3f149 Fix duplicate showing of new biometric lock dialog. 2021-02-25 11:25:52 -05:00
Greyson Parrelli
9d3cebf430 Fix possible NPE in ConversationListAdapter. 2021-02-25 09:29:29 -05:00
Greyson Parrelli
5e106bf510 Prevent possible NPE when handling early message content. 2021-02-25 09:29:29 -05:00
Angelo Trevisiol
fc617fb7a9 Fixes sharing empty messages via Invite Friends.
Fixes #10817
2021-02-25 09:17:18 -05:00
Alan Evans
b88ecb6370 Add "Enter your phone number" string for translation. 2021-02-25 10:06:46 -04:00
Greyson Parrelli
9b21001953 Remove research megaphone feature flag. 2021-02-24 20:23:36 -05:00
Greyson Parrelli
cb0e10c7ab Inline group migration job feature flag. 2021-02-24 20:18:10 -05:00
Greyson Parrelli
69884935f3 Inline manual group migration feature flag. 2021-02-24 20:17:01 -05:00
Greyson Parrelli
b809008291 Bump version to 5.4.8 2021-02-24 16:36:41 -05:00
Greyson Parrelli
79e77f871e Updated language translations. 2021-02-24 16:36:15 -05:00
Greyson Parrelli
32ac6e3429 Ensure blobs from old sessions are deleted before creating new ones.
There was a race condition where if you created a blob super-early in
the application lifecycle, you could create it *before* we deleted the
blobs from the previous session, leading you to lose the blob you just
created immediately.

In an effort to protect our cold start time, I just made a little
initialization flow where read/write calls to BlobProvider will block
until it's deleted blobs from the old session.
2021-02-24 16:04:58 -05:00
Alan Evans
da56c2790f Ensure no typing indicators can be sent to self or blocked recipients. 2021-02-24 16:04:29 -05:00
Fumiaki Yoshimatsu
687192f071 Fix crash when draft text is null.
Fixes #10913
2021-02-24 15:29:39 -05:00
Fumiaki Yoshimatsu
2e82ee0aaf Fix leaking cursor in Contact Selection list. 2021-02-24 15:28:53 -05:00
Fumiaki Yoshimatsu
4dacf4e342 Fix reaction overlay not showing on first try in RTL mode.
The view needs to request a call to `fitsSystemWindows`; otherwise it
cannot determine where to layout itself within the parent that uses
fitsSystemWindows method to determine the boundary of it.

This fixes the issue reported in [the beta 5.3 forum](https://community.signalusers.org/t/beta-feedback-for-the-upcoming-android-5-3-release/25088/315).
2021-02-24 15:09:31 -05:00
Cody Henthorne
b91f04316a Fix lint errors and add submodules to qa checks. 2021-02-24 15:07:56 -05:00
Fumiaki Yoshimatsu
3c4252a933 Enable lock screen fallback when biometric authentications may not work.
Fixes #9407
Fixes #10166
2021-02-24 08:54:25 -05:00
Alan Evans
be4b687e48 Always delete all messages when deleting a conversation. 2021-02-23 23:59:53 -04:00
Greyson Parrelli
8950100bd7 Decrypt and process messages all in one transaction.
Giddy up
2021-02-23 18:34:18 -05:00
Alan Evans
d651716d99 Remove two AsyncTasks. 2021-02-23 12:48:20 -04:00
Sgn-32
270606699b Do not send typing indicators in Note to Self.
Fixes #10988
2021-02-22 18:07:56 -04:00
Greyson Parrelli
cc7617a302 Throttle conversation list update frequency.
Reduces the load on the database (and UI jankiness) while processing a
large message backlog.
2021-02-22 11:48:41 -05:00
Cody Henthorne
c04b5f2085 Fix tint for bubbled conversation up icon. 2021-02-20 14:59:48 -05:00
Greyson Parrelli
28f3ded4bd Perform individual decryptions inside a database transaction.
Required a lot of random locking work to prevent deadlocking, but
overall this results in about a 2x speed increase for decryptions.
2021-02-20 14:45:36 -05:00
Greyson Parrelli
d56607a686 Log when screen lock toggle changes. 2021-02-20 14:45:36 -05:00
Greyson Parrelli
f8dde57133 Add notification settings to debuglogs. 2021-02-20 14:45:36 -05:00
Greyson Parrelli
f2ea13a142 Bump libsignal-client to 0.1.6 2021-02-20 14:45:36 -05:00
Greyson Parrelli
69a1fa0d3c Remove database transaction protections for job manager interactions.
Now that the JobDatabase is a separate physical database, we no longer
have to worry about people enqueuing jobs while in a transaction.
2021-02-20 14:45:36 -05:00
Greyson Parrelli
5cb54b9ad7 Only sync contact names in contact syncs. 2021-02-20 14:45:36 -05:00
Cody Henthorne
ee610cadd3 Bump version to 5.4.7 2021-02-19 13:29:31 -05:00
Cody Henthorne
acf131308b Updated language translations. 2021-02-19 13:29:31 -05:00
Cody Henthorne
c591ec3185 Fix bug with dropped ice candidates when receiving a call. 2021-02-19 13:07:20 -05:00
Cody Henthorne
264a245d27 Bump version to 5.4.6 2021-02-17 15:08:16 -05:00
Cody Henthorne
f6c25d2a8b Updated language translations. 2021-02-17 15:08:16 -05:00
Greyson Parrelli
d069d9331c Perform additional URI validation in ShareRepository.
Thanks to Shivasurya <s5sankar@uwaterloo.ca> for reporting this issue!
2021-02-17 15:07:37 -05:00
Greyson Parrelli
ba14031945 Revert "Perform additional URI validation in ShareRepository."
This reverts commit 04b7cb15cc.
2021-02-17 15:07:37 -05:00
Cody Henthorne
0b639e0169 Attempt to calculate orientation without magnetic field sensor data when it's missing. 2021-02-17 12:48:47 -05:00
Greyson Parrelli
afee8631e1 Do not compress stickers. 2021-02-17 12:33:38 -05:00
Cody Henthorne
214cb25d1b Fix crash when receiving call with no corresponding identity key. 2021-02-17 10:26:45 -05:00
Greyson Parrelli
a1457d22d6 Do not show recipient tooltip for the share flow. 2021-02-17 08:31:59 -05:00
Greyson Parrelli
82107e2938 Do not show GV1 manual migration if you don't have the capability. 2021-02-17 08:29:58 -05:00
Greyson Parrelli
e2f5fa6962 Bump version to 5.4.5 2021-02-16 17:45:06 -05:00
Greyson Parrelli
74336041ea Updated language translations. 2021-02-16 17:45:06 -05:00
Greyson Parrelli
0dea5eb779 Log both current and original priority for FCM messages. 2021-02-16 17:45:06 -05:00
Greyson Parrelli
267f759452 Fix possible crash during NotificationChannels initialization. 2021-02-16 17:45:06 -05:00
Greyson Parrelli
2779d7efc5 Target the middle of the screen when jumping to a message. 2021-02-16 17:45:06 -05:00
Greyson Parrelli
04b7cb15cc Perform additional URI validation in ShareRepository. 2021-02-16 17:38:05 -05:00
Cody Henthorne
eba04eb75b Revert "Fix reaction overlay not showing on first try in RTL mode."
This reverts commit 424979d91f.
2021-02-13 19:50:32 -05:00
Greyson Parrelli
d924fc92ab Improve accuracy of decryption drained constraint. 2021-02-12 22:42:01 -05:00
Greyson Parrelli
69de830a10 Fix conversation search bar tint. 2021-02-12 20:09:24 -05:00
Alan Evans
60b9811e08 Bump version to 5.4.4 2021-02-12 17:56:01 -04:00
Alan Evans
01418c0e36 Upgrade notification channels earlier. 2021-02-12 17:38:39 -04:00
Cody Henthorne
3f374eebc2 Bump version to 5.4.3 2021-02-12 15:48:45 -05:00
Cody Henthorne
502a2ead00 Updated language translations. 2021-02-12 15:47:56 -05:00
Alan Evans
0d9490e1fb Force disabling of foreground notification vibration. 2021-02-12 16:33:06 -04:00
Cody Henthorne
4afb459b30 Revert "Bump libsignal-client to 0.2.3"
This reverts commit 8b7506ed2d.
2021-02-12 15:03:37 -05:00
Greyson Parrelli
37f4557fef Fix status bar color on Android 5.x. 2021-02-12 14:53:40 -05:00
Fumiaki Yoshimatsu
424979d91f Fix reaction overlay not showing on first try in RTL mode.
Moved the code that does some layouts according to the view's layout direction to the place where we can safely use the value from getLayoutDirection().

This fixes the issue reported in [the beta 5.3 forum](https://community.signalusers.org/t/beta-feedback-for-the-upcoming-android-5-3-release/25088/315).
2021-02-12 14:53:14 -05:00
Greyson Parrelli
527fbee41e Improve experience of populating migration dialog. 2021-02-12 11:42:20 -05:00
Cody Henthorne
1935b0ebdd Do not show message notifications if disabled in settings. 2021-02-12 11:09:51 -05:00
Alan Evans
f6aaef1434 Ensure MessageProcessReceiver pending result finished is called timely. 2021-02-12 11:24:12 -04:00
Alan Evans
050b59f09d Increase maximum zoom level for small images. 2021-02-12 10:36:03 -04:00
Greyson Parrelli
db8dcf6073 Fix conversation banner layout issues. 2021-02-11 21:48:23 -05:00
1266 changed files with 71759 additions and 11122 deletions

4
.editorconfig Normal file
View File

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

View File

@@ -26,3 +26,10 @@ jobs:
- name: Build with Gradle
run: ./gradlew qa
- name: Archive reports for failed build
if: ${{ failure() }}
uses: actions/upload-artifact@v2
with:
name: reports
path: '*/build/reports'

View File

@@ -3,9 +3,12 @@ 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'
@@ -16,12 +19,6 @@ repositories {
includeGroupByRegex "com\\.github\\.chrisbanes.*"
}
}
maven {
url "https://raw.github.com/signalapp/maven/master/shortcutbadger/releases/"
content {
includeGroupByRegex "me\\.leolin.*"
}
}
maven {
url "https://raw.github.com/signalapp/maven/master/circular-progress-button/releases/"
content {
@@ -44,6 +41,9 @@ repositories {
mavenCentral()
jcenter()
mavenLocal()
maven {
url "https://dl.cloudsmith.io/qxAgwaeEE1vN8aLU/mobilecoin/mobilecoin/maven/"
}
}
protobuf {
@@ -61,8 +61,8 @@ protobuf {
}
}
def canonicalVersionCode = 788
def canonicalVersionName = "5.4.2"
def canonicalVersionCode = 829
def canonicalVersionName = "5.9.2"
def postFixSize = 100
def abiPostFix = ['universal' : 0,
@@ -129,6 +129,8 @@ 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}"
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
@@ -149,6 +151,7 @@ android {
}
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JAVA_VERSION
targetCompatibility JAVA_VERSION
}
@@ -161,6 +164,8 @@ android {
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
exclude 'META-INF/proguard/androidx-annotations.pro'
exclude '/org/spongycastle/x509/CertPathReviewerMessages.properties'
exclude '/org/spongycastle/x509/CertPathReviewerMessages_de.properties'
}
buildTypes {
@@ -208,6 +213,12 @@ android {
debuggable false
matchingFallbacks = ['debug']
}
mock {
initWith debug
isDefault false
minifyEnabled false
matchingFallbacks = ['debug']
}
}
productFlavors {
@@ -233,10 +244,21 @@ android {
buildConfigField "String", "NOPLAY_UPDATE_URL", "$ext.websiteUpdateUrl"
}
study {
dimension 'distribution'
applicationIdSuffix ".study"
ext.websiteUpdateUrl = "null"
buildConfigField "boolean", "PLAY_STORE_DISABLED", "false"
buildConfigField "String", "NOPLAY_UPDATE_URL", "$ext.websiteUpdateUrl"
}
prod {
dimension 'environment'
isDefault true
buildConfigField "String", "MOBILE_COIN_ENVIRONMENT", "\"mainnet\""
}
staging {
@@ -252,11 +274,12 @@ android {
buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api-staging.backup.signal.org\""
buildConfigField "String", "CDS_MRENCLAVE", "\"c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15\""
buildConfigField "KbsEnclave", "KBS_ENCLAVE", "new KbsEnclave(\"823a3b2c037ff0cbe305cc48928cfcc97c9ed4a8ca6d49af6f7d6981fb60a4e9\", " +
"\"038c40bbbacdc873caa81ac793bb75afde6dfe436a99ab1f15e3f0cbb7434ced\", " +
"\"51a56084c0b21c6b8f62b1bc792ec9bedac4c7c3964bb08ddcab868158c09982\", " +
"\"a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87\")"
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\""
}
}
@@ -272,6 +295,18 @@ android {
}
}
android.variantFilter { variant ->
def distribution = variant.getFlavors().get(0).name
def environment = variant.getFlavors().get(1).name
def buildType = variant.buildType.name
if (distribution == 'study' && buildType != 'perf' && buildType != 'mock') {
variant.setIgnore(true)
} else if (distribution != 'study' && buildType == 'mock') {
variant.setIgnore(true)
}
}
lintOptions {
abortOnError true
baseline file("lint-baseline.xml")
@@ -288,6 +323,8 @@ android {
dependencies {
lintChecks project(':lintchecks')
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation ('androidx.appcompat:appcompat:1.2.0') {
force = true
}
@@ -312,6 +349,7 @@ dependencies {
implementation "androidx.camera:camera-view:1.0.0-alpha18"
implementation "androidx.concurrent:concurrent-futures:1.0.0"
implementation "androidx.autofill:autofill:1.0.0"
implementation "androidx.biometric:biometric:1.1.0"
implementation ('com.google.firebase:firebase-messaging:20.2.0') {
exclude group: 'com.google.firebase', module: 'firebase-core'
@@ -333,22 +371,28 @@ dependencies {
implementation project(':paging')
implementation project(':core-util')
implementation project(':video')
implementation project(':device-transfer')
implementation 'org.signal:zkgroup-android:0.7.0'
implementation 'org.whispersystems:signal-client-android:0.2.3'
implementation 'org.whispersystems:signal-client-android:0.1.7'
implementation 'com.google.protobuf:protobuf-javalite:3.10.0'
implementation('com.mobilecoin:android-sdk:1.0.0') {
exclude group: 'com.google.protobuf'
}
implementation 'org.signal:argon2:13.1@aar'
implementation 'org.signal:ringrtc-android:2.9.2'
implementation 'org.signal:ringrtc-android:2.9.4'
implementation "me.leolin:ShortcutBadger:1.1.16"
implementation "me.leolin:ShortcutBadger:1.1.22"
implementation 'se.emilsjolander:stickylistheaders:2.7.0'
implementation 'com.jpardogo.materialtabstrip:library:1.0.9'
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'
@@ -408,6 +452,8 @@ dependencies {
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"
}
dependencyVerification {

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

@@ -377,6 +377,16 @@
android:windowSoftInputMode="stateAlwaysHidden">
</activity>
<activity android:name=".devicetransfer.olddevice.OldDeviceTransferActivity"
android:theme="@style/TextSecure.LightRegistrationTheme"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".devicetransfer.olddevice.OldDeviceExitActivity"
android:noHistory="true"
android:excludeFromRecents="true"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".registration.RegistrationNavigationActivity"
android:launchMode="singleTask"
android:theme="@style/TextSecure.LightRegistrationTheme"
@@ -487,6 +497,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"
@@ -571,7 +585,7 @@
android:screenOrientation="portrait"
android:theme="@style/Theme.Signal.WallpaperCropper" />
<service android:enabled="true" android:name=".service.WebRtcCallService"/>
<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"/>
@@ -702,6 +716,8 @@
<receiver android:name=".service.TrimThreadsByDateManager$TrimThreadsByDateAlarm" />
<receiver android:name=".payments.backup.phrase.ClearClipboardAlarmReceiver" />
<provider android:name=".providers.PartProvider"
android:grantUriPermissions="true"
android:exported="false"
@@ -730,10 +746,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" />

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

@@ -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

@@ -54,6 +54,7 @@ public final class AppInitialization {
ApplicationDependencies.getMegaphoneRepository().onFirstEverAppLaunch();
SignalStore.onFirstEverAppLaunch();
SignalStore.onboarding().clearAll();
TextSecurePreferences.onPostBackupRestore(context);
ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.ZOZO.getPackId(), BlessedPacks.ZOZO.getPackKey(), false));
ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.BANDIT.getPackId(), BlessedPacks.BANDIT.getPackKey(), false));
ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.DAY_BY_DAY.getPackId(), BlessedPacks.DAY_BY_DAY.getPackKey(), false));

View File

@@ -81,8 +81,6 @@ 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;
/**
@@ -95,11 +93,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();
@@ -126,6 +122,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
.addBlocking("crash-handling", this::initializeCrashHandling)
.addBlocking("eat-db", () -> DatabaseFactory.getInstance(this))
.addBlocking("app-dependencies", this::initializeAppDependencies)
.addBlocking("notification-channels", () -> NotificationChannels.create(this))
.addBlocking("first-launch", this::initializeFirstEverAppLaunch)
.addBlocking("app-migrations", this::initializeApplicationMigrations)
.addBlocking("ring-rtc", this::initializeRingRtc)
@@ -144,6 +141,8 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
Conscrypt.setUseEngineSocketByDefault(true);
}
})
.addBlocking("blob-provider", this::initializeBlobProvider)
.addBlocking("feature-flags", FeatureFlags::init)
.addNonBlocking(this::initializeRevealableMessageManager)
.addNonBlocking(this::initializeGcmCheck)
.addNonBlocking(this::initializeSignedPreKeyCheck)
@@ -152,13 +151,10 @@ 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())
.addPostRender(this::initializeExpiringMessageManager)
.addPostRender(this::initializeBlobProvider)
.addPostRender(() -> NotificationChannels.create(this))
.execute();
Log.d(TAG, "onCreate() took " + (System.currentTimeMillis() - startTime) + " ms");
@@ -196,17 +192,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;
}
@@ -244,7 +229,7 @@ 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);
org.signal.core.util.logging.Log.initialize(FeatureFlags::internalUser, new AndroidLogger(), persistentLogger);
SignalProtocolLoggerProvider.setProvider(new CustomSignalProtocolLogger());
}
@@ -301,11 +286,11 @@ 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 initializePeriodicTasks() {
@@ -322,31 +307,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);
}
@@ -387,7 +352,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
@WorkerThread
private void initializeBlobProvider() {
BlobProvider.getInstance().onSessionStart(this);
BlobProvider.getInstance().initialize(this);
}
@WorkerThread

View File

@@ -23,6 +23,7 @@ import android.content.SharedPreferences;
import android.graphics.PorterDuff;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -31,8 +32,12 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.conversationlist.model.UnreadPayments;
import org.thoughtcrime.securesms.conversationlist.model.UnreadPaymentsLiveData;
import org.thoughtcrime.securesms.help.HelpFragment;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.payments.preferences.PaymentsActivity;
import org.thoughtcrime.securesms.preferences.AdvancedPreferenceFragment;
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
import org.thoughtcrime.securesms.preferences.AppearancePreferenceFragment;
@@ -43,9 +48,9 @@ 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.PaymentsPreference;
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;
@@ -66,12 +71,13 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
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_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 TAG = Log.tag(ApplicationPreferencesActivity.class);
private static final String PREFERENCE_CATEGORY_PROFILE = "preference_category_profile";
private static final String PREFERENCE_CATEGORY_USERNAME = "preference_category_username";
@@ -85,6 +91,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
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 PREFERENCE_CATEGORY_PAYMENTS = "preference_category_payments";
private static final String WAS_CONFIGURATION_UPDATED = "was_configuration_updated";
@@ -109,9 +116,14 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
} 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());
Bundle bundle = new Bundle();
bundle.putInt(HelpFragment.START_CATEGORY_INDEX, getIntent().getIntExtra(HelpFragment.START_CATEGORY_INDEX, 0));
initFragment(android.R.id.content, new HelpFragment(), null, bundle);
} 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 {
@@ -187,6 +199,8 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
public static class ApplicationPreferenceFragment extends CorrectedPreferenceFragment {
private final UnreadPaymentsLiveData unreadPaymentsLiveData = new UnreadPaymentsLiveData();
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -216,9 +230,29 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
this.findPreference(PREFERENCE_CATEGORY_DONATE)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_DONATE));
Preference paymentsPreference = this.findPreference(PREFERENCE_CATEGORY_PAYMENTS);
if (SignalStore.paymentsValues().getPaymentsAvailability().showPaymentsMenu()) {
paymentsPreference.setVisible(true);
paymentsPreference.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_PAYMENTS));
} else {
paymentsPreference.setVisible(false);
}
tintIcons();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (SignalStore.paymentsValues().getPaymentsAvailability().showPaymentsMenu()) {
PaymentsPreference paymentsPreference = (PaymentsPreference) this.findPreference(PREFERENCE_CATEGORY_PAYMENTS);
unreadPaymentsLiveData.observe(getViewLifecycleOwner(), unreadPayments -> paymentsPreference.setUnreadCount(unreadPayments.transform(UnreadPayments::getUnreadCount).or(-1)));
}
}
private void tintIcons() {
if (Build.VERSION.SDK_INT >= 21) return;
@@ -332,6 +366,9 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
case PREFERENCE_CATEGORY_DONATE:
CommunicationActions.openBrowserLink(requireContext(), getString(R.string.donate_url));
break;
case PREFERENCE_CATEGORY_PAYMENTS:
startActivity(new Intent(requireContext(), PaymentsActivity.class));
break;
default:
throw new AssertionError();
}

View File

@@ -8,11 +8,15 @@ import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.Observer;
import org.thoughtcrime.securesms.components.MaskView;
import org.thoughtcrime.securesms.components.voice.VoiceNotePlaybackState;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.conversation.ConversationItem;
import org.thoughtcrime.securesms.conversation.ConversationMessage;
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 {
void bind(@NonNull LifecycleOwner lifecycleOwner,
@NonNull ConversationMessage messageRecord,
@NonNull Optional<MessageRecord> previousMessageRecord,
@@ -38,7 +43,9 @@ public interface BindableConversationItem extends Unbindable {
@Nullable String searchQuery,
boolean pulseMention,
boolean hasWallpaper,
boolean isMessageRequestAccepted);
boolean isMessageRequestAccepted,
@NonNull AttachmentMediaSourceFactory attachmentMediaSourceFactory,
boolean canPlayInline);
ConversationMessage getConversationMessage();
@@ -67,6 +74,9 @@ public interface BindableConversationItem extends Unbindable {
void onSafetyNumberLearnMoreClicked(@NonNull Recipient recipient);
void onJoinGroupCallClicked();
void onInviteFriendsToGroupClicked(@NonNull GroupId.V2 groupId);
void onEnableCallNotificationsClicked();
void onPlayInlineContent(ConversationMessage conversationMessage);
void onInMemoryMessageClicked(@NonNull InMemoryMessageRecord messageRecord);
/** @return true if handled, false if you want to let the normal url handling continue */
boolean onUrlClicked(@NonNull String url);

View File

@@ -11,6 +11,8 @@ import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import org.signal.core.util.logging.Log;
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;
@@ -27,17 +29,16 @@ 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;
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
public class ConfirmIdentityDialog extends AlertDialog {
@SuppressWarnings("unused")
private static final String TAG = ConfirmIdentityDialog.class.getSimpleName();
private static final String TAG = Log.tag(ConfirmIdentityDialog.class);
private OnClickListener callback;
@@ -94,7 +95,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
{
@Override
protected Void doInBackground(Void... params) {
synchronized (SESSION_LOCK) {
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(Recipient.resolved(recipientId).requireServiceId(), 1);
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext());
@@ -136,7 +137,6 @@ public class ConfirmIdentityDialog extends AlertDialog {
private void processIncomingMessageRecord(MessageRecord messageRecord) {
try {
PushDatabase pushDatabase = DatabaseFactory.getPushDatabase(getContext());
MessageDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext());
smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
@@ -155,9 +155,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
0,
null);
long pushId = pushDatabase.insert(envelope);
ApplicationDependencies.getJobManager().add(new PushDecryptMessageJob(getContext(), pushId, messageRecord.getId()));
ApplicationDependencies.getJobManager().add(new PushDecryptMessageJob(getContext(), envelope, messageRecord.getId()));
} catch (IOException e) {
throw new AssertionError(e);
}

View File

@@ -47,7 +47,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";

View File

@@ -58,6 +58,7 @@ 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.contacts.AbstractContactsCursorLoader;
import org.thoughtcrime.securesms.contacts.ContactChip;
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter;
import org.thoughtcrime.securesms.contacts.ContactSelectionListItem;
@@ -114,22 +115,23 @@ public final class ContactSelectionListFragment extends LoggingFragment
public static final String CAN_SELECT_SELF = "can_select_self";
public static final String DISPLAY_CHIPS = "display_chips";
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 WarningTextView groupLimit;
private OnSelectionLimitReachedListener onSelectionLimitReachedListener;
private AbstractContactsCursorLoaderFactoryProvider cursorFactoryProvider;
@Nullable private FixedViewsAdapter headerAdapter;
@@ -151,10 +153,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 +172,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 +208,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();
@@ -224,14 +242,18 @@ public final class ContactSelectionListFragment extends LoggingFragment
}
});
Intent intent = requireActivity().getIntent();
Intent intent = requireActivity().getIntent();
Bundle arguments = safeArguments();
swipeRefresh.setEnabled(intent.getBooleanExtra(REFRESHABLE, true));
swipeRefresh.setEnabled(arguments.getBoolean(REFRESHABLE, intent.getBooleanExtra(REFRESHABLE, true)));
hideCount = intent.getBooleanExtra(HIDE_COUNT, false);
selectionLimit = intent.getParcelableExtra(SELECTION_LIMITS);
isMulti = selectionLimit != null;
canSelectSelf = intent.getBooleanExtra(CAN_SELECT_SELF, !isMulti);
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;
@@ -244,6 +266,10 @@ public final class ContactSelectionListFragment extends LoggingFragment
return view;
}
private @NonNull Bundle safeArguments() {
return getArguments() != null ? getArguments() : new Bundle();
}
private void updateGroupLimit(int chipCount) {
int members = currentSelection.size() + chipCount;
groupLimit.setText(getResources().getQuantityString(R.plurals.ContactSelectionListFragment_d_members, members, members));
@@ -273,7 +299,10 @@ public final class ContactSelectionListFragment extends LoggingFragment
}
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()));
@@ -387,10 +416,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 = safeArguments().getBoolean(RECENTS, activity.getIntent().getBooleanExtra(RECENTS, false));
if (cursorFactoryProvider != null) {
return cursorFactoryProvider.get().create();
} else {
return new ContactsCursorLoader.Factory(activity, displayMode, cursorFilter, displayRecents).create();
}
}
@Override
@@ -656,7 +690,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;
}
@@ -696,4 +730,8 @@ public final class ContactSelectionListFragment extends LoggingFragment
public interface ScrollCallback {
void onBeginScroll();
}
public interface AbstractContactsCursorLoaderFactoryProvider {
@NonNull AbstractContactsCursorLoader.Factory get();
}
}

View File

@@ -18,6 +18,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
@@ -28,7 +29,6 @@ import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.InvalidKeyException;
@@ -45,7 +45,7 @@ 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 DynamicLanguage dynamicLanguage = new DynamicLanguage();
@@ -123,7 +123,7 @@ public class DeviceActivity extends PassphraseRequiredActivity
@Override
public void onQrDataFound(final String data) {
Util.runOnMain(() -> {
ThreadUtil.runOnMain(() -> {
((Vibrator)getSystemService(Context.VIBRATOR_SERVICE)).vibrate(50);
Uri uri = Uri.parse(data);
deviceLinkFragment.setLinkClickedListener(uri, DeviceActivity.this);

View File

@@ -40,7 +40,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;

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

@@ -7,6 +7,8 @@ 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;
@@ -40,6 +42,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.WindowUtil;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture.Listener;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.thoughtcrime.securesms.util.text.AfterTextChanged;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.concurrent.ExecutionException;
@@ -105,6 +108,14 @@ public class InviteActivity extends PassphraseRequiredActivity implements Contac
contactsFragment = (ContactSelectionListFragment)getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
inviteText.setText(getString(R.string.InviteActivity_lets_switch_to_signal, getString(R.string.install_url)));
inviteText.addTextChangedListener(new AfterTextChanged(editable -> {
boolean isEnabled = editable.length() > 0;
smsButton.setEnabled(isEnabled);
shareButton.setEnabled(isEnabled);
smsButton.animate().alpha(isEnabled ? 1f : 0.5f);
shareButton.animate().alpha(isEnabled ? 1f : 0.5f);
}));
updateSmsButtonText(contactsFragment.getSelectedContacts().size());
smsCancelButton.setOnClickListener(new SmsCancelClickListener());

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;
@@ -15,6 +16,12 @@ public abstract class LoggingFragment extends Fragment {
private static final String TAG = Log.tag(LoggingFragment.class);
public LoggingFragment() { }
public LoggingFragment(@LayoutRes int contentLayoutId) {
super(contentLayoutId);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
logEvent("onCreate()");

View File

@@ -9,6 +9,8 @@ import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.devicetransfer.olddevice.OldDeviceTransferLockedDialog;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.util.AppStartup;
import org.thoughtcrime.securesms.util.CachedInflater;
import org.thoughtcrime.securesms.util.CommunicationActions;
@@ -70,6 +72,9 @@ public class MainActivity extends PassphraseRequiredActivity {
protected void onResume() {
super.onResume();
dynamicTheme.onResume(this);
if (SignalStore.misc().isOldDeviceTransferLocked()) {
OldDeviceTransferLockedDialog.show(getSupportFragmentManager());
}
}
@Override

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;

View File

@@ -38,10 +38,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,7 +49,7 @@ 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) {

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

@@ -17,7 +17,6 @@
package org.thoughtcrime.securesms;
import android.animation.Animator;
import android.annotation.SuppressLint;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
@@ -46,9 +45,11 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
import androidx.core.os.CancellationSignal;
import androidx.biometric.BiometricManager;
import androidx.biometric.BiometricManager.Authenticators;
import androidx.biometric.BiometricPrompt;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
@@ -70,7 +71,12 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
*/
public class PassphrasePromptActivity extends PassphraseActivity {
private static final String TAG = PassphrasePromptActivity.class.getSimpleName();
private static final String TAG = Log.tag(PassphrasePromptActivity.class);
private static final int BIOMETRIC_AUTHENTICATORS = Authenticators.BIOMETRIC_STRONG | Authenticators.BIOMETRIC_WEAK;
private static final int ALLOWED_AUTHENTICATORS = BIOMETRIC_AUTHENTICATORS | Authenticators.DEVICE_CREDENTIAL;
private static final short AUTHENTICATE_REQUEST_CODE = 1007;
private static final String BUNDLE_ALREADY_SHOWN = "bundle_already_shown";
public static final String FROM_FOREGROUND = "from_foreground";
private DynamicIntroTheme dynamicTheme = new DynamicIntroTheme();
private DynamicLanguage dynamicLanguage = new DynamicLanguage();
@@ -84,12 +90,13 @@ public class PassphrasePromptActivity extends PassphraseActivity {
private ImageButton hideButton;
private AnimatingToggle visibilityToggle;
private FingerprintManagerCompat fingerprintManager;
private CancellationSignal fingerprintCancellationSignal;
private FingerprintListener fingerprintListener;
private BiometricManager biometricManager;
private BiometricPrompt biometricPrompt;
private BiometricPrompt.PromptInfo biometricPromptInfo;
private boolean authenticated;
private boolean failure;
private boolean hadFailure;
private boolean alreadyShown;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -102,6 +109,15 @@ public class PassphrasePromptActivity extends PassphraseActivity {
setContentView(R.layout.prompt_passphrase_activity);
initializeResources();
alreadyShown = (savedInstanceState != null && savedInstanceState.getBoolean(BUNDLE_ALREADY_SHOWN)) ||
getIntent().getBooleanExtra(FROM_FOREGROUND, false);
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(BUNDLE_ALREADY_SHOWN, alreadyShown);
}
@Override
@@ -112,20 +128,12 @@ public class PassphrasePromptActivity extends PassphraseActivity {
setLockTypeVisibility();
if (TextSecurePreferences.isScreenLockEnabled(this) && !authenticated && !failure) {
resumeScreenLock();
if (TextSecurePreferences.isScreenLockEnabled(this) && !authenticated && !hadFailure) {
resumeScreenLock(!alreadyShown);
alreadyShown = true;
}
failure = false;
}
@Override
public void onPause() {
super.onPause();
if (TextSecurePreferences.isScreenLockEnabled(this)) {
pauseScreenLock();
}
hadFailure = false;
}
@Override
@@ -160,15 +168,16 @@ public class PassphrasePromptActivity extends PassphraseActivity {
}
@Override
@SuppressLint("MissingSuperCall") // no fragments to dispatch to
public void onActivityResult(int requestCode, int resultcode, Intent data) {
if (requestCode != 1) return;
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultcode == RESULT_OK) {
if (requestCode != AUTHENTICATE_REQUEST_CODE) return;
if (resultCode == RESULT_OK) {
handleAuthenticated();
} else {
Log.w(TAG, "Authentication failed");
failure = true;
hadFailure = true;
}
}
@@ -219,16 +228,20 @@ public class PassphrasePromptActivity extends PassphraseActivity {
ImageButton okButton = findViewById(R.id.ok_button);
Toolbar toolbar = findViewById(R.id.toolbar);
showButton = findViewById(R.id.passphrase_visibility);
hideButton = findViewById(R.id.passphrase_visibility_off);
visibilityToggle = findViewById(R.id.button_toggle);
passphraseText = findViewById(R.id.passphrase_edit);
passphraseAuthContainer = findViewById(R.id.password_auth_container);
fingerprintPrompt = findViewById(R.id.fingerprint_auth_container);
lockScreenButton = findViewById(R.id.lock_screen_auth_container);
fingerprintManager = FingerprintManagerCompat.from(this);
fingerprintCancellationSignal = new CancellationSignal();
fingerprintListener = new FingerprintListener();
showButton = findViewById(R.id.passphrase_visibility);
hideButton = findViewById(R.id.passphrase_visibility_off);
visibilityToggle = findViewById(R.id.button_toggle);
passphraseText = findViewById(R.id.passphrase_edit);
passphraseAuthContainer = findViewById(R.id.password_auth_container);
fingerprintPrompt = findViewById(R.id.fingerprint_auth_container);
lockScreenButton = findViewById(R.id.lock_screen_auth_container);
biometricManager = BiometricManager.from(this);
biometricPrompt = new BiometricPrompt(this, new BiometricAuthenticationListener());
biometricPromptInfo = new BiometricPrompt.PromptInfo
.Builder()
.setAllowedAuthenticators(ALLOWED_AUTHENTICATORS)
.setTitle(getString(R.string.PassphrasePromptActivity_unlock_signal))
.build();
setSupportActionBar(toolbar);
getSupportActionBar().setTitle("");
@@ -248,20 +261,15 @@ public class PassphrasePromptActivity extends PassphraseActivity {
fingerprintPrompt.setImageResource(R.drawable.ic_fingerprint_white_48dp);
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.core_ultramarine), PorterDuff.Mode.SRC_IN);
lockScreenButton.setOnClickListener(v -> resumeScreenLock());
lockScreenButton.setOnClickListener(v -> resumeScreenLock(true));
}
private void setLockTypeVisibility() {
if (TextSecurePreferences.isScreenLockEnabled(this)) {
passphraseAuthContainer.setVisibility(View.GONE);
if (fingerprintManager.isHardwareDetected() && fingerprintManager.hasEnrolledFingerprints()) {
fingerprintPrompt.setVisibility(View.VISIBLE);
lockScreenButton.setVisibility(View.GONE);
} else {
fingerprintPrompt.setVisibility(View.GONE);
lockScreenButton.setVisibility(View.VISIBLE);
}
fingerprintPrompt.setVisibility(biometricManager.canAuthenticate(BIOMETRIC_AUTHENTICATORS) == BiometricManager.BIOMETRIC_SUCCESS ? View.VISIBLE
: View.GONE);
lockScreenButton.setVisibility(View.VISIBLE);
} else {
passphraseAuthContainer.setVisibility(View.VISIBLE);
fingerprintPrompt.setVisibility(View.GONE);
@@ -269,7 +277,7 @@ public class PassphrasePromptActivity extends PassphraseActivity {
}
}
private void resumeScreenLock() {
private void resumeScreenLock(boolean force) {
KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
assert keyguardManager != null;
@@ -280,26 +288,27 @@ public class PassphrasePromptActivity extends PassphraseActivity {
return;
}
if (fingerprintManager.isHardwareDetected() && fingerprintManager.hasEnrolledFingerprints()) {
Log.i(TAG, "Listening for fingerprints...");
fingerprintCancellationSignal = new CancellationSignal();
fingerprintManager.authenticate(null, 0, fingerprintCancellationSignal, fingerprintListener, null);
} else if (Build.VERSION.SDK_INT >= 21){
Log.i(TAG, "firing intent...");
Intent intent = keyguardManager.createConfirmDeviceCredentialIntent(getString(R.string.PassphrasePromptActivity_unlock_signal), "");
startActivityForResult(intent, 1);
if (Build.VERSION.SDK_INT != 29 && biometricManager.canAuthenticate(ALLOWED_AUTHENTICATORS) == BiometricManager.BIOMETRIC_SUCCESS) {
if (force) {
Log.i(TAG, "Listening for biometric authentication...");
biometricPrompt.authenticate(biometricPromptInfo);
} else {
Log.i(TAG, "Skipping show system biometric dialog unless forced");
}
} else if (Build.VERSION.SDK_INT >= 21) {
if (force) {
Log.i(TAG, "firing intent...");
Intent intent = keyguardManager.createConfirmDeviceCredentialIntent(getString(R.string.PassphrasePromptActivity_unlock_signal), "");
startActivityForResult(intent, AUTHENTICATE_REQUEST_CODE);
} else {
Log.i(TAG, "Skipping firing intent unless forced");
}
} else {
Log.w(TAG, "Not compatible...");
handleAuthenticated();
}
}
private void pauseScreenLock() {
if (fingerprintCancellationSignal != null) {
fingerprintCancellationSignal.cancel();
}
}
private void sendEmailToSupport() {
String body = SupportEmailUtil.generateSupportEmailBody(this,
R.string.PassphrasePromptActivity_signal_android_lock_screen,
@@ -359,15 +368,19 @@ public class PassphrasePromptActivity extends PassphraseActivity {
System.gc();
}
private class FingerprintListener extends FingerprintManagerCompat.AuthenticationCallback {
private class BiometricAuthenticationListener extends BiometricPrompt.AuthenticationCallback {
@Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
Log.w(TAG, "Authentication error: " + errMsgId + " " + errString);
onAuthenticationFailed();
public void onAuthenticationError(int errorCode, @NonNull CharSequence errorString) {
Log.w(TAG, "Authentication error: " + errorCode);
hadFailure = true;
if (errorCode != BiometricPrompt.ERROR_CANCELED && errorCode != BiometricPrompt.ERROR_USER_CANCELED) {
onAuthenticationFailed();
}
}
@Override
public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
Log.i(TAG, "onAuthenticationSucceeded");
fingerprintPrompt.setImageResource(R.drawable.ic_check_white_48dp);
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.green_500), PorterDuff.Mode.SRC_IN);
@@ -384,8 +397,7 @@ public class PassphrasePromptActivity extends PassphraseActivity {
@Override
public void onAuthenticationFailed() {
Log.w(TAG, "onAuthenticatoinFailed()");
FingerprintManagerCompat.AuthenticationCallback callback = this;
Log.w(TAG, "onAuthenticationFailed()");
fingerprintPrompt.setImageResource(R.drawable.ic_close_white_48dp);
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.red_500), PorterDuff.Mode.SRC_IN);
@@ -409,6 +421,5 @@ public class PassphrasePromptActivity extends PassphraseActivity {
fingerprintPrompt.startAnimation(shake);
}
}
}

View File

@@ -11,10 +11,13 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import org.greenrobot.eventbus.EventBus;
import org.signal.core.util.logging.Log;
import org.signal.core.util.tracing.Tracer;
import org.signal.devicetransfer.TransferStatus;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.devicetransfer.olddevice.OldDeviceTransferActivity;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity;
@@ -32,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";
@@ -45,6 +48,8 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
private static final int STATE_ENTER_SIGNAL_PIN = 5;
private static final int STATE_CREATE_PROFILE_NAME = 6;
private static final int STATE_CREATE_SIGNAL_PIN = 7;
private static final int STATE_TRANSFER_ONGOING = 8;
private static final int STATE_TRANSFER_LOCKED = 9;
private SignalServiceNetworkAccess networkAccess;
private BroadcastReceiver clearKeyReceiver;
@@ -146,6 +151,8 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
case STATE_ENTER_SIGNAL_PIN: return getEnterSignalPinIntent();
case STATE_CREATE_SIGNAL_PIN: return getCreateSignalPinIntent();
case STATE_CREATE_PROFILE_NAME: return getCreateProfileNameIntent();
case STATE_TRANSFER_ONGOING: return getOldDeviceTransferIntent();
case STATE_TRANSFER_LOCKED: return getOldDeviceTransferLockedIntent();
default: return null;
}
}
@@ -165,6 +172,10 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
return STATE_CREATE_PROFILE_NAME;
} else if (userMustCreateSignalPin()) {
return STATE_CREATE_SIGNAL_PIN;
} else if (EventBus.getDefault().getStickyEvent(TransferStatus.class) != null && getClass() != OldDeviceTransferActivity.class) {
return STATE_TRANSFER_ONGOING;
} else if (SignalStore.misc().isOldDeviceTransferLocked()) {
return STATE_TRANSFER_LOCKED;
} else {
return STATE_NORMAL;
}
@@ -183,7 +194,9 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
}
private Intent getPromptPassphraseIntent() {
return getRoutedIntent(PassphrasePromptActivity.class, getIntent());
Intent intent = getRoutedIntent(PassphrasePromptActivity.class, getIntent());
intent.putExtra(PassphrasePromptActivity.FROM_FOREGROUND, ApplicationDependencies.getAppForegroundObserver().isForegrounded());
return intent;
}
private Intent getUiBlockingUpgradeIntent() {
@@ -217,6 +230,19 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
return getRoutedIntent(EditProfileActivity.class, getIntent());
}
private Intent getOldDeviceTransferIntent() {
Intent intent = new Intent(this, OldDeviceTransferActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
return intent;
}
private @Nullable Intent getOldDeviceTransferLockedIntent() {
if (getClass() == MainActivity.class) {
return null;
}
return MainActivity.clearTop(this);
}
private Intent getRoutedIntent(Class<?> destination, @Nullable Intent nextIntent) {
final Intent intent = new Intent(this, destination);
if (nextIntent != null) intent.putExtra("next_intent", nextIntent);

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) {

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

@@ -60,9 +60,12 @@ import androidx.appcompat.widget.SwitchCompat;
import androidx.fragment.app.Fragment;
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.IdentityKeyParcelable;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
@@ -78,7 +81,6 @@ import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.DynamicDarkActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.IdentityUtil;
@@ -91,14 +93,13 @@ 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;
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
/**
* Activity for verifying identity keys.
*
@@ -185,7 +186,7 @@ public class VerifyIdentityActivity extends PassphraseRequiredActivity implement
@Override
public void onQrDataFound(final String data) {
Util.runOnMain(() -> {
ThreadUtil.runOnMain(() -> {
((Vibrator)getSystemService(Context.VIBRATOR_SERVICE)).vibrate(50);
getSupportFragmentManager().popBackStack();
@@ -301,6 +302,7 @@ public class VerifyIdentityActivity extends PassphraseRequiredActivity implement
byte[] localId;
byte[] remoteId;
//noinspection WrongThread
Recipient resolved = recipient.resolve();
if (FeatureFlags.verifyV2() && resolved.getUuid().isPresent()) {
@@ -617,36 +619,35 @@ public class VerifyIdentityActivity extends PassphraseRequiredActivity implement
@Override
public void onCheckedChanged(CompoundButton buttonView, final boolean isChecked) {
new AsyncTask<Recipient, Void, Void>() {
@Override
protected Void doInBackground(Recipient... params) {
synchronized (SESSION_LOCK) {
if (isChecked) {
Log.i(TAG, "Saving identity: " + params[0].getId());
DatabaseFactory.getIdentityDatabase(getActivity())
.saveIdentity(params[0].getId(),
remoteIdentity,
VerifiedStatus.VERIFIED, false,
System.currentTimeMillis(), true);
} else {
DatabaseFactory.getIdentityDatabase(getActivity())
.setVerified(params[0].getId(),
remoteIdentity,
VerifiedStatus.DEFAULT);
}
final Recipient recipient = this.recipient.get();
final RecipientId recipientId = recipient.getId();
ApplicationDependencies.getJobManager()
.add(new MultiDeviceVerifiedUpdateJob(recipient.getId(),
remoteIdentity,
isChecked ? VerifiedStatus.VERIFIED :
VerifiedStatus.DEFAULT));
StorageSyncHelper.scheduleSyncForDataChange();
IdentityUtil.markIdentityVerified(getActivity(), recipient.get(), isChecked, false);
SignalExecutors.BOUNDED.execute(() -> {
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
if (isChecked) {
Log.i(TAG, "Saving identity: " + recipientId);
DatabaseFactory.getIdentityDatabase(getActivity())
.saveIdentity(recipientId,
remoteIdentity,
VerifiedStatus.VERIFIED, false,
System.currentTimeMillis(), true);
} else {
DatabaseFactory.getIdentityDatabase(getActivity())
.setVerified(recipientId,
remoteIdentity,
VerifiedStatus.DEFAULT);
}
return null;
ApplicationDependencies.getJobManager()
.add(new MultiDeviceVerifiedUpdateJob(recipientId,
remoteIdentity,
isChecked ? VerifiedStatus.VERIFIED
: VerifiedStatus.DEFAULT));
StorageSyncHelper.scheduleSyncForDataChange();
IdentityUtil.markIdentityVerified(getActivity(), recipient, isChecked, false);
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipient.get());
});
}
}

View File

@@ -41,22 +41,22 @@ 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.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;
@@ -64,7 +64,6 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.signalservice.api.messages.calls.HangupMessage;
import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
import java.util.List;
@@ -141,7 +140,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 +157,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);
@@ -258,19 +263,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:
@@ -329,30 +328,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 +354,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 +377,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 +398,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 +411,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 +421,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 +452,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) {
@@ -518,6 +490,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
final Recipient recipient = event.getRemoteParticipants().get(0).getRecipient();
if (theirKey == null) {
Log.w(TAG, "Untrusted identity without an identity key, terminating call.");
handleTerminate(recipient, HangupMessage.Type.NORMAL);
}
@@ -536,7 +509,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) {
@@ -550,11 +523,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());
}
@@ -568,9 +543,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();
@@ -636,11 +609,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

@@ -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

@@ -8,11 +8,11 @@ import android.os.ParcelFileDescriptor;
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.providers.BlobProvider;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.whispersystems.libsignal.util.Pair;
@@ -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");
@@ -90,10 +90,10 @@ public class AudioRecorder {
}
private <T> void sendToFuture(final SettableFuture<T> future, final Exception exception) {
Util.runOnMain(() -> future.setException(exception));
ThreadUtil.runOnMain(() -> future.setException(exception));
}
private <T> void sendToFuture(final SettableFuture<T> future, final T result) {
Util.runOnMain(() -> future.set(result));
ThreadUtil.runOnMain(() -> future.set(result));
}
}

View File

@@ -16,6 +16,7 @@ import androidx.core.util.Consumer;
import com.google.protobuf.ByteString;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.attachments.Attachment;
@@ -26,7 +27,6 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.AudioWaveFormDat
import org.thoughtcrime.securesms.media.DecryptableUriMediaInput;
import org.thoughtcrime.securesms.media.MediaInput;
import org.thoughtcrime.securesms.mms.AudioSlide;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.SerialExecutor;
import java.io.IOException;
@@ -61,13 +61,13 @@ public final class AudioWaveForm {
if (uri == null) {
Log.w(TAG, "No uri");
Util.runOnMain(onFailure);
ThreadUtil.runOnMain(onFailure);
return;
}
if (!(attachment instanceof DatabaseAttachment)) {
Log.i(TAG, "Not yet in database");
Util.runOnMain(onFailure);
ThreadUtil.runOnMain(onFailure);
return;
}
@@ -75,7 +75,7 @@ public final class AudioWaveForm {
AudioFileInfo cached = WAVE_FORM_CACHE.get(cacheKey);
if (cached != null) {
Log.i(TAG, "Loaded wave form from cache " + cacheKey);
Util.runOnMain(() -> onSuccess.accept(cached));
ThreadUtil.runOnMain(() -> onSuccess.accept(cached));
return;
}
@@ -83,7 +83,7 @@ public final class AudioWaveForm {
AudioFileInfo cachedInExecutor = WAVE_FORM_CACHE.get(cacheKey);
if (cachedInExecutor != null) {
Log.i(TAG, "Loaded wave form from cache inside executor" + cacheKey);
Util.runOnMain(() -> onSuccess.accept(cachedInExecutor));
ThreadUtil.runOnMain(() -> onSuccess.accept(cachedInExecutor));
return;
}
@@ -92,14 +92,14 @@ public final class AudioWaveForm {
AudioFileInfo audioFileInfo = AudioFileInfo.fromDatabaseProtobuf(audioHash.getAudioWaveForm());
if (audioFileInfo.waveForm.length == 0) {
Log.w(TAG, "Recovering from a wave form generation error " + cacheKey);
Util.runOnMain(onFailure);
ThreadUtil.runOnMain(onFailure);
return;
} else if (audioFileInfo.waveForm.length != BAR_COUNT) {
Log.w(TAG, "Wave form from database does not match bar count, regenerating " + cacheKey);
} else {
WAVE_FORM_CACHE.put(cacheKey, audioFileInfo);
Log.i(TAG, "Loaded wave form from DB " + cacheKey);
Util.runOnMain(() -> onSuccess.accept(audioFileInfo));
ThreadUtil.runOnMain(() -> onSuccess.accept(audioFileInfo));
return;
}
}
@@ -120,10 +120,10 @@ public final class AudioWaveForm {
attachmentDatabase.writeAudioHash(dbAttachment.getAttachmentId(), fileInfo.toDatabaseProtobuf());
WAVE_FORM_CACHE.put(cacheKey, fileInfo);
Util.runOnMain(() -> onSuccess.accept(fileInfo));
ThreadUtil.runOnMain(() -> onSuccess.accept(fileInfo));
} catch (Throwable e) {
Log.w(TAG, "Failed to create audio wave form for " + cacheKey, e);
Util.runOnMain(onFailure);
ThreadUtil.runOnMain(onFailure);
}
});
}

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

@@ -10,7 +10,6 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.documentfile.provider.DocumentFile;
import com.annimon.stream.function.Consumer;
import com.annimon.stream.function.Predicate;
import com.google.protobuf.ByteString;
@@ -26,7 +25,6 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.GroupReceiptDatabase;
import org.thoughtcrime.securesms.database.JobDatabase;
import org.thoughtcrime.securesms.database.KeyValueDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.MmsSmsColumns;
@@ -36,9 +34,13 @@ import org.thoughtcrime.securesms.database.SessionDatabase;
import org.thoughtcrime.securesms.database.SignedPreKeyDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.StickerDatabase;
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.util.SetUtil;
import org.thoughtcrime.securesms.util.Stopwatch;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.kdf.HKDFv3;
import org.whispersystems.libsignal.util.ByteUtil;
@@ -66,8 +68,7 @@ import javax.crypto.spec.SecretKeySpec;
public class FullBackupExporter extends FullBackupBase {
@SuppressWarnings("unused")
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,
@@ -81,11 +82,12 @@ public class FullBackupExporter extends FullBackupBase {
@NonNull AttachmentSecret attachmentSecret,
@NonNull SQLiteDatabase input,
@NonNull File output,
@NonNull String passphrase)
@NonNull String passphrase,
@NonNull BackupCancellationSignal cancellationSignal)
throws IOException
{
try (OutputStream outputStream = new FileOutputStream(output)) {
internalExport(context, attachmentSecret, input, outputStream, passphrase);
internalExport(context, attachmentSecret, input, outputStream, passphrase, true, cancellationSignal);
}
}
@@ -94,19 +96,32 @@ public class FullBackupExporter extends FullBackupBase {
@NonNull AttachmentSecret attachmentSecret,
@NonNull SQLiteDatabase input,
@NonNull DocumentFile output,
@NonNull String passphrase)
@NonNull String passphrase,
@NonNull BackupCancellationSignal cancellationSignal)
throws IOException
{
try (OutputStream outputStream = Objects.requireNonNull(context.getContentResolver().openOutputStream(output.getUri()))) {
internalExport(context, attachmentSecret, input, outputStream, passphrase);
internalExport(context, attachmentSecret, input, outputStream, passphrase, true, cancellationSignal);
}
}
public static void transfer(@NonNull Context context,
@NonNull AttachmentSecret attachmentSecret,
@NonNull SQLiteDatabase input,
@NonNull OutputStream outputStream,
@NonNull String passphrase)
throws IOException
{
internalExport(context, attachmentSecret, input, outputStream, passphrase, false, () -> false);
}
private static void internalExport(@NonNull Context context,
@NonNull AttachmentSecret attachmentSecret,
@NonNull SQLiteDatabase input,
@NonNull OutputStream fileOutputStream,
@NonNull String passphrase)
@NonNull String passphrase,
boolean closeOutputStream,
@NonNull BackupCancellationSignal cancellationSignal)
throws IOException
{
BackupFrameOutputStream outputStream = new BackupFrameOutputStream(fileOutputStream, passphrase);
@@ -114,36 +129,51 @@ public class FullBackupExporter extends FullBackupBase {
try {
outputStream.writeDatabaseVersion(input.getVersion());
count++;
List<String> tables = exportSchema(input, outputStream);
count += tables.size() * 3;
Stopwatch stopwatch = new Stopwatch("Backup");
for (String table : tables) {
throwIfCanceled(cancellationSignal);
if (table.equals(MmsDatabase.TABLE_NAME)) {
count = exportTable(table, input, outputStream, FullBackupExporter::isNonExpiringMmsMessage, null, count);
count = exportTable(table, input, outputStream, FullBackupExporter::isNonExpiringMmsMessage, null, count, cancellationSignal);
} else if (table.equals(SmsDatabase.TABLE_NAME)) {
count = exportTable(table, input, outputStream, FullBackupExporter::isNonExpiringSmsMessage, null, count);
count = exportTable(table, input, outputStream, FullBackupExporter::isNonExpiringSmsMessage, null, count, cancellationSignal);
} else if (table.equals(GroupReceiptDatabase.TABLE_NAME)) {
count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(GroupReceiptDatabase.MMS_ID))), null, count);
count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(GroupReceiptDatabase.MMS_ID))), null, count, cancellationSignal);
} else if (table.equals(AttachmentDatabase.TABLE_NAME)) {
count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.MMS_ID))), cursor -> exportAttachment(attachmentSecret, cursor, outputStream), count);
count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.MMS_ID))), (cursor, innerCount) -> exportAttachment(attachmentSecret, cursor, outputStream, innerCount), count, cancellationSignal);
} else if (table.equals(StickerDatabase.TABLE_NAME)) {
count = exportTable(table, input, outputStream, cursor -> true, cursor -> exportSticker(attachmentSecret, cursor, outputStream), count);
count = exportTable(table, input, outputStream, cursor -> true, (cursor, innerCount) -> exportSticker(attachmentSecret, cursor, outputStream, innerCount), count, cancellationSignal);
} else if (!BLACKLISTED_TABLES.contains(table) && !table.startsWith("sqlite_")) {
count = exportTable(table, input, outputStream, null, null, count);
count = exportTable(table, input, outputStream, null, null, count, cancellationSignal);
}
stopwatch.split("table::" + table);
}
for (BackupProtos.SharedPreference preference : IdentityKeyUtil.getBackupRecord(context)) {
throwIfCanceled(cancellationSignal);
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
outputStream.write(preference);
}
for (BackupProtos.SharedPreference preference : TextSecurePreferences.getPreferencesToSaveToBackup(context)) {
throwIfCanceled(cancellationSignal);
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
outputStream.write(preference);
}
stopwatch.split("prefs");
count = exportKeyValues(outputStream, SignalStore.getKeysToIncludeInBackup(), count, cancellationSignal);
stopwatch.split("key_values");
for (AvatarHelper.Avatar avatar : AvatarHelper.getAvatars(context)) {
throwIfCanceled(cancellationSignal);
if (avatar != null) {
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
outputStream.write(avatar.getFilename(), avatar.getInputStream(), avatar.getLength());
@@ -155,11 +185,19 @@ public class FullBackupExporter extends FullBackupBase {
outputStream.writeEnd();
} finally {
outputStream.close();
if (closeOutputStream) {
outputStream.close();
}
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.FINISHED, ++count));
}
}
private static void throwIfCanceled(@NonNull BackupCancellationSignal cancellationSignal) throws BackupCanceledException {
if (cancellationSignal.isCanceled()) {
throw new BackupCanceledException();
}
}
private static List<String> exportSchema(@NonNull SQLiteDatabase input, @NonNull BackupFrameOutputStream outputStream)
throws IOException
{
@@ -190,19 +228,20 @@ public class FullBackupExporter extends FullBackupBase {
return tables;
}
private static int exportTable(@NonNull String table,
@NonNull SQLiteDatabase input,
@NonNull BackupFrameOutputStream outputStream,
@Nullable Predicate<Cursor> predicate,
@Nullable Consumer<Cursor> postProcess,
int count)
private static int exportTable(@NonNull String table,
@NonNull SQLiteDatabase input,
@NonNull BackupFrameOutputStream outputStream,
@Nullable Predicate<Cursor> predicate,
@Nullable PostProcessor postProcess,
int count,
@NonNull BackupCancellationSignal cancellationSignal)
throws IOException
{
String template = "INSERT INTO " + table + " VALUES ";
try (Cursor cursor = input.rawQuery("SELECT * FROM " + table, null)) {
while (cursor != null && cursor.moveToNext()) {
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
throwIfCanceled(cancellationSignal);
if (predicate == null || predicate.test(cursor)) {
StringBuilder statement = new StringBuilder(template);
@@ -234,9 +273,12 @@ public class FullBackupExporter extends FullBackupBase {
statement.append(')');
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
outputStream.write(statementBuilder.setStatement(statement.toString()).build());
if (postProcess != null) postProcess.accept(cursor);
if (postProcess != null) {
count = postProcess.postProcess(cursor, count);
}
}
}
}
@@ -244,7 +286,7 @@ public class FullBackupExporter extends FullBackupBase {
return count;
}
private static void exportAttachment(@NonNull AttachmentSecret attachmentSecret, @NonNull Cursor cursor, @NonNull BackupFrameOutputStream outputStream) {
private static int exportAttachment(@NonNull AttachmentSecret attachmentSecret, @NonNull Cursor cursor, @NonNull BackupFrameOutputStream outputStream, int count) {
try {
long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.ROW_ID));
long uniqueId = cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.UNIQUE_ID));
@@ -269,14 +311,17 @@ public class FullBackupExporter extends FullBackupBase {
if (random != null && random.length == 32) inputStream = ModernDecryptingPartInputStream.createFor(attachmentSecret, random, new File(data), 0);
else inputStream = ClassicDecryptingPartInputStream.createFor(attachmentSecret, new File(data));
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
outputStream.write(new AttachmentId(rowId, uniqueId), inputStream, size);
}
} catch (IOException e) {
Log.w(TAG, e);
}
return count;
}
private static void exportSticker(@NonNull AttachmentSecret attachmentSecret, @NonNull Cursor cursor, @NonNull BackupFrameOutputStream outputStream) {
private static int exportSticker(@NonNull AttachmentSecret attachmentSecret, @NonNull Cursor cursor, @NonNull BackupFrameOutputStream outputStream, int count) {
try {
long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(StickerDatabase._ID));
long size = cursor.getLong(cursor.getColumnIndexOrThrow(StickerDatabase.FILE_LENGTH));
@@ -285,12 +330,15 @@ public class FullBackupExporter extends FullBackupBase {
byte[] random = cursor.getBlob(cursor.getColumnIndexOrThrow(StickerDatabase.FILE_RANDOM));
if (!TextUtils.isEmpty(data) && size > 0) {
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
InputStream inputStream = ModernDecryptingPartInputStream.createFor(attachmentSecret, random, new File(data), 0);
outputStream.writeSticker(rowId, inputStream, size);
}
} catch (IOException e) {
Log.w(TAG, e);
}
return count;
}
private static long calculateVeryOldStreamLength(@NonNull AttachmentSecret attachmentSecret, @Nullable byte[] random, @NonNull String data) throws IOException {
@@ -310,6 +358,46 @@ public class FullBackupExporter extends FullBackupBase {
return result;
}
private static int exportKeyValues(@NonNull BackupFrameOutputStream outputStream,
@NonNull List<String> keysToIncludeInBackup,
int count,
BackupCancellationSignal cancellationSignal) throws IOException
{
KeyValueDataSet dataSet = KeyValueDatabase.getInstance(ApplicationDependencies.getApplication())
.getDataSet();
for (String key : keysToIncludeInBackup) {
throwIfCanceled(cancellationSignal);
if (!dataSet.containsKey(key)) {
continue;
}
BackupProtos.KeyValue.Builder builder = BackupProtos.KeyValue.newBuilder()
.setKey(key);
Class<?> type = dataSet.getType(key);
if (type == byte[].class) {
builder.setBlobValue(ByteString.copyFrom(dataSet.getBlob(key, null)));
} else if (type == Boolean.class) {
builder.setBooleanValue(dataSet.getBoolean(key, false));
} else if (type == Float.class) {
builder.setFloatValue(dataSet.getFloat(key, 0));
} else if (type == Integer.class) {
builder.setIntegerValue(dataSet.getInteger(key, 0));
} else if (type == Long.class) {
builder.setLongValue(dataSet.getLong(key, 0));
} else if (type == String.class) {
builder.setStringValue(dataSet.getString(key, null));
} else {
throw new AssertionError("Unknown type: " + type);
}
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
outputStream.write(builder.build());
}
return count;
}
private static boolean isNonExpiringMmsMessage(@NonNull Cursor cursor) {
return cursor.getInt(cursor.getColumnIndexOrThrow(MmsSmsColumns.EXPIRES_IN)) <= 0 &&
cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.VIEW_ONCE)) <= 0;
@@ -381,6 +469,10 @@ public class FullBackupExporter extends FullBackupBase {
write(outputStream, BackupProtos.BackupFrame.newBuilder().setPreference(preference).build());
}
public void write(BackupProtos.KeyValue keyValue) throws IOException {
write(outputStream, BackupProtos.BackupFrame.newBuilder().setKeyValue(keyValue).build());
}
public void write(BackupProtos.SqlStatement statement) throws IOException {
write(outputStream, BackupProtos.BackupFrame.newBuilder().setStatement(statement).build());
}
@@ -495,4 +587,14 @@ public class FullBackupExporter extends FullBackupBase {
outputStream.close();
}
}
public interface PostProcessor {
int postProcess(@NonNull Cursor cursor, int count);
}
public interface BackupCancellationSignal {
boolean isCanceled();
}
public static final class BackupCanceledException extends IOException { }
}

View File

@@ -26,8 +26,11 @@ 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.KeyValueDatabase;
import org.thoughtcrime.securesms.database.SearchDatabase;
import org.thoughtcrime.securesms.database.StickerDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.keyvalue.KeyValueDataSet;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.BackupUtil;
@@ -45,6 +48,8 @@ import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
@@ -60,25 +65,37 @@ 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)
throws IOException
{
try (InputStream is = getInputStream(context, uri)) {
importFile(context, attachmentSecret, db, is, passphrase);
}
}
public static void importFile(@NonNull Context context, @NonNull AttachmentSecret attachmentSecret,
@NonNull SQLiteDatabase db, @NonNull InputStream is, @NonNull String passphrase)
throws IOException
{
int count = 0;
try (InputStream is = getInputStream(context, uri)) {
SQLiteDatabase keyValueDatabase = KeyValueDatabase.getInstance(ApplicationDependencies.getApplication()).getSqlCipherDatabase();
try {
BackupRecordInputStream inputStream = new BackupRecordInputStream(is, passphrase);
db.beginTransaction();
keyValueDatabase.beginTransaction();
dropAllTables(db);
BackupFrame frame;
while (!(frame = inputStream.readFrame()).getEnd()) {
if (count++ % 100 == 0) EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, count));
if (count % 100 == 0) EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, count));
count++;
if (frame.hasVersion()) processVersion(db, frame.getVersion());
else if (frame.hasStatement()) processStatement(db, frame.getStatement());
@@ -86,18 +103,22 @@ public class FullBackupImporter extends FullBackupBase {
else if (frame.hasAttachment()) processAttachment(context, attachmentSecret, db, frame.getAttachment(), inputStream);
else if (frame.hasSticker()) processSticker(context, attachmentSecret, db, frame.getSticker(), inputStream);
else if (frame.hasAvatar()) processAvatar(context, db, frame.getAvatar(), inputStream);
else if (frame.hasKeyValue()) processKeyValue(frame.getKeyValue());
else count--;
}
db.setTransactionSuccessful();
keyValueDatabase.setTransactionSuccessful();
} finally {
db.endTransaction();
keyValueDatabase.endTransaction();
}
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.FINISHED, count));
}
private static @NonNull InputStream getInputStream(@NonNull Context context, @NonNull Uri uri) throws IOException{
if (BackupUtil.isUserSelectionRequired(context)) {
if (BackupUtil.isUserSelectionRequired(context) || uri.getScheme().equals("content")) {
return Objects.requireNonNull(context.getContentResolver().openInputStream(uri));
} else {
return new FileInputStream(new File(Objects.requireNonNull(uri.getPath())));
@@ -201,10 +222,40 @@ public class FullBackupImporter extends FullBackupBase {
}
}
private static void processKeyValue(BackupProtos.KeyValue keyValue) {
KeyValueDataSet dataSet = new KeyValueDataSet();
if (keyValue.hasBlobValue()) {
dataSet.putBlob(keyValue.getKey(), keyValue.getBlobValue().toByteArray());
} else if (keyValue.hasBooleanValue()) {
dataSet.putBoolean(keyValue.getKey(), keyValue.getBooleanValue());
} else if (keyValue.hasFloatValue()) {
dataSet.putFloat(keyValue.getKey(), keyValue.getFloatValue());
} else if (keyValue.hasIntegerValue()) {
dataSet.putInteger(keyValue.getKey(), keyValue.getIntegerValue());
} else if (keyValue.hasLongValue()) {
dataSet.putLong(keyValue.getKey(), keyValue.getLongValue());
} else if (keyValue.hasStringValue()) {
dataSet.putString(keyValue.getKey(), keyValue.getStringValue());
} else {
Log.i(TAG, "Unknown KeyValue backup value, skipping");
return;
}
KeyValueDatabase.getInstance(ApplicationDependencies.getApplication()).writeDataSet(dataSet, Collections.emptyList());
}
@SuppressLint("ApplySharedPref")
private static void processPreference(@NonNull Context context, SharedPreference preference) {
SharedPreferences preferences = context.getSharedPreferences(preference.getFile(), 0);
preferences.edit().putString(preference.getKey(), preference.getValue()).commit();
if (preference.hasValue()) {
preferences.edit().putString(preference.getKey(), preference.getValue()).commit();
} else if (preference.hasBooleanValue()) {
preferences.edit().putBoolean(preference.getKey(), preference.getBooleanValue()).commit();
} else if (preference.hasIsStringSetValue() && preference.getIsStringSetValue()) {
preferences.edit().putStringSet(preference.getKey(), new HashSet<>(preference.getStringSetValueList())).commit();
}
}
private static void dropAllTables(@NonNull SQLiteDatabase db) {

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;

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,7 +43,7 @@ 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 FORWARDS = 1;
private static final int REVERSE = -1;

View File

@@ -2,25 +2,33 @@ 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.drawable.Drawable;
import android.util.AttributeSet;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
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 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.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.ui.managegroup.ManageGroupActivity;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests;
@@ -28,9 +36,12 @@ 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 +50,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 +72,7 @@ public final class AvatarImageView extends AppCompatImageView {
private Paint outlinePaint;
private OnClickListener listener;
private Recipient.FallbackPhotoProvider fallbackPhotoProvider;
private boolean blurred;
private @Nullable RecipientContactPhoto recipientContactPhoto;
private @NonNull Drawable unknownRecipientDrawable;
@@ -88,15 +100,16 @@ public final class AvatarImageView extends AppCompatImageView {
outlinePaint = ThemeUtil.isDarkTheme(getContext()) ? 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);
blurred = false;
}
@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,11 +131,18 @@ 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);
}
}
@@ -151,20 +171,30 @@ public final class AvatarImageView extends AppCompatImageView {
Recipient.self().getProfileAvatar()))
: new RecipientContactPhoto(recipient);
if (!photo.equals(recipientContactPhoto)) {
boolean shouldBlur = recipient.shouldBlurAvatar();
if (!photo.equals(recipientContactPhoto) || shouldBlur != blurred) {
requestManager.clear(this);
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 (photo.contactPhoto != null) {
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;
requestManager.load(photo.contactPhoto)
.fallback(fallbackContactPhotoDrawable)
.error(fallbackContactPhotoDrawable)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.circleCrop()
.downsample(DownsampleStrategy.CENTER_INSIDE)
.transform(new MultiTransformation<>(transforms))
.into(this);
} else {
setImageDrawable(fallbackContactPhotoDrawable);
@@ -204,8 +234,7 @@ public final class AvatarImageView extends AppCompatImageView {
}
});
} else {
super.setOnClickListener(listener);
setClickable(listener != null);
disableQuickContact();
}
}
@@ -226,6 +255,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;

View File

@@ -40,6 +40,7 @@ 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 +85,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 +161,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) {
@@ -264,6 +265,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 +374,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

@@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.R;
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;
@@ -197,11 +198,11 @@ 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();

View File

@@ -19,6 +19,7 @@ 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.ViewUtil;
import java.util.List;
@@ -32,6 +33,8 @@ public class ConversationItemThumbnail extends FrameLayout {
private Outliner outliner;
private Outliner pulseOutliner;
private boolean borderless;
private int[] normalBounds;
private int[] gifBounds;
public ConversationItemThumbnail(Context context) {
super(context);
@@ -60,14 +63,28 @@ 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
};
}
@SuppressWarnings("SuspiciousNameCombination")
@@ -88,6 +105,18 @@ public class ConversationItemThumbnail extends FrameLayout {
}
}
public void hideThumbnailView() {
thumbnail.setAlpha(0f);
}
public void showThumbnailView() {
thumbnail.setAlpha(1f);
}
public @Nullable CornerMask getCornerMask() {
return cornerMask;
}
public void setPulseOutliner(@NonNull Outliner outliner) {
this.pulseOutliner = outliner;
}
@@ -137,6 +166,13 @@ 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);
}
thumbnail.setVisibility(VISIBLE);
album.setVisibility(GONE);
@@ -167,4 +203,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

@@ -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 {
@@ -20,19 +22,24 @@ public class CornerMask {
private final RectF bounds = new RectF();
public CornerMask(@NonNull View view) {
this(view, null);
}
public CornerMask(@NonNull View view, @Nullable CornerMask toClone) {
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
clearPaint.setColor(Color.BLACK);
clearPaint.setStyle(Paint.Style.FILL);
clearPaint.setAntiAlias(true);
clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
if (toClone != null) {
System.arraycopy(toClone.radii, 0, radii, 0, radii.length);
}
}
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);
@@ -72,4 +79,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

@@ -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

@@ -6,8 +6,8 @@ import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.signal.core.util.ThreadUtil;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.Util;
import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;
@@ -67,7 +67,7 @@ public class ExpirationTimerView extends androidx.appcompat.widget.AppCompatImag
else stopped = false;
}
Util.runOnMainDelayed(new AnimationUpdateRunnable(this), calculateAnimationDelay(this.startedAt, this.expiresIn));
ThreadUtil.runOnMainDelayed(new AnimationUpdateRunnable(this), calculateAnimationDelay(this.startedAt, this.expiresIn));
}
public void stopAnimation() {
@@ -116,7 +116,7 @@ public class ExpirationTimerView extends androidx.appcompat.widget.AppCompatImag
}
}
Util.runOnMainDelayed(this, timerView.calculateAnimationDelay(timerView.startedAt, timerView.expiresIn));
ThreadUtil.runOnMainDelayed(this, timerView.calculateAnimationDelay(timerView.startedAt, timerView.expiresIn));
}
}

View File

@@ -10,13 +10,14 @@ import android.util.AttributeSet;
import androidx.annotation.Nullable;
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;
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);

View File

@@ -22,10 +22,10 @@ import androidx.annotation.DimenRes;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
@@ -42,7 +42,6 @@ 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.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
@@ -59,7 +58,7 @@ public class InputPanel extends LinearLayout
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;
@@ -501,7 +500,7 @@ public class InputPanel extends LinearLayout
this.startTime = System.currentTimeMillis();
this.recordTimeView.setText(DateUtils.formatElapsedTime(0));
ViewUtil.fadeIn(this.recordTimeView, FADE_TIME);
Util.runOnMainDelayed(this, TimeUnit.SECONDS.toMillis(1));
ThreadUtil.runOnMainDelayed(this, TimeUnit.SECONDS.toMillis(1));
microphone.setVisibility(View.VISIBLE);
microphone.startAnimation(pulseAnimation());
}
@@ -527,7 +526,7 @@ public class InputPanel extends LinearLayout
onLimitHit.run();
} else {
recordTimeView.setText(DateUtils.formatElapsedTime(elapsedSeconds));
Util.runOnMainDelayed(this, TimeUnit.SECONDS.toMillis(1));
ThreadUtil.runOnMainDelayed(this, TimeUnit.SECONDS.toMillis(1));
}
}
}

View File

@@ -43,7 +43,7 @@ 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<>();

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();
}

View File

@@ -15,13 +15,17 @@ import android.view.ViewTreeObserver;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class MaskView extends View {
private View target;
private ViewGroup activityContentView;
private Paint maskPaint;
private Rect drawingRect = new Rect();
private float targetParentTranslationY;
private MaskTarget maskTarget;
private ViewGroup activityContentView;
private Paint maskPaint;
private Rect drawingRect = new Rect();
private float targetParentTranslationY;
private final ViewTreeObserver.OnDrawListener onDrawListener = this::invalidate;
@@ -50,15 +54,15 @@ public class MaskView extends View {
activityContentView = getRootView().findViewById(android.R.id.content);
}
public void setTarget(@Nullable View target) {
if (this.target != null) {
this.target.getViewTreeObserver().removeOnDrawListener(onDrawListener);
public void setTarget(@Nullable MaskTarget maskTarget) {
if (this.maskTarget != null) {
removeOnDrawListener(this.maskTarget, onDrawListener);
}
this.target = target;
this.maskTarget = maskTarget;
if (this.target != null) {
this.target.getViewTreeObserver().addOnDrawListener(onDrawListener);
if (this.maskTarget != null) {
addOnDrawListener(maskTarget, onDrawListener);
}
invalidate();
@@ -72,26 +76,77 @@ public class MaskView extends View {
protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);
if (target == null || !target.isAttachedToWindow()) {
if (nothingToMask(maskTarget)) {
return;
}
target.getDrawingRect(drawingRect);
activityContentView.offsetDescendantRectToMyCoords(target, drawingRect);
maskTarget.getPrimaryTarget().getDrawingRect(drawingRect);
activityContentView.offsetDescendantRectToMyCoords(maskTarget.getPrimaryTarget(), drawingRect);
drawingRect.top += targetParentTranslationY;
drawingRect.bottom += targetParentTranslationY;
Bitmap mask = Bitmap.createBitmap(target.getWidth(), drawingRect.height(), Bitmap.Config.ARGB_8888);
Bitmap mask = Bitmap.createBitmap(maskTarget.getPrimaryTarget().getWidth(), drawingRect.height(), Bitmap.Config.ARGB_8888);
Canvas maskCanvas = new Canvas(mask);
target.draw(maskCanvas);
maskTarget.draw(maskCanvas);
canvas.clipRect(drawingRect.left, Math.max(drawingRect.top, getTop() + getPaddingTop()), drawingRect.right, Math.min(drawingRect.bottom, getBottom() - getPaddingBottom()));
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) target.getLayoutParams();
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) maskTarget.getPrimaryTarget().getLayoutParams();
canvas.drawBitmap(mask, params.leftMargin, drawingRect.top, maskPaint);
mask.recycle();
}
private static void removeOnDrawListener(@NonNull MaskTarget maskTarget, @NonNull ViewTreeObserver.OnDrawListener onDrawListener) {
for (View view : maskTarget.getAllTargets()) {
if (view != null) {
view.getViewTreeObserver().removeOnDrawListener(onDrawListener);
}
}
}
private static void addOnDrawListener(@NonNull MaskTarget maskTarget, @NonNull ViewTreeObserver.OnDrawListener onDrawListener) {
for (View view : maskTarget.getAllTargets()) {
if (view != null) {
view.getViewTreeObserver().addOnDrawListener(onDrawListener);
}
}
}
private static boolean nothingToMask(@Nullable MaskTarget maskTarget) {
if (maskTarget == null) {
return true;
}
for (View view : maskTarget.getAllTargets()) {
if (view == null || !view.isAttachedToWindow()) {
return true;
}
}
return false;
}
public static class MaskTarget {
private final View primaryTarget;
public MaskTarget(@NonNull View primaryTarget) {
this.primaryTarget = primaryTarget;
}
final @NonNull View getPrimaryTarget() {
return primaryTarget;
}
protected @NonNull List<View> getAllTargets() {
return Collections.singletonList(primaryTarget);
}
protected void draw(@NonNull Canvas canvas) {
primaryTarget.draw(canvas);
}
}
}

View File

@@ -0,0 +1,64 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import org.thoughtcrime.securesms.R;
public class PaymentPillStrip extends ConstraintLayout {
private FrameLayout buttonStart;
private FrameLayout buttonEnd;
public PaymentPillStrip(@NonNull Context context) {
super(context);
}
public PaymentPillStrip(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public PaymentPillStrip(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public PaymentPillStrip(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
buttonStart = findViewById(R.id.button_start_frame);
buttonEnd = findViewById(R.id.button_end_frame);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (buttonStart.getMeasuredWidth() > buttonEnd.getMinimumWidth()) {
buttonEnd.setMinimumWidth(buttonStart.getMeasuredWidth());
}
if (buttonEnd.getMeasuredWidth() > buttonStart.getMinimumWidth()) {
buttonStart.setMinimumWidth(buttonEnd.getMeasuredWidth());
}
if (buttonStart.getMeasuredHeight() > buttonEnd.getMinimumHeight()) {
buttonEnd.setMinimumHeight(buttonStart.getMeasuredHeight());
}
if (buttonEnd.getMeasuredHeight() > buttonStart.getMinimumHeight()) {
buttonStart.setMinimumHeight(buttonEnd.getMeasuredHeight());
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}

View File

@@ -28,6 +28,7 @@ import androidx.annotation.NonNull;
import com.annimon.stream.Stream;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.RecipientsAdapter;
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
@@ -45,7 +46,7 @@ import java.util.StringTokenizer;
* @author Moxie Marlinspike
*/
public class PushRecipientsPanel extends RelativeLayout implements RecipientForeverObserver {
private final String TAG = PushRecipientsPanel.class.getSimpleName();
private final String TAG = Log.tag(PushRecipientsPanel.class);
private RecipientsPanelChangedListener panelChangeListener;
private RecipientsEditor recipientsText;

View File

@@ -21,6 +21,7 @@ import androidx.annotation.RequiresApi;
import com.annimon.stream.Stream;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.components.mention.MentionAnnotation;
@@ -38,7 +39,7 @@ import java.util.List;
public class QuoteView extends FrameLayout implements RecipientForeverObserver {
private static final String TAG = QuoteView.class.getSimpleName();
private static final String TAG = Log.tag(QuoteView.class);
private static final int MESSAGE_TYPE_PREVIEW = 0;
private static final int MESSAGE_TYPE_OUTGOING = 1;

View File

@@ -10,7 +10,9 @@ import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.PlayStoreUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.VersionTracker;
@@ -21,10 +23,10 @@ public class RatingManager {
private static final int DAYS_SINCE_INSTALL_THRESHOLD = 7;
private static final int DAYS_UNTIL_REPROMPT_THRESHOLD = 4;
private static final String TAG = RatingManager.class.getSimpleName();
private static final String TAG = Log.tag(RatingManager.class);
public static void showRatingDialogIfNecessary(Context context) {
if (!TextSecurePreferences.isRatingEnabled(context)) return;
if (!TextSecurePreferences.isRatingEnabled(context) || BuildConfig.PLAY_STORE_DISABLED) return;
long daysSinceInstall = VersionTracker.getDaysSinceFirstInstalled(context);
long laterTimestamp = TextSecurePreferences.getRatingLaterTimestamp(context);
@@ -44,7 +46,7 @@ public class RatingManager {
@Override
public void onClick(DialogInterface dialog, int which) {
TextSecurePreferences.setRatingEnabled(context, false);
startPlayStore(context);
PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(context);
}
})
.setNegativeButton(R.string.RatingManager_no_thanks, new DialogInterface.OnClickListener() {
@@ -62,15 +64,4 @@ public class RatingManager {
})
.show();
}
private static void startPlayStore(Context context) {
Uri uri = Uri.parse("market://details?id=" + context.getPackageName());
try {
context.startActivity(new Intent(Intent.ACTION_VIEW, uri));
} catch (ActivityNotFoundException e) {
Log.w(TAG, e);
Toast.makeText(context, R.string.RatingManager_whoops_the_play_store_app_does_not_appear_to_be_installed, Toast.LENGTH_LONG).show();
}
}
}

View File

@@ -28,6 +28,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import com.bumptech.glide.signature.MediaStoreSignature;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.database.loaders.RecentPhotosLoader;
@@ -82,7 +83,7 @@ public class RecentPhotoViewRail extends FrameLayout implements LoaderManager.Lo
private static class RecentPhotoAdapter extends CursorRecyclerViewAdapter<RecentPhotoAdapter.RecentPhotoViewHolder> {
@SuppressWarnings("unused")
private static final String TAG = RecentPhotoAdapter.class.getSimpleName();
private static final String TAG = Log.tag(RecentPhotoAdapter.class);
@NonNull private final Uri baseUri;
@Nullable private OnItemClickedListener clickedListener;

View File

@@ -15,6 +15,7 @@ import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.database.MediaDatabase;
@@ -61,7 +62,7 @@ public class ThreadPhotoRailView extends FrameLayout {
private static class ThreadPhotoRailAdapter extends CursorRecyclerViewAdapter<ThreadPhotoRailAdapter.ThreadPhotoViewHolder> {
@SuppressWarnings("unused")
private static final String TAG = ThreadPhotoRailAdapter.class.getSimpleName();
private static final String TAG = Log.tag(ThreadPhotoRailAdapter.class);
@NonNull private final GlideRequests glideRequests;

View File

@@ -36,6 +36,8 @@ import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.thoughtcrime.securesms.util.views.Stub;
import org.thoughtcrime.securesms.video.VideoPlayer;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Collections;
@@ -47,7 +49,7 @@ import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOption
public class ThumbnailView extends FrameLayout {
private static final String TAG = ThumbnailView.class.getSimpleName();
private static final String TAG = Log.tag(ThumbnailView.class);
private static final int WIDTH = 0;
private static final int HEIGHT = 1;
private static final int MIN_WIDTH = 0;
@@ -55,11 +57,12 @@ public class ThumbnailView extends FrameLayout {
private static final int MIN_HEIGHT = 2;
private static final int MAX_HEIGHT = 3;
private ImageView image;
private ImageView blurhash;
private View playOverlay;
private View captionIcon;
private OnClickListener parentClickListener;
private ImageView image;
private ImageView blurhash;
private View playOverlay;
private View captionIcon;
private Stub<VideoPlayer> videoPlayer;
private OnClickListener parentClickListener;
private final int[] dimens = new int[2];
private final int[] bounds = new int[4];
@@ -90,6 +93,7 @@ public class ThumbnailView extends FrameLayout {
this.blurhash = findViewById(R.id.thumbnail_blurhash);
this.playOverlay = findViewById(R.id.play_overlay);
this.captionIcon = findViewById(R.id.thumbnail_caption_icon);
this.videoPlayer = new Stub<>(findViewById(R.id.thumbnail_player_stub));
super.setOnClickListener(new ThumbnailClickDispatcher());
if (attrs != null) {
@@ -335,6 +339,7 @@ public class ThumbnailView extends FrameLayout {
}
buildThumbnailGlideRequest(glideRequests, slide).into(new GlideDrawableListeningTarget(image, result));
resultHandled = true;
} else {
glideRequests.clear(image);
@@ -442,7 +447,7 @@ public class ThumbnailView extends FrameLayout {
}
request = request.override(size[WIDTH], size[HEIGHT]);
if (radius > 0) {
return request.transforms(fitting, new RoundedCorners(radius));
} else {

View File

@@ -10,6 +10,7 @@ import androidx.lifecycle.MutableLiveData;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Util;
@@ -26,7 +27,7 @@ import java.util.concurrent.TimeUnit;
@SuppressLint("UseSparseArrays")
public class TypingStatusRepository {
private static final String TAG = TypingStatusRepository.class.getSimpleName();
private static final String TAG = Log.tag(TypingStatusRepository.class);
private static final long RECIPIENT_TYPING_TIMEOUT = TimeUnit.SECONDS.toMillis(15);
@@ -58,11 +59,11 @@ public class TypingStatusRepository {
Runnable timer = timers.get(typist);
if (timer != null) {
Util.cancelRunnableOnMain(timer);
ThreadUtil.cancelRunnableOnMain(timer);
}
timer = () -> onTypingStopped(context, threadId, author, device, false);
Util.runOnMainDelayed(timer, RECIPIENT_TYPING_TIMEOUT);
ThreadUtil.runOnMainDelayed(timer, RECIPIENT_TYPING_TIMEOUT);
timers.put(typist, timer);
}
@@ -85,7 +86,7 @@ public class TypingStatusRepository {
Runnable timer = timers.get(typist);
if (timer != null) {
Util.cancelRunnableOnMain(timer);
ThreadUtil.cancelRunnableOnMain(timer);
timers.remove(typist);
}
}

View File

@@ -1,11 +1,9 @@
package org.thoughtcrime.securesms.components;
import android.annotation.SuppressLint;
import android.app.Application;
import android.content.Context;
import androidx.annotation.NonNull;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.TypingSendJob;
import org.thoughtcrime.securesms.util.Util;
@@ -17,7 +15,7 @@ import java.util.concurrent.TimeUnit;
@SuppressLint("UseSparseArrays")
public class TypingStatusSender {
private static final String TAG = TypingStatusSender.class.getSimpleName();
private static final String TAG = Log.tag(TypingStatusSender.class);
private static final long REFRESH_TYPING_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
private static final long PAUSE_TYPING_TIMEOUT = TimeUnit.SECONDS.toMillis(3);
@@ -36,16 +34,16 @@ public class TypingStatusSender {
sendTyping(threadId, true);
Runnable start = new StartRunnable(threadId);
Util.runOnMainDelayed(start, REFRESH_TYPING_TIMEOUT);
ThreadUtil.runOnMainDelayed(start, REFRESH_TYPING_TIMEOUT);
pair.setStart(start);
}
if (pair.getStop() != null) {
Util.cancelRunnableOnMain(pair.getStop());
ThreadUtil.cancelRunnableOnMain(pair.getStop());
}
Runnable stop = () -> onTypingStopped(threadId, true);
Util.runOnMainDelayed(stop, PAUSE_TYPING_TIMEOUT);
ThreadUtil.runOnMainDelayed(stop, PAUSE_TYPING_TIMEOUT);
pair.setStop(stop);
}
@@ -62,7 +60,7 @@ public class TypingStatusSender {
selfTypingTimers.put(threadId, pair);
if (pair.getStart() != null) {
Util.cancelRunnableOnMain(pair.getStart());
ThreadUtil.cancelRunnableOnMain(pair.getStart());
if (notify) {
sendTyping(threadId, false);
@@ -70,7 +68,7 @@ public class TypingStatusSender {
}
if (pair.getStop() != null) {
Util.cancelRunnableOnMain(pair.getStop());
ThreadUtil.cancelRunnableOnMain(pair.getStop());
}
pair.setStart(null);
@@ -92,7 +90,7 @@ public class TypingStatusSender {
@Override
public void run() {
sendTyping(threadId, true);
Util.runOnMainDelayed(this, REFRESH_TYPING_TIMEOUT);
ThreadUtil.runOnMainDelayed(this, REFRESH_TYPING_TIMEOUT);
}
}

View File

@@ -0,0 +1,74 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.conversationlist.model.UnreadPayments;
/**
* Displays the data in a given UnreadPayments object in a banner.
*/
public class UnreadPaymentsView extends ConstraintLayout {
private TextView title;
private AvatarImageView avatar;
private Listener listener;
public UnreadPaymentsView(@NonNull Context context) {
super(context);
}
public UnreadPaymentsView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public UnreadPaymentsView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public UnreadPaymentsView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
title = findViewById(R.id.payment_notification_title);
avatar = findViewById(R.id.payment_notification_avatar);
View open = findViewById(R.id.payment_notification_touch_target);
View close = findViewById(R.id.payment_notification_close_touch_target);
open.setOnClickListener(v -> {
if (listener != null) listener.onOpenPaymentsNotificationClicked();
});
close.setOnClickListener(v -> {
if (listener != null) listener.onClosePaymentsNotificationClicked();
});
}
public void setListener(@NonNull Listener listener) {
this.listener = listener;
}
public void setUnreadPayments(@NonNull UnreadPayments unreadPayments) {
title.setText(unreadPayments.getDescription(getContext()));
avatar.setAvatar(unreadPayments.getRecipient());
avatar.setVisibility(unreadPayments.getRecipient() == null ? GONE : VISIBLE);
}
public interface Listener {
void onOpenPaymentsNotificationClicked();
void onClosePaymentsNotificationClicked();
}
}

View File

@@ -35,13 +35,17 @@ import java.io.InputStream;
public class ZoomingImageView extends FrameLayout {
private static final String TAG = ZoomingImageView.class.getSimpleName();
private static final String TAG = Log.tag(ZoomingImageView.class);
private static final int ZOOM_TRANSITION_DURATION = 300;
private static final float ZOOM_LEVEL_MIN = 1.0f;
private static final float ZOOM_LEVEL_MID = 1.5f;
private static final float ZOOM_LEVEL_MAX = 2.0f;
private static final float LARGE_IMAGES_ZOOM_LEVEL_MID = 1.5f;
private static final float LARGE_IMAGES_ZOOM_LEVEL_MAX = 2.0f;
private static final float SMALL_IMAGES_ZOOM_LEVEL_MID = 3.0f;
private static final float SMALL_IMAGES_ZOOM_LEVEL_MAX = 8.0f;
private final PhotoView photoView;
private final SubsamplingScaleImageView subsamplingImageView;
@@ -65,10 +69,11 @@ public class ZoomingImageView extends FrameLayout {
this.subsamplingImageView.setOrientation(SubsamplingScaleImageView.ORIENTATION_USE_EXIF);
this.photoView.setZoomTransitionDuration(ZOOM_TRANSITION_DURATION);
this.photoView.setScaleLevels(ZOOM_LEVEL_MIN, ZOOM_LEVEL_MID, ZOOM_LEVEL_MAX);
this.photoView.setScaleLevels(ZOOM_LEVEL_MIN, SMALL_IMAGES_ZOOM_LEVEL_MID, SMALL_IMAGES_ZOOM_LEVEL_MAX);
this.subsamplingImageView.setDoubleTapZoomDuration(ZOOM_TRANSITION_DURATION);
this.subsamplingImageView.setDoubleTapZoomScale(ZOOM_LEVEL_MID);
this.subsamplingImageView.setDoubleTapZoomScale(LARGE_IMAGES_ZOOM_LEVEL_MID);
this.subsamplingImageView.setMaxScale(LARGE_IMAGES_ZOOM_LEVEL_MAX);
this.photoView.setOnClickListener(v -> ZoomingImageView.this.callOnClick());
this.subsamplingImageView.setOnClickListener(v -> ZoomingImageView.this.callOnClick());

View File

@@ -21,7 +21,7 @@ import java.util.Locale;
@SuppressWarnings("deprecation")
public class CameraUtils {
private static final String TAG = CameraUtils.class.getSimpleName();
private static final String TAG = Log.tag(CameraUtils.class);
/*
* modified from: https://github.com/commonsguy/cwac-camera/blob/master/camera/src/com/commonsware/cwac/camera/CameraUtils.java
*/

View File

@@ -36,6 +36,7 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.BitmapUtil;
@@ -50,7 +51,7 @@ import java.util.List;
@SuppressWarnings("deprecation")
public class CameraView extends ViewGroup {
private static final String TAG = CameraView.class.getSimpleName();
private static final String TAG = Log.tag(CameraView.class);
private final CameraSurfaceView surface;
private final OnOrientationChange onOrientationChange;
@@ -320,7 +321,7 @@ public class CameraView extends ViewGroup {
camera.startPreview();
Log.i(TAG, "camera.startPreview() -> " + (System.currentTimeMillis() - previewStartMillis) + "ms");
state = State.ACTIVE;
Util.runOnMain(new Runnable() {
ThreadUtil.runOnMain(new Runnable() {
@Override
public void run() {
requestLayout();
@@ -495,9 +496,9 @@ public class CameraView extends ViewGroup {
return;
}
Util.runOnMainSync(this::onPreMain);
ThreadUtil.runOnMainSync(this::onPreMain);
final Result result = onRunBackground();
Util.runOnMainSync(() -> onPostMain(result));
ThreadUtil.runOnMainSync(() -> onPostMain(result));
}
protected boolean onWait() { return true; }

View File

@@ -10,13 +10,14 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatEditText;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
public class EmojiEditText extends AppCompatEditText {
private static final String TAG = EmojiEditText.class.getSimpleName();
private static final String TAG = Log.tag(EmojiEditText.class);
public EmojiEditText(Context context) {
this(context, null);

View File

@@ -10,12 +10,13 @@ import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.EmojiKeyboardProvider.EmojiEventListener;
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.VariationSelectorListener;
public class EmojiPageView extends FrameLayout implements VariationSelectorListener {
private static final String TAG = EmojiPageView.class.getSimpleName();
private static final String TAG = Log.tag(EmojiPageView.class);
private EmojiPageModel model;
private EmojiPageViewGridAdapter adapter;

View File

@@ -18,6 +18,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.parsing.EmojiDrawInfo;
@@ -25,7 +26,6 @@ import org.thoughtcrime.securesms.components.emoji.parsing.EmojiPageBitmap;
import org.thoughtcrime.securesms.components.emoji.parsing.EmojiParser;
import org.thoughtcrime.securesms.components.emoji.parsing.EmojiTree;
import org.thoughtcrime.securesms.util.FutureTaskListener;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.Pair;
import java.util.List;
@@ -33,7 +33,7 @@ import java.util.concurrent.ExecutionException;
class EmojiProvider {
private static final String TAG = EmojiProvider.class.getSimpleName();
private static final String TAG = Log.tag(EmojiProvider.class);
private static volatile EmojiProvider instance = null;
private static final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
@@ -118,7 +118,7 @@ class EmojiProvider {
final EmojiDrawable drawable = new EmojiDrawable(drawInfo, decodeScale);
drawInfo.getPage().get().addListener(new FutureTaskListener<Bitmap>() {
@Override public void onSuccess(final Bitmap result) {
Util.runOnMain(() -> drawable.setBitmap(result));
ThreadUtil.runOnMain(() -> drawable.setBitmap(result));
}
@Override public void onFailure(ExecutionException error) {
@@ -170,7 +170,7 @@ class EmojiProvider {
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public void setBitmap(Bitmap bitmap) {
Util.assertMainThread();
ThreadUtil.assertMainThread();
if (VERSION.SDK_INT < VERSION_CODES.HONEYCOMB_MR1 || bmp == null || !bmp.sameAs(bitmap)) {
bmp = bitmap;
invalidateSelf();

View File

@@ -16,6 +16,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
public final class EmojiUtil {
@@ -42,6 +43,8 @@ public final class EmojiUtil {
MAX_EMOJI_LENGTH = max;
}
private static final Pattern EMOJI_PATTERN = Pattern.compile("^(?:(?:[\u00a9\u00ae\u203c\u2049\u2122\u2139\u2194-\u2199\u21a9-\u21aa\u231a-\u231b\u2328\u23cf\u23e9-\u23f3\u23f8-\u23fa\u24c2\u25aa-\u25ab\u25b6\u25c0\u25fb-\u25fe\u2600-\u2604\u260e\u2611\u2614-\u2615\u2618\u261d\u2620\u2622-\u2623\u2626\u262a\u262e-\u262f\u2638-\u263a\u2648-\u2653\u2660\u2663\u2665-\u2666\u2668\u267b\u267f\u2692-\u2694\u2696-\u2697\u2699\u269b-\u269c\u26a0-\u26a1\u26aa-\u26ab\u26b0-\u26b1\u26bd-\u26be\u26c4-\u26c5\u26c8\u26ce-\u26cf\u26d1\u26d3-\u26d4\u26e9-\u26ea\u26f0-\u26f5\u26f7-\u26fa\u26fd\u2702\u2705\u2708-\u270d\u270f\u2712\u2714\u2716\u271d\u2721\u2728\u2733-\u2734\u2744\u2747\u274c\u274e\u2753-\u2755\u2757\u2763-\u2764\u2795-\u2797\u27a1\u27b0\u27bf\u2934-\u2935\u2b05-\u2b07\u2b1b-\u2b1c\u2b50\u2b55\u3030\u303d\u3297\u3299\ud83c\udc04\ud83c\udccf\ud83c\udd70-\ud83c\udd71\ud83c\udd7e-\ud83c\udd7f\ud83c\udd8e\ud83c\udd91-\ud83c\udd9a\ud83c\ude01-\ud83c\ude02\ud83c\ude1a\ud83c\ude2f\ud83c\ude32-\ud83c\ude3a\ud83c\ude50-\ud83c\ude51\u200d\ud83c\udf00-\ud83d\uddff\ud83d\ude00-\ud83d\ude4f\ud83d\ude80-\ud83d\udeff\ud83e\udd00-\ud83e\uddff\udb40\udc20-\udb40\udc7f]|\u200d[\u2640\u2642]|[\ud83c\udde6-\ud83c\uddff]{2}|.[\u20e0\u20e3\ufe0f]+)+)+$");
private EmojiUtil() {}
public static List<EmojiPageModel> getDisplayPages() {
@@ -90,4 +93,25 @@ public final class EmojiUtil {
return EmojiProvider.getInstance(context).getEmojiDrawable(emoji);
}
}
/**
* True if the text is likely a single, valid emoji. Otherwise false.
*
* We do a two-tier check: first using our own knowledge of emojis (which could be incomplete),
* followed by a more wide check for all of the valid emoji unicode ranges (which could lead to
* some false positives). YMMV.
*/
public static boolean isEmoji(@NonNull Context context, @Nullable String text) {
if (Util.isEmpty(text)) {
return false;
}
if (StringUtil.getGraphemeCount(text) != 1) {
return false;
}
EmojiParser.CandidateList candidates = EmojiProvider.getInstance(context).getCandidates(text);
return (candidates != null && candidates.size() > 0) || EMOJI_PATTERN.matcher(text).matches();
}
}

View File

@@ -24,7 +24,7 @@ import java.util.LinkedHashSet;
import java.util.List;
public class RecentEmojiPageModel implements EmojiPageModel {
private static final String TAG = RecentEmojiPageModel.class.getSimpleName();
private static final String TAG = Log.tag(RecentEmojiPageModel.class);
private static final int EMOJI_LRU_SIZE = 50;
private final SharedPreferences prefs;

View File

@@ -8,6 +8,7 @@ import android.graphics.BitmapFactory;
import androidx.annotation.NonNull;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.components.emoji.EmojiPageModel;
import org.thoughtcrime.securesms.util.ListenableFutureTask;
@@ -22,7 +23,7 @@ import java.util.concurrent.Callable;
public class EmojiPageBitmap {
private static final String TAG = EmojiPageBitmap.class.getSimpleName();
private static final String TAG = Log.tag(EmojiPageBitmap.class);
private final Context context;
private final EmojiPageModel model;
@@ -39,7 +40,7 @@ public class EmojiPageBitmap {
@SuppressLint("StaticFieldLeak")
public ListenableFutureTask<Bitmap> get() {
Util.assertMainThread();
ThreadUtil.assertMainThread();
if (bitmapReference != null && bitmapReference.get() != null) {
return new ListenableFutureTask<>(bitmapReference.get());

View File

@@ -8,15 +8,15 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.whispersystems.signalservice.api.SignalSessionLock;
import java.util.List;
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
public class UntrustedSendDialog extends AlertDialog.Builder implements DialogInterface.OnClickListener {
private final List<IdentityRecord> untrustedRecords;
@@ -43,7 +43,7 @@ public class UntrustedSendDialog extends AlertDialog.Builder implements DialogIn
final IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(getContext());
SimpleTask.run(() -> {
synchronized (SESSION_LOCK) {
try(SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
for (IdentityRecord identityRecord : untrustedRecords) {
identityDatabase.setApproval(identityRecord.getRecipientId(), true);
}

View File

@@ -22,7 +22,7 @@ import java.util.List;
public class UnverifiedBannerView extends LinearLayout {
private static final String TAG = UnverifiedBannerView.class.getSimpleName();
private static final String TAG = Log.tag(UnverifiedBannerView.class);
private View container;
private TextView text;

View File

@@ -8,14 +8,14 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.whispersystems.signalservice.api.SignalSessionLock;
import java.util.List;
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
public class UnverifiedSendDialog extends AlertDialog.Builder implements DialogInterface.OnClickListener {
private final List<IdentityRecord> untrustedRecords;
@@ -44,7 +44,7 @@ public class UnverifiedSendDialog extends AlertDialog.Builder implements DialogI
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
synchronized (SESSION_LOCK) {
try(SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
for (IdentityRecord identityRecord : untrustedRecords) {
identityDatabase.setVerified(identityRecord.getRecipientId(),
identityRecord.getIdentityKey(),

View File

@@ -20,7 +20,7 @@ import java.io.IOException;
public class SignalPlace {
private static final String URL = "https://maps.google.com/maps";
private static final String TAG = SignalPlace.class.getSimpleName();
private static final String TAG = Log.tag(SignalPlace.class);
@JsonProperty
private CharSequence name;

View File

@@ -65,7 +65,10 @@ public final class DeviceOrientationMonitor implements DefaultLifecycleObserver
}
private void updateOrientationAngles() {
SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading);
boolean success = SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading);
if (!success) {
SensorUtil.getRotationMatrixWithoutMagneticSensorData(rotationMatrix, accelerometerReading);
}
SensorManager.getOrientation(rotationMatrix, orientationAngles);
float pitch = orientationAngles[1];

View File

@@ -0,0 +1,92 @@
package org.thoughtcrime.securesms.components.sensors;
public final class SensorUtil {
private SensorUtil() { }
public static void getRotationMatrixWithoutMagneticSensorData(float[] rotationMatrix, float[] accelerometerReading) {
double gx, gy, gz;
gx = accelerometerReading[0] / 9.81f;
gy = accelerometerReading[1] / 9.81f;
gz = accelerometerReading[2] / 9.81f;
float pitch = (float) -Math.atan(gy / Math.sqrt(gx * gx + gz * gz));
float roll = (float) -Math.atan(gx / Math.sqrt(gy * gy + gz * gz));
float azimuth = 0;
float[] fakeMagnetometerReading = { azimuth, pitch, roll };
System.arraycopy(getRotationMatrixForOrientation(fakeMagnetometerReading), 0, rotationMatrix, 0, rotationMatrix.length);
}
private static float[] getRotationMatrixForOrientation(float[] o) {
float[] xM = new float[9];
float[] yM = new float[9];
float[] zM = new float[9];
float sinX = (float) Math.sin(o[1]);
float cosX = (float) Math.cos(o[1]);
float sinY = (float) Math.sin(o[2]);
float cosY = (float) Math.cos(o[2]);
float sinZ = (float) Math.sin(o[0]);
float cosZ = (float) Math.cos(o[0]);
xM[0] = 1.0f;
xM[1] = 0.0f;
xM[2] = 0.0f;
xM[3] = 0.0f;
xM[4] = cosX;
xM[5] = sinX;
xM[6] = 0.0f;
xM[7] = -sinX;
xM[8] = cosX;
yM[0] = cosY;
yM[1] = 0.0f;
yM[2] = sinY;
yM[3] = 0.0f;
yM[4] = 1.0f;
yM[5] = 0.0f;
yM[6] = -sinY;
yM[7] = 0.0f;
yM[8] = cosY;
zM[0] = cosZ;
zM[1] = sinZ;
zM[2] = 0.0f;
zM[3] = -sinZ;
zM[4] = cosZ;
zM[5] = 0.0f;
zM[6] = 0.0f;
zM[7] = 0.0f;
zM[8] = 1.0f;
float[] resultMatrix = matrixMultiplication(xM, yM);
resultMatrix = matrixMultiplication(zM, resultMatrix);
return resultMatrix;
}
private static float[] matrixMultiplication(float[] A, float[] B) {
float[] result = new float[9];
result[0] = A[0] * B[0] + A[1] * B[3] + A[2] * B[6];
result[1] = A[0] * B[1] + A[1] * B[4] + A[2] * B[7];
result[2] = A[0] * B[2] + A[1] * B[5] + A[2] * B[8];
result[3] = A[3] * B[0] + A[4] * B[3] + A[5] * B[6];
result[4] = A[3] * B[1] + A[4] * B[4] + A[5] * B[7];
result[5] = A[3] * B[2] + A[4] * B[5] + A[5] * B[8];
result[6] = A[6] * B[0] + A[7] * B[3] + A[8] * B[6];
result[7] = A[6] * B[1] + A[7] * B[4] + A[8] * B[7];
result[8] = A[6] * B[2] + A[7] * B[5] + A[8] * B[8];
return result;
}
}

View File

@@ -9,6 +9,12 @@ import org.thoughtcrime.securesms.util.MappingAdapter;
* Reusable adapter for generic settings list.
*/
public class BaseSettingsAdapter extends MappingAdapter {
public BaseSettingsAdapter() {
registerFactory(SettingHeader.Item.class, SettingHeader.ViewHolder::new, R.layout.base_settings_header_item);
registerFactory(SettingProgress.Item.class, SettingProgress.ViewHolder::new, R.layout.base_settings_progress_item);
}
public void configureSingleSelect(@NonNull SingleSelectSetting.SingleSelectSelectionChangedListener selectionChangedListener) {
registerFactory(SingleSelectSetting.Item.class,
new LayoutFactory<>(v -> new SingleSelectSetting.ViewHolder(v, selectionChangedListener), R.layout.single_select_item));

View File

@@ -1,8 +1,6 @@
package org.thoughtcrime.securesms.components.settings;
import android.view.View;
import android.widget.RadioButton;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -20,13 +18,11 @@ import java.util.Objects;
public class CustomizableSingleSelectSetting {
public interface CustomizableSingleSelectionListener extends SingleSelectSetting.SingleSelectSelectionChangedListener {
void onCustomizeClicked(@NonNull Item item);
void onCustomizeClicked(@Nullable Item item);
}
public static class ViewHolder extends MappingViewHolder<Item> {
private final TextView summaryText;
private final View customize;
private final RadioButton radio;
private final SingleSelectSetting.ViewHolder delegate;
private final Group customizeGroup;
private final CustomizableSingleSelectionListener selectionListener;
@@ -35,50 +31,34 @@ public class CustomizableSingleSelectSetting {
super(itemView);
this.selectionListener = selectionListener;
radio = findViewById(R.id.customizable_single_select_radio);
summaryText = findViewById(R.id.customizable_single_select_summary);
customize = findViewById(R.id.customizable_single_select_customize);
customizeGroup = findViewById(R.id.customizable_single_select_customize_group);
delegate = new SingleSelectSetting.ViewHolder(itemView, selectionListener) {
@Override
protected void setChecked(boolean checked) {
radio.setChecked(checked);
}
};
delegate = new SingleSelectSetting.ViewHolder(itemView, selectionListener);
}
@Override
public void bind(@NonNull Item model) {
delegate.bind(model.singleSelectItem);
customizeGroup.setVisibility(radio.isChecked() ? View.VISIBLE : View.GONE);
customizeGroup.setVisibility(model.singleSelectItem.isSelected() ? View.VISIBLE : View.GONE);
customize.setOnClickListener(v -> selectionListener.onCustomizeClicked(model));
if (model.getCustomValue() != null) {
summaryText.setText(model.getSummaryText());
}
}
}
public static class Item implements MappingModel<Item> {
private SingleSelectSetting.Item singleSelectItem;
private Object customValue;
private String summaryText;
private final SingleSelectSetting.Item singleSelectItem;
private final Object customValue;
public <T> Item(@NonNull T item, @Nullable String text, boolean isSelected, @Nullable Object customValue, @Nullable String summaryText) {
this.customValue = customValue;
this.summaryText = summaryText;
singleSelectItem = new SingleSelectSetting.Item(item, text, isSelected);
singleSelectItem = new SingleSelectSetting.Item(item, text, summaryText, isSelected);
}
public @Nullable Object getCustomValue() {
return customValue;
}
public @Nullable String getSummaryText() {
return summaryText;
}
@Override
public boolean areItemsTheSame(@NonNull Item newItem) {
return singleSelectItem.areItemsTheSame(newItem.singleSelectItem);
@@ -86,7 +66,7 @@ public class CustomizableSingleSelectSetting {
@Override
public boolean areContentsTheSame(@NonNull Item newItem) {
return singleSelectItem.areContentsTheSame(newItem.singleSelectItem) && Objects.equals(customValue, newItem.customValue) && Objects.equals(summaryText, newItem.summaryText);
return singleSelectItem.areContentsTheSame(newItem.singleSelectItem) && Objects.equals(customValue, newItem.customValue);
}
}
}

View File

@@ -0,0 +1,63 @@
package org.thoughtcrime.securesms.components.settings;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.MappingModel;
import org.thoughtcrime.securesms.util.MappingViewHolder;
import java.util.Objects;
/**
* Provide a default header {@link MappingModel} and {@link MappingViewHolder} for settings screens.
*/
public final class SettingHeader {
public static final class ViewHolder extends MappingViewHolder<Item> {
private final TextView headerText;
public ViewHolder(@NonNull View itemView) {
super(itemView);
this.headerText = findViewById(R.id.base_settings_header_item_text);
}
@Override
public void bind(@NonNull Item model) {
if (model.text != null) {
headerText.setText(model.text);
} else {
headerText.setText(model.textRes);
}
}
}
public static final class Item implements MappingModel<Item> {
private final int textRes;
private final String text;
public Item(String text) {
this.text = text;
this.textRes = 0;
}
public Item(@StringRes int textRes) {
this.text = null;
this.textRes = textRes;
}
@Override
public boolean areItemsTheSame(@NonNull Item newItem) {
return textRes == newItem.textRes && Objects.equals(text, newItem.text);
}
@Override
public boolean areContentsTheSame(@NonNull Item newItem) {
return areItemsTheSame(newItem);
}
}
}

View File

@@ -0,0 +1,46 @@
package org.thoughtcrime.securesms.components.settings;
import android.view.View;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.util.MappingModel;
import org.thoughtcrime.securesms.util.MappingViewHolder;
/**
* Simple progress indicator that can be used multiple times (if provided with different {@link Item#id}s).
*/
public final class SettingProgress {
public static final class ViewHolder extends MappingViewHolder<SettingProgress.Item> {
public ViewHolder(@NonNull View itemView) {
super(itemView);
}
@Override
public void bind(@NonNull SettingProgress.Item model) { }
}
public static final class Item implements MappingModel<SettingProgress.Item> {
private final int id;
public Item() {
this(0);
}
public Item(int id) {
this.id = id;
}
@Override
public boolean areItemsTheSame(@NonNull SettingProgress.Item newItem) {
return id == newItem.id;
}
@Override
public boolean areContentsTheSame(@NonNull SettingProgress.Item newItem) {
return areItemsTheSame(newItem);
}
}
}

View File

@@ -1,7 +1,10 @@
package org.thoughtcrime.securesms.components.settings;
import android.text.TextUtils;
import android.view.View;
import android.widget.CheckedTextView;
import android.widget.RadioButton;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -23,46 +26,62 @@ public class SingleSelectSetting {
public static class ViewHolder extends MappingViewHolder<Item> {
private final RadioButton radio;
protected final CheckedTextView text;
private final TextView summaryText;
protected final SingleSelectSelectionChangedListener selectionChangedListener;
public ViewHolder(@NonNull View itemView, @NonNull SingleSelectSelectionChangedListener selectionChangedListener) {
super(itemView);
this.selectionChangedListener = selectionChangedListener;
this.radio = findViewById(R.id.single_select_item_radio);
this.text = findViewById(R.id.single_select_item_text);
this.summaryText = findViewById(R.id.single_select_item_summary);
}
@Override
public void bind(@NonNull Item model) {
radio.setChecked(model.isSelected);
text.setText(model.text);
setChecked(model.isSelected);
if (!TextUtils.isEmpty(model.summaryText)) {
summaryText.setText(model.summaryText);
summaryText.setVisibility(View.VISIBLE);
} else {
summaryText.setVisibility(View.GONE);
}
itemView.setOnClickListener(v -> selectionChangedListener.onSelectionChanged(model.item));
}
protected void setChecked(boolean checked) {
text.setChecked(checked);
}
}
public static class Item implements MappingModel<Item> {
private final String text;
private final Object item;
private final String text;
private final String summaryText;
private final boolean isSelected;
public <T> Item(@NonNull T item, @Nullable String text, boolean isSelected) {
this.item = item;
this.text = text != null ? text : item.toString();
this.isSelected = isSelected;
}
public @NonNull String getText() {
return text;
public <T> Item(@NonNull T item, @Nullable String text, @Nullable String summaryText, boolean isSelected) {
this.item = item;
this.summaryText = summaryText;
this.text = text != null ? text : item.toString();
this.isSelected = isSelected;
}
public @NonNull Object getItem() {
return item;
}
public @Nullable String getText() {
return text;
}
public @Nullable String getSummaryText() {
return summaryText;
}
public boolean isSelected() {
return isSelected;
}
@Override
public boolean areItemsTheSame(@NonNull Item newItem) {
return item.equals(newItem.item);
@@ -70,7 +89,9 @@ public class SingleSelectSetting {
@Override
public boolean areContentsTheSame(@NonNull Item newItem) {
return Objects.equals(text, newItem.text) && isSelected == newItem.isSelected;
return Objects.equals(text, newItem.text) &&
Objects.equals(summaryText, newItem.summaryText) &&
isSelected == newItem.isSelected;
}
}
}

View File

@@ -19,7 +19,7 @@ import java.io.InputStream;
public class AttachmentRegionDecoder implements ImageRegionDecoder {
private static final String TAG = AttachmentRegionDecoder.class.getSimpleName();
private static final String TAG = Log.tag(AttachmentRegionDecoder.class);
private SkiaImageRegionDecoder passthrough;

View File

@@ -23,7 +23,7 @@ import org.signal.core.util.logging.Log;
*/
public class HackyViewPager extends ViewPager {
private static final String TAG = HackyViewPager.class.getSimpleName();
private static final String TAG = Log.tag(HackyViewPager.class);
public HackyViewPager(Context context) {
super(context);

View File

@@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.util.MessageRecordUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.thoughtcrime.securesms.video.exo.AttachmentMediaSourceFactory;
import java.util.Collections;
import java.util.List;
@@ -47,9 +48,9 @@ final class VoiceNotePlaybackPreparer implements MediaSessionConnector.PlaybackP
private final Context context;
private final SimpleExoPlayer player;
private final VoiceNoteQueueDataAdapter queueDataAdapter;
private final VoiceNoteMediaSourceFactory mediaSourceFactory;
private final ConcatenatingMediaSource dataSource;
private final VoiceNoteQueueDataAdapter queueDataAdapter;
private final AttachmentMediaSourceFactory mediaSourceFactory;
private final ConcatenatingMediaSource dataSource;
private boolean canLoadMore;
private Uri latestUri = Uri.EMPTY;
@@ -57,7 +58,7 @@ final class VoiceNotePlaybackPreparer implements MediaSessionConnector.PlaybackP
VoiceNotePlaybackPreparer(@NonNull Context context,
@NonNull SimpleExoPlayer player,
@NonNull VoiceNoteQueueDataAdapter queueDataAdapter,
@NonNull VoiceNoteMediaSourceFactory mediaSourceFactory)
@NonNull AttachmentMediaSourceFactory mediaSourceFactory)
{
this.context = context;
this.player = player;

View File

@@ -34,6 +34,7 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.ui.PlayerNotificationManager;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.video.exo.AttachmentMediaSourceFactory;
import java.util.Collections;
import java.util.List;
@@ -87,7 +88,7 @@ public class VoiceNotePlaybackService extends MediaBrowserServiceCompat {
new VoiceNoteNotificationManagerListener(),
queueDataAdapter);
VoiceNoteMediaSourceFactory mediaSourceFactory = new VoiceNoteMediaSourceFactory(this);
AttachmentMediaSourceFactory mediaSourceFactory = new AttachmentMediaSourceFactory(this);
voiceNotePlaybackPreparer = new VoiceNotePlaybackPreparer(this, player, queueDataAdapter, mediaSourceFactory);
voiceNoteProximityManager = new VoiceNoteProximityManager(this, player, queueDataAdapter);

View File

@@ -17,6 +17,7 @@ import androidx.core.widget.ImageViewCompat;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.signal.core.util.ThreadUtil;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
@@ -29,7 +30,6 @@ import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.AvatarUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.webrtc.RendererCommon;
@@ -154,7 +154,7 @@ public class CallParticipantView extends ConstraintLayout {
private boolean isMissingMediaKeys(@NonNull CallParticipant participant) {
if (missingMediaKeysUpdater != null) {
Util.cancelRunnableOnMain(missingMediaKeysUpdater);
ThreadUtil.cancelRunnableOnMain(missingMediaKeysUpdater);
missingMediaKeysUpdater = null;
}
@@ -168,7 +168,7 @@ public class CallParticipantView extends ConstraintLayout {
setCallParticipant(participant);
}
};
Util.runOnMainDelayed(missingMediaKeysUpdater, DELAY_SHOWING_MISSING_MEDIA_KEYS - time);
ThreadUtil.runOnMainDelayed(missingMediaKeysUpdater, DELAY_SHOWING_MISSING_MEDIA_KEYS - time);
}
}
return false;

View File

@@ -0,0 +1,55 @@
package org.thoughtcrime.securesms.contacts;
import android.content.Context;
import android.database.Cursor;
import android.database.MergeCursor;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.loader.content.CursorLoader;
import java.util.List;
public abstract class AbstractContactsCursorLoader extends CursorLoader {
private final String filter;
protected AbstractContactsCursorLoader(@NonNull Context context, @Nullable String filter) {
super(context);
this.filter = sanitizeFilter(filter);
}
@Override
public final Cursor loadInBackground() {
List<Cursor> cursorList = TextUtils.isEmpty(filter) ? getUnfilteredResults()
: getFilteredResults();
if (cursorList.size() > 0) {
return new MergeCursor(cursorList.toArray(new Cursor[0]));
}
return null;
}
protected final String getFilter() {
return filter;
}
protected abstract List<Cursor> getUnfilteredResults();
protected abstract List<Cursor> getFilteredResults();
private static @NonNull String sanitizeFilter(@Nullable String filter) {
if (filter == null) {
return "";
} else if (filter.startsWith("@")) {
return filter.substring(1);
} else {
return filter;
}
}
public interface Factory {
@NonNull AbstractContactsCursorLoader create();
}
}

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