Compare commits

...

953 Commits

Author SHA1 Message Date
Greyson Parrelli
bf40a07bb9 Bump version to 4.63.2 2020-06-10 14:43:24 -04:00
Greyson Parrelli
8f3a6b8479 Update unblock string. 2020-06-10 14:37:03 -04:00
Greyson Parrelli
7642b7cc72 Fix issue with typing indicators in blocked groups. 2020-06-10 14:28:12 -04:00
Greyson Parrelli
e12ea60d85 Bump version to 4.63.1 2020-06-10 12:48:15 -04:00
Greyson Parrelli
0b13c4aed6 Updated language translations. 2020-06-10 12:48:15 -04:00
Alan Evans
47919382e9 Show 'Add to another group' when launched from a group context. 2020-06-10 12:59:57 -03:00
Greyson Parrelli
d60d67ee7e Set contact colors more aggressively. 2020-06-10 10:49:22 -04:00
Alan Evans
559aa687a5 Show group participants menu item on a MMS group. 2020-06-10 11:32:50 -03:00
Cody Henthorne
bc0761f002 Fix navigate up behavior for Conversations. 2020-06-10 10:28:34 -04:00
Alan Evans
c0c2fc0eba When there are no recipients left on group create screen toast and return to list. 2020-06-10 09:07:12 -03:00
Alan Evans
44fe43c74c Hide 'Add to a group' for non-registered users. 2020-06-10 08:54:57 -03:00
Alan Evans
53a2a5d693 Prevent highlighter opacity affecting blur tool. 2020-06-09 23:56:03 -03:00
Greyson Parrelli
2334c26cbb Bump version to 4.63.0 2020-06-09 16:56:57 -04:00
Greyson Parrelli
0b6dde46d9 Updated language translations. 2020-06-09 16:55:50 -04:00
Greyson Parrelli
98d9d81aff Insert receipts in a transaction. 2020-06-09 15:11:37 -04:00
Greyson Parrelli
736a62b632 Update strings related to message requests. 2020-06-09 14:12:52 -04:00
Cody Henthorne
cea6a83d8a Show member count in contact selection list. 2020-06-09 13:32:48 -04:00
Greyson Parrelli
2751fd7efc Retrieve profiles in parallel. 2020-06-09 12:47:11 -04:00
Cody Henthorne
2822042eeb Show recent groups in Add to Groups screen. 2020-06-09 12:13:13 -04:00
Cody Henthorne
dc46d88ddd Provide two ways of listening for thread/message db updates. 2020-06-09 11:52:58 -04:00
Alex Hart
e04f76b558 Fix issue where invalid PagedList objects were passed to ConversationAdapter. 2020-06-09 12:37:19 -03:00
Alan Evans
a758056494 Take highlighter down from 50% to 37.5% opacity. 2020-06-09 12:35:53 -03:00
Alan Evans
1ecdea5db3 Reinstate highlighter under drawing menu. 2020-06-09 12:10:40 -03:00
Alan Evans
e1bb773d85 Add 'Add to a group' button to bottom sheet. 2020-06-09 12:09:59 -03:00
Alan Evans
7e934eff5d Make quotes not hold strong references to attachments. 2020-06-09 12:07:41 -03:00
Greyson Parrelli
cfdf5603af Bump version to 4.62.4 2020-06-09 00:39:55 -04:00
Greyson Parrelli
45bfb8c6b6 Updated language translations. 2020-06-09 00:38:19 -04:00
Alex Hart
65608a51b8 Fix API 19 crash by using different resource. 2020-06-09 00:33:38 -04:00
Greyson Parrelli
b6314597fe Bump version to 4.62.3 2020-06-08 16:32:08 -04:00
Greyson Parrelli
20a588199a Updated language translations. 2020-06-08 16:32:00 -04:00
Greyson Parrelli
59916f1e95 Add 'Add to contacts' button to bottom sheet. 2020-06-08 16:07:14 -04:00
Alan Evans
8b91f8f9e7 Disable disappearing messages option and remove from menu. 2020-06-08 12:31:55 -03:00
Alex Hart
cbc3cce66f Fix API 19 drawable crash in ManageGroupFragment. 2020-06-08 11:34:39 -03:00
Alex Hart
b4b63b5860 Add auto-mirroring to ic_forward_outline. 2020-06-08 10:23:43 -03:00
Alex Hart
b9ae15a890 Fix group name RTL alignment. 2020-06-08 10:21:55 -03:00
Greyson Parrelli
d955389c46 Bump version to 4.62.2 2020-06-07 22:05:02 -04:00
Greyson Parrelli
975eb885c1 Updated language translations. 2020-06-07 22:05:02 -04:00
Alan Evans
a3aed96757 Sort contacts without names after contacts with names. 2020-06-07 22:05:02 -04:00
Alan Evans
dc70bfabaf Lighter ultramarine + in dark mode. 2020-06-07 22:05:02 -04:00
Greyson Parrelli
6932340671 Add ability to copy a number via long-press. 2020-06-07 19:59:42 -04:00
Alan Evans
f6637b7caf Restore mute in conversation menu. 2020-06-07 19:59:42 -04:00
Alan Evans
4f4be44caa Load identities in transaction. 2020-06-07 19:59:42 -04:00
Greyson Parrelli
7832497ba7 Shorten logging in ConversationActivity. 2020-06-07 19:59:42 -04:00
Alex Hart
7d06e2395f Rework how ConversationFragment RecyclerView responds to data updates. 2020-06-07 19:59:42 -04:00
Greyson Parrelli
3a479d7eef Reduce database notifications for disappearing conversations. 2020-06-07 19:59:42 -04:00
Greyson Parrelli
8fe8a1e9ee Put refresh and upload profile jobs in the same queue. 2020-06-07 19:59:42 -04:00
Alan Evans
2d8b2e7fb0 Transitions for group settings. 2020-06-07 19:59:42 -04:00
Alan Evans
9c0365f92c Open group settings from group avatar click. 2020-06-07 19:59:42 -04:00
Greyson Parrelli
b48abb08d2 Show custom notifications for API < 26. 2020-06-07 19:59:42 -04:00
Alan Evans
d8f3e032c7 Fix group name clearing after avatar change. 2020-06-07 19:59:42 -04:00
Alan Evans
8dbcb255ad Hide Block and Leave options when not available in group settings, add unblock. 2020-06-07 19:59:42 -04:00
Greyson Parrelli
db06cbbc86 Remove unnecessary recipient refreshes. 2020-06-07 19:59:42 -04:00
Cody Henthorne
98ab23c1a3 Make Custom Notification dialog dismiss itself on up press. 2020-06-07 10:23:41 -04:00
Cody Henthorne
d0ca9ba6a6 Make text button color responsive to theme. 2020-06-07 09:43:14 -04:00
Alan Evans
b242368675 Remove group members button. 2020-06-07 09:31:18 -03:00
Alan Evans
664527ce63 Fix sort order for group members. 2020-06-07 08:25:31 -03:00
Alan Evans
99e4f80be0 Allow whole row selection for Shared media in group settings. 2020-06-07 08:12:25 -03:00
Alan Evans
702dae9fcd Fix double tap required for "See all" media in group settings. 2020-06-07 07:47:28 -03:00
Alan Evans
48fe1ba559 Fix group settings divider shade in dark mode. 2020-06-07 07:42:28 -03:00
Greyson Parrelli
382ac7ba0d Bump version to 4.62.1 2020-06-06 20:28:18 -04:00
Greyson Parrelli
a46f47f352 Updated language translations. 2020-06-06 20:27:48 -04:00
Greyson Parrelli
e984d8a42c Change Gif -> GIF. 2020-06-06 20:27:22 -04:00
Greyson Parrelli
554bad6b8d Improve DB access in group sends. 2020-06-06 20:25:02 -04:00
Jim Gustafson
ed13c97ad7 Handle legacy hangup properly. 2020-06-06 20:25:02 -04:00
Greyson Parrelli
d33873d59a Fix possible crash with null thread body. 2020-06-06 20:25:02 -04:00
Greyson Parrelli
1234899ea1 Add support for non-blocking media sends. 2020-06-06 20:25:02 -04:00
Cody Henthorne
13027dc44b Fix leaking MessageDetailsActivity via list items. 2020-06-06 20:25:02 -04:00
Cody Henthorne
5b4d74b7fe Move group resolution for conversations to background LiveData. 2020-06-06 20:25:02 -04:00
Alan Evans
18c7bc2b5b Prevent edit of a group post leave. 2020-06-06 20:25:02 -04:00
Alan Evans
bbbee0f372 Fix group create arrow for RTL. 2020-06-06 20:25:02 -04:00
Alex Hart
cf9d090154 Start Paging @ Unread count instead of -1. 2020-06-06 20:25:02 -04:00
Alan Evans
718471917f Separate text only message layouts. 2020-06-06 20:25:02 -04:00
Greyson Parrelli
bb97407cde Bump version to 4.62.0 2020-06-05 22:04:18 -04:00
Greyson Parrelli
92ce678e29 Updated language translations. 2020-06-05 22:04:16 -04:00
Cody Henthorne
e100aea2c7 Preserve scroll position in Message Details on update. 2020-06-05 21:46:04 -04:00
Greyson Parrelli
fea3b6cb4a Don't show 'conversation settings' for groups. 2020-06-05 21:46:04 -04:00
Cody Henthorne
afbc132faa Fix conversation item and data source memory leaks. 2020-06-05 21:46:04 -04:00
Alan Evans
b27198286d MMS proof new group UI. 2020-06-05 21:46:04 -04:00
Greyson Parrelli
ac93d81032 Remove pins4all feature flag. 2020-06-05 21:46:04 -04:00
Alan Evans
9981e5ca76 Enable new group UI. 2020-06-05 20:19:03 -03:00
Cody Henthorne
7dd3efeb53 Remove listeners when detaching conversation item views. 2020-06-05 19:29:55 -03:00
Greyson Parrelli
d38d702adf Parallelize group sends. 2020-06-05 18:10:50 -04:00
Alex Hart
04a000a8a8 Always display labels in contact search. 2020-06-05 15:16:55 -03:00
Fumiaki Yoshimatsu
3bbf0741ee Use localized string for Phone number.
Fixes #9626
2020-06-05 15:01:20 -03:00
Fumiaki Yoshimatsu
e9a336100b Display backup date in users locale on restore.
Fixes #9693
2020-06-05 15:01:20 -03:00
Cody Henthorne
fb600e9829 Update SMS/MMS as sending when retrying failed send.
This was only impacting SMS/MMS as Push already reset the status.
2020-06-05 13:46:25 -04:00
Alex Hart
4a455ff958 Implement new Add Members UI. 2020-06-05 13:44:02 -03:00
Cody Henthorne
707e238e5c Make borderless button style responsive to theme. 2020-06-05 12:01:00 -04:00
Alan Evans
90f22a4b66 Include face position and projection matrix into elements matrix. 2020-06-04 19:49:22 -03:00
Alex Hart
b4f134adf7 Add more descriptive messages for media notifications and chat previews. 2020-06-04 13:13:42 -03:00
Greyson Parrelli
1e00fc6149 Bump version to 4.61.6 2020-06-04 10:21:23 -04:00
Greyson Parrelli
f52133a69c Updated language translations. 2020-06-04 10:21:23 -04:00
Alan Evans
91b142e0d9 Fix waveform array out of bounds. 2020-06-04 10:21:10 -04:00
Greyson Parrelli
26a9dd98c1 Bump version to 4.61.5 2020-06-03 19:01:03 -04:00
Greyson Parrelli
99e38e1d23 Updated language translations. 2020-06-03 18:58:34 -04:00
Greyson Parrelli
a2d8a25fd9 Blur UI tweaks. 2020-06-03 18:51:38 -04:00
Alan Evans
d86d625bcc Smoother blur rendering. 2020-06-03 19:47:51 -03:00
Greyson Parrelli
18e3fb6609 Fix string format. 2020-06-03 17:19:32 -04:00
Greyson Parrelli
da33ba0ed5 Update blur UI. 2020-06-03 17:12:47 -04:00
Greyson Parrelli
66f021d01a Fix issue where rail wasn't showing in some situations. 2020-06-03 17:12:47 -04:00
Greyson Parrelli
40231ea45f Fix issue with view-once toggle and face blurring. 2020-06-03 17:12:42 -04:00
Alex Hart
cd80a47c04 Made edit profile save button move with the keyboard. 2020-06-03 17:12:27 -04:00
Alan Evans
1033bd7bda Blur faces rotation and crop and zoom support. 2020-06-03 14:02:24 -03:00
Greyson Parrelli
b4f60f3acb Bump version to 4.61.4 2020-06-03 06:40:09 -04:00
Greyson Parrelli
bed3b571cc Updated language translations. 2020-06-03 06:39:34 -04:00
Greyson Parrelli
c8dd4e5254 Added support for blurring faces.
Co-authored-by: Alan Evans <alan@signal.org>
2020-06-03 06:39:20 -04:00
Alan Evans
514048171b Add Image Editor support for blur mask layer. 2020-06-03 03:33:06 -03:00
Greyson Parrelli
32e9901592 Bump version to 4.61.3 2020-06-02 19:22:22 -04:00
Greyson Parrelli
d83f86a469 Revert "Make notifications and chat previews for media messages more descriptive."
This reverts commit a3f9737e63.
2020-06-02 19:19:30 -04:00
Greyson Parrelli
403d53586c Bump version to 4.61.2 2020-06-02 17:40:56 -04:00
Greyson Parrelli
6acae58694 Updated language translations. 2020-06-02 17:33:41 -04:00
Alex Hart
a3f9737e63 Make notifications and chat previews for media messages more descriptive. 2020-06-02 17:34:50 -03:00
Cody Henthorne
263af7c139 Add registration lock status to support email. 2020-06-02 16:14:19 -04:00
Alex Hart
7f2439f1e9 Fix contact selection behavior when searching and clear search on selection. 2020-06-02 16:27:04 -03:00
Alex Hart
ae87d23003 Always use the new group settings screen if the flag is enabled. 2020-06-02 16:09:48 -03:00
Alex Hart
3192cc0aac Add outlined view-once close icon. 2020-06-02 16:05:16 -03:00
Alex Hart
6102e9aa72 Apply better coordinatorlayout animation and RTL support. 2020-06-02 15:02:35 -03:00
Alan Evans
f4a152b0fe Fetch own profile after GV2 feature flag is enabled, improve GV2 capability check. 2020-06-02 11:48:40 -03:00
Greyson Parrelli
2b11bca7dc Guard against possible invalid conversation data loads. 2020-06-02 10:20:55 -04:00
Artem Varaksa
07d19f38e3 Fix typos in logging for remote delete. 2020-06-02 10:22:29 -03:00
Greyson Parrelli
cd228c439e Be more explicit with the ID we use for account updates. 2020-06-02 09:03:54 -04:00
Alan Evans
7a859c8961 For smaller width devices, use original 210dp for audio messages. 2020-06-02 09:32:59 -03:00
Alan Evans
543f38c75d Fix Wave form IOException thread issue. 2020-06-02 07:38:15 -03:00
Greyson Parrelli
f7b150f2d2 Bump version to 4.61.1 2020-06-01 17:43:05 -04:00
Greyson Parrelli
11328f643f Updated language translations. 2020-06-01 17:43:05 -04:00
Greyson Parrelli
f270a6b8c4 Fix potential crash by removing an unnecessary column.
The column I removed is already in the recipient half of the projection.
Having two representations of the groupId made reading the groupId out
of the cursor non-deterministic, and when compounded with another bug,
could cause a crash if one of them was null.
2020-06-01 17:43:05 -04:00
Alan Evans
3fec23fd36 Show remaining time on wave form view and cache wave form in database. 2020-06-01 17:43:05 -04:00
Alex Hart
e01838e996 Fix text size for pending members. 2020-06-01 17:43:05 -04:00
Greyson Parrelli
f70e41e7cd Don't allow account record updates to delete our profile key. 2020-06-01 17:43:05 -04:00
Greyson Parrelli
c4ec0c9897 Handle devices disallowing start of FcmFetchService.
Some devices are overzealous with battery management and disallow
starting services even when they're in response to a high-priority FCM
message (which should be allowed). So in these situations, we just
fall back to what we were doing before.
2020-06-01 17:43:05 -04:00
Greyson Parrelli
989b071a6d Ignore contacts that don't have a phone number. 2020-06-01 17:43:05 -04:00
Greyson Parrelli
c39751f9db Add info about play services to the debug log. 2020-06-01 17:43:05 -04:00
jimio-signal
dbf74a2234 Update copyright in README.md 2020-05-31 10:39:19 -07:00
Greyson Parrelli
837230d72d Bump version to 4.61.0 2020-05-29 19:18:55 -04:00
Greyson Parrelli
f544ec4126 Updated language translations. 2020-05-29 19:18:02 -04:00
Greyson Parrelli
79dbf85c1e Improve local encrypted PIN storage. 2020-05-29 19:15:56 -04:00
Greyson Parrelli
61fe6cc961 Enable the ability to react with any emoji. 2020-05-29 19:14:37 -04:00
Greyson Parrelli
70c88b68e2 Store recent reactions separately from keyboard emoji. 2020-05-29 19:14:37 -04:00
Greyson Parrelli
d70c33d20f Add support for mark as unread. 2020-05-29 19:14:37 -04:00
Greyson Parrelli
6b2e000e61 Prevent waiting for old queues in our retrieval strategies. 2020-05-29 19:14:37 -04:00
Alan Evans
b9f11dafff New internal testing flag and V1 group creation button. All menus create GV1. 2020-05-29 19:14:37 -04:00
Alan Evans
9b32eaeb8a Do not log URLs. 2020-05-29 19:14:37 -04:00
Alan Evans
a99c0d438e Rename GV2 "version" to "revision". 2020-05-29 19:14:37 -04:00
Alex Hart
c634c24afb Utilize Wrapper instead of dynamic theme. 2020-05-29 19:14:37 -04:00
Alex Hart
2ddd1437cf Utilize exclusive AudioFocus. 2020-05-29 19:14:37 -04:00
Alan Evans
9da309ca48 Enforce a local GV2 capacity limit driven by a feature flag. 2020-05-29 19:14:37 -04:00
henry
cfcd451db7 Fix crash on unlink device when offline. 2020-05-29 09:51:21 -04:00
Alex Hart
5ab72fd1a9 Ask for permission before launching avatar sheet. 2020-05-29 09:51:21 -04:00
Alan Evans
daace9bd1a Audio wave forms on voice notes. 2020-05-29 09:51:21 -04:00
Alan Evans
69adcd1d69 Tap avatar in chat preferences or group management to see full screen. 2020-05-29 09:51:21 -04:00
Alex Hart
0711a22188 Add overflow toast and fix edit menu option. 2020-05-29 09:51:21 -04:00
Greyson Parrelli
3a06412cd8 Throttle notifications when doing the intial message fetch. 2020-05-29 09:51:21 -04:00
Alan Evans
51c82702e2 Remove expectation of ActionBar in DeviceProvisioningActivity.
Fixes #9661
2020-05-29 09:51:21 -04:00
Greyson Parrelli
1b01196ec6 Refactor ThreadRecord. 2020-05-29 09:51:21 -04:00
Greyson Parrelli
1cd6b58ece Don't enqueue duplicate PushDecryptMessageJobs. 2020-05-29 09:51:21 -04:00
Greyson Parrelli
ea8e13b1c8 Create a WebsocketDrainedConstraint. 2020-05-29 09:51:21 -04:00
Greyson Parrelli
f392229393 Extract MessageNotifier interface. 2020-05-29 09:51:21 -04:00
Greyson Parrelli
a299bafe89 Create a new system for fetching the intial batch of messages. 2020-05-29 09:51:21 -04:00
Alex Hart
d2bf539504 Clear sticky WebRtcViewModel events when initiating a new call. 2020-05-29 09:51:21 -04:00
Alex Hart
903c3989b9 Fix chip jank and other groups v2 ux issues. 2020-05-29 09:51:21 -04:00
Alan Evans
00996f0d7a Rename back to build.gradle 2020-05-29 09:51:21 -04:00
Alan Evans
4aded3a436 Close keyboard on contact list scroll. 2020-05-29 09:51:20 -04:00
Alan Evans
9acdc37729 Alphabetical member order. 2020-05-29 09:51:20 -04:00
Greyson Parrelli
d4cdcbe54f Improve logging around group sends. 2020-05-29 09:51:20 -04:00
Alex Hart
6fa2a0f411 Polish UX for groups v2 management. 2020-05-29 09:51:20 -04:00
Alex Hart
558a8e4a14 Add polish to groups v2 creation flow. 2020-05-29 09:51:20 -04:00
Alan Evans
8947b82034 Make GV2 feature flags remote capable. 2020-05-29 09:51:20 -04:00
Alan Evans
56551025e9 Detect if group v2 is active from membership. 2020-05-29 09:51:20 -04:00
Alan Evans
befb4939d5 Restore groups from storage service. 2020-05-29 09:51:20 -04:00
Alan Evans
289f7aba63 Add versioned profiles feature flag. 2020-05-29 09:51:20 -04:00
Alan Evans
28bd245b96 While testing GV2 without UUID, fail jobs that hit UuidRecipientError. 2020-05-29 09:51:20 -04:00
Alan Evans
c5e7300df2 Fix matches logic in contact selection. 2020-05-29 09:51:20 -04:00
Greyson Parrelli
fe25d941bb Prevent FCM bottlenecking. 2020-05-29 09:51:20 -04:00
Alan Evans
4cda267f3b Show pending count and allow view of zero pending screen. 2020-05-29 09:51:20 -04:00
Alex Hart
82ba7e2b8b Display "You" at end of members list in ConversationTitleView. 2020-05-29 09:51:20 -04:00
Alex Hart
41ebaf3938 Clean up Overflow menu for GV2 groups. 2020-05-29 09:51:20 -04:00
Alex Hart
090c400037 Collapse title into toolbar on scroll in ManageGroupFragment. 2020-05-29 09:51:20 -04:00
Alan Evans
12b1232ac0 Fix groups v2 patch response handler. 2020-05-29 09:51:20 -04:00
Alex Hart
204a84c522 Apply proper spacing to RecipientBottomSheetDialogFragment. 2020-05-29 09:51:20 -04:00
Alan Evans
526afd539b Fix avatar tap in conversation multi-select mode. 2020-05-29 09:51:20 -04:00
Greyson Parrelli
d708984abd Require users be a system contact or whitelisted to appear in the contact list. 2020-05-29 09:51:20 -04:00
Greyson Parrelli
9d39db6428 Add additional account restore logging, prevent double avatar fetch. 2020-05-29 09:51:20 -04:00
Alan Evans
67a8ec0d39 Only admin can cancel any invite. 2020-05-29 09:51:20 -04:00
Alan Evans
297a7d0ef8 Handle absent change during invite. 2020-05-29 09:51:20 -04:00
Bastian Köcher
4712833853 Always convert HEIC images to JPEG.
This pr changes the behavior of sending HEIC images to always convert
them to JPEG. This conversion is required to support image inline
viewing accross different devices and operating systems. This follows
the same strategy as on IOS: https://github.com/signalapp/Signal-iOS/pull/2511

Fixes: https://github.com/signalapp/Signal-iOS/issues/4374 & https://github.com/signalapp/Signal-Android/issues/9395
2020-05-29 09:51:20 -04:00
Alan Evans
11d17f7496 GV2 storage service syncing. 2020-05-29 09:51:20 -04:00
Alan Evans
36df3f234f Enable the Zk group library. 2020-05-29 09:51:20 -04:00
Greyson Parrelli
098b298646 Add a network constraint to RemoteConfigRefreshJob. 2020-05-29 09:51:20 -04:00
Alan Evans
2f9320989a Server signed group v2 changes sent and received P2P. 2020-05-29 09:51:20 -04:00
Alan Evans
ec8d5defd4 Protect against unknown GV2 UUIDs. 2020-05-29 09:51:20 -04:00
Greyson Parrelli
981676c7f8 Bump version to 4.60.9 2020-05-29 09:40:26 -04:00
Greyson Parrelli
7c5ae57784 Updated language translations. 2020-05-29 09:38:57 -04:00
Alex Hart
fc7be87468 Downgrade AudioManagerCompat errors to warnings. 2020-05-29 10:31:36 -03:00
Greyson Parrelli
e55d8007fc Bump version to 4.60.8 2020-05-28 18:34:06 -04:00
Greyson Parrelli
43b7aa2d52 Updated language translations. 2020-05-28 18:33:46 -04:00
Alex Hart
cd1bad0718 Fix bluetooth behavior. 2020-05-28 17:36:40 -03:00
Greyson Parrelli
6b47618351 Bump version to 4.60.7 2020-05-26 18:27:05 -04:00
Greyson Parrelli
b6d384120d Updated language translations. 2020-05-26 18:26:39 -04:00
Greyson Parrelli
1268b26c1f Auto-dismiss PIN reminder dialog as you type. 2020-05-26 18:13:19 -04:00
Greyson Parrelli
f1233bfddc Bump version to 4.60.6 2020-05-25 14:57:59 -04:00
Greyson Parrelli
1aa3e6afea Updated language translations. 2020-05-25 14:57:59 -04:00
Greyson Parrelli
ce21eb241a Fix potential crash with data size in ConversationDataSource. 2020-05-25 14:57:59 -04:00
Greyson Parrelli
f96fb72eb1 Don't show PIN reminders if you're not registered.
Fixes #9657
2020-05-25 13:14:38 -04:00
Greyson Parrelli
207c467c6b Don't insert identity verification message for the initial restore. 2020-05-24 13:00:16 -04:00
Alex Hart
9d1d9e33ed Bumped version to 4.60.5 2020-05-21 20:03:31 -03:00
Alex Hart
e4a76c0690 Updated language translations. 2020-05-21 20:03:31 -03:00
Alex Hart
124c3e25e9 Implement layout changes to new call screen UX. 2020-05-21 20:03:31 -03:00
Greyson Parrelli
5cb1201903 Add the ability to disable PIN reminders. 2020-05-21 19:56:30 -03:00
Greyson Parrelli
bb6ca80d5a Don't create identity change methods for brand new contacts. 2020-05-21 19:56:30 -03:00
Greyson Parrelli
dc7c54a1f8 Ensure we upload the profile after a PIN restore. 2020-05-21 19:56:30 -03:00
Greyson Parrelli
23401440bf Prevent insertion of UUID-only contacts at the database level. 2020-05-21 19:56:30 -03:00
Greyson Parrelli
f8f959e05a Make rate limit message more generic. 2020-05-20 14:22:33 -04:00
Alex Hart
edbd4d2d03 Properly set profile key update flag. 2020-05-20 12:15:54 -03:00
Greyson Parrelli
a0b4065be3 Fix potention OOB error when pulse-highlighting a message.
This basically happened if you used full-text search to search for the
latest message in a conversation, but when you navigated there, it
*also* had a header set (like a typing indicator or unknownSenderView).
2020-05-19 17:09:25 -04:00
Greyson Parrelli
1b2f964f32 Fix possible crash around loading initial conversation pages. 2020-05-19 16:20:20 -04:00
Alex Hart
eaf5280d99 Bumped version to 4.60.4 2020-05-19 16:51:33 -03:00
Alex Hart
d435da980f Updated language translations. 2020-05-19 16:51:33 -03:00
Greyson Parrelli
8d3a91f3a4 Fix possible data source invalidation loop. 2020-05-19 16:51:33 -03:00
Greyson Parrelli
b80c339c5a Fix an issue where the add profile prompt wasn't dismissed. 2020-05-19 16:51:33 -03:00
Alan Evans
34159fc9da Log successful pin setting. 2020-05-19 16:34:52 -03:00
Alex Hart
b509ee9ee0 Bumped version to 4.60.3 2020-05-18 17:03:45 -03:00
Alex Hart
a6819448b9 fixup! Consolidate Call UI visibility selection logic. 2020-05-18 16:43:30 -03:00
Alex Hart
f2847f9aa5 Bumped version to 4.60.2 2020-05-18 16:31:20 -03:00
Alex Hart
8f01e5e1c3 Updated language translations. 2020-05-18 16:31:20 -03:00
Alan Evans
acb2f43620 Make Manage Group menu item replace Edit Group for GV2. 2020-05-18 16:31:20 -03:00
Greyson Parrelli
62ac65e4d8 Improve paging performance on slower devices. 2020-05-18 16:31:20 -03:00
Alex Hart
8f183bdcdc Consolidate Call UI visibility selection logic. 2020-05-18 16:31:20 -03:00
Greyson Parrelli
3d135d155e Disable view prefetching for now. 2020-05-18 16:31:20 -03:00
Alan Evans
090c811391 Force app compat version 1.1.0-beta01. 2020-05-18 16:31:20 -03:00
Alex Hart
2a9e8dc525 Bumped version to 4.60.1 2020-05-15 19:02:18 -03:00
Alex Hart
cb0b22cf2c Updated language translations. 2020-05-15 19:02:18 -03:00
Alex Hart
5aba3517ce Upgrade to RingRTC 2.0.3 and implement rounded corners for local pip. 2020-05-15 19:02:18 -03:00
Alex Hart
726f665388 Upgrade AppCompat to 1.2.0-rc01. 2020-05-15 19:02:18 -03:00
Alex Hart
e2ac55e9ac Fix ellapsed call timer restarting between activity restarts. 2020-05-15 19:02:18 -03:00
Greyson Parrelli
fa5729bac6 Better handle identity key changes in response to storage service syncs. 2020-05-15 19:02:18 -03:00
Greyson Parrelli
e714cb6423 Fix potential issues with ConversationDataSource boundaries. 2020-05-15 19:02:18 -03:00
Alex Hart
35a0162d5c Utilize EmojiTextView instead of TextView. 2020-05-15 19:02:18 -03:00
Alex Hart
76740adc3f Fix controls are removed when remote video is disabled. 2020-05-15 13:05:00 -03:00
Alex Hart
1c814141a2 Fix NullPointerException when trying to launch InviteActivity. 2020-05-15 10:43:25 -03:00
Alan Evans
5545daf992 Live group membership count in conversation. 2020-05-15 10:28:48 -03:00
Alan Evans
d300615d90 Ensure new group UI behind feature flag. 2020-05-15 10:27:39 -03:00
Alex Hart
908a5260c2 Enable note to self as recipient in share activity. 2020-05-15 10:03:49 -03:00
Alex Hart
7aac6644c3 Expand tappable area in header. 2020-05-14 16:27:52 -03:00
Alan Evans
3b673c07a0 Support gv2 avatar removal. 2020-05-14 15:57:40 -03:00
Alan Evans
d726da822c Add network constraint to GV2 messages. 2020-05-14 15:23:15 -03:00
Alex Hart
7894f72b0f Enable speaker when initiating a video call. 2020-05-14 14:18:49 -03:00
Alan Evans
4c5822ac67 GV2 Update message description. 2020-05-14 13:59:34 -03:00
Alex Hart
b917cccbee Bumped version to 4.60.0 2020-05-14 11:22:28 -03:00
Alex Hart
01d2d05d8e Updated language translations. 2020-05-14 11:22:28 -03:00
Alan Evans
4de86cb6cf Prevent ZkGroup link crashes. 2020-05-14 11:22:28 -03:00
henry
8861ad76ed Fix start SubmitDebugLog from registration and passphrase prompt. 2020-05-14 11:22:28 -03:00
Alan Evans
ef86372635 Ensure that the unknown UUID does not create an entry. 2020-05-14 11:22:28 -03:00
Alex Hart
ccff7b1148 Implement new group creation screens behind flag. 2020-05-14 11:22:28 -03:00
Greyson Parrelli
ed0825112d Fix some ordering problems with conversation data loading. 2020-05-14 11:22:28 -03:00
Alan Evans
b8df90531f GV2 message contexts. 2020-05-14 11:22:28 -03:00
Greyson Parrelli
f099c3591c Run PushProcessMessageJobs in parallel. 2020-05-14 11:22:28 -03:00
Greyson Parrelli
ed33e048ad Add CachedLayoutInflater to improve conversation render performance. 2020-05-14 11:22:28 -03:00
Greyson Parrelli
7fd3bfa30c Revert "Check to see if FCM is available at app launch."
This reverts commit eea7174f1d.
2020-05-14 11:22:28 -03:00
Alex Hart
07a492a32c Add dot character to reactions bottom sheet all tab label. 2020-05-14 11:22:28 -03:00
Alan Evans
11fffbd79e Remove P2P group change sending. 2020-05-14 11:22:28 -03:00
Alan Evans
eff564ad88 Adapt message requests to support invite flow. 2020-05-14 11:22:28 -03:00
Greyson Parrelli
d3d53e6099 Reduce recipient dirty state logging verbosity. 2020-05-14 11:22:28 -03:00
Greyson Parrelli
53d122ed55 Fix jumping to last seen position. 2020-05-14 11:22:28 -03:00
Alan Evans
1778c1ef7d Prevent some IOExceptions when past the end of stream. 2020-05-14 11:22:28 -03:00
Alan Evans
a510bc74e6 Recipient Id cache. 2020-05-14 11:22:28 -03:00
Alan Evans
a9ecdbdfec Groups V2 capability set by the feature flag. 2020-05-14 11:22:28 -03:00
Alan Evans
06ab3cf013 Fix cases of inlined & missing log tags. 2020-05-14 11:22:28 -03:00
Alan Evans
3db5da1c8d Generalize media input for use with Audio. 2020-05-14 11:22:28 -03:00
Greyson Parrelli
5937a50b6d Fix message receive timestamps on media messages. 2020-05-14 11:22:28 -03:00
Alan Evans
b4191ee5cc Fix usages of service logging in app. 2020-05-14 11:22:28 -03:00
Alan Evans
c63e42715e New logging lint checks.
[LogNotAppSignal] tells you about using signal service logger in the app.
[LogTagInlined] tells you about not using a constant tag.
2020-05-14 11:22:28 -03:00
Alex Hart
26e582d806 Integrate RingRTC v2.0.1 2020-05-14 11:22:28 -03:00
Alan Evans
ee9270845a Create GV2 group behind feature flag. 2020-05-14 11:22:28 -03:00
Alan Evans
6cf33897c0 Remove superfluous groups v2 capability checks. 2020-05-14 11:22:28 -03:00
Freddy Tuxworth
2161bbb8fa Display "No matching countries" when no filter matches found.
Fixes #9518
2020-05-14 11:22:28 -03:00
Greyson Parrelli
b75088874e Migrate conversation rendering to the paging library. 2020-05-14 11:22:28 -03:00
Alan Evans
9ac1897880 Job changes for GroupsV2 message receive and profile key updates. 2020-05-14 11:22:28 -03:00
Alan Evans
36c43ed2fa Ensure latest V2 group state from server upon conversation open. 2020-05-14 11:22:28 -03:00
Alan Evans
8084822f16 Connect GV2 title and avatar updates and prevent no-change avatar updates. 2020-05-14 11:22:28 -03:00
Alan Evans
959718618f Deprecate some ViewUtil methods. Inline others. Remove some old API code. 2020-05-14 11:22:28 -03:00
Alan Evans
75f3fe0cec Correct access control for MMS groups. 2020-05-14 11:22:28 -03:00
Alan Evans
b800477365 GV2 leave and eject operations. 2020-05-14 11:22:28 -03:00
Alex Hart
b191341c57 Add some polish to the groups V2 manager UI. 2020-05-14 11:22:28 -03:00
leet
88a40be901 Fix backup timestamp language.
Fixes #8842
Fixes #8986
2020-05-14 11:22:28 -03:00
Greyson Parrelli
3fef58057e Add additional info to support emails and debuglogs. 2020-05-14 11:22:28 -03:00
Greyson Parrelli
b156e4a79a Always use the UD cert with a UUID. 2020-05-14 11:22:28 -03:00
Alan Evans
30ac264cd3 Rename Group update message classes. 2020-05-14 11:22:28 -03:00
Alan Evans
a9b00e1cd3 Remove instances of Android logging. 2020-05-14 11:22:28 -03:00
Alex Hart
d94fc4bc13 Implement ability to react with any emoji behind a flag. 2020-05-14 11:22:28 -03:00
Greyson Parrelli
40b5339ef8 Allow auto-download for users you've shared your profile with. 2020-05-14 11:22:28 -03:00
Alan Evans
86f0456e8c Group Manager V2 operations. 2020-05-14 11:22:28 -03:00
Alan Evans
48a693793f GV2 Group Manager. 2020-05-14 11:22:28 -03:00
Alan Evans
ff28d72db6 New GV2 internal prefix and scrubber. 2020-05-13 16:18:18 -04:00
Alan Evans
456857bbbd Add custom lint rule project. 2020-05-13 16:18:18 -04:00
Alan Evans
7f17b66a6c Upgrade gradle and gradle plugin. 2020-05-13 16:18:18 -04:00
Greyson Parrelli
310ec8f296 Remove CellServiceConstraint in favor of NetworkOrCellServiceConstraint.
If a job was enqueued with a CellServiceConstraint (which is currently
only SMS jobs), then it'll never run until it gets service, even if you
flip the "enable SMS sending over wifi" toggle.

This has created bad situations in the past, where SMS jobs just get
stuck on devices that never report having cell service (like VM's or
wifi only devices).

This fixes it by *always* using NetworkOrCellServiceConstraint, and then
deciding whether a constraint is met by checking the "wifi SMS" setting
at decision-time.
2020-05-13 16:18:18 -04:00
Alan Evans
0c2afa9438 Fix FCM token via phone call registration.
Fixes #8992
2020-05-13 16:18:18 -04:00
Alex Hart
c3832cf8b1 New group notifications management ui. 2020-05-13 16:18:18 -04:00
Greyson Parrelli
a2de8a2a05 Ensure you can't set null values in DefaultValueLiveData. 2020-05-13 16:18:18 -04:00
Greyson Parrelli
3b601896d2 Fix crash in SubmitDebugLogActivity. 2020-05-13 16:18:18 -04:00
Greyson Parrelli
e1a90bcb00 Transition conversation loading from a Loader to a Repository. 2020-05-13 16:18:18 -04:00
Greyson Parrelli
2b65916344 Show calling foreground notification on all OS versions.
Fixes #9516
Fixes #9414
2020-05-13 16:18:18 -04:00
Greyson Parrelli
f149005026 Add support for remote config v1.1 2020-05-13 16:18:18 -04:00
Alex Hart
5eb663aa1b New group avatar and name selection screen. 2020-05-13 16:18:18 -04:00
Alan Evans
12b7d6c0e3 Use bottom sheet shape. 2020-05-13 16:18:18 -04:00
Alan Evans
723639d928 New group management screen. 2020-05-13 16:18:18 -04:00
Greyson Parrelli
e0502c24e1 Only search for visible parts of a contact. 2020-05-13 16:18:18 -04:00
Alex Hart
358d6333c3 Open new recipient bottom sheet when accessing contact from group context. 2020-05-13 16:18:18 -04:00
Alan Evans
0b279d1df3 Group contact chips behind feature flag. 2020-05-13 16:18:18 -04:00
Alan Evans
8e0fba7992 New group button behind new Group UI feature flag. 2020-05-13 16:18:18 -04:00
Alex Hart
d5419ec9fa Implement new call screen UI/UX. 2020-05-13 16:18:18 -04:00
Alan Evans
33e3f78be6 LiveDataUtil combineLatest. 2020-05-13 16:17:29 -04:00
Alex Hart
3c5ad519dd Decrease QuoteView reveal animation duration to 150ms. 2020-05-13 16:17:29 -04:00
Alan Evans
17c5b858b5 Recipient bottom sheet. 2020-05-13 16:17:29 -04:00
Greyson Parrelli
f6f6496c9c Bump version to 4.59.10 2020-05-13 15:40:44 -04:00
Greyson Parrelli
b1d725e23a Updated language translations. 2020-05-13 15:40:44 -04:00
Greyson Parrelli
a74622997e Bump libsignal-metadata to 0.1.2
Includes fix for how we're prioritizing UUID vs E164.

Fixes #9621
2020-05-13 15:40:42 -04:00
Greyson Parrelli
b1a200001e Bump version to 4.59.9 2020-05-09 13:19:57 -04:00
Greyson Parrelli
3b1041fa1f Updated language translations. 2020-05-09 13:19:31 -04:00
Greyson Parrelli
a83ccc18bb Fix processing of early messages.
1. Eliminated any possibility of infinite recursion.
2. Handle the fact that you can have multiple 'early contents' for a
   single message.
2020-05-09 13:16:45 -04:00
Greyson Parrelli
618b1b5ace Handle PIN creation failure better. 2020-05-09 13:16:45 -04:00
Greyson Parrelli
14858adc88 Bump version to 4.59.8 2020-05-04 18:22:45 -04:00
Greyson Parrelli
c07f35f3aa Updated language translations. 2020-05-04 18:21:50 -04:00
Alan Evans
87eab27996 Prevent the creation of 'weak' PINs.
Simple checks to prevent the same number, or sequentially
increasing/decreasing PINs. e.g. 1111, 1234, 54321, etc.
2020-05-04 18:17:36 -04:00
Greyson Parrelli
b7296a4fe3 Bump version to 4.59.7 2020-05-04 11:06:28 -04:00
Greyson Parrelli
3fb9ae1fb4 Updated language translations. 2020-05-04 11:05:55 -04:00
Greyson Parrelli
9705939489 Fix issue with editing and forwarding a received video. 2020-05-04 10:54:55 -04:00
Greyson Parrelli
eca67b1204 Broaden exception handling in custom DNS.
A set of LG devices is crashing when using the custom DNS. Safest thing
for now would be to treat all failures as network errors while we we try
to get a repro to figure out what's happening.
2020-05-04 10:54:55 -04:00
Greyson Parrelli
c59fc3581a Make LiveRecipientCache throw exceptions instead of errors.
Errors were causing crash loops if they occur in a job. This will still
allow the app to crash, but prevent loops.
2020-05-04 00:48:09 -04:00
Greyson Parrelli
e00f8c94ff Bumped version to 4.59.6 2020-04-30 17:03:28 -04:00
Greyson Parrelli
4186153f0c Updated language translations. 2020-04-30 17:03:28 -04:00
Greyson Parrelli
6c01807f4f Fix issue with PIN verification. 2020-04-30 17:03:28 -04:00
Greyson Parrelli
9d35fb397b Fix issue with re-using forwarded attachment pointers.
We were deleting upload data for incoming attachments when we shouldn't
have.

Fixes #9570
2020-04-30 16:36:06 -04:00
Jim Gustafson
c9f2f57427 Update to ringrtc v1.3.2 2020-04-30 08:12:31 -07:00
Greyson Parrelli
c862ab0c56 Bump version to 4.59.5 2020-04-28 10:41:37 -04:00
Greyson Parrelli
7aaaa57c14 Updated language translations. 2020-04-28 10:41:06 -04:00
Greyson Parrelli
11b6394a87 Fix issue with group storage IDs. 2020-04-28 10:38:58 -04:00
Greyson Parrelli
bdd48fd2df Show PIN reminder for non-reglock users. 2020-04-28 10:38:49 -04:00
Alan Evans
e99af75400 Fix crash when blocking group. 2020-04-27 16:52:17 -03:00
Greyson Parrelli
321440e13f Bump version to 4.59.4 2020-04-27 13:07:41 -04:00
Greyson Parrelli
0556d984e0 Updated language translations. 2020-04-27 13:07:19 -04:00
Greyson Parrelli
0ba1f66136 Use the same Recipient.self() instance in storage sync. 2020-04-27 13:05:22 -04:00
Greyson Parrelli
7562555687 Add additional storage sync validations. 2020-04-27 12:38:07 -04:00
Greyson Parrelli
668ccfcd12 Clean up logging. 2020-04-27 11:29:52 -04:00
Greyson Parrelli
9c0337c4ef Fix threading issue with message resends. 2020-04-27 11:11:24 -04:00
Greyson Parrelli
3fde06ab0f Bump version to 4.59.3 2020-04-24 19:48:20 -04:00
Greyson Parrelli
73959f328a Updated language translations. 2020-04-24 19:47:57 -04:00
Greyson Parrelli
cca85bfee3 Fix some PinState bugs. 2020-04-24 19:40:50 -04:00
Greyson Parrelli
575caa53d3 Fix some storage service consistency issues. 2020-04-24 19:14:08 -04:00
Greyson Parrelli
33874a8866 Fix attachment upload bug. 2020-04-24 09:24:43 -04:00
Greyson Parrelli
b8e909a134 Revert to preferring system photos over profile photos. 2020-04-24 08:45:58 -04:00
Greyson Parrelli
5193a5d309 Prevent some crash loops. 2020-04-23 22:25:56 -04:00
Greyson Parrelli
7db288b9aa Make PINs work with password managers. 2020-04-23 22:25:56 -04:00
Greyson Parrelli
9f033e64aa Fix lint error. 2020-04-23 22:25:56 -04:00
Greyson Parrelli
5a15ba97dc Bump version to 4.59.2 2020-04-23 13:33:59 -04:00
Greyson Parrelli
ce6ec72683 Updated language translations. 2020-04-23 13:33:59 -04:00
Greyson Parrelli
eedbcdd564 Fix issue with group storage sync. 2020-04-23 13:33:59 -04:00
Greyson Parrelli
0ca2848e01 Improve logging for storage service. 2020-04-23 12:03:31 -04:00
Greyson Parrelli
208275b6a9 Improve logging for PinState. 2020-04-23 11:24:23 -04:00
Greyson Parrelli
4bdcaa72cd Fix some more UX issues with blocked users. 2020-04-23 11:06:52 -04:00
Greyson Parrelli
8c6001fa5a Improve correctness and performance of camera contact search. 2020-04-23 10:25:45 -04:00
Greyson Parrelli
c4e88abce1 Update PIN change strings. 2020-04-22 19:42:17 -04:00
Greyson Parrelli
eea7174f1d Check to see if FCM is available at app launch. 2020-04-22 19:32:40 -04:00
Greyson Parrelli
3f7d0688fc Bump version to 4.59.1 2020-04-22 14:18:28 -04:00
Greyson Parrelli
6d319618c6 Updated language translations. 2020-04-22 14:18:28 -04:00
Greyson Parrelli
4250fa707b Fix crash when videos are missing a duration.
Fixes #9556
2020-04-22 14:18:28 -04:00
Greyson Parrelli
7734cd2c8f Clean up some corner cases in storage syncing. 2020-04-22 14:18:28 -04:00
Alan Evans
57467bb338 Dismiss group participant list on contact click. 2020-04-22 14:18:28 -04:00
Alex Hart
8ad61a52b9 Fix call termination when muting before call is connected. 2020-04-22 14:18:28 -04:00
Alan Evans
9742a212a2 Fix Transifex string name clash. 2020-04-22 14:18:28 -04:00
Greyson Parrelli
fd21fc1a31 Fix some UX issues with blocked users. 2020-04-22 14:18:28 -04:00
Greyson Parrelli
1b5a0ab9f3 Sync the profile photo to linked devices when appropriate. 2020-04-22 10:34:43 -04:00
Greyson Parrelli
f466fef20a Fix issue where contact photos weren't being shown at all. 2020-04-22 10:13:56 -04:00
Greyson Parrelli
9bc70adbbd Update PIN setting strings. 2020-04-21 19:23:47 -04:00
Greyson Parrelli
6f39f9849a Bump version to 4.59.0 2020-04-21 16:09:30 -04:00
Greyson Parrelli
5bc950ed28 Updated language translations. 2020-04-21 16:09:30 -04:00
Alan Evans
b80d460a8f Account for deleted conversations in profile key send job. 2020-04-21 16:09:30 -04:00
Alan Evans
3f555ce5e2 Extract method for creating safety number intents. 2020-04-21 16:09:30 -04:00
Jim Gustafson
9513b476ef Update to ringrtc v1.3.1 2020-04-21 16:09:30 -04:00
Greyson Parrelli
8f9e79ae37 Updated PIN strings. 2020-04-21 16:09:30 -04:00
Alan Evans
53b681ef67 Make reaction and remote delete jobs GV2 ready. 2020-04-21 16:09:30 -04:00
Alan Evans
9a8094cb8a Guard against malformed group ids. 2020-04-21 16:09:30 -04:00
Alex Hart
00ee6d0bbd Dialog theme rename. 2020-04-21 16:09:30 -04:00
Greyson Parrelli
83f6640bd3 Add a more generic system for handling early messages. 2020-04-21 16:09:30 -04:00
Alex Hart
2afb939ee6 Implement send support for resumable uploads behind a flag. 2020-04-21 16:09:30 -04:00
Greyson Parrelli
7c442865c5 Interpret non-present message bodies as 'null'. 2020-04-21 16:09:30 -04:00
Greyson Parrelli
b3d57edb24 Update and centralize block strings. 2020-04-21 16:09:30 -04:00
Alex Hart
6d6e017c71 Proactively share profile key after accepting a message request. 2020-04-21 16:09:30 -04:00
Greyson Parrelli
fc6b5c1d7c Add ultramarine as a conversation color option. 2020-04-21 16:09:30 -04:00
Greyson Parrelli
6ecd3b59fd Add pre-alpha receive support for remote delete. 2020-04-21 16:09:13 -04:00
Ehren Kret
456bcf3d57 Require CDN number match rather than use default CDN
This marks messages as failed if the CDN number does not match a
configured CDN number rather than falling back to the default CDN in
the event the CDN is not recognized.
2020-04-21 13:33:41 -04:00
Greyson Parrelli
f12a9b9ac7 Store the server timestamp for a message. 2020-04-21 13:33:41 -04:00
Greyson Parrelli
00b6a222bd Remove jumpiness when rendering reactions. 2020-04-21 13:33:41 -04:00
Greyson Parrelli
b8ccc4453e Update pins4all flag.
We still have to keep the legacy one though so that people in the old
bucket stay in the new one.
2020-04-21 13:33:41 -04:00
Alan Evans
dbb31420af Ensure all support article urls are the correct format and not translatable. 2020-04-21 13:33:41 -04:00
Greyson Parrelli
35f4f3f81e Add support for passing data between jobs. 2020-04-21 13:33:41 -04:00
Greyson Parrelli
acbfff89d3 Update registration to allow PIN entry. 2020-04-21 13:33:41 -04:00
Greyson Parrelli
6b37675a81 Remove long-press action in settings. 2020-04-21 13:33:41 -04:00
Greyson Parrelli
a471ffa6d8 Fix UD indicators for sent transcripts. 2020-04-21 13:33:41 -04:00
Alan Evans
7bf090fdab GroupsV2 state mapping. 2020-04-21 13:33:41 -04:00
Alan Evans
4e0279200f Refactor out MediaPreviewActivity Intent creation method. 2020-04-21 13:33:41 -04:00
Alan Evans
78055e3ccb GroupsV2 update sending and local context storage. 2020-04-21 13:33:41 -04:00
Alan Evans
f5e6fd6340 Allow RetrieveProfileJob to be used for self. 2020-04-21 13:33:33 -04:00
Alex Hart
2d60d5fb1f Check menu item visibility when calculating menu size. 2020-04-21 13:33:33 -04:00
Alan Evans
c6dd25a119 Ensure group membership for typing indicators. 2020-04-21 13:33:33 -04:00
Alan Evans
68d29d9a0f Allow pending member invite cancelation. 2020-04-21 13:33:33 -04:00
Alan Evans
1d63970a25 Hardcode all class names in old work manager migration. 2020-04-21 13:33:33 -04:00
Alan Evans
2b1ffac564 Groups V2 avatar download job. 2020-04-21 13:33:33 -04:00
Alan Evans
e2d3a43593 Use readBodyBytes for correct exceptions. 2020-04-21 13:33:33 -04:00
Greyson Parrelli
8e13403cca Separate PINs from Registration Lock.
You can now have a PIN without having registration lock.

Note: We still need to change the registration flow to allow non-reglock
users to enter their PIN.
2020-04-21 13:33:33 -04:00
Ehren Kret
3c6a7b76ca Send increased protocol version number if CDN key or attachment are used. 2020-04-21 13:33:33 -04:00
Alan Evans
428128651e Move database protos to separate files. 2020-04-21 13:33:33 -04:00
Alan Evans
326678f214 Add support for GV2 group update messages. 2020-04-21 13:33:29 -04:00
Alex Hart
1f994495f8 Clear search if user sends message. 2020-04-21 13:33:29 -04:00
Greyson Parrelli
fb1637006d Include screen size details in debuglogs. 2020-04-21 13:33:29 -04:00
Ehren Kret
37a35e8f70 Add initial support for send/receive on CDN2. 2020-04-21 13:33:29 -04:00
Alan Evans
1290d0ead9 Add pending member activity. 2020-04-21 13:33:25 -04:00
Greyson Parrelli
ef0f26b64c Remove borders from images in the gallery picker. 2020-04-03 12:20:15 -04:00
Greyson Parrelli
485d211768 Remove border from images in the attachment keyboard. 2020-04-03 12:20:15 -04:00
Greyson Parrelli
f1ea035197 Re-enable and clean up Signal PINs.
- Require PINs during registration agian.
- Change min length to 4.
- Allow the full-screen megaphone to be enabled remotely.
- Clean up and remove some code.
2020-04-03 12:20:15 -04:00
Martijn van den Hoek
6f961ade74 Fix crash when importing vcf after exporting it.
Fixes #9465
2020-04-03 12:20:15 -04:00
Alan Evans
b8e17e0116 Enable video trimming feature by default. 2020-04-03 12:20:15 -04:00
Alan Evans
040e1fe8f6 Apply dark theme to scroll to bottom button. 2020-04-03 12:20:15 -04:00
Alan Evans
e9c92bdf51 Show unblock dialog when tap blocked contact. 2020-04-03 12:20:15 -04:00
Alan Evans
48c33f3dcd GroupsV2 service changes. 2020-04-03 12:20:15 -04:00
Alex Hart
6b2bc924dd Prefer profile photo over system contact photo. 2020-04-03 12:20:15 -04:00
Ehren Kret
a65c4f90f4 Avoid potential race condition in attachment uploads. 2020-04-03 12:19:13 -04:00
Alan Evans
04bb4b351a Refactor group leave dialog out of conversation. 2020-04-03 12:19:13 -04:00
Alan Evans
e02e4d52b4 Prevent empty message processing. 2020-04-03 12:19:13 -04:00
Alex Hart
6f3c4434f6 Add animation when replying to a message. 2020-04-03 12:19:13 -04:00
Greyson Parrelli
711715ca1e Add DNS fallback system. 2020-04-03 12:19:12 -04:00
Greyson Parrelli
d6000af843 Re-use recently-acquired attachment pointers. 2020-04-03 12:19:12 -04:00
Greyson Parrelli
9b0954a898 Bump version to 4.58.5 2020-04-03 11:18:39 -04:00
Greyson Parrelli
42a2c33fd7 Updated language translations. 2020-04-03 11:18:13 -04:00
Greyson Parrelli
a4d18a18d9 Don't use vector assets for notification icon. 2020-04-03 11:14:36 -04:00
Alex Hart
bf32409d4e Split drawable into light and dark. 2020-04-03 12:14:09 -03:00
Greyson Parrelli
e38aec225f Bump version to 4.58.4 2020-04-01 14:48:47 -04:00
Greyson Parrelli
995b7a4712 Updated language translations. 2020-04-01 14:48:25 -04:00
Alex Hart
9fe3026941 Fix AdaptiveActionsToolbar sizing algorithm. 2020-04-01 11:26:19 -03:00
Greyson Parrelli
520658e1b8 Bump version to 4.58.3 2020-03-31 17:10:00 -04:00
Greyson Parrelli
f822d8eddb Updated language translations. 2020-03-31 16:59:36 -04:00
Alex Hart
2f879ce4d6 Remove MMS groups from message request logic. 2020-03-31 16:00:26 -03:00
Greyson Parrelli
24528bf101 Fix accent color in alert dialogs in dark theme. 2020-03-31 14:59:00 -04:00
Greyson Parrelli
822682caba Fix NPE in BitmapUtil.toJpeg()
Fixes #9513
2020-03-31 11:44:18 -04:00
Greyson Parrelli
5dc3cc65a8 Bump version to 4.58.2 2020-03-30 17:53:08 -04:00
Greyson Parrelli
0f80caffb5 Updated language translations. 2020-03-30 17:53:08 -04:00
Greyson Parrelli
6c428b2777 Fix issue with some notifications linking to the wrong conversation. 2020-03-30 17:53:08 -04:00
Greyson Parrelli
c9be37b84a Fix camera rotation issues.
Had to manually detect when CameraX is giving us bad data.

Fixes #9509
2020-03-30 16:04:28 -04:00
Jim Gustafson
87ea2f86c0 Update ringrtc to v1.2.0 2020-03-30 13:01:14 -07:00
Greyson Parrelli
7e80be5ca0 Separate out model info in debug logs. 2020-03-30 15:24:29 -04:00
Greyson Parrelli
989a818a67 Fix issue where reaction notifications may jump to the wrong message. 2020-03-30 12:49:08 -04:00
Greyson Parrelli
af2e17df9e Ensure old typing observers are unsubscribed. 2020-03-30 11:33:21 -04:00
Greyson Parrelli
728ec1c16d Fix issue where leave messages were pending forever. 2020-03-30 10:11:15 -04:00
Greyson Parrelli
f859c5b1b5 Prevent conscrypt crash during profile retrieval.
This was a mitigation that was previously in place that was forgotten
during the recent avatar refactor.
2020-03-29 18:53:30 -04:00
Greyson Parrelli
ab600d7df1 Bump version to 4.58.1 2020-03-27 16:51:21 -04:00
Greyson Parrelli
4644f64fd6 Updated language translations. 2020-03-27 16:51:21 -04:00
Greyson Parrelli
c274312265 Fix scrolling in the emoji variation popup. 2020-03-27 16:51:21 -04:00
Greyson Parrelli
f8e63098a2 Don't show empty date popovers. 2020-03-27 16:40:55 -04:00
Greyson Parrelli
264d353ec2 Bump quality of camera1 photos. 2020-03-27 16:30:36 -04:00
Alex Hart
2b58dcbe7f Remove explicit CameraX initialization. 2020-03-27 16:30:36 -04:00
Greyson Parrelli
dc791487c5 Jump to the relevant message when tapping a reaction notification.
Fixes #9503
2020-03-27 16:30:36 -04:00
Greyson Parrelli
5637f132d4 Fixed issue where leave message wasn't displayed locally. 2020-03-27 16:30:36 -04:00
Alan Evans
9e6cca1cd0 GV2 database. 2020-03-27 16:30:36 -04:00
Alan Evans
640c82d517 GV2 group context proto. 2020-03-27 16:30:36 -04:00
Greyson Parrelli
20d1a93b09 Don't refresh own profile if not registered. 2020-03-27 16:30:36 -04:00
Greyson Parrelli
f5d1b11bda Fix some dark theme text contrast issues. 2020-03-27 16:30:36 -04:00
Alan Evans
66c7f8bcb2 GroupId for GV2. 2020-03-27 11:28:48 -03:00
Alex Hart
d8fa46c558 Copy action should display if message body is not empty.
Fixes #9491
2020-03-27 11:18:02 -03:00
Greyson Parrelli
10bfc8a753 Migrate avatars and group avatars. 2020-03-26 22:38:33 -04:00
Greyson Parrelli
9848599807 Bump version to 4.58.0 2020-03-26 17:49:10 -04:00
Greyson Parrelli
2e38ebcfbb Updated language translations. 2020-03-26 17:46:51 -04:00
Greyson Parrelli
f875623cd0 Resize avatars to 1024x1024. 2020-03-26 17:37:52 -04:00
Greyson Parrelli
e6f9cb9929 Remove TextSecurePreferences.getAvatarId() 2020-03-26 17:37:52 -04:00
Greyson Parrelli
6aac3baa55 Remove TextSecurePreferences.getProfileName() 2020-03-26 17:37:52 -04:00
Alan Evans
a860315587 GroupId class. 2020-03-26 17:37:52 -04:00
Greyson Parrelli
a73a73e42c Fix AudioView tinting on Android 10. 2020-03-26 17:37:52 -04:00
Greyson Parrelli
a3358e5b21 Rotate profile key after blocking if shared via group. 2020-03-26 17:37:52 -04:00
Alex Hart
7e9e2fead2 Fix NPE after call failure. 2020-03-26 17:37:52 -04:00
Alan Evans
0269a3eb6f Groups V2 protobufs and local conflict resolution. 2020-03-26 17:37:51 -04:00
Alex Hart
f449a45912 Utilize normal fallback for homescreen icons. 2020-03-26 17:37:51 -04:00
Alan Evans
f69d4ccd22 Increase Lib Signal Service compatibility to Java 8. 2020-03-26 17:37:51 -04:00
Alan Evans
0e2df2adbb Image Editor: Keep text on top.
Sorts children by a new z-order.
2020-03-26 17:37:51 -04:00
Alex Hart
d46894e5db Upgrade CameraX to Beta01. 2020-03-26 17:37:51 -04:00
Greyson Parrelli
951a61117a Add storage support for the AccountRecord. 2020-03-26 17:37:51 -04:00
Greyson Parrelli
7a038ab09d Add interim storage support for GroupV2Record. 2020-03-26 17:37:51 -04:00
Alex Hart
707a2aca0a Swap profile megaphone icon and use user avatar if present. 2020-03-26 17:37:51 -04:00
Alan Evans
624837fcf1 Include zkgroup 0.4.1
All behind feature flag, excluding .so files for space.
2020-03-26 17:37:51 -04:00
Greyson Parrelli
e3ea36c76f Remove unnecessary okhttp close when canceling.
Canceling should handle closing stuff now. And if we close from a
different thread than the calling thread, okhttp will crash.
2020-03-26 17:37:51 -04:00
Alan Evans
453996c374 Restrict CI branches. 2020-03-26 17:37:51 -04:00
Greyson Parrelli
8add9ba0a6 Removed ExperienceUpgradeActivity.
Pour one out.
2020-03-26 17:37:51 -04:00
Alan Evans
da11b56eab Check for and clear quote on new intent.
Fixes #9478
2020-03-26 17:37:51 -04:00
Greyson Parrelli
19377c2132 Remote maxInstance restriction on RetrieveProfileAvatarJob. 2020-03-26 17:37:51 -04:00
Greyson Parrelli
b2bff39fe1 Don't send group info requests in response to group info requests. 2020-03-26 17:37:51 -04:00
Greyson Parrelli
5f7075d39a Update and refactor storage service syncing.
Switched to proto3, updated protos, and generally refactored things to
make it easier to add new storage record types.
2020-03-26 17:37:51 -04:00
Alex Hart
40d9d663ec Disable auto-mirror for help icon 2020-03-26 17:37:51 -04:00
Greyson Parrelli
31f9b77c32 Ignore empty names when populating contact list. 2020-03-26 17:37:51 -04:00
Greyson Parrelli
690a66a093 Show any user with a displayable name in the contact list. 2020-03-26 17:37:51 -04:00
Ehren Kret
e7e7d36774 Configure Android Studio to pickup protobuf generated sources 2020-03-26 17:37:51 -04:00
Greyson Parrelli
f95a37956c Improve emoji sticker suggestions.
There was a bug around some emoji being marked as 'obsolete' and
therefore not being found.

I also made a change so that you can use skin variations of emoji and
still find emoji tagged with the default yellow version of it.

Fixes #9471
2020-03-26 17:37:51 -04:00
Greyson Parrelli
1e2a27f902 Close dangling groups cursor. 2020-03-26 17:37:51 -04:00
Greyson Parrelli
d90e3dc210 Fix crash when syncing empty usernames. 2020-03-26 17:37:51 -04:00
Greyson Parrelli
5df4b56c0d Update okhttp to 3.12.10 2020-03-26 17:37:51 -04:00
Jim Gustafson
436da1cb32 Update ringrtc to v1.1.0 2020-03-26 17:37:51 -04:00
Greyson Parrelli
4d0dbbc6cd Add ability to listen to jobs based on a filter. 2020-03-26 17:37:51 -04:00
Alan Evans
033bf77cbb Allow future display of pending member count. 2020-03-26 17:37:51 -04:00
Greyson Parrelli
1068c3ca7e Fix UnknownSenderView in dark theme. 2020-03-26 17:37:51 -04:00
Greyson Parrelli
df4422369d Update icons and colors. 2020-03-26 17:37:51 -04:00
Greyson Parrelli
a62183c9e0 Reduce AttachmentCipherTest flakiness. 2020-03-19 14:50:06 -04:00
Greyson Parrelli
de48cf8243 Bump version to 4.57.2 2020-03-19 14:49:52 -04:00
Greyson Parrelli
acd4fc4518 Fix issue where minimum PIN length was miscalculated.
Fixes #9484
2020-03-19 14:49:10 -04:00
Greyson Parrelli
da59ed019f Bump version to 4.57.1 2020-03-07 16:43:35 -05:00
Greyson Parrelli
e73b174d1d Updated language translations. 2020-03-07 16:43:08 -05:00
Alan Evans
2753a22e3a Remove old activity from manifest. 2020-03-07 16:40:17 -05:00
Alex Hart
79fc33630b Add toast instead of crash if no email app installed. 2020-03-07 16:40:17 -05:00
Greyson Parrelli
bf5331ba6e Bump version to 4.57.0 2020-03-05 18:51:45 -05:00
Greyson Parrelli
3be47d3e54 Updated language translations. 2020-03-05 18:51:21 -05:00
Alex Hart
f9de131017 Add new contact us flow. 2020-03-05 18:42:17 -05:00
Alan Evans
f1f505d41c Try getKeyStoreEntry twice on UnrecoverableKeyException.
To try to get around potentially temporary UnrecoverableKeyExceptions.
2020-03-05 18:42:17 -05:00
Alan Evans
51603be5ec Add video trimming time indication pill. 2020-03-05 18:42:17 -05:00
Alex Hart
2152b4a2cd Add warning dialog for insecure calls. 2020-03-05 18:42:17 -05:00
Alan Evans
a70023a32b Use group manager to leave group. 2020-03-05 18:42:17 -05:00
Alan Evans
5038210d78 Add tap to pause to video trimming editor. 2020-03-05 18:42:17 -05:00
Alan Evans
28bbfd88b2 Group member dialog update. 2020-03-05 18:42:17 -05:00
Greyson Parrelli
d05a71c8fe Update Glide to 4.11.0 2020-03-03 08:52:11 -05:00
Greyson Parrelli
245b0a7e50 Add a new buildType with Flipper. 2020-03-02 16:25:05 -05:00
Alan Evans
ceb9e4aee2 Write capabilities to service. 2020-03-02 12:01:50 -04:00
Alan Evans
d2e94dad7e Remove argon2 test job. 2020-03-02 11:22:37 -04:00
Alex Hart
240b2108f3 Use the image editor for avatars. 2020-03-02 11:21:57 -04:00
Greyson Parrelli
f68d99d16d Clean up dangling transfer files.
Fixes #9033
2020-03-02 10:11:40 -05:00
Greyson Parrelli
44e845c875 Update emoji. 2020-03-02 10:11:40 -05:00
Alan Evans
d8e2368a18 Convert UUID supported into a Capability enum. 2020-03-02 10:11:40 -05:00
Alan Evans
172a43679d Add GV2 recipient capability. 2020-03-02 10:11:40 -05:00
Greyson Parrelli
82305ce2b3 Bump version to 4.56.4 2020-03-02 09:55:04 -05:00
Greyson Parrelli
eaf73edcad Updated language translations. 2020-03-02 09:54:35 -05:00
Greyson Parrelli
543a4ee177 Fix backup restore crash. 2020-03-02 09:48:18 -05:00
Greyson Parrelli
fd2a464bae Use internal contact viewer for avatar clicks. 2020-03-02 08:33:25 -05:00
Alex Hart
b06152ba58 Fix and simplify Y translation calculation for Conversation. 2020-03-02 08:57:11 -04:00
Greyson Parrelli
c24d285cd3 Bump version to 4.56.3 2020-02-28 17:35:13 -05:00
Greyson Parrelli
be39cd653e Updated language translations. 2020-02-28 17:35:13 -05:00
Greyson Parrelli
6813f47bc1 Remove feature flags that are no longer remote capable. 2020-02-28 17:35:10 -05:00
Greyson Parrelli
8e795c4177 Read the sticker length during backup import. 2020-02-28 16:58:47 -05:00
Greyson Parrelli
9c96afee09 Fix storage service crash with new users. 2020-02-28 16:58:47 -05:00
Greyson Parrelli
d507be0ab0 Don't backup the KeyValueDatabase. 2020-02-28 16:58:47 -05:00
Greyson Parrelli
75a52f801a Implement storage service protocol changes. 2020-02-28 16:58:47 -05:00
Greyson Parrelli
d3b123f3a9 Fix StorageSyncHelperTest. 2020-02-28 08:07:36 -05:00
Greyson Parrelli
da3cdd984b Bump version to 4.56.2 2020-02-26 17:57:36 -05:00
Greyson Parrelli
6184e5f828 Update the storage service. 2020-02-26 17:11:34 -05:00
Curt Brune
133bd44b85 Update ringrtc to v1.0.2 2020-02-26 17:11:34 -05:00
Greyson Parrelli
0c254c9621 Improve debuglog submission. 2020-02-26 17:11:34 -05:00
Greyson Parrelli
1faf196f82 Implement additional message request improvements. 2020-02-26 17:11:29 -05:00
Greyson Parrelli
81c7887d47 Switch language string to 'System default.' 2020-02-26 17:08:27 -05:00
Greyson Parrelli
e62e630987 Fix theming issue in CameraContactSelectionFragment. 2020-02-26 17:08:27 -05:00
Alex Hart
739e38a047 Fix reaction details bottom sheet scrolling. 2020-02-26 17:08:27 -05:00
Greyson Parrelli
8c23b17517 Fix some state issues post backup restore. 2020-02-26 17:08:27 -05:00
Greyson Parrelli
fda8f3e1ce Refer to yourself as 'you' in reactions and group membership. 2020-02-26 17:08:27 -05:00
Alex Hart
9e5f64c431 Improve message requests, add megaphone. 2020-02-26 17:08:27 -05:00
Alex Hart
dc689d325b Various PIN bug fixes. 2020-02-26 17:06:21 -05:00
Greyson Parrelli
0a883dc234 Fix issue with mimeType resolution in share flow. 2020-02-26 17:06:06 -05:00
Greyson Parrelli
3824e90997 Improve prekey refresh logic. 2020-02-26 17:06:06 -05:00
Greyson Parrelli
5158a15379 Disable PIN requirement for new registrations. 2020-02-26 17:06:06 -05:00
Curt Brune
1bae79af5b Check callManager reference is still valid in ListenableFutureTask callbacks. 2020-02-26 17:06:04 -05:00
Curt Brune
58b7612987 Drop requests to deny stale incoming calls.
This is not an illegal state, as the remote side could have hung-up
a microsecond before the local side tries to deny the call.
2020-02-26 17:06:02 -05:00
Curt Brune
9506da6dd3 Validate activePeer during Bluetooth and Speaker audio state transitions. 2020-02-26 17:05:54 -05:00
Greyson Parrelli
4ea886d05a Bump version to 4.56.1 2020-02-14 12:20:40 -05:00
Greyson Parrelli
7a52fccfd1 Updated language translations. 2020-02-14 12:20:40 -05:00
Greyson Parrelli
f79d308a9f Update profile name megaphone. 2020-02-14 12:20:40 -05:00
Curt Brune
5aa64641d2 Convert IllegalStateException to warning log in receivedBusy()
This is not a fatal condition.  Convert to warning log message in
order to gather more information.
2020-02-14 08:13:11 -08:00
Curt Brune
ae594a0400 Check activeRemote is non-null during handleScreenOffChange() 2020-02-14 08:13:11 -08:00
Curt Brune
eeece55b45 Check activeRemote is non-null during handleWiredHeadsetChange() 2020-02-14 08:13:11 -08:00
Greyson Parrelli
9fbc50d26f Fix crash when cleared ShareViewModel. 2020-02-14 09:57:26 -05:00
Greyson Parrelli
16ebf0556a Bump version to 4.56.0 2020-02-13 20:53:10 -05:00
Greyson Parrelli
bb104b5763 Updated language translations. 2020-02-13 20:53:10 -05:00
Greyson Parrelli
9bac88697b Support sharing multiple photos/videos into Signal. 2020-02-13 20:53:10 -05:00
Alan Evans
7ab240643e Remove build instructions. 2020-02-13 20:53:10 -05:00
Greyson Parrelli
70d5b798b2 Add some developer utils. 2020-02-13 20:53:10 -05:00
Greyson Parrelli
4e7a92637c Follow system theme on Android 10+. 2020-02-13 20:53:10 -05:00
Greyson Parrelli
2e19d0459b Fix album ordering issue.
Fixes #9381
2020-02-13 20:53:10 -05:00
Curt Brune
0970fd7040 Update ringrtc to v1.0.1
Add support for RingRTC Call Manager, a new component which provides
the control layer for all calling features.
2020-02-13 20:53:10 -05:00
Greyson Parrelli
81532cad95 Use a min length of 6 for new PIN reminders. 2020-02-13 20:53:10 -05:00
Greyson Parrelli
dcb5f7b211 Update copy for PIN setting. 2020-02-13 20:53:10 -05:00
Alan Evans
40fd7ca332 Video trimming behind feature flag. 2020-02-13 20:53:10 -05:00
Alan Evans
7f867a6185 Remove KBS restore after set check. 2020-02-13 20:53:10 -05:00
Alan Evans
23e55ac5f7 Clear unidentified access mode when profile key changes. 2020-02-13 20:53:10 -05:00
Jonathan Leitschuh
455974cb05 Add official Gradle Wrapper validation action.
See: https://github.com/gradle/wrapper-validation-action
2020-02-13 20:53:10 -05:00
Greyson Parrelli
66a668f55b Add some Swoon blessed packs. 2020-02-13 20:53:10 -05:00
Alan Evans
7ecb50a3fe Versioned Profiles support (disabled). 2020-02-13 20:53:10 -05:00
Alan Evans
f10d1eac61 Migrate profile key into database. 2020-02-13 20:53:10 -05:00
Alan Evans
b92c389a5b Fix media rail thumbnail for in-app recorded video. 2020-02-13 20:53:10 -05:00
Alex Hart
9dfc57c462 Fix RTL icon issue in toolbar. 2020-02-13 20:53:10 -05:00
Alex Hart
3ea1492d67 Add profile names megaphone. 2020-02-13 20:53:07 -05:00
Greyson Parrelli
c041614d1f Only store remote values for flags in a whitelist. 2020-02-11 12:03:34 -05:00
Greyson Parrelli
d9c5907ea9 Bump version to 4.55.8 2020-02-11 12:03:12 -05:00
Greyson Parrelli
0b6a52277d Fixed PIN reminders showing too often, reminder UI improvements. 2020-02-11 12:03:12 -05:00
Greyson Parrelli
944adb5d7c Bump version to 4.55.7 2020-02-10 20:01:47 -05:00
Greyson Parrelli
ac4129c1e1 Updated language translations. 2020-02-10 20:01:28 -05:00
Greyson Parrelli
0220a88ea5 Mark registration complete after PIN failure. 2020-02-10 19:56:41 -05:00
Greyson Parrelli
6d33763ec9 Bump version to 4.55.6 2020-02-08 10:58:43 -05:00
Greyson Parrelli
3cedadbb97 Fix video duration rendering issue. 2020-02-08 10:58:12 -05:00
Greyson Parrelli
5a6d339a89 Bump version to 4.55.5 2020-02-08 10:26:01 -05:00
Greyson Parrelli
905d8a4f33 Updated language translations. 2020-02-08 10:26:01 -05:00
Greyson Parrelli
917d312ea0 Reduced padding above megaphone button. 2020-02-08 10:26:01 -05:00
Greyson Parrelli
ddc01b539f Use megaphones for PIN reminders. 2020-02-07 21:14:20 -05:00
Greyson Parrelli
38e4733433 Improve network reliability when setting PINs. 2020-02-07 01:06:41 -05:00
Greyson Parrelli
cbd7160e23 Show profile creation before PIN creation. 2020-02-07 00:47:52 -05:00
Greyson Parrelli
f2b3acb0c9 Update string for creating PIN. 2020-02-07 00:15:42 -05:00
Greyson Parrelli
49c7b5c442 Bump version to 4.55.4 2020-02-06 11:19:46 -05:00
Greyson Parrelli
7b7d180207 Updated language translations. 2020-02-06 11:19:46 -05:00
Alex Hart
21cf390d0e Clip reactions mask if the view falls below the input panel. 2020-02-06 11:19:46 -05:00
Greyson Parrelli
5e59f77f83 Update PIN feature flags. 2020-02-06 11:19:46 -05:00
Greyson Parrelli
62814490b3 Only store remote values for flags in a whitelist. 2020-02-06 11:19:46 -05:00
Alan Evans
e5fedb8163 Add pull request trigger. 2020-02-06 07:52:32 -05:00
Greyson Parrelli
cfc74c1080 Bump version to 4.55.3 2020-02-05 20:50:25 -05:00
Greyson Parrelli
eaf53ad3b9 Updated language translations. 2020-02-05 20:49:59 -05:00
Greyson Parrelli
a1f0c198a7 Auto-show keyboard for PIN entry. 2020-02-05 18:49:31 -05:00
Greyson Parrelli
6b1e48e485 Don't show the full-screen PIN megaphone. 2020-02-05 18:49:31 -05:00
Greyson Parrelli
83ea919434 Remove "(You)" from the reactions details.
This reverts commit ed2b049ad4.
2020-02-05 11:36:57 -05:00
Greyson Parrelli
c94e93b916 Fix issue with reactions pill not appearing. 2020-02-05 11:32:20 -05:00
Alex Hart
49d418bb39 Fix RTL support for reaction sending. 2020-02-05 11:20:36 -05:00
Greyson Parrelli
23adda1817 Update reaction pill padding. 2020-02-05 10:40:08 -05:00
Alex Hart
7c729c2c4e Add AdaptiveActionsToolbar for better context bar controls. 2020-02-05 08:51:05 -04:00
Greyson Parrelli
ecf7a416eb Bump version to 4.55.2 2020-02-04 20:10:36 -05:00
Greyson Parrelli
2f87f2bb62 Updated language translations. 2020-02-04 20:10:00 -05:00
Greyson Parrelli
39f4102e81 Fix issue where quoted replies showed a 'save' option. 2020-02-04 19:21:01 -05:00
Greyson Parrelli
4fcd6b15ed Improved feel of reaction popover. 2020-02-04 19:21:01 -05:00
Greyson Parrelli
8f9ed4bc40 Fix the shape of reaction pills. 2020-02-04 19:21:01 -05:00
Alex Hart
73dedd79d2 Fix crash in reactions overlay OnTouch. 2020-02-04 19:21:01 -05:00
Greyson Parrelli
2754b397d5 Darken the background more when selecting a message. 2020-02-04 19:21:01 -05:00
Greyson Parrelli
7d949ee8fd Prevent crash in megaphones after backup restore. 2020-02-04 19:21:01 -05:00
Greyson Parrelli
28e2f22550 Fix layout issues with reaction badges. 2020-02-04 19:21:01 -05:00
Alex Hart
eaa1760511 Animate toolbar in reactions overlay. 2020-02-04 19:21:01 -05:00
Alex Hart
53dfd3f4c0 Fix wrong avatar displaying in reactions fragment. 2020-02-04 19:21:01 -05:00
Alex Hart
ed2b049ad4 Add You to reactions you've sent in the bottom dialog fragment. 2020-02-04 10:35:45 -04:00
Alex Hart
092fb40333 Check if target is attached to window before trying to mask it. 2020-02-04 09:47:26 -04:00
Alan Evans
36a4225858 Always return passphrase without spaces. 2020-02-04 08:10:58 -05:00
Greyson Parrelli
e551ea8bd9 Bump version to 4.55.1 2020-02-04 00:59:45 -05:00
Greyson Parrelli
5a28b1bf1c Updated language translations. 2020-02-04 00:59:15 -05:00
Greyson Parrelli
4cd1129c92 Show snooze snackbars for longer. 2020-02-04 00:55:38 -05:00
Greyson Parrelli
a5d7bc4efc Force custom emoji for reactions. 2020-02-04 00:42:41 -05:00
Greyson Parrelli
1ff5b2af2a Capitalize 'PIN' in strings. 2020-02-04 00:01:07 -05:00
Greyson Parrelli
82446ce30a Hide attachment keyboard after selecting an action. 2020-02-04 00:00:06 -05:00
Alan Evans
6465248483 Fix megaphone snooze. 2020-02-03 22:44:43 -05:00
Greyson Parrelli
48e7f82466 Fix issue with view-once toggle breaking video controls. 2020-02-03 18:26:49 -05:00
Greyson Parrelli
6fef21ebc0 Update permission copy in attachment keyboard. 2020-02-03 15:32:20 -05:00
Greyson Parrelli
837e594607 Bump version to 4.55.0 2020-02-03 15:05:05 -05:00
Greyson Parrelli
ab0cb55b80 Updated language translations. 2020-02-03 15:05:05 -05:00
Greyson Parrelli
2d24c8c525 Update conditions for PIN megaphone.
Handles additional corner cases.
- Shows megaphone when you register with a v1 pin.
- Show fullscreen when you fail to set a PIN during registration.
2020-02-03 15:04:53 -05:00
Alan Evans
40383f3733 Handle presenting KBS account locked cases. 2020-02-03 15:04:53 -05:00
Alex Hart
e14861d79d CreatePinActivity naming update and copy fixes. 2020-02-03 15:04:53 -05:00
Alan Evans
b29b3d0432 Require at least 4 digits during registration. 2020-02-03 15:04:53 -05:00
Greyson Parrelli
c21d4861c0 Clear text entry after changing PIN types. 2020-02-03 15:04:53 -05:00
Greyson Parrelli
a6786e5c2b Fix strings in KBS PIN flow. 2020-02-03 15:04:53 -05:00
Greyson Parrelli
77caa9e9d4 Fix crash in getIntentForPinCreate(), show 'Create' in prefs. 2020-02-03 15:04:53 -05:00
Greyson Parrelli
835ef02872 Add an 'All' tab to reaction details. 2020-02-03 15:04:53 -05:00
Alex Hart
279dcb1428 Apply KBS Lock fixes and pluralization 2020-02-03 15:04:53 -05:00
Alan Evans
4a8c312e0a Clear pin on confirm screen on submit. 2020-02-03 15:04:53 -05:00
Alan Evans
c2bc376f87 Hide PIN from summary when feature flag set. 2020-02-03 15:04:37 -05:00
Greyson Parrelli
73160d4d26 Update reactions UI. 2020-02-03 14:20:08 -05:00
Alan Evans
1dd2a4e9c5 Allow backup passphrase verification. 2020-02-03 14:20:08 -05:00
Alan Evans
ed0c4b8de5 Remove KBS feature flag. 2020-02-03 14:20:08 -05:00
Greyson Parrelli
4f921d761d Enable reaction sending. 2020-02-03 14:20:08 -05:00
Greyson Parrelli
37f85d6deb Delete unused megaphones from the database. 2020-02-03 14:20:08 -05:00
Alex Hart
e1b75c78ab Add Pins for All Megaphone Kill Switch. 2020-02-03 14:20:08 -05:00
Alan Evans
5e83206e6e Fix group timer message. 2020-02-03 14:20:08 -05:00
Alan Evans
1ea6838db6 Bring KBS fragment source into RegistrationLockFragment and handle account locked. 2020-02-03 14:20:08 -05:00
Alex Hart
fb82420376 Implement new PIN UX. 2020-02-01 12:42:29 -04:00
Greyson Parrelli
109d67956f Implement new attachment keyboard.
Such beauty. Such grace.
2020-02-01 12:38:53 -04:00
Greyson Parrelli
9f7b2e2cfd Track the first time a megaphone appeared. 2020-01-30 11:40:22 -05:00
Greyson Parrelli
22f9bfeceb Add support for creating Megaphones. Includes reactions megaphone. 2020-01-29 19:15:02 -05:00
Greyson Parrelli
ef4c7e96da Bump version to 4.54.3 2020-01-29 18:31:07 -05:00
Greyson Parrelli
02865f99a9 Limit impact of crash on unexpected SMS receive. 2020-01-29 18:28:59 -05:00
Greyson Parrelli
ef6019f13b Fix reddit link previews. 2020-01-29 18:26:16 -05:00
Greyson Parrelli
33d02bb7b8 Bump version to 4.54.2 2020-01-28 15:31:11 -05:00
Greyson Parrelli
d34df2c1cf Updated language translations. 2020-01-28 15:30:11 -05:00
Alex Hart
7fdf540742 Implement new reaction notifications. 2020-01-28 15:48:24 -04:00
Alex Hart
f916aabb98 Fix NPE when retrieving display name of unknown recipient. 2020-01-28 15:22:41 -04:00
Alex Hart
4ae7d56db4 Fix NPE when returning to profile from background.
Also generally improves saved-state management for Profile editor.
2020-01-28 14:57:17 -04:00
Alex Hart
e3878ffde7 Change profile preference screen to use toolbar. 2020-01-28 13:41:06 -04:00
Alex Hart
5221b6fb43 Fix expiration timer display issue on devices with modified font sizes.
Fixes #9335
2020-01-27 14:54:04 -04:00
Alex Hart
5e0fe86858 Add SM-G920F and BLK-L09 to LegacyCameraModels. 2020-01-27 14:32:48 -04:00
Alex Hart
c86ced0911 Add back arrow to profile editor. 2020-01-27 14:30:53 -04:00
Alex Hart
0aad82d3d7 Check content disposition flag in carrier config before parsing PDU.
Fixes #9081
2020-01-27 12:15:32 -04:00
Greyson Parrelli
ce86adab82 Bump version to 4.54.1 2020-01-27 10:09:48 -05:00
Greyson Parrelli
b543f727be Fix lint error. 2020-01-27 10:09:24 -05:00
Greyson Parrelli
8de7e0f198 Bump version to 4.54.0 2020-01-27 09:51:00 -05:00
Alan Evans
866dacf198 Updated language translations. 2020-01-27 09:51:00 -05:00
Alan Evans
3589fa381d Make better effort to delete leftover temporary backup files. 2020-01-27 09:51:00 -05:00
Greyson Parrelli
5d54ebfaa0 Fix crash when notification state is empty. 2020-01-27 09:51:00 -05:00
Alan Evans
fea2b6253f KBS remote feature flag. 2020-01-27 09:51:00 -05:00
Alan Evans
ba6e1ab15a Add type to KBS json. 2020-01-27 09:51:00 -05:00
Curt Brune
1d9fff3c98 Update ringrtc to 0.3.3 2020-01-27 09:51:00 -05:00
Greyson Parrelli
526adce603 Add support for sticky and hot-swappable feature flags. 2020-01-27 09:51:00 -05:00
Alan Evans
e7f568e162 Trimming profile names to fit byte budget and remove leading/trailing spaces. 2020-01-27 09:51:00 -05:00
Alan Evans
7d15c602a6 Enable KBS. 2020-01-27 09:51:00 -05:00
Greyson Parrelli
bdb30ebc48 Set a better User-Agent on requests. 2020-01-27 09:51:00 -05:00
Greyson Parrelli
a31da7616d Rename 'userAgent' to 'signalAgent'.
This wasn't actually being used in the User-Agent header. Instead, it
was used as the value for an X-Signal-Agent header. To avoid confusion,
I'm renaming this.
2020-01-27 09:51:00 -05:00
Alex Hart
f1147c10ee Disable reaction sending on update messages. 2020-01-27 09:51:00 -05:00
Alan Evans
544a5386ad Always show sticker icon in image editor.
Fixes flicker seen jumping toggling view once.
2020-01-27 09:51:00 -05:00
Greyson Parrelli
2d502213e4 Remove forced feature flag for reaction sending. 2020-01-27 09:51:00 -05:00
Greyson Parrelli
55e9f8722f Add support for remote feature flags. 2020-01-27 09:51:00 -05:00
Greyson Parrelli
b8602ee004 Fix issues with Mexican phone number formatting.
Fixes #9317
2020-01-27 09:40:27 -05:00
Alan Evans
e37c4b1f87 Replace pinstretcher with Argon2 and new PIN encryption. 2020-01-24 10:54:39 -05:00
Greyson Parrelli
f7a3bb2ae8 Add the ability to re-order sticker packs. 2020-01-24 10:54:39 -05:00
Alan Evans
7d70ea78cd Hmac-SIV encryption/decryption. 2020-01-24 10:54:39 -05:00
Alex Hart
3907ec8b51 Add support for setting an optional last name in profiles. 2020-01-24 10:54:39 -05:00
Alan Evans
f2b9bf0b8c Use SignalStore for KBS Values. 2020-01-24 10:54:38 -05:00
Greyson Parrelli
fadcc606f8 Optimize uploads during media composition.
By uploading in advance (when on unmetered connections), media messages
can send almost instantly.
2020-01-24 10:54:38 -05:00
Alex Hart
92e97e61c1 Clear cached self id on successful registration. 2020-01-24 10:54:38 -05:00
Greyson Parrelli
4b5b9fbde8 Add an encrypted key-value store.
SignalStore is backed by SQLCipher and is intended to be used instead of
TextSecurePreferences moving forward.
2020-01-24 10:54:38 -05:00
Alan Evans
711d22a0ed Do not specify random provider. 2020-01-24 10:54:38 -05:00
Greyson Parrelli
06757153b3 Add support for adding jobs with existing dependencies. 2020-01-24 10:54:38 -05:00
Greyson Parrelli
38597aea00 Add support for canceling Jobs. 2020-01-24 10:54:38 -05:00
Alex Hart
b10ce080a9 Consolidate Notification Ids to a centralized constants class. 2020-01-16 05:41:27 -05:00
Alan Evans
72e10ac597 Bump version to 4.53.7 2020-01-15 16:20:47 -05:00
Alan Evans
5b591364ba Updated language translations. 2020-01-15 16:18:45 -05:00
Alan Evans
ace1855797 Test various Argon2 parameters. 2020-01-15 16:11:41 -05:00
Alan Evans
ddedf73939 Bump version to 4.53.6 2020-01-13 14:15:48 -05:00
Alan Evans
538014935e Updated language translations. 2020-01-13 12:22:55 -05:00
Greyson Parrelli
5e9bbf1200 Reduce avatar outlines to 1px. 2020-01-13 12:13:22 -05:00
qrest
29d6d3c041 Add Xiaomi Mi A2 to echo cancellation blacklist. 2020-01-13 08:09:00 -05:00
Alan Evans
53ab303fd9 Bump version to 4.53.5 2020-01-10 16:54:40 -05:00
Alan Evans
24103ee856 Updated language translations. 2020-01-10 16:48:54 -05:00
Greyson Parrelli
20e368ab5e Update outgoing view-once message toast. 2020-01-10 16:28:30 -05:00
Greyson Parrelli
05763191ce Show the correct quoted media type for unviewed view-once messages. 2020-01-10 15:34:38 -05:00
Alan Evans
f6685fb9c9 Fix content NPE. 2020-01-10 07:48:06 -05:00
Greyson Parrelli
dbdf9602c2 Bump version to 4.53.4 2020-01-09 18:45:38 -05:00
Greyson Parrelli
6173f7049c Updated language translations. 2020-01-09 18:44:48 -05:00
Greyson Parrelli
4adacf4b98 Fix issue where you could send text with a view-once message. 2020-01-09 18:38:34 -05:00
Greyson Parrelli
8cb6ed26a1 Update view-once design. 2020-01-09 18:32:14 -05:00
Greyson Parrelli
fd7aa9ccfa Fix view-once sync and quote descriptions. 2020-01-09 18:32:14 -05:00
Alex Hart
e2a48d1714 Fix notification reply image. 2020-01-09 12:36:46 -04:00
Greyson Parrelli
a5c4c1e0a6 Converted outlines from 1px to 1dp. 2020-01-08 15:27:55 -05:00
Alan Evans
b29d03e872 Bump version to 4.53.3 2020-01-08 11:51:27 -05:00
Alan Evans
dff11092ec Updated language translations. 2020-01-08 11:50:39 -05:00
Greyson Parrelli
5e9c4e8fa3 Remove tap-to-dismiss from view-once. 2020-01-07 21:44:41 -05:00
Greyson Parrelli
c346f32762 Made view-once a non-sticky setting. 2020-01-07 16:18:07 -05:00
Greyson Parrelli
d2d450aff2 Make view-once viewed bubble match the conversation background. 2020-01-07 16:14:11 -05:00
Greyson Parrelli
09af858be8 Show upload progress for view-once messages. 2020-01-07 15:50:12 -05:00
Alan Evans
9c8a99c79c Bump version to 4.53.2 2020-01-07 15:02:43 -05:00
Greyson Parrelli
c6b9855198 Always show view-once video remaining time. 2020-01-07 15:02:43 -05:00
Alan Evans
c142928fad Updated language translations. 2020-01-07 13:45:45 -05:00
Greyson Parrelli
fc0cfd5188 Disable multiselect actions for inapplicable message types. 2020-01-07 13:09:25 -05:00
Greyson Parrelli
d9c78e5c3e Fix toolbar overlap with cutouts in recipient preferences.
Fixes #9323
2020-01-07 12:17:41 -05:00
Greyson Parrelli
b449fceca0 Dismiss conversation search after swipe-to-reply.
Fixes #9325
2020-01-07 11:13:16 -05:00
Greyson Parrelli
f0d15c0bce Fix crash when clicking group avatars. 2020-01-07 10:51:40 -05:00
Greyson Parrelli
8f031f61ea Fix group update string when re-added to group. 2020-01-06 18:36:56 -05:00
Alan Evans
502e8559f0 Bump version to 4.53.1 2020-01-06 16:53:31 -05:00
Alan Evans
8cf6f7e936 Relocate jni libs. 2020-01-06 16:52:41 -05:00
Alan Evans
0ef01cc620 Bump version to 4.53.0 2020-01-06 11:08:30 -05:00
Alan Evans
930828ef86 Updated language translations. 2020-01-06 11:08:30 -05:00
Alan Evans
9ebe920195 Move all files to natural position. 2020-01-06 11:08:30 -05:00
Greyson Parrelli
0df36047e7 Ensure transport type gets reset in onNewIntent()
Fixes #9319
2020-01-06 11:08:27 -05:00
Greyson Parrelli
94604921f9 Fix issue with swipe-to-reply triggering incorrectly.
Fixes #9227
2020-01-06 11:08:27 -05:00
Curt Brune
284fe294ac Skip monochrome cameras when switching cameras on video calls. 2020-01-06 11:08:27 -05:00
Greyson Parrelli
ffa01d491a Do not list yourself in group membership updates. 2020-01-06 11:08:27 -05:00
Kevin T. Berstene
1d9513e743 Respect local camera aspect ratio in calls.
Fixes #8615
2020-01-06 11:08:27 -05:00
Greyson Parrelli
277c9e22f1 Show long-press-magnify in sticker preview screen. 2020-01-06 11:08:27 -05:00
Greyson Parrelli
4e7b4da941 Implement resumable downloads. 2020-01-06 11:08:27 -05:00
Greyson Parrelli
7e72c9c33b Add view-once tooltip. 2020-01-06 11:08:27 -05:00
Greyson Parrelli
e0e2c3a3f5 Fix some UI bugs in view-once sending. 2020-01-06 11:08:27 -05:00
Greyson Parrelli
3b5e444e76 Enable view-once send support. 2020-01-06 11:08:27 -05:00
Alan Evans
02006e3ff5 Update copyright year. 2020-01-06 11:08:27 -05:00
Greyson Parrelli
8083596f19 Use FitCenter sizing for stickers. 2020-01-06 11:08:27 -05:00
Greyson Parrelli
6551689a0c Increase the size of stickers in the conversation. 2020-01-06 11:08:27 -05:00
Greyson Parrelli
3fbf21a34e Don't crash on packs missing metadata. 2020-01-06 11:08:27 -05:00
Alan Evans
b598431237 Separate message decryption from message processing. 2020-01-06 11:08:27 -05:00
Greyson Parrelli
3b5d9a2cae Consider groups 'unknown' if they have no title, avatar, or members. 2020-01-06 11:08:27 -05:00
Alex Hart
3bd8aa8a86 Apply MessageStyle and fix chronology issues. 2020-01-06 11:08:27 -05:00
Greyson Parrelli
fe5fca8eaf Sync thread order and archive status with linked devices. 2020-01-06 11:08:27 -05:00
Greyson Parrelli
848101a783 Refactor AvatarImageView#setAvatarClickHandler.
This triggered a call to Recipient#isGroup when rendering a view, which
itself could trigger a DB read. This delays the call to #isGroup to
lower the possibility of doing DB reads on the main thread.
2019-12-19 18:06:29 -05:00
Alex Hart
d7c350f987 Fix in-call proximity lock for wireless, speaker, and bluetooth. 2019-12-19 18:06:29 -05:00
Curt Brune
4c526f0b3c Update ringrtc to 0.3.0 2019-12-19 18:06:29 -05:00
Greyson Parrelli
876ffb5b13 Bump version to 4.52.4 2019-12-19 18:00:56 -05:00
Greyson Parrelli
0b14cf3d6a Show sticker install tooltips less often. 2019-12-19 17:59:42 -05:00
Greyson Parrelli
c2044b36b1 Bump version to 4.52.3 2019-12-18 20:58:07 -05:00
Greyson Parrelli
f970b4acfa Revert "Remove the Pixel 4 from the CameraX blacklist."
This reverts commit 1f5a597d50.
2019-12-18 20:55:03 -05:00
Greyson Parrelli
e69c0af613 Bump version to 4.52.2 2019-12-18 19:56:16 -05:00
Greyson Parrelli
42e2dd8b30 Updated language translations. 2019-12-18 18:04:42 -05:00
Greyson Parrelli
4fd6e7b033 Fix issue with stickers flickering on send. 2019-12-18 16:45:07 -05:00
Greyson Parrelli
3580816eac Bump version to 4.52.1 2019-12-18 00:50:02 -05:00
Greyson Parrelli
f1c2e5f194 Updated language translations. 2019-12-18 00:49:36 -05:00
Greyson Parrelli
b6d59f1d46 Fix sticker manager text wrapping issues. 2019-12-17 22:26:09 -05:00
Greyson Parrelli
a3521681e7 Bump version to 4.52.0 2019-12-17 15:38:12 -05:00
Alan Evans
ca10e1136c Updated language translations. 2019-12-17 15:37:11 -05:00
Greyson Parrelli
282fb2af0e Fix tinting issues on API 21. 2019-12-17 14:36:54 -05:00
Greyson Parrelli
1f5a597d50 Remove the Pixel 4 from the CameraX blacklist. 2019-12-17 14:36:54 -05:00
Greyson Parrelli
a32666817c Remove old image editor stickers. 2019-12-17 14:36:54 -05:00
Greyson Parrelli
b9bd3f2b4c Launch stickers. 2019-12-17 14:36:54 -05:00
Alex Hart
4173efbe5a Fix NPE on call initalization 2019-12-16 11:01:32 -04:00
yate
8121c8bd41 Remove blank MMS config overrides.
Some SIM cards include blank values for `uaProfUrl` and `userAgent`.

Passing in these blank values along with the overrides causes a 412 precondition error when downloading MMS from ATT&T.

If these values happen to be blank, then they should be removed completely from the overrides, allowing the request to use the default `uaProfUrl` and `userAgent` for the system.

Fixes #9173
2019-12-14 11:29:46 -05:00
Greyson Parrelli
6ca8218d55 Bump version to 4.51.6 2019-12-06 11:58:12 -05:00
Greyson Parrelli
653afbdd46 Updated language translations. 2019-12-06 11:57:33 -05:00
Alex Hart
4453d1752f Fix reactions scrubber positioning on vertically split multiscreen. 2019-12-06 11:43:51 -05:00
Alan Evans
5782c8a58b Make media overview view pager scrollable for long translations. 2019-12-06 11:43:51 -05:00
Greyson Parrelli
3e041befc8 Bump version to 4.51.5 2019-12-06 01:14:24 -05:00
Greyson Parrelli
6d3847c26f Updated language translations. 2019-12-06 01:10:47 -05:00
Alex Hart
4bdfc37bc1 Fix swipe to reply clobbering long press events. 2019-12-06 01:00:39 -05:00
Alan Evans
28afe0d829 Fix first media overview item selected text. 2019-12-06 01:00:39 -05:00
Greyson Parrelli
04dddd3378 Bump version to 4.51.4 2019-12-05 19:15:10 -05:00
Greyson Parrelli
23a14583fe Updated language translations. 2019-12-05 19:14:48 -05:00
Greyson Parrelli
19c83de510 Fix possible performance issues with reactions query. 2019-12-05 19:10:37 -05:00
Greyson Parrelli
e94d4b64cf Bump version to 4.51.3 2019-12-05 12:21:12 -05:00
Greyson Parrelli
7380c99f3a Updated language translations. 2019-12-05 12:20:28 -05:00
Greyson Parrelli
2961a372c3 Avoid unneccessary recipient refreshes. 2019-12-05 12:20:28 -05:00
Greyson Parrelli
5e2a4fb058 Put safeguards around Recipient creation in the IdentityStore. 2019-12-05 12:20:28 -05:00
Greyson Parrelli
a16242b9f8 Fix issues with RecipientDatabase#update(). 2019-12-05 12:20:28 -05:00
Alan Evans
acfba9ac96 Show total file size of selected items when appropriate. 2019-12-05 12:20:28 -05:00
Alan Evans
006343460e Fix long press on image thumbnail in detail view.
Allow long press to select inside select mode.
2019-12-05 12:20:28 -05:00
Alex Hart
1cc8634cc7 Display reactions in ImageView instead of TextView on message bubbles. 2019-12-05 12:20:27 -05:00
Greyson Parrelli
9f8a110428 Bump version to 4.51.2 2019-12-05 02:59:42 -05:00
Greyson Parrelli
07b4279d0b Updated language translations. 2019-12-05 02:59:42 -05:00
Greyson Parrelli
6a33b231e3 Add a FrameRateTracker to log frame drops. 2019-12-05 02:59:42 -05:00
Greyson Parrelli
b38d02061d Improve logging in RetrieveProfileJob. 2019-12-05 02:59:42 -05:00
Greyson Parrelli
f832a36a5e Prevent possible UUID-only recipient creations. 2019-12-05 02:59:42 -05:00
Alan Evans
911ca7c29d Improve storage management detail view descriptions. 2019-12-05 02:59:42 -05:00
Greyson Parrelli
544b75a2a7 Bump version to 4.51.2 2019-12-04 15:30:30 -05:00
Greyson Parrelli
56e8d4fb06 Updated language translations. 2019-12-04 15:28:45 -05:00
Greyson Parrelli
36a2278aef Add sanity checks for phone number during link process. 2019-12-04 15:28:45 -05:00
Alex Hart
0c785b85b8 Fix video icon issue. 2019-12-04 15:28:45 -05:00
Alan Evans
a0ecba147e Use decimal digit groups in file size pretty printing. 2019-12-04 15:28:45 -05:00
Alan Evans
977591ac82 Remove assertion error in recipient preferences. 2019-12-04 15:28:28 -05:00
Greyson Parrelli
fe1838d3fe Fix issue where unlocking would dismiss MainActivity. 2019-12-04 15:25:26 -05:00
Greyson Parrelli
ede06cf97d Remove unnecessary directory refreshes. 2019-12-04 15:25:26 -05:00
Greyson Parrelli
93deee6824 Attempt to fix crash in ClassicOpenHelper.
These users must have > 1.5-year-old installs that haven't been updated,
but it's still a top crash for whatever reason, so gotta try to fix it.
2019-12-04 15:25:26 -05:00
Greyson Parrelli
db19077834 Fix crash when receiving a PreKey message in a new session.
We don't allow creating recipients with only a UUID at the moment
(for good reason), but the way the decrypt method was written, it
was possible to do so. Until we have CDS, we should prefer the phone
number in scenarios like these.
2019-12-04 15:25:26 -05:00
Alex Hart
6f91f62db2 Fix InviteActivity sendSmsMessage type error. 2019-12-04 15:25:26 -05:00
Greyson Parrelli
1fc63b7597 Fix crash in conversation search. 2019-12-04 15:25:26 -05:00
Alex Hart
a079e479ec Skip call dialog if signal call is already active. 2019-12-04 15:25:26 -05:00
Alex Hart
e90705b459 Add correct archive icon to light theme. 2019-12-04 15:25:26 -05:00
Alex Hart
244db437cb Fix reaction display issues. 2019-12-04 15:25:25 -05:00
Alan Evans
6558eae032 Fix audio color on light theme. 2019-12-04 09:10:48 -05:00
Greyson Parrelli
88b54a262b Bump version to 4.51.0 2019-12-04 00:17:03 -05:00
Alan Evans
4fa8b8a4bd Updated language translations. 2019-12-04 00:08:01 -05:00
Greyson Parrelli
cc0ced9a81 Add internal pre-alpha support for storage service. 2019-12-04 00:08:01 -05:00
Alan Evans
52447f5e97 Add storage management features. 2019-12-04 00:07:49 -05:00
Alex Hart
bceb69b284 Add internal pre-alpha support for receiving reactions. 2019-12-04 00:07:49 -05:00
Alex Hart
a8d826020d Implement new video call experience. 2019-12-04 00:07:49 -05:00
Alan Evans
2ada7f87f2 Update lib phone number. 2019-12-04 00:07:49 -05:00
Alan Evans
7f8ca58762 Add internal pre-alpha support for Registration Lock v2. 2019-12-04 00:07:49 -05:00
Greyson Parrelli
058c25808b Clean up website auto-update APKs.
I think previously we thought that because we always used the same
filename, that we were just overwring the same file repeatedly. However,
DownloadManager will actually append numbers to a filename instead of
overwriting, so we were generating a ton of files.

Now we just clear out any previous files before downloading a new one.

Related to #9033
2019-12-04 00:07:49 -05:00
Greyson Parrelli
c1e6b6b086 Add better Loader performance logs. 2019-12-04 00:07:49 -05:00
Greyson Parrelli
b0a6bb79f6 Fix issue with setting Note to Self color.
Fixes #9235
2019-12-04 00:07:49 -05:00
Greyson Parrelli
de52bf50a2 Improve image and sticker notifications. 2019-12-04 00:07:49 -05:00
Greyson Parrelli
207dd23c86 Fix performance issue with large number of notifications.
Constructing the notification would call KeyCachingService#isLocked()
many times in a loop. This call is slow, because when the device isn't
locked, it would construct the master secret each time, which can take
50+ ms. Do that twice in a loop a hundred times, and it adds up.

This simplified #isLocked to avoid the unnecessary master secret
generation.
2019-12-04 00:07:49 -05:00
Alex Hart
7e0de29dd7 Add notification policy permission and runtime check.
Fixes #9217
2019-12-04 00:07:49 -05:00
Greyson Parrelli
eaa8f1ee8f Ignore expiration updates from groups you've left. 2019-12-04 00:07:49 -05:00
Alex Hart
fb494c1151 Implement new invite screen. 2019-12-04 00:07:49 -05:00
Greyson Parrelli
49ecd9ef5d Added additional logging in RetrieveProfileJob. 2019-12-04 00:07:49 -05:00
Greyson Parrelli
8772214fd4 Increase number of log files to 7. 2019-12-04 00:07:49 -05:00
Greyson Parrelli
5b74d0cbeb Improve activity lifecycle logging. 2019-12-04 00:07:49 -05:00
Greyson Parrelli
1e7c93007d Convert the conversation list into a real fragment.
It was a fragment before, but it's functionality was inappropriately
split between the various layers.

This also sets us up better to do tablet stuff in the future, if we
choose to do that.
2019-12-04 00:07:49 -05:00
Greyson Parrelli
608815a69b Add internal pre-alpha support for usernames. 2019-12-04 00:07:42 -05:00
Greyson Parrelli
fb49efa34d Add internal pre-alpha libsignal support for reactions. 2019-12-04 00:07:13 -05:00
Android Team
b20e8616ec Move libsignal-service-java into this repo.
libsignal-service-java repo commit: 1a01c22636
2019-12-04 00:07:13 -05:00
Alan Evans
acf78b6b63 Make new top level gradle file, make app dir and move build.gradle. 2019-12-04 00:07:13 -05:00
Alex Hart
d68fe928b8 Fix nav bar contrast on sticker preview. 2019-12-04 00:07:13 -05:00
Alex Hart
db4d561d9e Fix nav bar color on shared contacts edit screen. 2019-12-04 00:07:07 -05:00
Alex Hart
6933ca50a7 Implement new Message Request UI. 2019-12-04 00:07:07 -05:00
Greyson Parrelli
88c0e6f8ab Fail a job when you can't instantiate it.
This will still crash, but prevent apps from getting into crash loops
when we have bad data in a job.
2019-11-25 11:32:16 -05:00
Greyson Parrelli
6cd4728e3c Update job serialized data after retry. 2019-11-25 11:32:16 -05:00
Greyson Parrelli
97cc82837c Don't backup the JobsDatabase. 2019-11-25 11:32:16 -05:00
Greyson Parrelli
5f0b0e8495 Bump version to 4.50.6 2019-11-25 11:32:07 -05:00
Greyson Parrelli
c837d590ab Removed RefreshUnidentifiedDeliveryAvailabilityJob.
It's been long enough -- it's no longer necessary to check.

Also, the service is going to start returning certs no matter what, so
at this point it's just an unnecessary network call.
2019-11-25 11:32:06 -05:00
Greyson Parrelli
6108a32631 Prevent reading UUIDs from external contacts. 2019-11-25 11:29:58 -05:00
Greyson Parrelli
6d78e1760d Fix possible crash in very old database migration.
The NumberMigrator constructor is crashing. One possibility is that this
migration is running before the user registered, in which case they
wouldn't have a number. Wrapped the parts that don't need to run in this
situation in an `if` block.
2019-11-25 11:29:58 -05:00
Greyson Parrelli
c7b7242eff Fix issues with blocking and MMS groups.
Fixes #9218.

Note that this removes MMS group blocking for now, just because it never
really worked, and I don't want to hotfix in a feature.
2019-11-25 11:29:19 -05:00
Greyson Parrelli
77c687efcf Bump version to 4.50.5 2019-11-21 13:28:23 -05:00
Greyson Parrelli
274af2f010 Ensure group message is being sent to a group. 2019-11-21 13:28:00 -05:00
Greyson Parrelli
6cd5100530 Ensure Recipient.self() is available. 2019-11-21 13:24:54 -05:00
Alan Evans
115a408b0b Bump version to 4.50.4 2019-11-18 09:36:14 -05:00
Alan Evans
d983016137 Updated language translations. 2019-11-18 09:33:53 -05:00
Greyson Parrelli
a8309b6b2b Fix attachment sizes during backup exports. 2019-11-18 09:19:11 -05:00
Alan Evans
e241589c92 Bump version to 4.50.3 2019-11-15 15:43:47 -05:00
Alan Evans
dcb6240a6a Updated language translations. 2019-11-15 15:39:39 -05:00
Alex Hart
599bb5ab0f Update insights copy and queries. 2019-11-15 16:33:54 -04:00
Alan Evans
739fe584c6 Bump version to 4.50.2 2019-11-14 14:49:48 -05:00
Alan Evans
920d1c6207 Updated language translations. 2019-11-14 14:49:48 -05:00
Greyson Parrelli
c1d3d26a15 Removed unnecessary subtext in Insights. 2019-11-14 14:49:48 -05:00
theBoatman
e3b66dc7e8 Fixing RealtimeSleepTimer to work in concurrent threads. 2019-11-14 13:56:12 -05:00
Alan Evans
dfa08d1356 In dark mode, use a dark theme base for RegistrationLockDialog and RationaleDialog.
Fixes #8995
Closes #9077
2019-11-14 13:51:01 -05:00
Curt Brune
6da734c2f9 Update ringrtc to 0.2.0 2019-11-13 14:05:22 -08:00
Alan Evans
a9f3141a77 Create android.yml running qa task. 2019-11-13 15:49:22 -05:00
Alan Evans
ee267d4702 Bump version to 4.50.1 2019-11-13 14:40:04 -05:00
Alan Evans
c39174ed8a Updated language translations. 2019-11-13 14:40:04 -05:00
Greyson Parrelli
9c872b6da6 Fix issue where SMS groups showing up as 'Unnamed Group'. 2019-11-13 11:37:49 -05:00
Greyson Parrelli
2924a09936 Don't run UuidMigration if not registered. 2019-11-13 11:37:49 -05:00
Greyson Parrelli
b9da012cc4 Fix media send issues on Android 4.4.
Before lollipop, Android would ignore activity results for activities
launched with singleTask. So I switched to singleTop, since that should
still solve the problem of double-activity-opens without running into
this issue.

Fixes #9149
2019-11-13 11:37:49 -05:00
Alan Evans
8626d41787 I18N for "Unlock Signal". 2019-11-13 11:37:49 -05:00
Alex Hart
c58705722a Fix Insights Adapter item text clashing. 2019-11-13 11:37:49 -05:00
Greyson Parrelli
c702ff676a Support additional sync behavior for linked devices. 2019-11-13 11:37:49 -05:00
Alan Evans
995569dd50 Bump version to 4.50.0 2019-11-12 10:55:41 -05:00
Greyson Parrelli
a0dfd42527 Fix possible context crash. 2019-11-12 10:55:41 -05:00
Alex Hart
a7dd78cce6 Implement in-app insights. 2019-11-12 10:55:41 -05:00
Greyson Parrelli
e2e9cd40b3 Blacklist additional phone models from the new camera. 2019-11-11 18:13:34 -05:00
Alan Evans
e4fc6f41b8 Schedule first backup after restore for 24hrs time. 2019-11-11 13:00:01 -05:00
Alan Evans
778b2a0d27 Remove old strings file. 2019-11-10 09:23:44 -05:00
Alan Evans
64cd15ca0b Make AlbumThumbnailView_plus non-translatable. 2019-11-10 09:23:43 -05:00
Alan Evans
1e46c3c0ba Make app_name non-translatable. 2019-11-10 09:23:43 -05:00
Alan Evans
91772b4e11 Exclude non-translatable strings.
Lint exclude ExtraTranslation.
2019-11-10 09:23:42 -05:00
Alan Evans
af5c7cb7ca Added dotted lines to time icons. 2019-11-09 07:45:53 -05:00
Alan Evans
e760474fa9 Clear unauthorized flag on connect. 2019-11-09 07:01:08 -05:00
Greyson Parrelli
28bb88e347 Lower max number of JobScheduler IDs.
Some devices can only handle 25 job IDs, so I lowered it to 20.
2019-11-09 07:01:08 -05:00
Curt Brune
17a1fe97ca Send hangup message when terminating on call errors.
When errors occur during a call, attempt to send the remote peer a
hangup message before terminating the call.  This allows the remote
peer to shutdown their side of the call in a timely manner.
2019-11-09 07:01:08 -05:00
Alan Evans
7348237862 Lint.
- baseline update.
- eradicate ButtonOrder.
- eradicate VectorRaster.
- eradicate HardcodedText.
2019-11-09 07:01:08 -05:00
Alex Hart
4e66db5dc1 Fix timer disabled icon. 2019-11-09 07:01:08 -05:00
Alex Hart
ffa6c9acd1 Utilize icon_tint color filter for UpdateItem icons. 2019-11-09 07:01:08 -05:00
Greyson Parrelli
b3e66a9259 Prevent creation of UUID-only recipients. 2019-11-09 07:01:08 -05:00
Alan Evans
5c0cf8c1f0 English staging app name. 2019-11-09 07:01:08 -05:00
Alex Hart
89c716fca4 Fix layout gravity on recv long message footer. 2019-11-09 07:01:08 -05:00
Alan Evans
aae9830dfa Use lib-signal-servivce 2.15.1 2019-11-09 07:01:08 -05:00
Alan Evans
48c72697c1 Add protobuf lite exception for proguard. 2019-11-09 07:01:08 -05:00
Greyson Parrelli
aac7e9ea53 Fix issue with ShareActivity contact display. 2019-11-09 07:01:08 -05:00
Alan Evans
a1b10b3222 Update to protobuf 3.10 lite.
Update fingerprint logic.
Update for additional validation around SignalServiceAddresses.

Co-authored-by: Greyson Parrelli <greyson@signal.org>
2019-11-09 07:01:08 -05:00
Curt Brune
2ab8db33e3 Update ringrtc to 0.1.9 2019-11-09 07:01:08 -05:00
Curt Brune
2b1386232f Implement logging interface for ringrtc-0.1.8
Implement the org.signal.ringrtc.Log.Logger interface, using
org.thoughtcrime.securesms.logging.Log as the underlying logger.

This allows ringrtc to log in the same way as the rest of the
application.
2019-11-09 07:01:07 -05:00
Alex Hart
7bb1caa22e Remove unused pictures that were captured outside of the in-app camera. 2019-11-09 07:01:07 -05:00
Alex Hart
72c14b8651 Apply new Keyboard and Dial Pad icons. 2019-11-09 07:01:07 -05:00
Alex Hart
f85c3bb1e6 Add feature flag to new Profile display. 2019-11-09 07:01:07 -05:00
Alan Evans
d8714fe3b4 Delegate to library Base64. 2019-11-09 07:01:07 -05:00
Alex Hart
c54c1907b6 Fix nav bar colors on reg and call screen. 2019-11-09 07:01:07 -05:00
Greyson Parrelli
55257155dd Handle ProtocolInvalidMessageExceptions with no sender. 2019-11-09 07:01:07 -05:00
alex-signal
55a8bd86de Consolidate profile display labels. 2019-11-09 07:01:07 -05:00
Greyson Parrelli
c60909272b UUID migration. 2019-11-09 07:01:07 -05:00
Greyson Parrelli
3dcc2d8171 Moved DirectoryHelper. 2019-11-09 07:01:07 -05:00
Greyson Parrelli
0ccbb22e4c Unsubscribe from old recipient observers in ConversationActivity. 2019-11-09 07:01:07 -05:00
Greyson Parrelli
0ffa10eaea Bump version to 4.49.18 2019-11-08 20:27:14 -05:00
Alan Evans
9824073023 Keep line numbers within R8. 2019-11-08 16:17:32 -05:00
Alan Evans
d2adc11a2d New languages.
bn,bs,ms,mr,ta,ur
2019-11-08 16:17:32 -05:00
Alan Evans
4d0b870c88 Updated language translations. 2019-11-08 16:17:32 -05:00
Alan Evans
840997cb7d Translate tasks. 2019-11-08 11:41:38 -05:00
Alex Hart
e4406621ed Fix lint issues. 2019-11-08 11:41:38 -05:00
Greyson Parrelli
7f96ee0b62 Clear DATA_HASH column. 2019-11-08 11:30:48 -05:00
Alan Evans
17f850b31a Ensure that we can still navigate when we get a response from server. 2019-11-08 11:19:57 -05:00
Alan Evans
5d87ad0301 Ignore period in phone number entry.
Fixes #9168
2019-11-08 11:19:32 -05:00
Greyson Parrelli
a33eeed6d3 Bump version to 4.49.17 2019-11-07 16:18:27 -05:00
Alex Hart
554ce29337 Adjust Typing Indicator margin in groups. 2019-11-07 16:03:13 -05:00
Alex Hart
6054d0b36f Nullify Data Hash when Data is Nullified 2019-11-07 16:02:56 -05:00
Greyson Parrelli
8ba15cf3b4 Bump version to 4.49.16 2019-11-06 16:42:16 -05:00
Greyson Parrelli
b15e5b4867 Fix potential invalid context crash. 2019-11-06 16:38:28 -05:00
Greyson Parrelli
544511905a Fix issue where deduped media might not be kept in sync. 2019-11-06 16:37:25 -05:00
Greyson Parrelli
02e3f511bd Bump version to 4.49.15 2019-11-04 11:30:08 -05:00
Alex Hart
0539b071e7 Fix message info icon inflation error on API 19 devices.
Fixes #9148
2019-11-04 11:29:31 -05:00
Greyson Parrelli
36e400650c Fix backup restore issue.
Fixes #9133
2019-11-04 11:21:11 -05:00
Greyson Parrelli
d8930daf4b Bump version to 4.49.14 2019-11-01 17:23:52 -04:00
Greyson Parrelli
17b5816d5b Skip corrupt attachments during backup restore.
Instead of aborting the entire backup restore.

Related to #8355
2019-11-01 17:11:10 -04:00
Curt Brune
e574c81ea2 Update ringrtc to 0.1.7.2 2019-11-01 12:26:00 -07:00
Greyson Parrelli
6799a2f841 Bump version to 4.49.13 2019-10-29 16:10:09 -04:00
Curt Brune
61fb20f412 Update ringrtc to v0.1.7 2019-10-29 12:46:01 -07:00
Alan Evans
47f648e7bc Correct text on registration lock fragment. 2019-10-29 13:47:14 -04:00
Alan Evans
064c0ddb82 Manually restrict to 30 digits to allow pasting containing any number of spaces. 2019-10-29 09:59:53 -04:00
Greyson Parrelli
b42c42007d Bump version to 4.49.12 2019-10-28 19:48:07 -04:00
Greyson Parrelli
807cdfce2e Increase backup passphrase input length to 35.
The way pasting works, it could prevent you from pasting in text if you
included the spaces in between number chunks.
2019-10-28 19:47:05 -04:00
Greyson Parrelli
43dc3aeebd Fix 'direct share' icon rendering issue.
Fixes #9138
2019-10-28 16:51:06 -04:00
Greyson Parrelli
0369c5ee16 Make avatars larger in conversations. 2019-10-28 16:46:21 -04:00
4523 changed files with 268869 additions and 141532 deletions

30
.github/workflows/android.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: Android CI
on:
pull_request:
push:
branches:
- 'master'
- '4.**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Install NDK
run: echo "y" | sudo /usr/local/lib/android/sdk/tools/bin/sdkmanager --install "ndk;20.0.5594570" --sdk_root=${ANDROID_SDK_ROOT}
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
- name: Build with Gradle
run: ./gradlew qa

View File

@@ -1,10 +0,0 @@
[main]
host = https://www.transifex.com
lang_map = da_DK:da-rDK,he:iw,id:in,kn_IN:kn-rIN,pt_BR:pt-rBR,pt_PT:pt,qu_EC:qu-rEC,sv_SE:sv-rSE,zh_CN:zh-rCN,zh_HK:zh-rHK,zh_TW:zh-rTW
[signal-android.master]
file_filter = res/values-<lang>/strings.xml
source_file = res/values/strings.xml
source_lang = en
type = ANDROID

View File

@@ -1,737 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.thoughtcrime.securesms">
<uses-sdk tools:overrideLibrary="androidx.camera.core,androidx.camera.camera2"/>
<permission android:name="org.thoughtcrime.securesms.ACCESS_SECRETS"
android:label="Access to TextSecure Secrets"
android:protectionLevel="signature" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.bluetooth" android:required="false" />
<uses-feature android:name="android.hardware.location" android:required="false"/>
<uses-feature android:name="android.hardware.location.network" android:required="false"/>
<uses-feature android:name="android.hardware.location.gps" android:required="false"/>
<uses-feature android:name="android.hardware.microphone" android:required="false"/>
<uses-feature android:name="android.hardware.wifi" android:required="false"/>
<uses-feature android:name="android.hardware.portrait" android:required="false"/>
<uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
<uses-permission android:name="org.thoughtcrime.securesms.ACCESS_SECRETS"/>
<uses-permission android:name="android.permission.READ_PROFILE"/>
<uses-permission android:name="android.permission.WRITE_PROFILE"/>
<uses-permission android:name="android.permission.BROADCAST_WAP_PUSH"
tools:ignore="ProtectedPermissions"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_MMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.WRITE_SMS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_CALL_STATE"/>
<!-- For sending/receiving events -->
<uses-permission android:name="android.permission.WRITE_CALENDAR"/>
<uses-permission android:name="android.permission.READ_CALENDAR"/>
<!-- Normal -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<!-- So we can add a TextSecure 'Account' -->
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<!-- For conversation 'shortcuts' on the desktop -->
<uses-permission android:name="android.permission.INSTALL_SHORTCUT"/>
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<!-- For fixing MMS -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<!-- Set image as wallpaper -->
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.RAISED_THREAD_PRIORITY" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>
<application android:name=".ApplicationContext"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
tools:replace="android:allowBackup"
android:allowBackup="false"
android:theme="@style/TextSecure.LightTheme"
android:largeHeap="true">
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyCSx9xea86GwDKGznCAULE9Y5a8b-TfN9U"/>
<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<meta-data android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" />
<meta-data android:name="firebase_analytics_collection_deactivated" android:value="true" />
<meta-data android:name="google_analytics_adid_collection_enabled" android:value="false" />
<meta-data android:name="firebase_messaging_auto_init_enabled" android:value="false" />
<activity android:name="org.thoughtcrime.securesms.WebRtcCallActivity"
android:theme="@style/TextSecure.LightTheme.WebRTCCall"
android:excludeFromRecents="true"
android:screenOrientation="portrait"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|fontScale"
android:launchMode="singleTask"/>
<activity android:name=".InviteActivity"
android:theme="@style/TextSecure.HighlightTheme"
android:windowSoftInputMode="stateHidden"
android:parentActivityName=".ConversationListActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.thoughtcrime.securesms.ConversationListActivity" />
</activity>
<activity android:name=".PromptMmsActivity"
android:label="Configure MMS Settings"
android:windowSoftInputMode="stateUnchanged"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".DeviceProvisioningActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="tsdevice"/>
</intent-filter>
</activity>
<activity android:name=".preferences.MmsPreferencesActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ShareActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:noHistory="true"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="audio/*" />
<data android:mimeType="image/*" />
<data android:mimeType="text/plain" />
<data android:mimeType="video/*" />
<data android:mimeType="application/*"/>
<data android:mimeType="text/*"/>
<data android:mimeType="*/*"/>
</intent-filter>
<meta-data
android:name="android.service.chooser.chooser_target_service"
android:value=".service.DirectShareService" />
</activity>
<activity android:name=".stickers.StickerPackPreviewActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:launchMode="singleTask"
android:noHistory="true"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="sgnl"
android:host="addstickers" />
</intent-filter>
</activity>
<activity android:name=".ConversationListActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:theme="@style/TextSecure.LightNoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:exported="true" />
<activity-alias android:name=".RoutingActivity"
android:targetActivity=".ConversationListActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
<meta-data android:name="com.sec.minimode.icon.portrait.normal"
android:resource="@mipmap/ic_launcher" />
<meta-data android:name="com.sec.minimode.icon.landscape.normal"
android:resource="@mipmap/ic_launcher" />
</activity-alias>
<activity android:name=".ConversationListArchiveActivity"
android:label="@string/AndroidManifest_archived_conversations"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:parentActivityName=".ConversationListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.thoughtcrime.securesms.ConversationListActivity" />
</activity>
<activity android:name=".conversation.ConversationActivity"
android:windowSoftInputMode="stateUnchanged"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:parentActivityName=".ConversationListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.thoughtcrime.securesms.ConversationListActivity" />
</activity>
<activity android:name=".longmessage.LongMessageActivity" />
<activity android:name=".conversation.ConversationPopupActivity"
android:windowSoftInputMode="stateVisible"
android:launchMode="singleTask"
android:taskAffinity=""
android:excludeFromRecents="true"
android:theme="@style/TextSecure.LightTheme.Popup"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
<activity android:name=".MessageDetailsActivity"
android:label="@string/AndroidManifest__message_details"
android:windowSoftInputMode="stateHidden"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".GroupCreateActivity"
android:windowSoftInputMode="stateVisible"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".DatabaseMigrationActivity"
android:theme="@style/NoAnimation.Theme.AppCompat.Light.DarkActionBar"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".migrations.ApplicationMigrationActivity"
android:theme="@style/NoAnimation.Theme.AppCompat.Light.DarkActionBar"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ExperienceUpgradeActivity"
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".PassphraseCreateActivity"
android:label="@string/AndroidManifest__create_passphrase"
android:windowSoftInputMode="stateUnchanged"
android:theme="@style/TextSecure.LightNoActionBar"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".PassphrasePromptActivity"
android:launchMode="singleTask"
android:theme="@style/TextSecure.LightIntroTheme"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".NewConversationActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:windowSoftInputMode="stateAlwaysVisible"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".PushContactSelectionActivity"
android:label="@string/AndroidManifest__select_contacts"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".giph.ui.GiphyActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".mediasend.MediaSendActivity"
android:theme="@style/TextSecure.FullScreenMedia"
android:windowSoftInputMode="stateHidden"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".PassphraseChangeActivity"
android:label="@string/AndroidManifest__change_passphrase"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".VerifyIdentityActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ApplicationPreferencesActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.NOTIFICATION_PREFERENCES" />
</intent-filter>
</activity>
<activity android:name=".registration.RegistrationNavigationActivity"
android:launchMode="singleTask"
android:theme="@style/TextSecure.LightRegistrationTheme"
android:windowSoftInputMode="stateUnchanged"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".revealable.ViewOnceMessageActivity"
android:launchMode="singleTask"
android:theme="@style/TextSecure.FullScreenMedia"
android:windowSoftInputMode="stateHidden"
android:excludeFromRecents="true"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".stickers.StickerManagementActivity"
android:launchMode="singleTask"
android:theme="@style/TextSecure.LightTheme"
android:windowSoftInputMode="stateUnchanged"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".DeviceActivity"
android:label="@string/AndroidManifest__linked_devices"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".LogSubmitActivity"
android:label="@string/AndroidManifest__log_submit"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".MediaPreviewActivity"
android:label="@string/AndroidManifest__media_preview"
android:windowSoftInputMode="stateHidden"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".MediaOverviewActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:windowSoftInputMode="stateHidden"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".DummyActivity"
android:theme="@android:style/Theme.NoDisplay"
android:enabled="true"
android:allowTaskReparenting="true"
android:noHistory="true"
android:excludeFromRecents="true"
android:alwaysRetainTaskState="false"
android:stateNotNeeded="true"
android:clearTaskOnLaunch="true"
android:finishOnTaskLaunch="true" />
<activity android:name=".PlayServicesProblemActivity"
android:theme="@style/TextSecure.DialogActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".SmsSendtoActivity">
<intent-filter>
<action android:name="android.intent.action.SENDTO" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.contact" />
</intent-filter>
</activity>
<activity android:name="org.thoughtcrime.securesms.webrtc.VoiceCallShare"
android:excludeFromRecents="true"
android:theme="@style/NoAnimation.Theme.BlackScreen"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.call" />
</intent-filter>
</activity>
<activity android:name=".RecipientPreferenceActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".BlockedContactsActivity"
android:theme="@style/TextSecure.LightTheme"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".scribbles.StickerSelectActivity"
android:theme="@style/TextSecure.DarkTheme"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".scribbles.NewStickerSelectActivity"
android:theme="@style/TextSecure.DarkTheme"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
android:theme="@style/TextSecure.DarkTheme"/>
<activity android:name=".CreateProfileActivity"
android:theme="@style/TextSecure.LightRegistrationTheme"
android:windowSoftInputMode="stateVisible"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ClearProfileAvatarActivity"
android:theme="@style/Theme.AppCompat.Dialog.Alert"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:icon="@drawable/clear_profile_avatar"
android:label="@string/AndroidManifest_remove_photo">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.action.CLEAR_PROFILE_PHOTO"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity android:name=".contactshare.ContactShareEditActivity"
android:theme="@style/TextSecure.LightTheme"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".contactshare.ContactNameEditActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".contactshare.SharedContactDetailsActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ShortcutLauncherActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:exported="true"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity
android:name=".maps.PlacePickerActivity"
android:label="@string/PlacePickerActivity_title"
android:theme="@style/TextSecure.LightNoActionBar" />
<service android:enabled="true" android:name="org.thoughtcrime.securesms.service.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=".service.IncomingMessageObserver$ForegroundService"/>
<service android:name=".service.QuickResponseService"
android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>
</service>
<service android:name=".service.AccountAuthenticatorService" android:exported="true">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator" android:resource="@xml/authenticator" />
</service>
<service android:name=".service.ContactsSyncAdapterService" android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data android:name="android.content.SyncAdapter" android:resource="@xml/syncadapter" />
<meta-data android:name="android.provider.CONTACTS_STRUCTURE" android:resource="@xml/contactsformat" />
</service>
<service android:name=".service.DirectShareService"
android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
<intent-filter>
<action android:name="android.service.chooser.ChooserTargetService" />
</intent-filter>
</service>
<service android:name=".service.GenericForegroundService"/>
<service android:name=".gcm.FcmService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<receiver android:name=".service.SmsListener"
android:permission="android.permission.BROADCAST_SMS"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="1001">
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
<intent-filter>
<action android:name="android.provider.Telephony.SMS_DELIVER"/>
</intent-filter>
</receiver>
<receiver android:name=".service.SmsDeliveryListener"
android:exported="true">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.services.MESSAGE_SENT"/>
</intent-filter>
</receiver>
<receiver android:name=".service.MmsListener"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BROADCAST_WAP_PUSH">
<intent-filter android:priority="1001">
<action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED"/>
<data android:mimeType="application/vnd.wap.mms-message" />
</intent-filter>
<intent-filter>
<action android:name="android.provider.Telephony.WAP_PUSH_DELIVER"/>
<data android:mimeType="application/vnd.wap.mms-message" />
</intent-filter>
</receiver>
<receiver android:name=".notifications.MarkReadReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.notifications.CLEAR"/>
</intent-filter>
</receiver>
<receiver android:name=".notifications.RemoteReplyReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.notifications.WEAR_REPLY"/>
</intent-filter>
</receiver>
<receiver android:name=".notifications.AndroidAutoHeardReceiver"
android:exported="false">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.notifications.ANDROID_AUTO_HEARD"/>
</intent-filter>
</receiver>
<receiver android:name=".notifications.AndroidAutoReplyReceiver"
android:exported="false">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.notifications.ANDROID_AUTO_REPLY"/>
</intent-filter>
</receiver>
<receiver android:name=".service.ExpirationListener" />
<receiver android:name=".revealable.ViewOnceMessageManager$ViewOnceAlarm" />
<provider android:name=".providers.PartProvider"
android:grantUriPermissions="true"
android:exported="false"
android:authorities="org.thoughtcrime.provider.securesms" />
<provider android:name=".providers.MmsBodyProvider"
android:grantUriPermissions="true"
android:exported="false"
android:authorities="org.thoughtcrime.provider.securesms.mms" />
<provider android:name="androidx.core.content.FileProvider"
android:authorities="org.thoughtcrime.securesms.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_provider_paths" />
</provider>
<provider android:name=".database.DatabaseContentProviders$Conversation"
android:authorities="org.thoughtcrime.securesms.database.conversation"
android:exported="false" />
<provider android:name=".database.DatabaseContentProviders$ConversationList"
android:authorities="org.thoughtcrime.securesms.database.conversationlist"
android:exported="false" />
<provider android:name=".database.DatabaseContentProviders$Attachment"
android:authorities="org.thoughtcrime.securesms.database.attachment"
android:exported="false" />
<provider android:name=".database.DatabaseContentProviders$Sticker"
android:authorities="org.thoughtcrime.securesms.database.sticker"
android:exported="false" />
<provider android:name=".database.DatabaseContentProviders$StickerPack"
android:authorities="org.thoughtcrime.securesms.database.stickerpack"
android:exported="false" />
<provider android:name="androidx.camera.camera2.impl.Camera2Initializer"
android:authorities="${applicationId}.camerax-init"
android:exported="false"
android:enabled="false" />
<receiver android:name=".service.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="org.thoughtcrime.securesms.RESTART"/>
</intent-filter>
</receiver>
<receiver android:name=".service.DirectoryRefreshListener">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".service.RotateSignedPreKeyListener">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".service.RotateSenderCertificateListener">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".service.LocalBackupListener">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".service.PersistentConnectionBootListener">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
<receiver android:name=".notifications.LocaleChangedReceiver">
<intent-filter>
<action android:name="android.intent.action.LOCALE_CHANGED"/>
</intent-filter>
</receiver>
<receiver android:name=".notifications.MessageNotifier$ReminderReceiver">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.MessageNotifier.REMINDER_ACTION"/>
</intent-filter>
</receiver>
<receiver android:name=".notifications.DeleteNotificationReceiver">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.DELETE_NOTIFICATION"/>
</intent-filter>
</receiver>
<receiver android:name=".ExperienceUpgradeActivity$AppUpgradeReceiver">
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
<data android:scheme="package" />
</intent-filter>
<intent-filter>
<action android:name="org.thoughtcrime.securesms.ExperienceUpgradeActivity.DISMISS_ACTION"/>
</intent-filter>
</receiver>
<receiver
android:name=".service.PanicResponderListener"
android:exported="true">
<intent-filter>
<action android:name="info.guardianproject.panic.action.TRIGGER" />
</intent-filter>
</receiver>
<service
android:name=".gcm.FcmJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:enabled="@bool/enable_job_service"
tools:targetApi="26" />
<service
android:name=".jobmanager.JobSchedulerScheduler$SystemService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:enabled="@bool/enable_job_service"
tools:targetApi="26" />
<service
android:name=".jobmanager.KeepAliveService"
android:enabled="@bool/enable_alarm_manager" />
<receiver
android:name=".jobmanager.AlarmManagerScheduler$RetryReceiver"
android:enabled="@bool/enable_alarm_manager" />
<!-- Probably don't need this one -->
<receiver
android:name=".jobmanager.BootReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<uses-library android:name="com.sec.android.app.multiwindow" android:required="false"/>
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
<meta-data android:name="com.sec.android.multiwindow.DEFAULT_SIZE_W" android:value="632.0dip" />
<meta-data android:name="com.sec.android.multiwindow.DEFAULT_SIZE_H" android:value="598.0dip" />
<meta-data android:name="com.sec.android.multiwindow.MINIMUM_SIZE_W" android:value="632.0dip" />
<meta-data android:name="com.sec.android.multiwindow.MINIMUM_SIZE_H" android:value="598.0dip" />
</application>
</manifest>

View File

@@ -1,74 +0,0 @@
Building Signal
===============
Basics
------
Signal uses [Gradle](http://gradle.org) to build the project and to maintain
dependencies. However, you needn't install it yourself; the
"gradle wrapper" `gradlew`, mentioned below, will do that for you.
Building Signal
---------------
The following steps should help you (re)build Signal from the command line.
1. Checkout the Signal-Android project source with the command:
git clone https://github.com/signalapp/Signal-Android.git
2. Make sure you have the [Android SDK](https://developer.android.com/sdk/index.html) installed.
3. Ensure that the following packages are installed from the Android SDK manager:
* Android SDK Build Tools (see buildToolsVersion in build.gradle)
* SDK Platform (All API levels)
* Android Support Repository
* Google Repository
4. Create a local.properties file at the root of your source checkout and add an sdk.dir entry to it. For example:
sdk.dir=/Application/android-sdk-macosx
5. Using Java 8
6. Execute Gradle:
./gradlew build
Visual assets
----------------------
Source assets tend to be large binary blobs, which are best stored outside of git repositories. Some source files are SVGs that can be auto-colored and sized using a tool like [android-res-utils](https://github.com/sebkur/android-res-utils).
Sample command for generating our audio placeholder image:
```bash
pngs_from_svg.py ic_audio.svg /path/to/Signal/res/ 150 --color #000 --opacity 0.54 --suffix _light
pngs_from_svg.py ic_audio.svg /path/to/Signal/res/ 150 --color #fff --opacity 1.00 --suffix _light
```
Setting up a development environment
------------------------------------
[Android Studio](https://developer.android.com/sdk/installing/studio.html) is the recommended development environment.
1. Install Android Studio.
2. Open Android Studio. On a new installation, the Quickstart panel will appear. If you have open projects, close them using "File > Close Project" to see the Quickstart panel.
3. From the Quickstart panel, choose "Configure" then "SDK Manager".
4. In the SDK Tools tab of the SDK Manager, make sure that the "Android Support Repository" is installed, and that the latest "Android SDK build-tools" are installed. Click "OK" to return to the Quickstart panel.
5. From the Quickstart panel, choose "Checkout from Version Control" then "git".
6. Paste the URL for the Signal-Android project when prompted (https://github.com/signalapp/Signal-Android.git).
7. Android studio should detect the presence of a project file and ask you whether to open it. Click "yes".
9. Default config options should be good enough.
9. Project initialisation and build should proceed.
Contributing code
-----------------
Code contributions should be sent via github as pull requests, from feature branches [as explained here](https://help.github.com/articles/using-pull-requests).
Mailing list
------------
Development discussion happens on the whispersystems mailing list.
[To join](https://lists.riseup.net/www/info/whispersystems)
Send emails to whispersystems@lists.riseup.net

View File

@@ -27,7 +27,6 @@ Interested in helping to translate Signal? Contribute here:
https://www.transifex.com/projects/p/signal-android/
## Contributing Code
Instructions on how to setup your development environment and build Signal can be found in [BUILDING.md](https://github.com/signalapp/Signal-Android/blob/master/BUILDING.md).
If you're new to the Signal codebase, we recommend going through our issues and picking out a simple bug to fix (check the "easy" label in our issues) in order to get yourself familiar. Also please have a look at the [CONTRIBUTING.md](https://github.com/signalapp/Signal-Android/blob/master/CONTRIBUTING.md), that might answer some of your questions.
@@ -60,9 +59,7 @@ The form and manner of this distribution makes it eligible for export under the
## License
Copyright 2011 Whisper Systems
Copyright 2013-2017 Open Whisper Systems
Copyright 2013-2020 Signal
Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html

10
app/.tx/config Normal file
View File

@@ -0,0 +1,10 @@
[main]
host = https://www.transifex.com
lang_map = da_DK:da-rDK,fil:tl,he:iw,id:in,kn_IN:kn-rIN,pa_PK:pa-rPK,pt_BR:pt-rBR,pt_PT:pt,qu_EC:qu-rEC,sv_SE:sv-rSE,zh_CN:zh-rCN,zh_HK:zh-rHK,zh_TW:zh-rTW
[signal-android.master]
file_filter = src/main/res/values-<lang>/strings.xml
source_file = src/main/res/values/strings.xml
source_lang = en
type = ANDROID

457
app/build.gradle Normal file
View File

@@ -0,0 +1,457 @@
import org.signal.signing.ApkSignerUtil
import java.security.MessageDigest
buildscript {
repositories {
google()
maven {
url "https://repo1.maven.org/maven2"
}
jcenter {
content {
includeVersion 'org.jetbrains.trove4j', 'trove4j', '20160824'
}
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.3'
classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.1.0'
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.10'
}
}
apply plugin: 'com.android.application'
apply plugin: 'com.google.protobuf'
apply plugin: 'androidx.navigation.safeargs'
apply plugin: 'witness'
apply from: 'translations.gradle'
apply from: 'witness-verifications.gradle'
repositories {
maven {
url "https://raw.github.com/signalapp/maven/master/photoview/releases/"
content {
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 {
includeGroupByRegex "com\\.github\\.dmytrodanylyk\\.circular-progress-button\\.*"
}
}
maven {
url "https://raw.github.com/signalapp/maven/master/sqlcipher/release/"
content {
includeGroupByRegex "org\\.signal.*"
}
}
maven { // textdrawable
url 'https://dl.bintray.com/amulyakhare/maven'
content {
includeGroupByRegex "com\\.amulyakhare.*"
}
}
google()
mavenCentral()
jcenter()
mavenLocal()
}
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.10.0'
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option "lite"
}
}
}
}
}
def canonicalVersionCode = 655
def canonicalVersionName = "4.63.2"
def postFixSize = 10
def abiPostFix = ['universal' : 0,
'armeabi-v7a' : 1,
'arm64-v8a' : 2,
'x86' : 3,
'x86_64' : 4]
android {
flavorDimensions "none"
compileSdkVersion 28
buildToolsVersion '28.0.3'
useLibrary 'org.apache.http.legacy'
dexOptions {
javaMaxHeapSize "4g"
}
defaultConfig {
versionCode canonicalVersionCode * postFixSize
versionName canonicalVersionName
minSdkVersion 19
targetSdkVersion 28
multiDexEnabled true
vectorDrawables.useSupportLibrary = true
project.ext.set("archivesBaseName", "Signal");
buildConfigField "long", "BUILD_TIMESTAMP", getLastCommitTimestamp() + "L"
buildConfigField "String", "SIGNAL_URL", "\"https://textsecure-service.whispersystems.org\""
buildConfigField "String", "STORAGE_URL", "\"https://storage.signal.org\""
buildConfigField "String", "SIGNAL_CDN_URL", "\"https://cdn.signal.org\""
buildConfigField "String", "SIGNAL_CDN2_URL", "\"https://cdn2.signal.org\""
buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"https://api.directory.signal.org\""
buildConfigField "String", "SIGNAL_SERVICE_STATUS_URL", "\"uptime.signal.org\""
buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api.backup.signal.org\""
buildConfigField "String", "CONTENT_PROXY_HOST", "\"contentproxy.signal.org\""
buildConfigField "int", "CONTENT_PROXY_PORT", "443"
buildConfigField "String", "SIGNAL_AGENT", "\"OWA\""
buildConfigField "String", "CDS_MRENCLAVE", "\"cd6cfc342937b23b1bdd3bbf9721aa5615ac9ff50a75c5527d441cd3276826c9\""
buildConfigField "String", "KBS_ENCLAVE_NAME", "\"fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe\""
buildConfigField "String", "KBS_MRENCLAVE", "\"a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87\""
buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\""
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"
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
resConfigs autoResConfig()
splits {
abi {
enable true
reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
universalApk true
}
}
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
packagingOptions {
exclude 'LICENSE.txt'
exclude 'LICENSE'
exclude 'NOTICE'
exclude 'asm-license.txt'
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
exclude 'META-INF/proguard/androidx-annotations.pro'
}
buildTypes {
debug {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard/proguard-firebase-messaging.pro',
'proguard/proguard-google-play-services.pro',
'proguard/proguard-jackson.pro',
'proguard/proguard-sqlite.pro',
'proguard/proguard-appcompat-v7.pro',
'proguard/proguard-square-okhttp.pro',
'proguard/proguard-square-okio.pro',
'proguard/proguard-spongycastle.pro',
'proguard/proguard-rounded-image-view.pro',
'proguard/proguard-glide.pro',
'proguard/proguard-shortcutbadger.pro',
'proguard/proguard-retrofit.pro',
'proguard/proguard-webrtc.pro',
'proguard/proguard-klinker.pro',
'proguard/proguard-retrolambda.pro',
'proguard/proguard-okhttp.pro',
'proguard/proguard-ez-vcard.pro',
'proguard/proguard.cfg'
testProguardFiles 'proguard/proguard-automation.pro',
'proguard/proguard.cfg'
}
staging {
initWith debug
buildConfigField "String", "SIGNAL_URL", "\"https://textsecure-service-staging.whispersystems.org\""
buildConfigField "String", "STORAGE_URL", "\"https://storage-staging.signal.org\""
buildConfigField "String", "SIGNAL_CDN_URL", "\"https://cdn-staging.signal.org\""
buildConfigField "String", "SIGNAL_CDN2_URL", "\"https://cdn2-staging.signal.org\""
buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"https://api-staging.directory.signal.org\""
buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api-staging.backup.signal.org\""
buildConfigField "String", "CDS_MRENCLAVE", "\"ba4ebb438bc07713819ee6c98d94037747006d7df63fc9e44d2d6f1fec962a79\""
buildConfigField "String", "KBS_ENCLAVE_NAME", "\"823a3b2c037ff0cbe305cc48928cfcc97c9ed4a8ca6d49af6f7d6981fb60a4e9\""
buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx\""
buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdls=\""
}
flipper {
initWith debug
minifyEnabled false
}
release {
minifyEnabled true
proguardFiles = buildTypes.debug.proguardFiles
}
}
productFlavors {
play {
dimension "none"
ext.websiteUpdateUrl = "null"
buildConfigField "boolean", "PLAY_STORE_DISABLED", "false"
buildConfigField "String", "NOPLAY_UPDATE_URL", "$ext.websiteUpdateUrl"
}
website {
dimension "none"
ext.websiteUpdateUrl = "https://updates.signal.org/android"
buildConfigField "boolean", "PLAY_STORE_DISABLED", "true"
buildConfigField "String", "NOPLAY_UPDATE_URL", "\"$ext.websiteUpdateUrl\""
}
}
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
output.outputFileName = output.outputFileName.replace(".apk", "-${variant.versionName}.apk")
def abiName = output.getFilter("ABI") ?: 'universal'
def postFix = abiPostFix.get(abiName, 0)
if (postFix >= postFixSize) throw new AssertionError("postFix is too large")
output.versionCodeOverride = canonicalVersionCode * postFixSize + postFix
}
}
lintOptions {
abortOnError true
baseline file("lint-baseline.xml")
disable "LintError"
}
testOptions {
unitTests {
includeAndroidResources = true
}
}
}
dependencies {
lintChecks project(':lintchecks')
implementation('androidx.appcompat:appcompat:1.1.0-beta01') {
force = true
}
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.legacy:legacy-support-v13:1.0.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.preference:preference:1.0.0'
implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
implementation 'androidx.gridlayout:gridlayout:1.0.0'
implementation 'androidx.exifinterface:exifinterface:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.navigation:navigation-fragment:2.1.0'
implementation 'androidx.navigation:navigation-ui:2.1.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:1.0.0-alpha05'
implementation 'androidx.lifecycle:lifecycle-common-java8:2.1.0'
implementation "androidx.camera:camera-core:1.0.0-beta01"
implementation "androidx.camera:camera-camera2:1.0.0-beta01"
implementation "androidx.camera:camera-lifecycle:1.0.0-beta01"
implementation "androidx.concurrent:concurrent-futures:1.0.0"
implementation "androidx.autofill:autofill:1.0.0"
implementation "androidx.paging:paging-common:2.1.2"
implementation "androidx.paging:paging-runtime:2.1.2"
implementation 'com.google.firebase:firebase-ml-vision:24.0.3'
implementation 'com.google.firebase:firebase-ml-vision-face-model:20.0.1'
implementation ('com.google.firebase:firebase-messaging:20.2.0') {
exclude group: 'com.google.firebase', module: 'firebase-core'
exclude group: 'com.google.firebase', module: 'firebase-analytics'
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
}
implementation 'com.google.android.gms:play-services-maps:16.1.0'
implementation 'com.google.android.gms:play-services-auth:16.0.1'
implementation 'com.google.android.exoplayer:exoplayer-core:2.9.1'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.1'
implementation 'org.conscrypt:conscrypt-android:2.0.0'
implementation 'org.signal:aesgcmprovider:0.0.3'
implementation project(':libsignal-service')
implementation 'org.signal:zkgroup-android:0.7.0'
implementation 'org.signal:argon2:13.1@aar'
implementation 'org.signal:ringrtc-android:2.0.3'
implementation "me.leolin:ShortcutBadger:1.1.16"
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'
implementation 'com.makeramen:roundedimageview:2.1.0'
implementation 'com.pnikosis:materialish-progress:1.5'
implementation 'org.greenrobot:eventbus:3.0.0'
implementation 'pl.tajchert:waitingdots:0.1.0'
implementation 'com.melnykov:floatingactionbutton:1.3.0'
implementation 'com.google.zxing:android-integration:3.1.0'
implementation 'mobi.upod:time-duration-picker:1.1.3'
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
implementation 'com.google.zxing:core:3.2.1'
implementation ('com.davemorrissey.labs:subsampling-scale-image-view:3.6.0') {
exclude group: 'com.android.support', module: 'support-annotations'
}
implementation ('cn.carbswang.android:NumberPickerView:1.0.9') {
exclude group: 'com.android.support', module: 'appcompat-v7'
}
implementation ('com.tomergoldst.android:tooltips:1.0.6') {
exclude group: 'com.android.support', module: 'appcompat-v7'
}
implementation ('com.klinkerapps:android-smsmms:4.0.1') {
exclude group: 'com.squareup.okhttp', module: 'okhttp'
exclude group: 'com.squareup.okhttp', module: 'okhttp-urlconnection'
}
implementation 'com.annimon:stream:1.1.8'
implementation ('com.takisoft.fix:colorpicker:0.9.1') {
exclude group: 'com.android.support', module: 'appcompat-v7'
exclude group: 'com.android.support', module: 'recyclerview-v7'
}
implementation 'com.airbnb.android:lottie:3.0.7'
implementation 'com.codewaves.stickyheadergrid:stickyheadergrid:0.9.4'
implementation 'com.github.dmytrodanylyk.circular-progress-button:library:1.1.3-S2'
implementation 'org.signal:android-database-sqlcipher:3.5.9-S3'
implementation ('com.googlecode.ez-vcard:ez-vcard:0.9.11') {
exclude group: 'com.fasterxml.jackson.core'
exclude group: 'org.freemarker'
}
implementation 'dnsjava:dnsjava:2.1.9'
flipperImplementation 'com.facebook.flipper:flipper:0.32.2'
flipperImplementation 'com.facebook.soloader:soloader:0.8.2'
testImplementation 'junit:junit:4.12'
testImplementation 'org.assertj:assertj-core:3.11.1'
testImplementation 'org.mockito:mockito-core:1.9.5'
testImplementation 'org.powermock:powermock-api-mockito:1.6.1'
testImplementation 'org.powermock:powermock-module-junit4:1.6.1'
testImplementation 'org.powermock:powermock-module-junit4-rule:1.6.1'
testImplementation 'org.powermock:powermock-classloading-xstream:1.6.1'
testImplementation 'androidx.test:core:1.2.0'
testImplementation ('org.robolectric:robolectric:4.2') {
exclude group: 'com.google.protobuf', module: 'protobuf-java'
}
testImplementation 'org.robolectric:shadows-multidex:4.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
dependencyVerification {
configuration = '(play|website)(Debug|Release)RuntimeClasspath'
}
def assembleWebsiteDescriptor = { variant, file ->
if (file.exists()) {
MessageDigest md = MessageDigest.getInstance("SHA-256");
file.eachByte 4096, {bytes, size ->
md.update(bytes, 0, size);
}
String digest = md.digest().collect {String.format "%02x", it}.join();
String url = variant.productFlavors.get(0).ext.websiteUpdateUrl
String apkName = file.getName()
String descriptor = "{" +
"\"versionCode\" : ${canonicalVersionCode * postFixSize + abiPostFix['universal']}," +
"\"versionName\" : \"$canonicalVersionName\"," +
"\"sha256sum\" : \"$digest\"," +
"\"url\" : \"$url/$apkName\"" +
"}"
File descriptorFile = new File(file.getParent(), apkName.replace(".apk", ".json"))
descriptorFile.write(descriptor)
}
}
def signProductionRelease = { variant ->
variant.outputs.collect { output ->
String apkName = output.outputFile.name
File inputFile = new File(output.outputFile.path)
File outputFile = new File(output.outputFile.parent, apkName.replace('-unsigned', ''))
new ApkSignerUtil('sun.security.pkcs11.SunPKCS11',
'pkcs11.config',
'PKCS11',
'file:pkcs11.password').calculateSignature(inputFile.getAbsolutePath(),
outputFile.getAbsolutePath())
inputFile.delete()
outputFile
}
}
task signProductionPlayRelease {
doLast {
signProductionRelease(android.applicationVariants.find { (it.name == 'playRelease') })
}
}
task signProductionWebsiteRelease {
doLast {
def variant = android.applicationVariants.find { (it.name == 'websiteRelease') }
File signedRelease = signProductionRelease(variant).find { it.name.contains('universal') }
assembleWebsiteDescriptor(variant, signedRelease)
}
}
tasks.whenTaskAdded { task ->
if (task.name.equals("assemblePlayRelease")) {
task.finalizedBy signProductionPlayRelease
}
if (task.name.equals("assembleWebsiteRelease")) {
task.finalizedBy signProductionWebsiteRelease
}
}
def getLastCommitTimestamp() {
new ByteArrayOutputStream().withStream { os ->
def result = exec {
executable = 'git'
args = ['log', '-1', '--pretty=format:%ct']
standardOutput = os
}
return os.toString() + "000"
}
}

48
app/lint-baseline.xml Normal file
View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<issues format="5" by="lint 3.3.2" client="gradle" variant="playRelease" version="3.3.2">
<issue
id="MissingPermission"
message="Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with `checkPermission`) or explicitly handle a potential `SecurityException`"
errorLine1=" List&lt;SubscriptionInfo> list = subscriptionManager.getActiveSubscriptionInfoList();"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/org/thoughtcrime/securesms/util/dualsim/SubscriptionManagerCompat.java"
line="101"
column="35"/>
</issue>
<issue
id="ResourceType"
message="Expected resource of type styleable"
errorLine1=" drawables.getColor(1, 0xff000000);"
errorLine2=" ~">
<location
file="src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java"
line="187"
column="36"/>
</issue>
<issue
id="AppLinkUrlError"
message="Missing URL"
errorLine1=" &lt;intent-filter>"
errorLine2=" ^">
<location
file="AndroidManifest.xml"
line="368"
column="9"/>
</issue>
<issue
id="AppLinkUrlError"
message="Missing URL"
errorLine1=" &lt;intent-filter>"
errorLine2=" ^">
<location
file="AndroidManifest.xml"
line="381"
column="9"/>
</issue>
</issues>

33
app/lint.xml Normal file
View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<!-- L10N errors -->
<!-- This is a runtime crash so we don't want to ship with this. -->
<issue id="StringFormatMatches" severity="error" />
<!-- L10N warnings -->
<issue id="MissingTranslation" severity="warning" />
<issue id="MissingQuantity" severity="warning" />
<issue id="ExtraTranslation" severity="warning" />
<issue id="ImpliedQuantity" severity="warning" />
<issue id="CanvasSize" severity="error" />
<issue id="HardcodedText" severity="error" />
<issue id="VectorRaster" severity="error" />
<issue id="ButtonOrder" severity="error" />
<issue id="ExtraTranslation" severity="warning" />
<!-- Custom lints -->
<issue id="LogNotSignal" severity="error" />
<issue id="LogNotAppSignal" severity="error" />
<issue id="LogTagInlined" severity="error" />
<issue id="RestrictedApi" severity="error">
<ignore path="*/org/thoughtcrime/securesms/mediasend/camerax/VideoCapture.java" />
<ignore path="*/org/thoughtcrime/securesms/mediasend/camerax/CameraXModule.java" />
<ignore path="*/org/thoughtcrime/securesms/conversation/*.java" />
<ignore path="*/org/thoughtcrime/securesms/lock/v2/CreateKbsPinViewModel.java" />
<ignore path="*/org/thoughtcrime/securesms/jobs/StickerPackDownloadJob.java" />
</issue>
</lint>

11
app/proguard/proguard.cfg Normal file
View File

@@ -0,0 +1,11 @@
-dontoptimize
-dontobfuscate
-keepattributes SourceFile,LineNumberTable
-keep class org.whispersystems.** { *; }
-keep class org.thoughtcrime.securesms.** { *; }
-keepclassmembers class ** {
public void onEvent*(**);
}
# Protobuf lite
-keep class * extends com.google.protobuf.GeneratedMessageLite { *; }

View File

@@ -0,0 +1,29 @@
{
"data": [
{
"name": "Ottttooooooooo Ocataaaaaaaavius",
"number": "+1 (555) 555-5555",
"label": "Mobile"
},
{
"name": "Victor Von Doom Phd",
"number": "+1 (555) 123-4567",
"label": "Home"
},
{
"name": "Flash Thompson",
"number": "+1 (555) 435-1261",
"label": "Work"
},
{
"name": "Dr. Curtis Connors",
"number": "+1 (555) 992-1567",
"label": "Mobile"
},
{
"name": "Billy Russo",
"number": "+1 (555) 234-1516",
"label": "Mobile"
}
]
}

View File

@@ -0,0 +1,93 @@
package org.thoughtcrime.securesms.lock;
import org.junit.Test;
import org.thoughtcrime.securesms.util.Hex;
import org.whispersystems.signalservice.api.kbs.HashedPin;
import org.whispersystems.signalservice.api.kbs.KbsData;
import org.whispersystems.signalservice.api.kbs.MasterKey;
import java.io.IOException;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public final class PinHashing_hashPin_Test {
@Test
public void argon2_hashed_pin_password() throws IOException {
String pin = "password";
byte[] backupId = Hex.fromStringCondensed("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
MasterKey masterKey = new MasterKey(Hex.fromStringCondensed("202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"));
HashedPin hashedPin = PinHashing.hashPin(pin, () -> backupId);
KbsData kbsData = hashedPin.createNewKbsData(masterKey);
assertArrayEquals(hashedPin.getKbsAccessKey(), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("ab7e8499d21f80a6600b3b9ee349ac6d72c07e3359fe885a934ba7aa844429f8"), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("3f33ce58eb25b40436592a30eae2a8fabab1899095f4e2fba6e2d0dc43b4a2d9cac5a3931748522393951e0e54dec769"), kbsData.getCipherText());
assertEquals(masterKey, kbsData.getMasterKey());
String localPinHash = PinHashing.localPinHash(pin);
assertTrue(PinHashing.verifyLocalPinHash(localPinHash, pin));
}
@Test
public void argon2_hashed_pin_another_password() throws IOException {
String pin = "anotherpassword";
byte[] backupId = Hex.fromStringCondensed("202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f");
MasterKey masterKey = new MasterKey(Hex.fromStringCondensed("88a787415a2ecd79da0d1016a82a27c5c695c9a19b88b0aa1d35683280aa9a67"));
HashedPin hashedPin = PinHashing.hashPin(pin, () -> backupId);
KbsData kbsData = hashedPin.createNewKbsData(masterKey);
assertArrayEquals(hashedPin.getKbsAccessKey(), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("301d9dd1e96f20ce51083f67d3298fd37b97525de8324d5e12ed2d407d3d927b"), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("9d9b05402ea39c17ff1c9298c8a0e86784a352aa02a74943bf8bcf07ec0f4b574a5b786ad0182c8d308d9eb06538b8c9"), kbsData.getCipherText());
assertEquals(masterKey, kbsData.getMasterKey());
String localPinHash = PinHashing.localPinHash(pin);
assertTrue(PinHashing.verifyLocalPinHash(localPinHash, pin));
}
@Test
public void argon2_hashed_pin_password_with_spaces_diacritics_and_non_arabic_numerals() throws IOException {
String pin = " Pass६örd ";
byte[] backupId = Hex.fromStringCondensed("cba811749042b303a6a7efa5ccd160aea5e3ea243c8d2692bd13d515732f51a8");
MasterKey masterKey = new MasterKey(Hex.fromStringCondensed("9571f3fde1e58588ba49bcf82be1b301ca3859a6f59076f79a8f47181ef952bf"));
HashedPin hashedPin = PinHashing.hashPin(pin, () -> backupId);
KbsData kbsData = hashedPin.createNewKbsData(masterKey);
assertArrayEquals(hashedPin.getKbsAccessKey(), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("ab645acdccc1652a48a34b2ac6926340ff35c03034013f68760f20013f028dd8"), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("11c0ba1834db15e47c172f6c987c64bd4cfc69c6047dd67a022afeec0165a10943f204d5b8f37b3cb7bab21c6dfc39c8"), kbsData.getCipherText());
assertEquals(masterKey, kbsData.getMasterKey());
assertEquals("577939bccb2b6638c39222d5a97998a867c5e154e30b82cc120f2dd07a3de987", kbsData.getMasterKey().deriveRegistrationLock());
String localPinHash = PinHashing.localPinHash(pin);
assertTrue(PinHashing.verifyLocalPinHash(localPinHash, pin));
}
@Test
public void argon2_hashed_pin_password_with_just_non_arabic_numerals() throws IOException {
String pin = " ६१८ ";
byte[] backupId = Hex.fromStringCondensed("717dc111a98423a57196512606822fca646c653facd037c10728f14ba0be2ab3");
MasterKey masterKey = new MasterKey(Hex.fromStringCondensed("0432d735b32f66d0e3a70d4f9cc821a8529521a4937d26b987715d8eff4e4c54"));
HashedPin hashedPin = PinHashing.hashPin(pin, () -> backupId);
KbsData kbsData = hashedPin.createNewKbsData(masterKey);
assertArrayEquals(hashedPin.getKbsAccessKey(), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("d2fedabd0d4c17a371491c9722578843a26be3b4923e28d452ab2fc5491e794b"), kbsData.getKbsAccessKey());
assertArrayEquals(Hex.fromStringCondensed("877ef871ef1fc668401c717ef21aa12e8523579fb1ff4474b76f28c2293537c80cc7569996c9e0229bea7f378e3a824e"), kbsData.getCipherText());
assertEquals(masterKey, kbsData.getMasterKey());
assertEquals("23a75cb1df1a87df45cc2ed167c2bdc85ab1220b847c88761b0005cac907fce5", kbsData.getMasterKey().deriveRegistrationLock());
String localPinHash = PinHashing.localPinHash(pin);
assertTrue(PinHashing.verifyLocalPinHash(localPinHash, pin));
}
}

View File

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

View File

@@ -0,0 +1,27 @@
package org.thoughtcrime.securesms;
import com.facebook.flipper.android.AndroidFlipperClient;
import com.facebook.flipper.core.FlipperClient;
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
import com.facebook.soloader.SoLoader;
import org.thoughtcrime.securesms.database.FlipperSqlCipherAdapter;
public class FlipperApplicationContext extends ApplicationContext {
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, false);
FlipperClient client = AndroidFlipperClient.getInstance(this);
client.addPlugin(new InspectorFlipperPlugin(this, DescriptorMapping.withDefaults()));
client.addPlugin(new DatabasesFlipperPlugin(new FlipperSqlCipherAdapter(this)));
client.addPlugin(new SharedPreferencesFlipperPlugin(this));
client.start();
}
}

View File

@@ -0,0 +1,245 @@
package org.thoughtcrime.securesms.database;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.flipper.plugins.databases.DatabaseDescriptor;
import com.facebook.flipper.plugins.databases.DatabaseDriver;
import net.sqlcipher.DatabaseUtils;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteStatement;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A lot of this code is taken from {@link com.facebook.flipper.plugins.databases.impl.SqliteDatabaseDriver}
* and made to work with SqlCipher. Unfortunately I couldn't use it directly, nor subclass it.
*/
public class FlipperSqlCipherAdapter extends DatabaseDriver<FlipperSqlCipherAdapter.Descriptor> {
public FlipperSqlCipherAdapter(Context context) {
super(context);
}
@Override
public List<Descriptor> getDatabases() {
return Collections.singletonList(new Descriptor(DatabaseFactory.getRawDatabase(getContext())));
}
@Override
public List<String> getTableNames(Descriptor descriptor) {
SQLiteDatabase db = descriptor.getReadable();
List<String> tableNames = new ArrayList<>();
try (Cursor cursor = db.rawQuery("SELECT name FROM sqlite_master WHERE type IN (?, ?)", new String[] { "table", "view" })) {
while (cursor != null && cursor.moveToNext()) {
tableNames.add(cursor.getString(0));
}
}
return tableNames;
}
@Override
public DatabaseGetTableDataResponse getTableData(Descriptor descriptor, String table, String order, boolean reverse, int start, int count) {
SQLiteDatabase db = descriptor.getReadable();
long total = DatabaseUtils.queryNumEntries(db, table);
String orderBy = order != null ? order + (reverse ? " DESC" : " ASC") : null;
String limitBy = start + ", " + count;
try (Cursor cursor = db.query(table, null, null, null, null, null, orderBy, limitBy)) {
String[] columnNames = cursor.getColumnNames();
List<List<Object>> rows = cursorToList(cursor);
return new DatabaseGetTableDataResponse(Arrays.asList(columnNames), rows, start, rows.size(), total);
}
}
@Override
public DatabaseGetTableStructureResponse getTableStructure(Descriptor descriptor, String table) {
SQLiteDatabase db = descriptor.getReadable();
Map<String, String> foreignKeyValues = new HashMap<>();
try(Cursor cursor = db.rawQuery("PRAGMA foreign_key_list(" + table + ")", null)) {
while (cursor != null && cursor.moveToNext()) {
String from = cursor.getString(cursor.getColumnIndex("from"));
String to = cursor.getString(cursor.getColumnIndex("to"));
String tableName = cursor.getString(cursor.getColumnIndex("table")) + "(" + to + ")";
foreignKeyValues.put(from, tableName);
}
}
List<String> structureColumns = Arrays.asList("column_name", "data_type", "nullable", "default", "primary_key", "foreign_key");
List<List<Object>> structureValues = new ArrayList<>();
try (Cursor cursor = db.rawQuery("PRAGMA table_info(" + table + ")", null)) {
while (cursor != null && cursor.moveToNext()) {
String columnName = cursor.getString(cursor.getColumnIndex("name"));
String foreignKey = foreignKeyValues.containsKey(columnName) ? foreignKeyValues.get(columnName) : null;
structureValues.add(Arrays.asList(columnName,
cursor.getString(cursor.getColumnIndex("type")),
cursor.getInt(cursor.getColumnIndex("notnull")) == 0,
getObjectFromColumnIndex(cursor, cursor.getColumnIndex("dflt_value")),
cursor.getInt(cursor.getColumnIndex("pk")) == 1,
foreignKey));
}
}
List<String> indexesColumns = Arrays.asList("index_name", "unique", "indexed_column_name");
List<List<Object>> indexesValues = new ArrayList<>();
try (Cursor indexesCursor = db.rawQuery("PRAGMA index_list(" + table + ")", null)) {
List<String> indexedColumnNames = new ArrayList<>();
String indexName = indexesCursor.getString(indexesCursor.getColumnIndex("name"));
try(Cursor indexInfoCursor = db.rawQuery("PRAGMA index_info(" + indexName + ")", null)) {
while (indexInfoCursor.moveToNext()) {
indexedColumnNames.add(indexInfoCursor.getString(indexInfoCursor.getColumnIndex("name")));
}
}
indexesValues.add(Arrays.asList(indexName,
indexesCursor.getInt(indexesCursor.getColumnIndex("unique")) == 1,
TextUtils.join(",", indexedColumnNames)));
}
return new DatabaseGetTableStructureResponse(structureColumns, structureValues, indexesColumns, indexesValues);
}
@Override
public DatabaseGetTableInfoResponse getTableInfo(Descriptor databaseDescriptor, String table) {
SQLiteDatabase db = databaseDescriptor.getReadable();
try (Cursor cursor = db.rawQuery("SELECT sql FROM sqlite_master WHERE name = ?", new String[] { table })) {
cursor.moveToFirst();
return new DatabaseGetTableInfoResponse(cursor.getString(cursor.getColumnIndex("sql")));
}
}
@Override
public DatabaseExecuteSqlResponse executeSQL(Descriptor descriptor, String query) {
SQLiteDatabase db = descriptor.getWritable();
String firstWordUpperCase = getFirstWord(query).toUpperCase();
switch (firstWordUpperCase) {
case "UPDATE":
case "DELETE":
return executeUpdateDelete(db, query);
case "INSERT":
return executeInsert(db, query);
case "SELECT":
case "PRAGMA":
case "EXPLAIN":
return executeSelect(db, query);
default:
return executeRawQuery(db, query);
}
}
private static String getFirstWord(String s) {
s = s.trim();
int firstSpace = s.indexOf(' ');
return firstSpace >= 0 ? s.substring(0, firstSpace) : s;
}
private static DatabaseExecuteSqlResponse executeUpdateDelete(SQLiteDatabase database, String query) {
SQLiteStatement statement = database.compileStatement(query);
int count = statement.executeUpdateDelete();
return DatabaseExecuteSqlResponse.successfulUpdateDelete(count);
}
private static DatabaseExecuteSqlResponse executeInsert(SQLiteDatabase database, String query) {
SQLiteStatement statement = database.compileStatement(query);
long insertedId = statement.executeInsert();
return DatabaseExecuteSqlResponse.successfulInsert(insertedId);
}
private static DatabaseExecuteSqlResponse executeSelect(SQLiteDatabase database, String query) {
try (Cursor cursor = database.rawQuery(query, null)) {
String[] columnNames = cursor.getColumnNames();
List<List<Object>> rows = cursorToList(cursor);
return DatabaseExecuteSqlResponse.successfulSelect(Arrays.asList(columnNames), rows);
}
}
private static DatabaseExecuteSqlResponse executeRawQuery(SQLiteDatabase database, String query) {
database.execSQL(query);
return DatabaseExecuteSqlResponse.successfulRawQuery();
}
private static @NonNull List<List<Object>> cursorToList(Cursor cursor) {
List<List<Object>> rows = new ArrayList<>();
int numColumns = cursor.getColumnCount();
while (cursor.moveToNext()) {
List<Object> values = new ArrayList<>(numColumns);
for (int column = 0; column < numColumns; column++) {
values.add(getObjectFromColumnIndex(cursor, column));
}
rows.add(values);
}
return rows;
}
private static @Nullable Object getObjectFromColumnIndex(Cursor cursor, int column) {
switch (cursor.getType(column)) {
case Cursor.FIELD_TYPE_NULL:
return null;
case Cursor.FIELD_TYPE_INTEGER:
return cursor.getLong(column);
case Cursor.FIELD_TYPE_FLOAT:
return cursor.getDouble(column);
case Cursor.FIELD_TYPE_BLOB:
return cursor.getBlob(column);
case Cursor.FIELD_TYPE_STRING:
default:
return cursor.getString(column);
}
}
static class Descriptor implements DatabaseDescriptor {
private final SQLCipherOpenHelper sqlCipherOpenHelper;
Descriptor(@NonNull SQLCipherOpenHelper sqlCipherOpenHelper) {
this.sqlCipherOpenHelper = sqlCipherOpenHelper;
}
@Override
public String name() {
return sqlCipherOpenHelper.getDatabaseName();
}
public @NonNull SQLiteDatabase getReadable() {
return sqlCipherOpenHelper.getReadableDatabase();
}
public @NonNull SQLiteDatabase getWritable() {
return sqlCipherOpenHelper.getWritableDatabase();
}
}
}

View File

@@ -0,0 +1,4 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="app_name">Signal (Flipper)</string>
</resources>

View File

@@ -0,0 +1,762 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.thoughtcrime.securesms">
<uses-sdk tools:overrideLibrary="androidx.camera.core,androidx.camera.camera2,androidx.camera.lifecycle" />
<permission android:name="org.thoughtcrime.securesms.ACCESS_SECRETS"
android:label="Access to TextSecure Secrets"
android:protectionLevel="signature" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.bluetooth" android:required="false" />
<uses-feature android:name="android.hardware.location" android:required="false"/>
<uses-feature android:name="android.hardware.location.network" android:required="false"/>
<uses-feature android:name="android.hardware.location.gps" android:required="false"/>
<uses-feature android:name="android.hardware.microphone" android:required="false"/>
<uses-feature android:name="android.hardware.wifi" android:required="false"/>
<uses-feature android:name="android.hardware.portrait" android:required="false"/>
<uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
<uses-permission android:name="org.thoughtcrime.securesms.ACCESS_SECRETS"/>
<uses-permission android:name="android.permission.READ_PROFILE"/>
<uses-permission android:name="android.permission.WRITE_PROFILE"/>
<uses-permission android:name="android.permission.BROADCAST_WAP_PUSH"
tools:ignore="ProtectedPermissions"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_MMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.WRITE_SMS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_CALL_STATE"/>
<!-- For sending/receiving events -->
<uses-permission android:name="android.permission.WRITE_CALENDAR"/>
<uses-permission android:name="android.permission.READ_CALENDAR"/>
<!-- Normal -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<!-- So we can add a TextSecure 'Account' -->
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<!-- For conversation 'shortcuts' on the desktop -->
<uses-permission android:name="android.permission.INSTALL_SHORTCUT"/>
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<!-- For fixing MMS -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<!-- Set image as wallpaper -->
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.RAISED_THREAD_PRIORITY" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>
<application android:name=".ApplicationContext"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
tools:replace="android:allowBackup"
android:allowBackup="false"
android:theme="@style/TextSecure.LightTheme"
android:largeHeap="true">
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyCSx9xea86GwDKGznCAULE9Y5a8b-TfN9U"/>
<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<meta-data android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" />
<meta-data android:name="firebase_analytics_collection_deactivated" android:value="true" />
<meta-data android:name="google_analytics_adid_collection_enabled" android:value="false" />
<meta-data android:name="firebase_messaging_auto_init_enabled" android:value="false" />
<activity android:name="org.thoughtcrime.securesms.WebRtcCallActivity"
android:theme="@style/TextSecure.LightTheme.WebRTCCall"
android:excludeFromRecents="true"
android:screenOrientation="portrait"
android:supportsPictureInPicture="true"
android:windowSoftInputMode="stateAlwaysHidden"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:launchMode="singleTask"/>
<activity android:name=".InviteActivity"
android:theme="@style/Signal.Light.NoActionBar.Invite"
android:windowSoftInputMode="stateHidden"
android:parentActivityName=".MainActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.thoughtcrime.securesms.MainActivity" />
</activity>
<activity android:name=".PromptMmsActivity"
android:label="Configure MMS Settings"
android:windowSoftInputMode="stateUnchanged"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".DeviceProvisioningActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="tsdevice"/>
</intent-filter>
</activity>
<activity android:name=".preferences.MmsPreferencesActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".sharing.ShareActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:noHistory="true"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="audio/*" />
<data android:mimeType="image/*" />
<data android:mimeType="text/plain" />
<data android:mimeType="video/*" />
<data android:mimeType="application/*"/>
<data android:mimeType="text/*"/>
<data android:mimeType="*/*"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/*" />
<data android:mimeType="video/*" />
</intent-filter>
<meta-data
android:name="android.service.chooser.chooser_target_service"
android:value=".service.DirectShareService" />
</activity>
<activity android:name=".stickers.StickerPackPreviewActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:launchMode="singleTask"
android:noHistory="true"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="sgnl"
android:host="addstickers" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https"
android:host="signal.art"
android:pathPrefix="/addstickers"/>
</intent-filter>
</activity>
<activity-alias android:name=".RoutingActivity"
android:targetActivity=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
<meta-data android:name="com.sec.minimode.icon.portrait.normal"
android:resource="@mipmap/ic_launcher" />
<meta-data android:name="com.sec.minimode.icon.landscape.normal"
android:resource="@mipmap/ic_launcher" />
</activity-alias>
<activity android:name=".conversation.ConversationActivity"
android:windowSoftInputMode="stateUnchanged"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.thoughtcrime.securesms.MainActivity" />
</activity>
<activity android:name=".longmessage.LongMessageActivity" />
<activity android:name=".conversation.ConversationPopupActivity"
android:windowSoftInputMode="stateVisible"
android:launchMode="singleTask"
android:taskAffinity=""
android:excludeFromRecents="true"
android:theme="@style/TextSecure.LightTheme.Popup"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
<activity android:name=".MessageDetailsActivity"
android:label="@string/AndroidManifest__message_details"
android:windowSoftInputMode="stateHidden"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".GroupCreateActivity"
android:windowSoftInputMode="stateVisible"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".groups.ui.pendingmemberinvites.PendingMemberInvitesActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:theme="@style/TextSecure.LightNoActionBar" />
<activity android:name=".groups.ui.managegroup.ManageGroupActivity"
android:windowSoftInputMode="stateAlwaysHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".DatabaseMigrationActivity"
android:theme="@style/NoAnimation.Theme.AppCompat.Light.DarkActionBar"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".migrations.ApplicationMigrationActivity"
android:theme="@style/NoAnimation.Theme.AppCompat.Light.DarkActionBar"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".PassphraseCreateActivity"
android:label="@string/AndroidManifest__create_passphrase"
android:windowSoftInputMode="stateUnchanged"
android:theme="@style/TextSecure.LightNoActionBar"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".PassphrasePromptActivity"
android:launchMode="singleTask"
android:theme="@style/TextSecure.LightIntroTheme"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".NewConversationActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:windowSoftInputMode="stateAlwaysVisible"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".PushContactSelectionActivity"
android:label="@string/AndroidManifest__select_contacts"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".giph.ui.GiphyActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".mediasend.MediaSendActivity"
android:theme="@style/TextSecure.FullScreenMedia"
android:windowSoftInputMode="stateHidden"
android:launchMode="singleTop"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".PassphraseChangeActivity"
android:label="@string/AndroidManifest__change_passphrase"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".VerifyIdentityActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ApplicationPreferencesActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.NOTIFICATION_PREFERENCES" />
</intent-filter>
</activity>
<activity android:name=".registration.RegistrationNavigationActivity"
android:launchMode="singleTask"
android:theme="@style/TextSecure.LightRegistrationTheme"
android:windowSoftInputMode="stateUnchanged"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".revealable.ViewOnceMessageActivity"
android:launchMode="singleTask"
android:theme="@style/TextSecure.FullScreenMedia"
android:windowSoftInputMode="stateHidden"
android:excludeFromRecents="true"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".stickers.StickerManagementActivity"
android:launchMode="singleTask"
android:theme="@style/TextSecure.LightTheme"
android:windowSoftInputMode="stateUnchanged"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".DeviceActivity"
android:label="@string/AndroidManifest__linked_devices"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".logsubmit.SubmitDebugLogActivity"
android:label="@string/AndroidManifest__log_submit"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".MediaPreviewActivity"
android:label="@string/AndroidManifest__media_preview"
android:windowSoftInputMode="stateHidden"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".AvatarPreviewActivity"
android:label="@string/AndroidManifest__media_preview"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".mediaoverview.MediaOverviewActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".DummyActivity"
android:theme="@android:style/Theme.NoDisplay"
android:enabled="true"
android:allowTaskReparenting="true"
android:noHistory="true"
android:excludeFromRecents="true"
android:alwaysRetainTaskState="false"
android:stateNotNeeded="true"
android:clearTaskOnLaunch="true"
android:finishOnTaskLaunch="true" />
<activity android:name=".PlayServicesProblemActivity"
android:theme="@style/TextSecure.DialogActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".SmsSendtoActivity">
<intent-filter>
<action android:name="android.intent.action.SENDTO" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.contact" />
</intent-filter>
</activity>
<activity android:name="org.thoughtcrime.securesms.webrtc.VoiceCallShare"
android:excludeFromRecents="true"
android:theme="@style/NoAnimation.Theme.BlackScreen"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.call" />
</intent-filter>
</activity>
<activity android:name=".RecipientPreferenceActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".mediasend.AvatarSelectionActivity"
android:theme="@style/TextSecure.FullScreenMedia"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".BlockedContactsActivity"
android:theme="@style/TextSecure.LightTheme"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".scribbles.ImageEditorStickerSelectActivity"
android:theme="@style/TextSecure.DarkTheme"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".profiles.edit.EditProfileActivity"
android:theme="@style/TextSecure.LightRegistrationTheme"
android:windowSoftInputMode="stateVisible|adjustResize" />
<activity android:name=".lock.v2.CreateKbsPinActivity"
android:theme="@style/TextSecure.LightRegistrationTheme"
android:windowSoftInputMode="adjustResize"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".lock.v2.KbsMigrationActivity"
android:theme="@style/TextSecure.LightRegistrationTheme"
android:windowSoftInputMode="adjustResize"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ClearProfileAvatarActivity"
android:theme="@style/Theme.AppCompat.Dialog.Alert"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:icon="@drawable/clear_profile_avatar"
android:label="@string/AndroidManifest_remove_photo">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.action.CLEAR_PROFILE_PHOTO"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity android:name=".messagerequests.MessageRequestMegaphoneActivity"
android:theme="@style/TextSecure.LightRegistrationTheme"
android:windowSoftInputMode="adjustResize"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".contactshare.ContactShareEditActivity"
android:theme="@style/TextSecure.LightTheme"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".contactshare.ContactNameEditActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".contactshare.SharedContactDetailsActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ShortcutLauncherActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:exported="true"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity
android:name=".maps.PlacePickerActivity"
android:label="@string/PlacePickerActivity_title"
android:theme="@style/TextSecure.LightNoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".MainActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
<activity android:name=".pin.PinRestoreActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
<activity android:name=".groups.ui.creategroup.CreateGroupActivity"
android:theme="@style/TextSecure.LightNoActionBar" />
<activity android:name=".groups.ui.addtogroup.AddToGroupsActivity"
android:theme="@style/TextSecure.LightNoActionBar" />
<activity android:name=".groups.ui.addmembers.AddMembersActivity"
android:theme="@style/TextSecure.LightNoActionBar" />
<activity android:name=".groups.ui.creategroup.details.AddGroupDetailsActivity"
android:theme="@style/TextSecure.LightNoActionBar" />
<service android:enabled="true" android:name="org.thoughtcrime.securesms.service.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"/>
<service android:name=".service.QuickResponseService"
android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>
</service>
<service android:name=".service.AccountAuthenticatorService" android:exported="true">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator" android:resource="@xml/authenticator" />
</service>
<service android:name=".service.ContactsSyncAdapterService" android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data android:name="android.content.SyncAdapter" android:resource="@xml/syncadapter" />
<meta-data android:name="android.provider.CONTACTS_STRUCTURE" android:resource="@xml/contactsformat" />
</service>
<service android:name=".service.DirectShareService"
android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
<intent-filter>
<action android:name="android.service.chooser.ChooserTargetService" />
</intent-filter>
</service>
<service android:name=".service.GenericForegroundService"/>
<service android:name=".gcm.FcmFetchService" />
<service android:name=".gcm.FcmReceiveService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<receiver android:name=".service.SmsListener"
android:permission="android.permission.BROADCAST_SMS"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="1001">
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
<intent-filter>
<action android:name="android.provider.Telephony.SMS_DELIVER"/>
</intent-filter>
</receiver>
<receiver android:name=".service.SmsDeliveryListener"
android:exported="true">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.services.MESSAGE_SENT"/>
</intent-filter>
</receiver>
<receiver android:name=".service.MmsListener"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BROADCAST_WAP_PUSH">
<intent-filter android:priority="1001">
<action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED"/>
<data android:mimeType="application/vnd.wap.mms-message" />
</intent-filter>
<intent-filter>
<action android:name="android.provider.Telephony.WAP_PUSH_DELIVER"/>
<data android:mimeType="application/vnd.wap.mms-message" />
</intent-filter>
</receiver>
<receiver android:name=".notifications.MarkReadReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.notifications.CLEAR"/>
</intent-filter>
</receiver>
<receiver android:name=".notifications.RemoteReplyReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.notifications.WEAR_REPLY"/>
</intent-filter>
</receiver>
<receiver android:name=".notifications.AndroidAutoHeardReceiver"
android:exported="false">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.notifications.ANDROID_AUTO_HEARD"/>
</intent-filter>
</receiver>
<receiver android:name=".notifications.AndroidAutoReplyReceiver"
android:exported="false">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.notifications.ANDROID_AUTO_REPLY"/>
</intent-filter>
</receiver>
<receiver android:name=".service.ExpirationListener" />
<receiver android:name=".revealable.ViewOnceMessageManager$ViewOnceAlarm" />
<provider android:name=".providers.PartProvider"
android:grantUriPermissions="true"
android:exported="false"
android:authorities="org.thoughtcrime.provider.securesms" />
<provider android:name=".providers.MmsBodyProvider"
android:grantUriPermissions="true"
android:exported="false"
android:authorities="org.thoughtcrime.provider.securesms.mms" />
<provider android:name="androidx.core.content.FileProvider"
android:authorities="org.thoughtcrime.securesms.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_provider_paths" />
</provider>
<provider android:name=".database.DatabaseContentProviders$Conversation"
android:authorities="org.thoughtcrime.securesms.database.conversation"
android:exported="false" />
<provider android:name=".database.DatabaseContentProviders$ConversationList"
android:authorities="org.thoughtcrime.securesms.database.conversationlist"
android:exported="false" />
<provider android:name=".database.DatabaseContentProviders$Attachment"
android:authorities="org.thoughtcrime.securesms.database.attachment"
android:exported="false" />
<provider android:name=".database.DatabaseContentProviders$Sticker"
android:authorities="org.thoughtcrime.securesms.database.sticker"
android:exported="false" />
<provider android:name=".database.DatabaseContentProviders$StickerPack"
android:authorities="org.thoughtcrime.securesms.database.stickerpack"
android:exported="false" />
<receiver android:name=".service.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="org.thoughtcrime.securesms.RESTART"/>
</intent-filter>
</receiver>
<receiver android:name=".service.DirectoryRefreshListener">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".service.RotateSignedPreKeyListener">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".service.RotateSenderCertificateListener">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".service.LocalBackupListener">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".service.PersistentConnectionBootListener">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
<receiver android:name=".notifications.LocaleChangedReceiver">
<intent-filter>
<action android:name="android.intent.action.LOCALE_CHANGED"/>
</intent-filter>
</receiver>
<receiver android:name=".notifications.MessageNotifier$ReminderReceiver">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.MessageNotifier.REMINDER_ACTION"/>
</intent-filter>
</receiver>
<receiver android:name=".notifications.DeleteNotificationReceiver">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.DELETE_NOTIFICATION"/>
</intent-filter>
</receiver>
<receiver
android:name=".service.PanicResponderListener"
android:exported="true">
<intent-filter>
<action android:name="info.guardianproject.panic.action.TRIGGER" />
</intent-filter>
</receiver>
<service
android:name=".gcm.FcmJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:enabled="@bool/enable_job_service"
tools:targetApi="26" />
<service
android:name=".jobmanager.JobSchedulerScheduler$SystemService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:enabled="@bool/enable_job_service"
tools:targetApi="26" />
<service
android:name=".jobmanager.KeepAliveService"
android:enabled="@bool/enable_alarm_manager" />
<receiver
android:name=".jobmanager.AlarmManagerScheduler$RetryReceiver"
android:enabled="@bool/enable_alarm_manager" />
<!-- Probably don't need this one -->
<receiver
android:name=".jobmanager.BootReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<uses-library android:name="com.sec.android.app.multiwindow" android:required="false"/>
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
<meta-data android:name="com.sec.android.multiwindow.DEFAULT_SIZE_W" android:value="632.0dip" />
<meta-data android:name="com.sec.android.multiwindow.DEFAULT_SIZE_H" android:value="598.0dip" />
<meta-data android:name="com.sec.android.multiwindow.MINIMUM_SIZE_W" android:value="632.0dip" />
<meta-data android:name="com.sec.android.multiwindow.MINIMUM_SIZE_H" android:value="598.0dip" />
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 647 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

View File

@@ -0,0 +1,20 @@
package org.thoughtcrime.securesms;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
public final class AppCapabilities {
private AppCapabilities() {
}
private static final boolean UUID_CAPABLE = false;
/**
* @param storageCapable Whether or not the user can use storage service. This is another way of
* asking if the user has set a Signal PIN or not.
*/
public static SignalServiceProfile.Capabilities getCapabilities(boolean storageCapable) {
return new SignalServiceProfile.Capabilities(UUID_CAPABLE, FeatureFlags.groupsV2(), storageCapable);
}
}

View File

@@ -0,0 +1,55 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.insights.InsightsOptOut;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.migrations.ApplicationMigrations;
import org.thoughtcrime.securesms.stickers.BlessedPacks;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
/**
* Rule of thumb: if there's something you want to do on the first app launch that involves
* persisting state to the database, you'll almost certainly *also* want to do it post backup
* restore, since a backup restore will wipe the current state of the database.
*/
public final class AppInitialization {
private static final String TAG = Log.tag(AppInitialization.class);
private AppInitialization() {}
public static void onFirstEverAppLaunch(@NonNull Context context) {
Log.i(TAG, "onFirstEverAppLaunch()");
InsightsOptOut.userRequestedOptOut(context);
TextSecurePreferences.setAppMigrationVersion(context, ApplicationMigrations.CURRENT_VERSION);
TextSecurePreferences.setJobManagerVersion(context, JobManager.CURRENT_VERSION);
TextSecurePreferences.setLastExperienceVersionCode(context, Util.getCanonicalVersionCode());
TextSecurePreferences.setHasSeenStickerIntroTooltip(context, true);
ApplicationDependencies.getMegaphoneRepository().onFirstEverAppLaunch();
SignalStore.onFirstEverAppLaunch();
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.forReference(BlessedPacks.SWOON_HANDS.getPackId(), BlessedPacks.SWOON_HANDS.getPackKey()));
ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forReference(BlessedPacks.SWOON_FACES.getPackId(), BlessedPacks.SWOON_FACES.getPackKey()));
}
public static void onPostBackupRestore(@NonNull Context context) {
Log.i(TAG, "onPostBackupRestore()");
ApplicationDependencies.getMegaphoneRepository().onFirstEverAppLaunch();
SignalStore.onFirstEverAppLaunch();
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.forReference(BlessedPacks.SWOON_HANDS.getPackId(), BlessedPacks.SWOON_HANDS.getPackKey()));
ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forReference(BlessedPacks.SWOON_FACES.getPackId(), BlessedPacks.SWOON_FACES.getPackKey()));
}
}

View File

@@ -17,24 +17,22 @@
package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.camera.camera2.Camera2AppConfig;
import androidx.camera.core.CameraX;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner;
import androidx.multidex.MultiDexApplication;
import com.google.android.gms.security.ProviderInstaller;
import org.conscrypt.Conscrypt;
import org.signal.aesgcmprovider.AesGcmProvider;
import org.signal.ringrtc.CallConnectionFactory;
import org.signal.ringrtc.CallManager;
import org.thoughtcrime.securesms.components.TypingStatusRepository;
import org.thoughtcrime.securesms.components.TypingStatusSender;
import org.thoughtcrime.securesms.database.DatabaseFactory;
@@ -42,39 +40,36 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencyProvider;
import org.thoughtcrime.securesms.gcm.FcmJobService;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.JobMigrator;
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
import org.thoughtcrime.securesms.jobs.FastJobStorage;
import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
import org.thoughtcrime.securesms.jobs.JobManagerFactories;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
import org.thoughtcrime.securesms.logging.AndroidLogger;
import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.logging.PersistentLogger;
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
import org.thoughtcrime.securesms.mediasend.LegacyCameraModels;
import org.thoughtcrime.securesms.mediasend.camerax.CameraXUtil;
import org.thoughtcrime.securesms.logging.SignalUncaughtExceptionHandler;
import org.thoughtcrime.securesms.messages.InitialMessageRetriever;
import org.thoughtcrime.securesms.migrations.ApplicationMigrations;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.registration.RegistrationUtil;
import org.thoughtcrime.securesms.revealable.ViewOnceMessageManager;
import org.thoughtcrime.securesms.ringrtc.RingRtcLogger;
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
import org.thoughtcrime.securesms.messages.IncomingMessageObserver;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.service.LocalBackupListener;
import org.thoughtcrime.securesms.revealable.ViewOnceMessageManager;
import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.VersionTracker;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;
import org.webrtc.voiceengine.WebRtcAudioManager;
import org.webrtc.voiceengine.WebRtcAudioUtils;
@@ -117,8 +112,8 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
initializeSecurityProvider();
initializeLogging();
initializeCrashHandling();
initializeFirstEverAppLaunch();
initializeAppDependencies();
initializeFirstEverAppLaunch();
initializeApplicationMigrations();
initializeMessageRetrieval();
initializeExpiringMessageManager();
@@ -131,10 +126,14 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
initializeCircumvention();
initializeRingRtc();
initializePendingMessages();
initializeUnidentifiedDeliveryAbilityRefresh();
initializeBlobProvider();
initializeCameraX();
initializeCleanup();
FeatureFlags.init();
NotificationChannels.create(this);
RefreshPreKeysJob.scheduleIfNecessary();
StorageSyncHelper.scheduleRoutineSync();
RegistrationUtil.markRegistrationPossiblyComplete();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
if (Build.VERSION.SDK_INT < 21) {
@@ -148,9 +147,13 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
public void onStart(@NonNull LifecycleOwner owner) {
isAppVisible = true;
Log.i(TAG, "App is now visible.");
FeatureFlags.refreshIfNecessary();
ApplicationDependencies.getRecipientCache().warmUp();
executePendingContactSync();
KeyCachingService.onAppForegrounded(this);
ApplicationDependencies.getFrameRateTracker().begin();
ApplicationDependencies.getMegaphoneRepository().onAppForegrounded();
catchUpOnMessages();
}
@Override
@@ -158,7 +161,8 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
isAppVisible = false;
Log.i(TAG, "App is no longer visible.");
KeyCachingService.onAppBackgrounded(this);
MessageNotifier.setVisibleThread(-1);
ApplicationDependencies.getMessageNotifier().clearVisibleThread();
ApplicationDependencies.getFrameRateTracker().end();
}
public ExpiringMessageManager getExpiringMessageManager() {
@@ -218,7 +222,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
private void initializeCrashHandling() {
final Thread.UncaughtExceptionHandler originalHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionLogger(originalHandler));
Thread.setDefaultUncaughtExceptionHandler(new SignalUncaughtExceptionHandler(originalHandler));
}
private void initializeApplicationMigrations() {
@@ -237,9 +241,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
if (TextSecurePreferences.getFirstInstallVersion(this) == -1) {
if (!SQLCipherOpenHelper.databaseFileExists(this)) {
Log.i(TAG, "First ever app launch!");
TextSecurePreferences.setAppMigrationVersion(this, ApplicationMigrations.CURRENT_VERSION);
TextSecurePreferences.setJobManagerVersion(this, JobManager.CURRENT_VERSION);
AppInitialization.onFirstEverAppLaunch(this);
}
Log.i(TAG, "Setting first install version to " + BuildConfig.CANONICAL_VERSION_CODE);
@@ -300,6 +302,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
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
@@ -319,9 +322,9 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true);
}
CallConnectionFactory.initialize(this);
CallManager.initialize(this, new RingRtcLogger());
} catch (UnsatisfiedLinkError e) {
Log.w(TAG, e);
throw new AssertionError("Unable to load ringrtc library", e);
}
}
@@ -362,29 +365,47 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
}
}
private void initializeUnidentifiedDeliveryAbilityRefresh() {
if (TextSecurePreferences.isMultiDevice(this) && !TextSecurePreferences.isUnidentifiedDeliveryEnabled(this)) {
ApplicationDependencies.getJobManager().add(new RefreshUnidentifiedDeliveryAbilityJob());
}
}
private void initializeBlobProvider() {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
SignalExecutors.BOUNDED.execute(() -> {
BlobProvider.getInstance().onSessionStart(this);
});
}
@SuppressLint("RestrictedApi")
private void initializeCameraX() {
if (CameraXUtil.isSupported()) {
new Thread(() -> {
try {
CameraX.init(this, Camera2AppConfig.create(this));
} catch (Throwable t) {
Log.w(TAG, "Failed to initialize CameraX.");
}
}, "signal-camerax-initialization").start();
private void initializeCleanup() {
SignalExecutors.BOUNDED.execute(() -> {
int deleted = DatabaseFactory.getAttachmentDatabase(this).deleteAbandonedPreuploadedAttachments();
Log.i(TAG, "Deleted " + deleted + " abandoned attachments.");
});
}
private void catchUpOnMessages() {
InitialMessageRetriever retriever = ApplicationDependencies.getInitialMessageRetriever();
if (retriever.isCaughtUp()) {
return;
}
SignalExecutors.UNBOUNDED.execute(() -> {
long startTime = System.currentTimeMillis();
switch (retriever.begin(TimeUnit.SECONDS.toMillis(60))) {
case SUCCESS:
Log.i(TAG, "Successfully caught up on messages. " + (System.currentTimeMillis() - startTime) + " ms");
break;
case FAILURE_TIMEOUT:
Log.w(TAG, "Did not finish catching up due to a timeout. " + (System.currentTimeMillis() - startTime) + " ms");
break;
case FAILURE_ERROR:
Log.w(TAG, "Did not finish catching up due to an error. " + (System.currentTimeMillis() - startTime) + " ms");
break;
case SKIPPED_ALREADY_CAUGHT_UP:
Log.i(TAG, "Already caught up. " + (System.currentTimeMillis() - startTime) + " ms");
break;
case SKIPPED_ALREADY_RUNNING:
Log.i(TAG, "Already in the process of catching up. " + (System.currentTimeMillis() - startTime) + " ms");
break;
}
});
}
@Override

View File

@@ -19,15 +19,17 @@ package org.thoughtcrime.securesms;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.PorterDuff;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.preference.Preference;
import org.thoughtcrime.securesms.help.HelpFragment;
import org.thoughtcrime.securesms.preferences.AdvancedPreferenceFragment;
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
import org.thoughtcrime.securesms.preferences.AppearancePreferenceFragment;
@@ -35,11 +37,14 @@ import org.thoughtcrime.securesms.preferences.ChatsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment;
import org.thoughtcrime.securesms.preferences.NotificationsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.SmsMmsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.StoragePreferenceFragment;
import org.thoughtcrime.securesms.preferences.widgets.ProfilePreference;
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ThemeUtil;
/**
* The Activity for application preference display and management.
@@ -60,7 +65,9 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
private static final String PREFERENCE_CATEGORY_APP_PROTECTION = "preference_category_app_protection";
private static final String PREFERENCE_CATEGORY_APPEARANCE = "preference_category_appearance";
private static final String PREFERENCE_CATEGORY_CHATS = "preference_category_chats";
private static final String PREFERENCE_CATEGORY_STORAGE = "preference_category_storage";
private static final String PREFERENCE_CATEGORY_DEVICES = "preference_category_devices";
private static final String PREFERENCE_CATEGORY_HELP = "preference_category_help";
private static final String PREFERENCE_CATEGORY_ADVANCED = "preference_category_advanced";
private final DynamicTheme dynamicTheme = new DynamicTheme();
@@ -105,7 +112,8 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStack();
} else {
Intent intent = new Intent(this, ConversationListActivity.class);
// TODO [greyson] Navigation
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
@@ -144,10 +152,23 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_APPEARANCE));
this.findPreference(PREFERENCE_CATEGORY_CHATS)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_CHATS));
this.findPreference(PREFERENCE_CATEGORY_STORAGE)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_STORAGE));
this.findPreference(PREFERENCE_CATEGORY_DEVICES)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_DEVICES));
this.findPreference(PREFERENCE_CATEGORY_HELP)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_HELP));
this.findPreference(PREFERENCE_CATEGORY_ADVANCED)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_ADVANCED));
tintIcons();
}
private void tintIcons() {
if (Build.VERSION.SDK_INT >= 21) return;
Preference preference = this.findPreference(PREFERENCE_CATEGORY_SMS_MMS);
preference.getIcon().setColorFilter(ThemeUtil.getThemedColor(requireContext(), R.attr.icon_tint), PorterDuff.Mode.SRC_IN);
}
@Override
@@ -213,6 +234,9 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
case PREFERENCE_CATEGORY_CHATS:
fragment = new ChatsPreferenceFragment();
break;
case PREFERENCE_CATEGORY_STORAGE:
fragment = new StoragePreferenceFragment();
break;
case PREFERENCE_CATEGORY_DEVICES:
Intent intent = new Intent(getActivity(), DeviceActivity.class);
startActivity(intent);
@@ -220,6 +244,9 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
case PREFERENCE_CATEGORY_ADVANCED:
fragment = new AdvancedPreferenceFragment();
break;
case PREFERENCE_CATEGORY_HELP:
fragment = new HelpFragment();
break;
default:
throw new AssertionError();
}
@@ -245,11 +272,12 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
private class ProfileClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
Intent intent = new Intent(preference.getContext(), CreateProfileActivity.class);
intent.putExtra(CreateProfileActivity.EXCLUDE_SYSTEM, true);
Intent intent = new Intent(preference.getContext(), EditProfileActivity.class);
intent.putExtra(EditProfileActivity.EXCLUDE_SYSTEM, true);
intent.putExtra(EditProfileActivity.DISPLAY_USERNAME, true);
intent.putExtra(EditProfileActivity.NEXT_BUTTON_TEXT, R.string.save);
getActivity().startActivity(intent);
// ((BaseActionBarActivity)getActivity()).startActivitySceneTransition(intent, getActivity().findViewById(R.id.avatar), "avatar");
requireActivity().startActivity(intent);
return true;
}
}

View File

@@ -0,0 +1,152 @@
package org.thoughtcrime.securesms;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityOptionsCompat;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
/**
* Activity for displaying avatars full screen.
*/
public final class AvatarPreviewActivity extends PassphraseRequiredActionBarActivity {
private static final String TAG = Log.tag(AvatarPreviewActivity.class);
private static final String RECIPIENT_ID_EXTRA = "recipient_id";
public static @NonNull Intent intentFromRecipientId(@NonNull Context context,
@NonNull RecipientId recipientId)
{
Intent intent = new Intent(context, AvatarPreviewActivity.class);
intent.putExtra(RECIPIENT_ID_EXTRA, recipientId.serialize());
return intent;
}
public static Bundle createTransitionBundle(@NonNull Activity activity, @NonNull View from) {
return ActivityOptionsCompat.makeSceneTransitionAnimation(activity, from, "avatar").toBundle();
}
@Override
protected void onCreate(Bundle savedInstanceState, boolean ready) {
super.onCreate(savedInstanceState, ready);
setTheme(R.style.TextSecure_MediaPreview);
setContentView(R.layout.contact_photo_preview_activity);
Toolbar toolbar = findViewById(R.id.toolbar);
ImageView avatar = findViewById(R.id.avatar);
setSupportActionBar(toolbar);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
showSystemUI();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Context context = getApplicationContext();
RecipientId recipientId = RecipientId.from(getIntent().getStringExtra(RECIPIENT_ID_EXTRA));
Recipient.live(recipientId).observe(this, recipient -> {
ContactPhoto contactPhoto = recipient.isLocalNumber() ? new ProfileContactPhoto(recipient, recipient.getProfileAvatar())
: recipient.getContactPhoto();
FallbackContactPhoto fallbackPhoto = recipient.isLocalNumber() ? new ResourceContactPhoto(R.drawable.ic_profile_outline_40, R.drawable.ic_profile_outline_20, R.drawable.ic_person_large)
: recipient.getFallbackContactPhoto();
GlideApp.with(this).load(contactPhoto)
.fallback(fallbackPhoto.asCallCard(this))
.error(fallbackPhoto.asCallCard(this))
.diskCacheStrategy(DiskCacheStrategy.ALL)
.addListener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
Log.w(TAG, "Unable to load avatar, or avatar removed, closing");
finish();
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
return false;
}
})
.into(avatar);
toolbar.setTitle(recipient.toShortString(context));
});
avatar.setOnClickListener(v -> toggleUiVisibility());
showAndHideWithSystemUI(getWindow(), findViewById(R.id.toolbar_layout));
}
private static void showAndHideWithSystemUI(@NonNull Window window, @NonNull View... views) {
window.getDecorView().setOnSystemUiVisibilityChangeListener(visibility -> {
boolean hide = (visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
for (View view : views) {
view.animate()
.alpha(hide ? 0 : 1)
.start();
}
});
}
private void toggleUiVisibility() {
int systemUiVisibility = getWindow().getDecorView().getSystemUiVisibility();
if ((systemUiVisibility & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) {
showSystemUI();
} else {
hideSystemUI();
}
}
private void hideSystemUI() {
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_IMMERSIVE |
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_FULLSCREEN );
}
private void showSystemUI() {
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN );
}
@Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
}

View File

@@ -5,9 +5,10 @@ import androidx.annotation.Nullable;
import android.view.View;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.database.model.ReactionRecord;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -44,5 +45,7 @@ public interface BindableConversationItem extends Unbindable {
void onAddToContactsClicked(@NonNull Contact contact);
void onMessageSharedContactClicked(@NonNull List<Recipient> choices);
void onInviteSharedContactClicked(@NonNull List<Recipient> choices);
void onReactionClicked(long messageId, boolean isMms);
void onGroupMemberAvatarClicked(@NonNull RecipientId recipientId, @NonNull GroupId groupId);
}
}

View File

@@ -0,0 +1,17 @@
package org.thoughtcrime.securesms;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.mms.GlideRequests;
import java.util.Locale;
import java.util.Set;
public interface BindableConversationListItem extends Unbindable {
void bind(@NonNull ThreadRecord thread,
@NonNull GlideRequests glideRequests, @NonNull Locale locale,
@NonNull Set<Long> typingThreads,
@NonNull Set<Long> selectedThreads, boolean batchMode);
}

View File

@@ -0,0 +1,128 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import android.content.res.Resources;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.Lifecycle;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
/**
* This should be used whenever we want to prompt the user to block/unblock a recipient.
*/
public final class BlockUnblockDialog {
private BlockUnblockDialog() { }
public static void showBlockFor(@NonNull Context context,
@NonNull Lifecycle lifecycle,
@NonNull Recipient recipient,
@NonNull Runnable onBlock)
{
SimpleTask.run(lifecycle,
() -> buildBlockFor(context, recipient, onBlock, null),
AlertDialog.Builder::show);
}
public static void showBlockAndDeleteFor(@NonNull Context context,
@NonNull Lifecycle lifecycle,
@NonNull Recipient recipient,
@NonNull Runnable onBlock,
@NonNull Runnable onBlockAndDelete)
{
SimpleTask.run(lifecycle,
() -> buildBlockFor(context, recipient, onBlock, onBlockAndDelete),
AlertDialog.Builder::show);
}
public static void showUnblockFor(@NonNull Context context,
@NonNull Lifecycle lifecycle,
@NonNull Recipient recipient,
@NonNull Runnable onUnblock)
{
SimpleTask.run(lifecycle,
() -> buildUnblockFor(context, recipient, onUnblock),
AlertDialog.Builder::show);
}
@WorkerThread
private static AlertDialog.Builder buildBlockFor(@NonNull Context context,
@NonNull Recipient recipient,
@NonNull Runnable onBlock,
@Nullable Runnable onBlockAndDelete)
{
recipient = recipient.resolve();
AlertDialog.Builder builder = new AlertDialog.Builder(context);
Resources resources = context.getResources();
if (recipient.isGroup()) {
if (DatabaseFactory.getGroupDatabase(context).isActive(recipient.requireGroupId())) {
builder.setTitle(resources.getString(R.string.BlockUnblockDialog_block_and_leave_s, recipient.getDisplayName(context)));
builder.setMessage(R.string.BlockUnblockDialog_you_will_no_longer_receive_messages_or_updates);
builder.setPositiveButton(R.string.BlockUnblockDialog_block_and_leave, ((dialog, which) -> onBlock.run()));
builder.setNegativeButton(android.R.string.cancel, null);
} else {
builder.setTitle(resources.getString(R.string.BlockUnblockDialog_block_s, recipient.getDisplayName(context)));
builder.setMessage(R.string.BlockUnblockDialog_group_members_wont_be_able_to_add_you);
builder.setPositiveButton(R.string.RecipientPreferenceActivity_block, ((dialog, which) -> onBlock.run()));
builder.setNegativeButton(android.R.string.cancel, null);
}
} else {
builder.setTitle(resources.getString(R.string.BlockUnblockDialog_block_s, recipient.getDisplayName(context)));
builder.setMessage(R.string.BlockUnblockDialog_blocked_people_wont_be_able_to_call_you_or_send_you_messages);
if (onBlockAndDelete != null) {
builder.setNeutralButton(android.R.string.cancel, null);
builder.setPositiveButton(R.string.BlockUnblockDialog_block_and_delete, (d, w) -> onBlockAndDelete.run());
builder.setNegativeButton(R.string.BlockUnblockDialog_block, (d, w) -> onBlock.run());
} else {
builder.setPositiveButton(R.string.BlockUnblockDialog_block, ((dialog, which) -> onBlock.run()));
builder.setNegativeButton(android.R.string.cancel, null);
}
}
return builder;
}
@WorkerThread
private static AlertDialog.Builder buildUnblockFor(@NonNull Context context,
@NonNull Recipient recipient,
@NonNull Runnable onUnblock)
{
recipient = recipient.resolve();
AlertDialog.Builder builder = new AlertDialog.Builder(context);
Resources resources = context.getResources();
if (recipient.isGroup()) {
if (DatabaseFactory.getGroupDatabase(context).isActive(recipient.requireGroupId())) {
builder.setTitle(resources.getString(R.string.BlockUnblockDialog_unblock_s, recipient.getDisplayName(context)));
builder.setMessage(R.string.BlockUnblockDialog_group_members_will_be_able_to_add_you);
builder.setPositiveButton(R.string.RecipientPreferenceActivity_unblock, ((dialog, which) -> onUnblock.run()));
builder.setNegativeButton(android.R.string.cancel, null);
} else {
builder.setTitle(resources.getString(R.string.BlockUnblockDialog_unblock_s, recipient.getDisplayName(context)));
builder.setMessage(R.string.BlockUnblockDialog_group_members_will_be_able_to_add_you);
builder.setPositiveButton(R.string.RecipientPreferenceActivity_unblock, ((dialog, which) -> onUnblock.run()));
builder.setNegativeButton(android.R.string.cancel, null);
}
} else {
builder.setTitle(resources.getString(R.string.BlockUnblockDialog_unblock_s, recipient.getDisplayName(context)));
builder.setMessage(R.string.BlockUnblockDialog_you_will_be_able_to_call_and_message_each_other);
builder.setPositiveButton(R.string.RecipientPreferenceActivity_unblock, ((dialog, which) -> onUnblock.run()));
builder.setNegativeButton(android.R.string.cancel, null);
}
return builder;
}
}

View File

@@ -1,15 +1,8 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.ListFragment;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.cursoradapter.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
@@ -17,6 +10,13 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.cursoradapter.widget.CursorAdapter;
import androidx.fragment.app.ListFragment;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.loaders.BlockedContactsLoader;
import org.thoughtcrime.securesms.mms.GlideApp;
@@ -25,21 +25,18 @@ import org.thoughtcrime.securesms.preferences.BlockedContactListItem;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.DynamicTheme;
public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity {
private final DynamicTheme dynamicTheme = new DynamicTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private final DynamicTheme dynamicTheme = new DynamicTheme();
@Override
public void onPreCreate() {
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
}
@Override
public void onCreate(Bundle bundle, boolean ready) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@@ -51,16 +48,12 @@ public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity
public void onResume() {
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home: finish(); return true;
}
return false;
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
public static class BlockedContactsFragment
@@ -76,14 +69,14 @@ public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setListAdapter(new BlockedContactAdapter(getActivity(), GlideApp.with(this), null));
getLoaderManager().initLoader(0, null, this);
setListAdapter(new BlockedContactAdapter(requireActivity(), GlideApp.with(this), null));
LoaderManager.getInstance(this).initLoader(0, null, this);
}
@Override
public void onStart() {
super.onStart();
getLoaderManager().restartLoader(0, null, this);
LoaderManager.getInstance(this).restartLoader(0, null, this);
}
@Override
@@ -114,10 +107,10 @@ public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Recipient recipient = ((BlockedContactListItem)view).getRecipient();
Intent intent = new Intent(getActivity(), RecipientPreferenceActivity.class);
intent.putExtra(RecipientPreferenceActivity.RECIPIENT_ID, recipient.getId());
startActivity(intent);
BlockUnblockDialog.showUnblockFor(requireContext(), getLifecycle(), recipient, () -> {
RecipientUtil.unblock(requireContext(), recipient);
LoaderManager.getInstance(this).restartLoader(0, null, this);
});
}
private static class BlockedContactAdapter extends CursorAdapter {
@@ -143,7 +136,5 @@ public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity
((BlockedContactListItem) view).set(glideRequests, recipient);
}
}
}
}

View File

@@ -0,0 +1,49 @@
package org.thoughtcrime.securesms;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.ContextThemeWrapper;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.ThemeUtil;
public class ClearProfileAvatarActivity extends Activity {
private static final String ARG_TITLE = "arg_title";
public static Intent createForUserProfilePhoto() {
return new Intent("org.thoughtcrime.securesms.action.CLEAR_PROFILE_PHOTO");
}
public static Intent createForGroupProfilePhoto() {
Intent intent = new Intent("org.thoughtcrime.securesms.action.CLEAR_PROFILE_PHOTO");
intent.putExtra(ARG_TITLE, R.string.ClearProfileActivity_remove_group_photo);
return intent;
}
@Override
public void onResume() {
super.onResume();
int titleId = getIntent().getIntExtra(ARG_TITLE, R.string.ClearProfileActivity_remove_profile_photo);
new AlertDialog.Builder(new ContextThemeWrapper(this, DynamicTheme.isDarkTheme(this) ? R.style.TextSecure_DarkTheme : R.style.TextSecure_LightTheme))
.setMessage(titleId)
.setNegativeButton(android.R.string.cancel, (dialog, which) -> finish())
.setPositiveButton(R.string.ClearProfileActivity_remove, (dialog, which) -> {
Intent result = new Intent();
result.putExtra("delete", true);
setResult(Activity.RESULT_OK, result);
finish();
})
.setOnCancelListener(dialog -> finish())
.show();
}
}

View File

@@ -5,12 +5,13 @@ import android.content.Context;
import android.content.DialogInterface;
import android.database.Cursor;
import android.os.AsyncTask;
import androidx.appcompat.app.AlertDialog;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
@@ -20,13 +21,15 @@ import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
import org.thoughtcrime.securesms.jobs.PushDecryptMessageJob;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.VerifySpan;
import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
@@ -48,7 +51,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
super(context);
Recipient recipient = Recipient.resolved(mismatch.getRecipientId(context));
String name = recipient.toShortString();
String name = recipient.toShortString(context);
String introduction = context.getString(R.string.ConfirmIdentityDialog_your_safety_number_with_s_has_changed, name, name);
SpannableString spannableString = new SpannableString(introduction + " " +
context.getString(R.string.ConfirmIdentityDialog_you_may_wish_to_verify_your_safety_number_with_this_contact));
@@ -95,7 +98,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
@Override
protected Void doInBackground(Void... params) {
synchronized (SESSION_LOCK) {
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(Recipient.resolved(recipientId).requireAddress().toPhoneString(), 1);
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(Recipient.resolved(recipientId).requireServiceId(), 1);
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext());
identityKeyStore.saveIdentity(mismatchAddress, mismatch.getIdentityKey(), true);
@@ -167,7 +170,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
boolean legacy = !messageRecord.isContentBundleKeyExchange();
SignalServiceEnvelope envelope = new SignalServiceEnvelope(SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE,
messageRecord.getIndividualRecipient().requireAddress().toPhoneString(),
Optional.of(RecipientUtil.toSignalServiceAddress(getContext(), messageRecord.getIndividualRecipient())),
messageRecord.getRecipientDeviceId(),
messageRecord.getDateSent(),
legacy ? Base64.decode(messageRecord.getBody()) : null,
@@ -176,7 +179,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
long pushId = pushDatabase.insert(envelope);
ApplicationDependencies.getJobManager().add(new PushDecryptJob(getContext(), pushId, messageRecord.getId()));
ApplicationDependencies.getJobManager().add(new PushDecryptMessageJob(getContext(), pushId, messageRecord.getId()));
} catch (IOException e) {
throw new AssertionError(e);
}

View File

@@ -19,18 +19,19 @@ package org.thoughtcrime.securesms;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.thoughtcrime.securesms.logging.Log;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
import java.lang.ref.WeakReference;
@@ -43,12 +44,14 @@ import java.lang.ref.WeakReference;
*/
public abstract class ContactSelectionActivity extends PassphraseRequiredActionBarActivity
implements SwipeRefreshLayout.OnRefreshListener,
ContactSelectionListFragment.OnContactSelectedListener
ContactSelectionListFragment.OnContactSelectedListener,
ContactSelectionListFragment.ScrollCallback
{
private static final String TAG = ContactSelectionActivity.class.getSimpleName();
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
public static final String EXTRA_LAYOUT_RES_ID = "layout_res_id";
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
protected ContactSelectionListFragment contactsFragment;
@@ -57,18 +60,17 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
}
@Override
protected void onCreate(Bundle icicle, boolean ready) {
if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) {
int displayMode = TextSecurePreferences.isSmsEnabled(this) ? DisplayMode.FLAG_ALL
: DisplayMode.FLAG_PUSH | DisplayMode.FLAG_ACTIVE_GROUPS | DisplayMode.FLAG_INACTIVE_GROUPS;
: DisplayMode.FLAG_PUSH | DisplayMode.FLAG_ACTIVE_GROUPS | DisplayMode.FLAG_INACTIVE_GROUPS | DisplayMode.FLAG_SELF;
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, displayMode);
}
setContentView(R.layout.contact_selection_activity);
setContentView(getIntent().getIntExtra(EXTRA_LAYOUT_RES_ID, R.layout.contact_selection_activity));
initializeToolbar();
initializeResources();
@@ -79,7 +81,6 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
public void onResume() {
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
}
protected ContactFilterToolbar getToolbar() {
@@ -87,10 +88,9 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
}
private void initializeToolbar() {
this.toolbar = ViewUtil.findById(this, R.id.toolbar);
this.toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
assert getSupportActionBar() != null;
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
getSupportActionBar().setDisplayShowTitleEnabled(false);
getSupportActionBar().setIcon(null);
@@ -113,10 +113,21 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
}
@Override
public void onContactSelected(String number) {}
public void onContactSelected(Optional<RecipientId> recipientId, String number) {}
@Override
public void onContactDeselected(String number) {}
public void onContactDeselected(Optional<RecipientId> recipientId, String number) {}
@Override
public void onBeginScroll() {
hideKeyboard();
}
private void hideKeyboard() {
ServiceUtil.getInputMethodManager(this)
.hideSoftInputFromWindow(toolbar.getWindowToken(), 0);
toolbar.clearFocus();
}
private static class RefreshDirectoryTask extends AsyncTask<Context, Void, Void> {
@@ -128,7 +139,6 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
@Override
protected Void doInBackground(Context... params) {
try {
DirectoryHelper.refreshDirectory(params[0], true);
} catch (IOException e) {

View File

@@ -0,0 +1,646 @@
/*
* Copyright (C) 2015 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms;
import android.Manifest;
import android.animation.LayoutTransition;
import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.CycleInterpolator;
import android.widget.Button;
import android.widget.HorizontalScrollView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.transition.AutoTransition;
import androidx.transition.TransitionManager;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import com.google.android.material.chip.ChipGroup;
import com.pnikosis.materialishprogress.ProgressWheel;
import org.thoughtcrime.securesms.components.RecyclerViewFastScroller;
import org.thoughtcrime.securesms.contacts.ContactChip;
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter;
import org.thoughtcrime.securesms.contacts.ContactSelectionListItem;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.SelectedContact;
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.UsernameUtil;
import org.thoughtcrime.securesms.util.adapter.FixedViewsAdapter;
import org.thoughtcrime.securesms.util.adapter.RecyclerViewConcatenateAdapterStickyHeader;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Set;
/**
* Fragment for selecting a one or more contacts from a list.
*
* @author Moxie Marlinspike
*
*/
public final class ContactSelectionListFragment extends Fragment
implements LoaderManager.LoaderCallbacks<Cursor>
{
@SuppressWarnings("unused")
private static final String TAG = Log.tag(ContactSelectionListFragment.class);
private static final int CHIP_GROUP_EMPTY_CHILD_COUNT = 1;
private static final int CHIP_GROUP_REVEAL_DURATION_MS = 150;
public static final int NO_LIMIT = Integer.MAX_VALUE;
public static final String DISPLAY_MODE = "display_mode";
public static final String MULTI_SELECT = "multi_select";
public static final String REFRESHABLE = "refreshable";
public static final String RECENTS = "recents";
public static final String TOTAL_CAPACITY = "total_capacity";
public static final String CURRENT_SELECTION = "current_selection";
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 TextView groupLimit;
@Nullable private FixedViewsAdapter headerAdapter;
@Nullable private FixedViewsAdapter footerAdapter;
@Nullable private ListCallback listCallback;
@Nullable private ScrollCallback scrollCallback;
private GlideRequests glideRequests;
private int selectionLimit;
private Set<RecipientId> currentSelection;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof ListCallback) {
listCallback = (ListCallback) context;
}
if (context instanceof ScrollCallback) {
scrollCallback = (ScrollCallback) context;
}
}
@Override
public void onActivityCreated(Bundle icicle) {
super.onActivityCreated(icicle);
initializeCursor();
}
@Override
public void onStart() {
super.onStart();
Permissions.with(this)
.request(Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_CONTACTS)
.ifNecessary()
.onAllGranted(() -> {
if (!TextSecurePreferences.hasSuccessfullyRetrievedDirectory(getActivity())) {
handleContactPermissionGranted();
} else {
LoaderManager.getInstance(this).initLoader(0, null, this);
}
})
.onAnyDenied(() -> {
FragmentActivity activity = requireActivity();
activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
if (activity.getIntent().getBooleanExtra(RECENTS, false)) {
LoaderManager.getInstance(this).initLoader(0, null, ContactSelectionListFragment.this);
} else {
initializeNoContactsPermission();
}
})
.execute();
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.contact_selection_list_fragment, container, false);
emptyText = view.findViewById(android.R.id.empty);
recyclerView = view.findViewById(R.id.recycler_view);
swipeRefresh = view.findViewById(R.id.swipe_refresh);
fastScroller = view.findViewById(R.id.fast_scroller);
showContactsLayout = view.findViewById(R.id.show_contacts_container);
showContactsButton = view.findViewById(R.id.show_contacts_button);
showContactsDescription = view.findViewById(R.id.show_contacts_description);
showContactsProgress = view.findViewById(R.id.progress);
chipGroup = view.findViewById(R.id.chipGroup);
chipGroupScrollContainer = view.findViewById(R.id.chipGroupScrollContainer);
groupLimit = view.findViewById(R.id.group_limit);
constraintLayout = view.findViewById(R.id.container);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setItemAnimator(new DefaultItemAnimator() {
@Override
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) {
return true;
}
});
swipeRefresh.setEnabled(requireActivity().getIntent().getBooleanExtra(REFRESHABLE, true));
selectionLimit = requireActivity().getIntent().getIntExtra(TOTAL_CAPACITY, NO_LIMIT);
currentSelection = getCurrentSelection();
updateGroupLimit(getChipCount());
return view;
}
private void updateGroupLimit(int chipCount) {
if (selectionLimit != NO_LIMIT) {
groupLimit.setText(String.format(Locale.getDefault(), "%d/%d", currentSelection.size() + chipCount, selectionLimit));
groupLimit.setVisibility(View.VISIBLE);
} else {
groupLimit.setVisibility(View.GONE);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}
public @NonNull List<SelectedContact> getSelectedContacts() {
if (cursorRecyclerViewAdapter == null) {
return Collections.emptyList();
}
return cursorRecyclerViewAdapter.getSelectedContacts();
}
public int getSelectedContactsCount() {
if (cursorRecyclerViewAdapter == null) {
return 0;
}
return cursorRecyclerViewAdapter.getSelectedContactsCount();
}
private Set<RecipientId> getCurrentSelection() {
List<RecipientId> currentSelection = requireActivity().getIntent().getParcelableArrayListExtra(CURRENT_SELECTION);
return currentSelection == null ? Collections.emptySet()
: Collections.unmodifiableSet(Stream.of(currentSelection).collect(Collectors.toSet()));
}
public boolean isMulti() {
return requireActivity().getIntent().getBooleanExtra(MULTI_SELECT, false);
}
private void initializeCursor() {
glideRequests = GlideApp.with(this);
cursorRecyclerViewAdapter = new ContactSelectionListAdapter(requireContext(),
glideRequests,
null,
new ListClickListener(),
isMulti(),
currentSelection);
RecyclerViewConcatenateAdapterStickyHeader concatenateAdapter = new RecyclerViewConcatenateAdapterStickyHeader();
if (listCallback != null && FeatureFlags.newGroupUI()) {
if (FeatureFlags.groupsV2create() && FeatureFlags.groupsV2internalTest()) {
headerAdapter = new FixedViewsAdapter(createNewGroupItem(listCallback), createNewGroupsV1GroupItem(listCallback));
} else {
headerAdapter = new FixedViewsAdapter(createNewGroupItem(listCallback));
}
headerAdapter.hide();
concatenateAdapter.addAdapter(headerAdapter);
}
concatenateAdapter.addAdapter(cursorRecyclerViewAdapter);
if (listCallback != null) {
footerAdapter = new FixedViewsAdapter(createInviteActionView(listCallback));
footerAdapter.hide();
concatenateAdapter.addAdapter(footerAdapter);
}
recyclerView.setAdapter(concatenateAdapter);
recyclerView.addItemDecoration(new StickyHeaderDecoration(concatenateAdapter, true, true));
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
if (scrollCallback != null) {
scrollCallback.onBeginScroll();
}
}
}
});
}
private View createInviteActionView(@NonNull ListCallback listCallback) {
View view = LayoutInflater.from(requireContext())
.inflate(R.layout.contact_selection_invite_action_item, (ViewGroup) requireView(), false);
view.setOnClickListener(v -> listCallback.onInvite());
return view;
}
private View createNewGroupItem(@NonNull ListCallback listCallback) {
View view = LayoutInflater.from(requireContext())
.inflate(R.layout.contact_selection_new_group_item, (ViewGroup) requireView(), false);
view.setOnClickListener(v -> listCallback.onNewGroup(false));
return view;
}
private View createNewGroupsV1GroupItem(@NonNull ListCallback listCallback) {
View view = LayoutInflater.from(requireContext())
.inflate(R.layout.contact_selection_new_group_v1_item, (ViewGroup) requireView(), false);
view.setOnClickListener(v -> listCallback.onNewGroup(true));
return view;
}
private void initializeNoContactsPermission() {
swipeRefresh.setVisibility(View.GONE);
showContactsLayout.setVisibility(View.VISIBLE);
showContactsProgress.setVisibility(View.INVISIBLE);
showContactsDescription.setText(R.string.contact_selection_list_fragment__signal_needs_access_to_your_contacts_in_order_to_display_them);
showContactsButton.setVisibility(View.VISIBLE);
showContactsButton.setOnClickListener(v -> {
Permissions.with(this)
.request(Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_CONTACTS)
.ifNecessary()
.withPermanentDenialDialog(getString(R.string.ContactSelectionListFragment_signal_requires_the_contacts_permission_in_order_to_display_your_contacts))
.onSomeGranted(permissions -> {
if (permissions.contains(Manifest.permission.WRITE_CONTACTS)) {
handleContactPermissionGranted();
}
})
.execute();
});
}
public void setQueryFilter(String filter) {
this.cursorFilter = filter;
LoaderManager.getInstance(this).restartLoader(0, null, this);
}
public void resetQueryFilter() {
setQueryFilter(null);
swipeRefresh.setRefreshing(false);
}
public boolean hasQueryFilter() {
return !TextUtils.isEmpty(cursorFilter);
}
public void setRefreshing(boolean refreshing) {
swipeRefresh.setRefreshing(refreshing);
}
public void reset() {
cursorRecyclerViewAdapter.clearSelectedContacts();
if (!isDetached() && !isRemoving() && getActivity() != null && !getActivity().isFinishing()) {
LoaderManager.getInstance(this).restartLoader(0, null, this);
}
}
@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));
}
@Override
public void onLoadFinished(@NonNull Loader<Cursor> loader, @Nullable Cursor data) {
swipeRefresh.setVisibility(View.VISIBLE);
showContactsLayout.setVisibility(View.GONE);
cursorRecyclerViewAdapter.changeCursor(data);
if (footerAdapter != null) {
footerAdapter.show();
}
if (headerAdapter != null) {
if (TextUtils.isEmpty(cursorFilter)) {
headerAdapter.show();
} else {
headerAdapter.hide();
}
}
emptyText.setText(R.string.contact_selection_group_activity__no_contacts);
boolean useFastScroller = data != null && data.getCount() > 20;
recyclerView.setVerticalScrollBarEnabled(!useFastScroller);
if (useFastScroller) {
fastScroller.setVisibility(View.VISIBLE);
fastScroller.setRecyclerView(recyclerView);
} else {
fastScroller.setRecyclerView(null);
fastScroller.setVisibility(View.GONE);
}
}
@Override
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
cursorRecyclerViewAdapter.changeCursor(null);
fastScroller.setVisibility(View.GONE);
}
@SuppressLint("StaticFieldLeak")
private void handleContactPermissionGranted() {
final Context context = requireContext();
new AsyncTask<Void, Void, Boolean>() {
@Override
protected void onPreExecute() {
swipeRefresh.setVisibility(View.GONE);
showContactsLayout.setVisibility(View.VISIBLE);
showContactsButton.setVisibility(View.INVISIBLE);
showContactsDescription.setText(R.string.ConversationListFragment_loading);
showContactsProgress.setVisibility(View.VISIBLE);
showContactsProgress.spin();
}
@Override
protected Boolean doInBackground(Void... voids) {
try {
DirectoryHelper.refreshDirectory(context, false);
return true;
} catch (IOException e) {
Log.w(TAG, e);
}
return false;
}
@Override
protected void onPostExecute(Boolean result) {
if (result) {
showContactsLayout.setVisibility(View.GONE);
swipeRefresh.setVisibility(View.VISIBLE);
reset();
} else {
Toast.makeText(getContext(), R.string.ContactSelectionListFragment_error_retrieving_contacts_check_your_network_connection, Toast.LENGTH_LONG).show();
initializeNoContactsPermission();
}
}
}.execute();
}
private class ListClickListener implements ContactSelectionListAdapter.ItemClickListener {
@Override
public void onItemClick(ContactSelectionListItem contact) {
SelectedContact selectedContact = contact.isUsernameType() ? SelectedContact.forUsername(contact.getRecipientId().orNull(), contact.getNumber())
: SelectedContact.forPhone(contact.getRecipientId().orNull(), contact.getNumber());
if (!isMulti() || !cursorRecyclerViewAdapter.isSelectedContact(selectedContact)) {
if (selectionLimitReached()) {
Toast.makeText(requireContext(), R.string.ContactSelectionListFragment_the_group_is_full, Toast.LENGTH_SHORT).show();
groupLimit.animate().scaleX(1.3f).scaleY(1.3f).setInterpolator(new CycleInterpolator(0.5f)).start();
return;
}
if (contact.isUsernameType()) {
AlertDialog loadingDialog = SimpleProgressDialog.show(requireContext());
SimpleTask.run(getViewLifecycleOwner().getLifecycle(), () -> {
return UsernameUtil.fetchUuidForUsername(requireContext(), contact.getNumber());
}, uuid -> {
loadingDialog.dismiss();
if (uuid.isPresent()) {
Recipient recipient = Recipient.externalUsername(requireContext(), uuid.get(), contact.getNumber());
SelectedContact selected = SelectedContact.forUsername(recipient.getId(), contact.getNumber());
markContactSelected(selected);
cursorRecyclerViewAdapter.notifyItemChanged(recyclerView.getChildAdapterPosition(contact), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
if (onContactSelectedListener != null) {
onContactSelectedListener.onContactSelected(Optional.of(recipient.getId()), null);
}
} else {
new AlertDialog.Builder(requireContext())
.setTitle(R.string.ContactSelectionListFragment_username_not_found)
.setMessage(getString(R.string.ContactSelectionListFragment_s_is_not_a_signal_user, contact.getNumber()))
.setPositiveButton(R.string.ContactSelectionListFragment_okay, (dialog, which) -> dialog.dismiss())
.show();
}
});
} else {
markContactSelected(selectedContact);
cursorRecyclerViewAdapter.notifyItemChanged(recyclerView.getChildAdapterPosition(contact), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
if (onContactSelectedListener != null) {
onContactSelectedListener.onContactSelected(contact.getRecipientId(), contact.getNumber());
}
}
} else {
markContactUnselected(selectedContact);
cursorRecyclerViewAdapter.notifyItemChanged(recyclerView.getChildAdapterPosition(contact), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
if (onContactSelectedListener != null) {
onContactSelectedListener.onContactDeselected(contact.getRecipientId(), contact.getNumber());
}
}}
}
private boolean selectionLimitReached() {
return getChipCount() >= selectionLimit;
}
private void markContactSelected(@NonNull SelectedContact selectedContact) {
cursorRecyclerViewAdapter.addSelectedContact(selectedContact);
if (isMulti() && FeatureFlags.newGroupUI()) {
addChipForSelectedContact(selectedContact);
}
}
private void markContactUnselected(@NonNull SelectedContact selectedContact) {
cursorRecyclerViewAdapter.removeFromSelectedContacts(selectedContact);
cursorRecyclerViewAdapter.notifyItemRangeChanged(0, cursorRecyclerViewAdapter.getItemCount(), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
removeChipForContact(selectedContact);
}
private void removeChipForContact(@NonNull SelectedContact contact) {
for (int i = chipGroup.getChildCount() - 1; i >= 0; i--) {
View v = chipGroup.getChildAt(i);
if (v instanceof ContactChip && contact.matches(((ContactChip) v).getContact())) {
chipGroup.removeView(v);
}
}
updateGroupLimit(getChipCount());
if (getChipCount() == 0) {
setChipGroupVisibility(ConstraintSet.GONE);
}
}
private void addChipForSelectedContact(@NonNull SelectedContact selectedContact) {
SimpleTask.run(getViewLifecycleOwner().getLifecycle(),
() -> Recipient.resolved(selectedContact.getOrCreateRecipientId(requireContext())),
resolved -> addChipForRecipient(resolved, selectedContact));
}
private void addChipForRecipient(@NonNull Recipient recipient, @NonNull SelectedContact selectedContact) {
final ContactChip chip = new ContactChip(requireContext());
if (getChipCount() == 0) {
setChipGroupVisibility(ConstraintSet.VISIBLE);
}
chip.setText(recipient.getShortDisplayName(requireContext()));
chip.setContact(selectedContact);
chip.setCloseIconVisible(true);
chip.setOnCloseIconClickListener(view -> {
markContactUnselected(selectedContact);
if (onContactSelectedListener != null) {
onContactSelectedListener.onContactDeselected(Optional.of(recipient.getId()), recipient.getE164().orNull());
}
});
chipGroup.getLayoutTransition().addTransitionListener(new LayoutTransition.TransitionListener() {
@Override
public void startTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType) {
}
@Override
public void endTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType) {
if (view == chip && transitionType == LayoutTransition.APPEARING) {
chipGroup.getLayoutTransition().removeTransitionListener(this);
registerChipRecipientObserver(chip, recipient.live());
chipGroup.post(ContactSelectionListFragment.this::smoothScrollChipsToEnd);
}
}
});
chip.setAvatar(glideRequests, recipient, () -> addChip(chip));
}
private void addChip(@NonNull ContactChip chip) {
chipGroup.addView(chip);
updateGroupLimit(getChipCount());
}
private int getChipCount() {
int count = chipGroup.getChildCount() - CHIP_GROUP_EMPTY_CHILD_COUNT;
if (count < 0) throw new AssertionError();
return count;
}
private void registerChipRecipientObserver(@NonNull ContactChip chip, @Nullable LiveRecipient recipient) {
if (recipient != null) {
recipient.observe(getViewLifecycleOwner(), resolved -> {
if (chip.isAttachedToWindow()) {
chip.setAvatar(glideRequests, resolved, null);
chip.setText(resolved.getShortDisplayName(chip.getContext()));
}
});
}
}
private void setChipGroupVisibility(int visibility) {
TransitionManager.beginDelayedTransition(constraintLayout, new AutoTransition().setDuration(CHIP_GROUP_REVEAL_DURATION_MS));
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(constraintLayout);
constraintSet.setVisibility(R.id.chipGroupScrollContainer, visibility);
constraintSet.applyTo(constraintLayout);
}
public void setOnContactSelectedListener(OnContactSelectedListener onContactSelectedListener) {
this.onContactSelectedListener = onContactSelectedListener;
}
public void setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener onRefreshListener) {
this.swipeRefresh.setOnRefreshListener(onRefreshListener);
}
private void smoothScrollChipsToEnd() {
int x = chipGroupScrollContainer.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR ? chipGroup.getWidth() : 0;
chipGroupScrollContainer.smoothScrollTo(x, 0);
}
public interface OnContactSelectedListener {
void onContactSelected(Optional<RecipientId> recipientId, String number);
void onContactDeselected(Optional<RecipientId> recipientId, String number);
}
public interface ListCallback {
void onInvite();
void onNewGroup(boolean forceV1);
}
public interface ScrollCallback {
void onBeginScroll();
}
}

View File

@@ -149,7 +149,8 @@ public class DatabaseMigrationActivity extends PassphraseRequiredActionBarActivi
if (getIntent().hasExtra("next_intent")) {
startActivity((Intent)getIntent().getParcelableExtra("next_intent"));
} else {
startActivity(new Intent(this, ConversationListActivity.class));
// TODO [greyson] Navigation
startActivity(new Intent(this, MainActivity.class));
}
}

View File

@@ -20,6 +20,7 @@ import androidx.core.content.ContextCompat;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.push.AccountManagerFactory;
@@ -177,7 +178,7 @@ public class DeviceActivity extends PassphraseRequiredActionBarActivity
try {
Context context = DeviceActivity.this;
SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context);
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
String verificationCode = accountManager.getNewDeviceVerificationCode();
String ephemeralId = uri.getQueryParameter("uuid");
String publicKeyEncoded = uri.getQueryParameter("pub_key");

View File

@@ -1,5 +1,6 @@
package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
@@ -13,7 +14,6 @@ import androidx.appcompat.app.AlertDialog;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.devicelist.Device;
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
import org.thoughtcrime.securesms.logging.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -162,27 +162,31 @@ public class DeviceListFragment extends ListFragment
builder.show();
}
@SuppressLint("StaticFieldLeak")
private void handleDisconnectDevice(final long deviceId) {
new ProgressDialogAsyncTask<Void, Void, Void>(getActivity(),
R.string.DeviceListActivity_unlinking_device_no_ellipsis,
R.string.DeviceListActivity_unlinking_device)
new ProgressDialogAsyncTask<Void, Void, Boolean>(getActivity(),
R.string.DeviceListActivity_unlinking_device_no_ellipsis,
R.string.DeviceListActivity_unlinking_device)
{
@Override
protected Void doInBackground(Void... params) {
protected Boolean doInBackground(Void... params) {
try {
accountManager.removeDevice(deviceId);
ApplicationDependencies.getJobManager().add(new RefreshUnidentifiedDeliveryAbilityJob());
return true;
} catch (IOException e) {
Log.w(TAG, e);
Toast.makeText(getActivity(), R.string.DeviceListActivity_network_failed, Toast.LENGTH_LONG).show();
return false;
}
return null;
}
@Override
protected void onPostExecute(Void result) {
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
getLoaderManager().restartLoader(0, null, DeviceListFragment.this);
if (result) {
getLoaderManager().restartLoader(0, null, DeviceListFragment.this);
} else {
Toast.makeText(getActivity(), R.string.DeviceListActivity_network_failed, Toast.LENGTH_LONG).show();
}
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}

View File

@@ -17,9 +17,6 @@ public class DeviceProvisioningActivity extends PassphraseRequiredActionBarActiv
@Override
protected void onCreate(Bundle bundle, boolean ready) {
assert getSupportActionBar() != null;
getSupportActionBar().hide();
AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle(getString(R.string.DeviceProvisioningActivity_link_a_signal_device))
.setMessage(getString(R.string.DeviceProvisioningActivity_it_looks_like_youre_trying_to_link_a_signal_device_using_a_3rd_party_scanner))

View File

@@ -18,20 +18,14 @@
package org.thoughtcrime.securesms;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import android.text.TextUtils;
import org.thoughtcrime.securesms.avatar.AvatarSelection;
import org.thoughtcrime.securesms.conversation.ConversationActivity;
import org.thoughtcrime.securesms.logging.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -42,6 +36,10 @@ import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.transition.Transition;
@@ -52,29 +50,35 @@ import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.conversation.ConversationActivity;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.groups.GroupManager.GroupActionResult;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mediasend.AvatarSelectionActivity;
import org.thoughtcrime.securesms.mediasend.AvatarSelectionBottomSheetDialogFragment;
import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter;
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter.OnRecipientDeletedListener;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
@@ -82,7 +86,7 @@ import java.util.List;
import java.util.Set;
/**
* Activity to create and update groups
* Activity to create and update {@link GroupId.V1} groups
*
* @author Jake McGinty
*/
@@ -93,27 +97,31 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private final static String TAG = GroupCreateActivity.class.getSimpleName();
public static final String GROUP_ADDRESS_EXTRA = "group_recipient";
public static final String GROUP_THREAD_EXTRA = "group_thread";
private static final String GROUP_ID_EXTRA = "group_id";
private static final String GROUP_THREAD_EXTRA = "group_thread";
private final DynamicTheme dynamicTheme = new DynamicTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private final DynamicTheme dynamicTheme = new DynamicTheme();
private static final int PICK_CONTACT = 1;
public static final int AVATAR_SIZE = 210;
private static final short REQUEST_CODE_SELECT_AVATAR = 26165;
private static final int PICK_CONTACT = 1;
private EditText groupName;
private ListView lv;
private ImageView avatar;
private TextView creatingText;
private Bitmap avatarBmp;
private EditText groupName;
private ListView listView;
private ImageView avatar;
private TextView creatingText;
private Bitmap avatarBmp;
@NonNull private Optional<GroupData> groupToUpdate = Optional.absent();
public static Intent newEditGroupIntent(@NonNull Context context, @NonNull GroupId.V1 groupId) {
Intent intent = new Intent(context, GroupCreateActivity.class);
intent.putExtra(GroupCreateActivity.GROUP_ID_EXTRA, groupId.toString());
return intent;
}
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
}
@Override
@@ -129,7 +137,6 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
public void onResume() {
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
updateViewState();
}
@@ -186,27 +193,42 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
private void initializeResources() {
RecipientsEditor recipientsEditor = ViewUtil.findById(this, R.id.recipients_text);
PushRecipientsPanel recipientsPanel = ViewUtil.findById(this, R.id.recipients);
lv = ViewUtil.findById(this, R.id.selected_contacts_list);
avatar = ViewUtil.findById(this, R.id.avatar);
groupName = ViewUtil.findById(this, R.id.group_name);
creatingText = ViewUtil.findById(this, R.id.creating_group_text);
RecipientsEditor recipientsEditor = findViewById(R.id.recipients_text);
PushRecipientsPanel recipientsPanel = findViewById(R.id.recipients);
listView = findViewById(R.id.selected_contacts_list);
avatar = findViewById(R.id.avatar);
groupName = findViewById(R.id.group_name);
creatingText = findViewById(R.id.creating_group_text);
SelectedRecipientsAdapter adapter = new SelectedRecipientsAdapter(this);
adapter.setOnRecipientDeletedListener(this);
lv.setAdapter(adapter);
listView.setAdapter(adapter);
recipientsEditor.setHint(R.string.recipients_panel__add_members);
recipientsPanel.setPanelChangeListener(this);
findViewById(R.id.contacts_button).setOnClickListener(new AddRecipientButtonListener());
avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_group_outline_40, R.drawable.ic_group_outline_20).asDrawable(this, ContactColors.UNKNOWN_COLOR.toConversationColor(this)));
avatar.setOnClickListener(view -> AvatarSelection.startAvatarSelection(this, false, false));
avatar.setImageDrawable(getDefaultGroupAvatar());
avatar.setOnClickListener(view -> AvatarSelectionBottomSheetDialogFragment.create(avatarBmp != null, false, REQUEST_CODE_SELECT_AVATAR, true).show(getSupportFragmentManager(), null));
}
private Drawable getDefaultGroupAvatar() {
return new ResourceContactPhoto(R.drawable.ic_group_outline_34, R.drawable.ic_group_outline_20).asDrawable(this, ContactColors.UNKNOWN_COLOR.toConversationColor(this));
}
private void initializeExistingGroup() {
final Address groupAddress = getIntent().getParcelableExtra(GROUP_ADDRESS_EXTRA);
final GroupId groupId = GroupId.parseNullableOrThrow(getIntent().getStringExtra(GROUP_ID_EXTRA));
if (groupAddress != null) {
new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, groupAddress.toGroupString());
if (groupId != null) {
GroupId.V1 groupIdV1 = groupId.requireV1();
new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, groupIdV1);
if (FeatureFlags.newGroupUI()) {
avatar.setOnClickListener(v -> startActivity(EditProfileActivity.getIntentForGroupProfile(this, groupIdV1)));
}
}
}
@@ -261,8 +283,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
private void handleGroupUpdate() {
new UpdateSignalGroupTask(this, groupToUpdate.get().id, avatarBmp,
getGroupName(), getAdapter().getRecipients()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
new UpdateSignalGroupV1Task(this, groupToUpdate.get().id, avatarBmp,
getGroupName(), getAdapter().getRecipients()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void handleOpenConversation(long threadId, Recipient recipient) {
@@ -275,7 +297,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
private SelectedRecipientsAdapter getAdapter() {
return (SelectedRecipientsAdapter)lv.getAdapter();
return (SelectedRecipientsAdapter) listView.getAdapter();
}
private @Nullable String getGroupName() {
@@ -285,37 +307,41 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
@Override
public void onActivityResult(int reqCode, int resultCode, final Intent data) {
super.onActivityResult(reqCode, resultCode, data);
Uri outputFile = Uri.fromFile(new File(getCacheDir(), "cropped"));
if (data == null || resultCode != Activity.RESULT_OK)
return;
switch (reqCode) {
case PICK_CONTACT:
List<String> selected = data.getStringArrayListExtra("contacts");
List<RecipientId> selected = data.getParcelableArrayListExtra(PushContactSelectionActivity.KEY_SELECTED_RECIPIENTS);
for (String contact : selected) {
Recipient recipient = Recipient.external(this, contact);
for (RecipientId contact : selected) {
Recipient recipient = Recipient.resolved(contact);
addSelectedContacts(recipient);
}
break;
case AvatarSelection.REQUEST_CODE_AVATAR:
AvatarSelection.circularCropImage(this, data.getData(), outputFile, R.string.CropImageActivity_group_avatar);
break;
case AvatarSelection.REQUEST_CODE_CROP_IMAGE:
final Uri resultUri = AvatarSelection.getResultUri(data);
case REQUEST_CODE_SELECT_AVATAR:
if (data.getBooleanExtra("delete", false)) {
avatarBmp = null;
avatar.setImageDrawable(getDefaultGroupAvatar());
return;
}
final Media result = data.getParcelableExtra(AvatarSelectionActivity.EXTRA_MEDIA);
final DecryptableUri decryptableUri = new DecryptableUri(result.getUri());
GlideApp.with(this)
.asBitmap()
.load(resultUri)
.load(decryptableUri)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.centerCrop()
.override(AVATAR_SIZE, AVATAR_SIZE)
.override(AvatarHelper.AVATAR_DIMENSIONS, AvatarHelper.AVATAR_DIMENSIONS)
.into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
setAvatar(resultUri, resource);
setAvatar(decryptableUri, resource);
}
});
}
@@ -352,7 +378,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
memberAddresses.add(Recipient.self().getId());
String groupId = DatabaseFactory.getGroupDatabase(activity).getOrCreateGroupForMembers(memberAddresses, true);
GroupId.Mms groupId = DatabaseFactory.getGroupDatabase(activity).getOrCreateMmsGroupForMembers(memberAddresses);
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(activity).getOrInsertFromGroupId(groupId);
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
long threadId = DatabaseFactory.getThreadDatabase(activity).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.DEFAULT);
@@ -416,7 +442,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
@Override
protected Optional<GroupActionResult> doInBackground(Void... aVoid) {
return Optional.of(GroupManager.createGroup(activity, members, avatar, name, false));
return Optional.of(GroupManager.createGroupV1(activity, members, BitmapUtil.toByteArray(avatar), name, false));
}
@Override
@@ -433,11 +459,11 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
}
private static class UpdateSignalGroupTask extends SignalGroupTask {
private String groupId;
private static class UpdateSignalGroupV1Task extends SignalGroupTask {
private final GroupId.V1 groupId;
public UpdateSignalGroupTask(GroupCreateActivity activity, String groupId,
Bitmap avatar, String name, Set<Recipient> members)
UpdateSignalGroupV1Task(GroupCreateActivity activity, GroupId.V1 groupId,
Bitmap avatar, String name, Set<Recipient> members)
{
super(activity, avatar, name, members);
this.groupId = groupId;
@@ -445,11 +471,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
@Override
protected Optional<GroupActionResult> doInBackground(Void... aVoid) {
try {
return Optional.of(GroupManager.updateGroup(activity, groupId, members, avatar, name));
} catch (InvalidNumberException e) {
return Optional.absent();
}
return Optional.fromNullable(GroupManager.updateGroup(activity, groupId, members, BitmapUtil.toByteArray(avatar), name));
}
@Override
@@ -458,7 +480,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
if (!activity.isFinishing()) {
Intent intent = activity.getIntent();
intent.putExtra(GROUP_THREAD_EXTRA, result.get().getThreadId());
intent.putExtra(GROUP_ADDRESS_EXTRA, result.get().getGroupRecipient().requireAddress());
intent.putExtra(GROUP_ID_EXTRA, result.get().getGroupRecipient().requireGroupId().toString());
activity.setResult(RESULT_OK, intent);
activity.finish();
}
@@ -500,8 +522,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
if (failIfNotPush && !isPush) {
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_cannot_add_non_push_to_existing_group,
recipient.toShortString())));
} else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipient.requireAddress().serialize())) {
recipient.toShortString(activity))));
} else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipient.getE164().or(""))) {
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_youre_already_in_the_group)));
} else {
results.add(new Result(recipient, isPush, null));
@@ -525,7 +547,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
}
private static class FillExistingGroupInfoAsyncTask extends ProgressDialogAsyncTask<String,Void,Optional<GroupData>> {
private static class FillExistingGroupInfoAsyncTask extends ProgressDialogAsyncTask<GroupId.V1, Void, Optional<GroupData>> {
private GroupCreateActivity activity;
public FillExistingGroupInfoAsyncTask(GroupCreateActivity activity) {
@@ -536,18 +558,24 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
@Override
protected Optional<GroupData> doInBackground(String... groupIds) {
protected Optional<GroupData> doInBackground(GroupId.V1... groupIds) {
final GroupDatabase db = DatabaseFactory.getGroupDatabase(activity);
final List<Recipient> recipients = db.getGroupMembers(groupIds[0], false);
final List<Recipient> recipients = db.getGroupMembers(groupIds[0], GroupDatabase.MemberSet.FULL_MEMBERS_EXCLUDING_SELF);
final Optional<GroupRecord> group = db.getGroup(groupIds[0]);
final Set<Recipient> existingContacts = new HashSet<>(recipients.size());
existingContacts.addAll(recipients);
if (group.isPresent()) {
Bitmap avatar = null;
try {
avatar = BitmapFactory.decodeStream(AvatarHelper.getAvatar(getContext(), group.get().getRecipientId()));
} catch (IOException e) {
Log.w(TAG, "Failed to read avatar.");
}
return Optional.of(new GroupData(groupIds[0],
existingContacts,
BitmapUtil.fromByteArray(group.get().getAvatar()),
group.get().getAvatar(),
avatar,
BitmapUtil.toByteArray(avatar),
group.get().getTitle()));
} else {
return Optional.absent();
@@ -567,7 +595,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
SelectedRecipientsAdapter adapter = new SelectedRecipientsAdapter(activity, group.get().recipients);
adapter.setOnRecipientDeletedListener(activity);
activity.lv.setAdapter(adapter);
activity.listView.setAdapter(adapter);
activity.updateViewState();
}
}
@@ -584,13 +612,13 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
private static class GroupData {
String id;
GroupId.V1 id;
Set<Recipient> recipients;
Bitmap avatarBmp;
byte[] avatarBytes;
String name;
public GroupData(String id, Set<Recipient> recipients, Bitmap avatarBmp, byte[] avatarBytes, String name) {
GroupData(GroupId.V1 id, Set<Recipient> recipients, Bitmap avatarBmp, byte[] avatarBytes, String name) {
this.id = id;
this.recipients = recipients;
this.avatarBmp = avatarBmp;

View File

@@ -0,0 +1,57 @@
package org.thoughtcrime.securesms;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LiveData;
import org.thoughtcrime.securesms.groups.LiveGroup;
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry;
import org.thoughtcrime.securesms.groups.ui.GroupMemberListView;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.ui.bottomsheet.RecipientBottomSheetDialogFragment;
import java.util.List;
public final class GroupMembersDialog {
private final FragmentActivity fragmentActivity;
private final Recipient groupRecipient;
public GroupMembersDialog(@NonNull FragmentActivity activity,
@NonNull Recipient groupRecipient)
{
this.fragmentActivity = activity;
this.groupRecipient = groupRecipient;
}
public void display() {
AlertDialog dialog = new AlertDialog.Builder(fragmentActivity)
.setTitle(R.string.ConversationActivity_group_members)
.setIconAttribute(R.attr.group_members_dialog_icon)
.setCancelable(true)
.setView(R.layout.dialog_group_members)
.setPositiveButton(android.R.string.ok, null)
.show();
GroupMemberListView memberListView = dialog.findViewById(R.id.list_members);
LiveGroup liveGroup = new LiveGroup(groupRecipient.requireGroupId());
LiveData<List<GroupMemberEntry.FullMember>> fullMembers = liveGroup.getFullMembers();
//noinspection ConstantConditions
fullMembers.observe(fragmentActivity, memberListView::setMembers);
dialog.setOnDismissListener(d -> fullMembers.removeObservers(fragmentActivity));
memberListView.setRecipientClickListener(recipient -> {
dialog.dismiss();
contactClick(recipient);
});
}
private void contactClick(@NonNull Recipient recipient) {
RecipientBottomSheetDialogFragment.create(recipient.getId(), groupRecipient.requireGroupId())
.show(fragmentActivity.getSupportFragmentManager(), "BOTTOM");
}
}

View File

@@ -0,0 +1,291 @@
package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.AnimRes;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.SelectedContact;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarInviteTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.ThemeUtil;
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.whispersystems.libsignal.util.guava.Optional;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class InviteActivity extends PassphraseRequiredActionBarActivity implements ContactSelectionListFragment.OnContactSelectedListener {
private ContactSelectionListFragment contactsFragment;
private EditText inviteText;
private ViewGroup smsSendFrame;
private Button smsSendButton;
private Animation slideInAnimation;
private Animation slideOutAnimation;
private DynamicTheme dynamicTheme = new DynamicNoActionBarInviteTheme();
private Toolbar primaryToolbar;
@Override
protected void onPreCreate() {
super.onPreCreate();
dynamicTheme.onCreate(this);
}
@Override
protected void onCreate(Bundle savedInstanceState, boolean ready) {
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_SMS);
getIntent().putExtra(ContactSelectionListFragment.MULTI_SELECT, true);
getIntent().putExtra(ContactSelectionListFragment.REFRESHABLE, false);
setContentView(R.layout.invite_activity);
initializeAppBar();
initializeResources();
}
@Override
protected void onResume() {
super.onResume();
dynamicTheme.onResume(this);
}
private void initializeAppBar() {
primaryToolbar = findViewById(R.id.toolbar);
setSupportActionBar(primaryToolbar);
assert getSupportActionBar() != null;
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle(R.string.AndroidManifest__invite_friends);
}
private void initializeResources() {
slideInAnimation = loadAnimation(R.anim.slide_from_bottom);
slideOutAnimation = loadAnimation(R.anim.slide_to_bottom);
View shareButton = ViewUtil.findById(this, R.id.share_button);
View smsButton = ViewUtil.findById(this, R.id.sms_button);
Button smsCancelButton = ViewUtil.findById(this, R.id.cancel_sms_button);
ContactFilterToolbar contactFilter = ViewUtil.findById(this, R.id.contact_filter);
inviteText = ViewUtil.findById(this, R.id.invite_text);
smsSendFrame = ViewUtil.findById(this, R.id.sms_send_frame);
smsSendButton = ViewUtil.findById(this, R.id.send_sms_button);
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)));
updateSmsButtonText();
contactsFragment.setOnContactSelectedListener(this);
shareButton.setOnClickListener(new ShareClickListener());
smsButton.setOnClickListener(new SmsClickListener());
smsCancelButton.setOnClickListener(new SmsCancelClickListener());
smsSendButton.setOnClickListener(new SmsSendClickListener());
contactFilter.setOnFilterChangedListener(new ContactFilterChangedListener());
contactFilter.setNavigationIcon(R.drawable.ic_search_conversation_24);
}
private Animation loadAnimation(@AnimRes int animResId) {
final Animation animation = AnimationUtils.loadAnimation(this, animResId);
animation.setInterpolator(new FastOutSlowInInterpolator());
return animation;
}
@Override
public void onContactSelected(Optional<RecipientId> recipientId, String number) {
updateSmsButtonText();
}
@Override
public void onContactDeselected(Optional<RecipientId> recipientId, String number) {
updateSmsButtonText();
}
private void sendSmsInvites() {
new SendSmsInvitesAsyncTask(this, inviteText.getText().toString())
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
contactsFragment.getSelectedContacts()
.toArray(new SelectedContact[0]));
}
private void updateSmsButtonText() {
List<SelectedContact> selectedContacts = contactsFragment.getSelectedContacts();
smsSendButton.setText(getResources().getQuantityString(R.plurals.InviteActivity_send_sms_to_friends,
selectedContacts.size(),
selectedContacts.size()));
smsSendButton.setEnabled(!selectedContacts.isEmpty());
}
@Override public void onBackPressed() {
if (smsSendFrame.getVisibility() == View.VISIBLE) {
cancelSmsSelection();
} else {
super.onBackPressed();
}
}
private void cancelSmsSelection() {
setPrimaryColorsToolbarNormal();
contactsFragment.reset();
updateSmsButtonText();
ViewUtil.animateOut(smsSendFrame, slideOutAnimation, View.GONE);
}
private void setPrimaryColorsToolbarNormal() {
primaryToolbar.setBackgroundColor(0);
primaryToolbar.getNavigationIcon().setColorFilter(null);
primaryToolbar.setTitleTextColor(ThemeUtil.getThemedColor(this, R.attr.title_text_color_primary));
if (Build.VERSION.SDK_INT >= 23) {
getWindow().setStatusBarColor(ThemeUtil.getThemedColor(this, android.R.attr.statusBarColor));
getWindow().setNavigationBarColor(ThemeUtil.getThemedColor(this, android.R.attr.navigationBarColor));
WindowUtil.setLightStatusBarFromTheme(this);
}
WindowUtil.setLightNavigationBarFromTheme(this);
}
private void setPrimaryColorsToolbarForSms() {
primaryToolbar.setBackgroundColor(ContextCompat.getColor(this, R.color.core_ultramarine));
primaryToolbar.getNavigationIcon().setColorFilter(ThemeUtil.getThemedColor(this, R.attr.conversation_subtitle_color), PorterDuff.Mode.SRC_IN);
primaryToolbar.setTitleTextColor(ThemeUtil.getThemedColor(this, R.attr.conversation_title_color));
if (Build.VERSION.SDK_INT >= 23) {
getWindow().setStatusBarColor(ContextCompat.getColor(this, R.color.core_ultramarine));
WindowUtil.clearLightStatusBar(getWindow());
}
if (Build.VERSION.SDK_INT >= 27) {
getWindow().setNavigationBarColor(ContextCompat.getColor(this, R.color.core_ultramarine));
WindowUtil.clearLightNavigationBar(getWindow());
}
}
private class ShareClickListener implements OnClickListener {
@Override
public void onClick(View v) {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, inviteText.getText().toString());
sendIntent.setType("text/plain");
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(Intent.createChooser(sendIntent, getString(R.string.InviteActivity_invite_to_signal)));
} else {
Toast.makeText(InviteActivity.this, R.string.InviteActivity_no_app_to_share_to, Toast.LENGTH_LONG).show();
}
}
}
private class SmsClickListener implements OnClickListener {
@Override
public void onClick(View v) {
setPrimaryColorsToolbarForSms();
ViewUtil.animateIn(smsSendFrame, slideInAnimation);
}
}
private class SmsCancelClickListener implements OnClickListener {
@Override
public void onClick(View v) {
cancelSmsSelection();
}
}
private class SmsSendClickListener implements OnClickListener {
@Override
public void onClick(View v) {
new AlertDialog.Builder(InviteActivity.this)
.setTitle(getResources().getQuantityString(R.plurals.InviteActivity_send_sms_invites,
contactsFragment.getSelectedContacts().size(),
contactsFragment.getSelectedContacts().size()))
.setMessage(inviteText.getText().toString())
.setPositiveButton(R.string.yes, (dialog, which) -> sendSmsInvites())
.setNegativeButton(R.string.no, (dialog, which) -> dialog.dismiss())
.show();
}
}
private class ContactFilterChangedListener implements OnFilterChangedListener {
@Override
public void onFilterChanged(String filter) {
contactsFragment.setQueryFilter(filter);
}
}
@SuppressLint("StaticFieldLeak")
private class SendSmsInvitesAsyncTask extends ProgressDialogAsyncTask<SelectedContact,Void,Void> {
private final String message;
SendSmsInvitesAsyncTask(Context context, String message) {
super(context, R.string.InviteActivity_sending, R.string.InviteActivity_sending);
this.message = message;
}
@Override
protected Void doInBackground(SelectedContact... contacts) {
final Context context = getContext();
if (context == null) return null;
for (SelectedContact contact : contacts) {
RecipientId recipientId = contact.getOrCreateRecipientId(context);
Recipient recipient = Recipient.resolved(recipientId);
int subscriptionId = recipient.getDefaultSubscriptionId().or(-1);
MessageSender.send(context, new OutgoingTextMessage(recipient, message, subscriptionId), -1L, true, null);
if (recipient.getContactUri() != null) {
DatabaseFactory.getRecipientDatabase(context).setHasSentInvite(recipient.getId());
}
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
final Context context = getContext();
if (context == null) return;
ViewUtil.animateOut(smsSendFrame, slideOutAnimation, View.GONE).addListener(new Listener<Boolean>() {
@Override
public void onSuccess(Boolean result) {
contactsFragment.reset();
}
@Override
public void onFailure(ExecutionException e) {}
});
Toast.makeText(context, R.string.InviteActivity_invitations_sent, Toast.LENGTH_LONG).show();
}
}
}

View File

@@ -0,0 +1,45 @@
package org.thoughtcrime.securesms;
import android.os.Bundle;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
public class MainActivity extends PassphraseRequiredActionBarActivity {
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
private final MainNavigator navigator = new MainNavigator(this);
@Override
protected void onCreate(Bundle savedInstanceState, boolean ready) {
super.onCreate(savedInstanceState, ready);
setContentView(R.layout.main_activity);
navigator.onCreate(savedInstanceState);
}
@Override
protected void onPreCreate() {
super.onPreCreate();
dynamicTheme.onCreate(this);
}
@Override
protected void onResume() {
super.onResume();
dynamicTheme.onResume(this);
}
@Override
public void onBackPressed() {
if (!navigator.onBackPressed()) {
super.onBackPressed();
}
}
public @NonNull MainNavigator getNavigator() {
return navigator;
}
}

View File

@@ -0,0 +1,22 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
public class MainFragment extends Fragment {
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (!(requireActivity() instanceof MainActivity)) {
throw new IllegalStateException("Can only be used inside of MainActivity!");
}
}
protected @NonNull MainNavigator getNavigator() {
return MainNavigator.get(requireActivity());
}
}

View File

@@ -0,0 +1,104 @@
package org.thoughtcrime.securesms;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import org.thoughtcrime.securesms.conversation.ConversationActivity;
import org.thoughtcrime.securesms.conversationlist.ConversationListArchiveFragment;
import org.thoughtcrime.securesms.conversationlist.ConversationListFragment;
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity;
import org.thoughtcrime.securesms.insights.InsightsLauncher;
import org.thoughtcrime.securesms.recipients.RecipientId;
public class MainNavigator {
private final MainActivity activity;
public MainNavigator(@NonNull MainActivity activity) {
this.activity = activity;
}
public static MainNavigator get(@NonNull Activity activity) {
if (!(activity instanceof MainActivity)) {
throw new IllegalArgumentException("Activity must be an instance of MainActivity!");
}
return ((MainActivity) activity).getNavigator();
}
public void onCreate(@Nullable Bundle savedInstanceState) {
if (savedInstanceState != null) {
return;
}
getFragmentManager().beginTransaction()
.add(R.id.fragment_container, ConversationListFragment.newInstance())
.commit();
}
/**
* @return True if the back pressed was handled in our own custom way, false if it should be given
* to the system to do the default behavior.
*/
public boolean onBackPressed() {
Fragment fragment = getFragmentManager().findFragmentById(R.id.fragment_container);
if (fragment instanceof BackHandler) {
return ((BackHandler) fragment).onBackPressed();
}
return false;
}
public void goToConversation(@NonNull RecipientId recipientId, long threadId, int distributionType, int startingPosition) {
Intent intent = ConversationActivity.buildIntent(activity, recipientId, threadId, distributionType, startingPosition);
activity.startActivity(intent);
activity.overridePendingTransition(R.anim.slide_from_end, R.anim.fade_scale_out);
}
public void goToAppSettings() {
Intent intent = new Intent(activity, ApplicationPreferencesActivity.class);
activity.startActivity(intent);
}
public void goToArchiveList() {
getFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_from_end, R.anim.slide_to_start, R.anim.slide_from_start, R.anim.slide_to_end)
.replace(R.id.fragment_container, ConversationListArchiveFragment.newInstance())
.addToBackStack(null)
.commit();
}
public void goToGroupCreation() {
activity.startActivity(CreateGroupActivity.newIntent(activity));
}
public void goToInvite() {
Intent intent = new Intent(activity, InviteActivity.class);
activity.startActivity(intent);
}
public void goToInsights() {
InsightsLauncher.showInsightsDashboard(activity.getSupportFragmentManager());
}
private @NonNull FragmentManager getFragmentManager() {
return activity.getSupportFragmentManager();
}
public interface BackHandler {
/**
* @return True if the back pressed was handled in our own custom way, false if it should be given
* to the system to do the default behavior.
*/
boolean onBackPressed();
}
}

View File

@@ -52,11 +52,11 @@ import androidx.viewpager.widget.ViewPager;
import org.thoughtcrime.securesms.animation.DepthPageTransformer;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.components.viewpager.ExtendedOnPageChangedListener;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity;
import org.thoughtcrime.securesms.mediapreview.MediaPreviewFragment;
import org.thoughtcrime.securesms.mediapreview.MediaPreviewViewModel;
import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter;
@@ -64,11 +64,11 @@ import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sharing.ShareActivity;
import org.thoughtcrime.securesms.util.AttachmentUtil;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment;
import org.thoughtcrime.securesms.util.Util;
import java.util.HashMap;
import java.util.Locale;
@@ -85,12 +85,16 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
private final static String TAG = MediaPreviewActivity.class.getSimpleName();
public static final String RECIPIENT_EXTRA = "recipient_id";
private static final int NOT_IN_A_THREAD = -2;
public static final String THREAD_ID_EXTRA = "thread_id";
public static final String DATE_EXTRA = "date";
public static final String SIZE_EXTRA = "size";
public static final String CAPTION_EXTRA = "caption";
public static final String OUTGOING_EXTRA = "outgoing";
public static final String LEFT_IS_RECENT_EXTRA = "left_is_recent";
public static final String HIDE_ALL_MEDIA_EXTRA = "came_from_all_media";
public static final String SHOW_THREAD_EXTRA = "show_thread";
public static final String SORTING_EXTRA = "sorting";
private ViewPager mediaPager;
private View detailsContainer;
@@ -103,12 +107,29 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
private String initialMediaType;
private long initialMediaSize;
private String initialCaption;
private Recipient conversationRecipient;
private boolean leftIsRecent;
private MediaPreviewViewModel viewModel;
private ViewPagerListener viewPagerListener;
private int restartItem = -1;
private int restartItem = -1;
private long threadId = NOT_IN_A_THREAD;
private boolean cameFromAllMedia;
private boolean showThread;
private MediaDatabase.Sorting sorting;
public static @NonNull Intent intentFromMediaRecord(@NonNull Context context,
@NonNull MediaRecord mediaRecord,
boolean leftIsRecent)
{
Intent intent = new Intent(context, MediaPreviewActivity.class);
intent.putExtra(MediaPreviewActivity.THREAD_ID_EXTRA, mediaRecord.getThreadId());
intent.putExtra(MediaPreviewActivity.DATE_EXTRA, mediaRecord.getDate());
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, mediaRecord.getAttachment().getSize());
intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, mediaRecord.getAttachment().getCaption());
intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, leftIsRecent);
intent.setDataAndType(mediaRecord.getAttachment().getDataUri(), mediaRecord.getContentType());
return intent;
}
@SuppressWarnings("ConstantConditions")
@Override
@@ -152,19 +173,45 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
MediaItem mediaItem = getCurrentMediaItem();
if (mediaItem != null) {
CharSequence relativeTimeSpan;
getSupportActionBar().setTitle(getTitleText(mediaItem));
getSupportActionBar().setSubtitle(getSubTitleText(mediaItem));
}
}
if (mediaItem.date > 0) {
relativeTimeSpan = DateUtils.getExtendedRelativeTimeSpanString(this, Locale.getDefault(), mediaItem.date);
} else {
relativeTimeSpan = getString(R.string.MediaPreviewActivity_draft);
private @NonNull String getTitleText(@NonNull MediaItem mediaItem) {
String from;
if (mediaItem.outgoing) from = getString(R.string.MediaPreviewActivity_you);
else if (mediaItem.recipient != null) from = mediaItem.recipient.toShortString(this);
else from = "";
if (showThread) {
String to = null;
Recipient threadRecipient = mediaItem.threadRecipient;
if (threadRecipient != null) {
if (mediaItem.outgoing || threadRecipient.isGroup()) {
if (threadRecipient.isLocalNumber()) {
from = getString(R.string.note_to_self);
} else {
to = threadRecipient.toShortString(this);
}
} else {
to = getString(R.string.MediaPreviewActivity_you);
}
}
if (mediaItem.outgoing) getSupportActionBar().setTitle(getString(R.string.MediaPreviewActivity_you));
else if (mediaItem.recipient != null) getSupportActionBar().setTitle(mediaItem.recipient.toShortString());
else getSupportActionBar().setTitle("");
return to != null ? getString(R.string.MediaPreviewActivity_s_to_s, from, to)
: from;
} else {
return from;
}
}
getSupportActionBar().setSubtitle(relativeTimeSpan);
private @NonNull String getSubTitleText(@NonNull MediaItem mediaItem) {
if (mediaItem.date > 0) {
return DateUtils.getExtendedRelativeTimeSpanString(this, Locale.getDefault(), mediaItem.date);
} else {
return getString(R.string.MediaPreviewActivity_draft);
}
}
@@ -217,20 +264,19 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
}
private void initializeResources() {
RecipientId recipientId = getIntent().getParcelableExtra(RECIPIENT_EXTRA);
Intent intent = getIntent();
initialMediaUri = getIntent().getData();
initialMediaType = getIntent().getType();
initialMediaSize = getIntent().getLongExtra(SIZE_EXTRA, 0);
initialCaption = getIntent().getStringExtra(CAPTION_EXTRA);
leftIsRecent = getIntent().getBooleanExtra(LEFT_IS_RECENT_EXTRA, false);
threadId = intent.getLongExtra(THREAD_ID_EXTRA, NOT_IN_A_THREAD);
cameFromAllMedia = intent.getBooleanExtra(HIDE_ALL_MEDIA_EXTRA, false);
showThread = intent.getBooleanExtra(SHOW_THREAD_EXTRA, false);
sorting = MediaDatabase.Sorting.values()[intent.getIntExtra(SORTING_EXTRA, 0)];
initialMediaUri = intent.getData();
initialMediaType = intent.getType();
initialMediaSize = intent.getLongExtra(SIZE_EXTRA, 0);
initialCaption = intent.getStringExtra(CAPTION_EXTRA);
leftIsRecent = intent.getBooleanExtra(LEFT_IS_RECENT_EXTRA, false);
restartItem = -1;
if (recipientId != null) {
conversationRecipient = Recipient.live(recipientId).get();
} else {
conversationRecipient = null;
}
}
private void initializeObservers() {
@@ -280,7 +326,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
Log.i(TAG, "Loading Part URI: " + initialMediaUri);
if (conversationRecipient != null) {
if (isMediaInDb()) {
LoaderManager.getInstance(this).restartLoader(0, null, this);
} else {
mediaPager.setAdapter(new SingleItemPagerAdapter(getSupportFragmentManager(), initialMediaUri, initialMediaType, initialMediaSize));
@@ -303,9 +349,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
}
private void showOverview() {
Intent intent = new Intent(this, MediaOverviewActivity.class);
intent.putExtra(MediaOverviewActivity.RECIPIENT_EXTRA, conversationRecipient.getId());
startActivity(intent);
startActivity(MediaOverviewActivity.forThread(this, threadId));
}
private void forward() {
@@ -383,6 +427,10 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
menu.findItem(R.id.delete).setVisible(false);
}
if (cameFromAllMedia) {
menu.findItem(R.id.media_preview__overview).setVisible(false);
}
return true;
}
@@ -402,7 +450,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
}
private boolean isMediaInDb() {
return conversationRecipient != null;
return threadId != NOT_IN_A_THREAD;
}
private @Nullable MediaItem getCurrentMediaItem() {
@@ -421,7 +469,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
@Override
public @NonNull Loader<Pair<Cursor, Integer>> onCreateLoader(int id, Bundle args) {
return new PagingMediaLoader(this, conversationRecipient, initialMediaUri, leftIsRecent);
return new PagingMediaLoader(this, threadId, initialMediaUri, leftIsRecent, sorting);
}
@Override
@@ -551,7 +599,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
@Override
public MediaItem getMediaItemFor(int position) {
return new MediaItem(null, null, uri, mediaType, -1, true);
return new MediaItem(null, null, null, uri, mediaType, -1, true);
}
@Override
@@ -685,12 +733,15 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
public MediaItem getMediaItemFor(int position) {
cursor.moveToPosition(getCursorPosition(position));
MediaRecord mediaRecord = MediaRecord.from(context, cursor);
RecipientId recipientId = mediaRecord.getRecipientId();
MediaRecord mediaRecord = MediaRecord.from(context, cursor);
RecipientId recipientId = mediaRecord.getRecipientId();
RecipientId threadRecipientId = mediaRecord.getThreadRecipientId();
if (mediaRecord.getAttachment().getDataUri() == null) throw new AssertionError();
return new MediaItem(Recipient.live(recipientId).get(),
Recipient.live(threadRecipientId).get(),
mediaRecord.getAttachment(),
mediaRecord.getAttachment().getDataUri(),
mediaRecord.getContentType(),
@@ -724,6 +775,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
private static class MediaItem {
private final @Nullable Recipient recipient;
private final @Nullable Recipient threadRecipient;
private final @Nullable DatabaseAttachment attachment;
private final @NonNull Uri uri;
private final @NonNull String type;
@@ -731,18 +783,20 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
private final boolean outgoing;
private MediaItem(@Nullable Recipient recipient,
@Nullable Recipient threadRecipient,
@Nullable DatabaseAttachment attachment,
@NonNull Uri uri,
@NonNull String type,
long date,
boolean outgoing)
{
this.recipient = recipient;
this.attachment = attachment;
this.uri = uri;
this.type = type;
this.date = date;
this.outgoing = outgoing;
this.recipient = recipient;
this.threadRecipient = threadRecipient;
this.attachment = attachment;
this.uri = uri;
this.type = type;
this.date = date;
this.outgoing = outgoing;
}
}

View File

@@ -31,7 +31,11 @@ import androidx.loader.app.LoaderManager.LoaderCallbacks;
import androidx.loader.content.Loader;
import org.thoughtcrime.securesms.conversation.ConversationItem;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.logging.Log;
import android.os.Parcelable;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
@@ -51,7 +55,6 @@ import org.thoughtcrime.securesms.database.loaders.MessageDetailsLoader;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.sms.MessageSender;
@@ -61,6 +64,7 @@ import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.whispersystems.libsignal.util.guava.Optional;
import java.lang.ref.WeakReference;
@@ -131,13 +135,13 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
assert getSupportActionBar() != null;
getSupportActionBar().setTitle(R.string.AndroidManifest__message_details);
MessageNotifier.setVisibleThread(threadId);
ApplicationDependencies.getMessageNotifier().setVisibleThread(threadId);
}
@Override
protected void onPause() {
super.onPause();
MessageNotifier.setVisibleThread(-1L);
ApplicationDependencies.getMessageNotifier().clearVisibleThread();
}
@Override
@@ -268,7 +272,9 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
}
toFrom.setText(toFromRes);
conversationItem.bind(messageRecord, Optional.absent(), Optional.absent(), glideRequests, dynamicLanguage.getCurrentLocale(), new HashSet<>(), recipient, null, false);
Parcelable state = recipientsList.onSaveInstanceState();
recipientsList.setAdapter(new MessageDetailsRecipientAdapter(this, glideRequests, messageRecord, recipients, isPushGroup));
recipientsList.onRestoreInstanceState(state);
}
private void inflateMessageViewIfAbsent(MessageRecord messageRecord) {
@@ -276,9 +282,9 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
if (messageRecord.isGroupAction()) {
conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_update, itemParent, false);
} else if (messageRecord.isOutgoing()) {
conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_sent, itemParent, false);
conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_sent_multimedia, itemParent, false);
} else {
conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_received, itemParent, false);
conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_received_multimedia, itemParent, false);
}
itemParent.addView(conversationItem);
}
@@ -368,7 +374,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
List<GroupReceiptInfo> receiptInfoList = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageRecord.getId());
if (receiptInfoList.isEmpty()) {
List<Recipient> group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().requireAddress().toGroupString(), false);
List<Recipient> group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().requireGroupId(), GroupDatabase.MemberSet.FULL_MEMBERS_EXCLUDING_SELF);
for (Recipient recipient : group) {
recipients.add(new RecipientDeliveryStatus(recipient, RecipientDeliveryStatus.Status.UNKNOWN, false, -1));
@@ -438,8 +444,8 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
}
private void onResendClicked(View v) {
MessageSender.resend(MessageDetailsActivity.this, messageRecord);
resendButton.setVisibility(View.GONE);
SignalExecutors.BOUNDED.execute(() -> MessageSender.resend(MessageDetailsActivity.this, messageRecord));
}
}
}

View File

@@ -11,7 +11,9 @@ import android.widget.BaseAdapter;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Conversions;
import org.thoughtcrime.securesms.util.adapter.StableIdGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -19,11 +21,12 @@ import java.util.List;
class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.RecyclerListener {
private final Context context;
private final GlideRequests glideRequests;
private final MessageRecord record;
private final List<RecipientDeliveryStatus> members;
private final boolean isPushGroup;
private final Context context;
private final GlideRequests glideRequests;
private final MessageRecord record;
private final List<RecipientDeliveryStatus> members;
private final boolean isPushGroup;
private final StableIdGenerator<RecipientId> idGenerator;
MessageDetailsRecipientAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
@NonNull MessageRecord record, @NonNull List<RecipientDeliveryStatus> members,
@@ -34,6 +37,7 @@ class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.
this.record = record;
this.isPushGroup = isPushGroup;
this.members = members;
this.idGenerator = new StableIdGenerator<>();
}
@Override
@@ -48,11 +52,7 @@ class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.
@Override
public long getItemId(int position) {
try {
return Conversions.byteArrayToLong(MessageDigest.getInstance("SHA1").digest(members.get(position).recipient.requireAddress().serialize().getBytes()));
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
return idGenerator.getId(members.get(position).recipient.getId());
}
@Override

View File

@@ -81,23 +81,45 @@ public class MessageRecipientListItem extends RelativeLayout
this.deliveryStatusView = findViewById(R.id.delivery_status);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
observeMember();
}
@Override
protected void onDetachedFromWindow() {
unsubscribeFromMember();
super.onDetachedFromWindow();
}
public void set(final GlideRequests glideRequests,
final MessageRecord record,
final RecipientDeliveryStatus member,
final boolean isPushGroup)
{
if (this.member != null) this.member.getRecipient().live().removeForeverObserver(this);
unsubscribeFromMember();
this.glideRequests = glideRequests;
this.member = member;
observeMember();
member.getRecipient().live().observeForever(this);
fromView.setText(member.getRecipient());
contactPhotoImage.setAvatar(glideRequests, member.getRecipient(), false);
setIssueIndicators(record, isPushGroup);
unidentifiedDeliveryIcon.setVisibility(TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()) && member.isUnidentified() ? VISIBLE : GONE);
}
private void observeMember() {
if (isAttachedToWindow() && member != null && member.getRecipient() != null) {
member.getRecipient().live().observeForever(this);
}
}
private void unsubscribeFromMember() {
if (member != null && member.getRecipient() != null) member.getRecipient().live().removeForeverObserver(this);
}
private void setIssueIndicators(final MessageRecord record,
final boolean isPushGroup)
{
@@ -162,7 +184,7 @@ public class MessageRecipientListItem extends RelativeLayout
}
public void unbind() {
if (this.member != null && this.member.getRecipient() != null) this.member.getRecipient().live().removeForeverObserver(this);
unsubscribeFromMember();
}
@Override

View File

@@ -1,8 +1,10 @@
package org.thoughtcrime.securesms;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import java.util.concurrent.TimeUnit;
@@ -23,6 +25,10 @@ public class MuteDialog extends AlertDialog {
}
public static void show(final Context context, final @NonNull MuteSelectionListener listener) {
show(context, listener, null);
}
public static void show(final Context context, final @NonNull MuteSelectionListener listener, @Nullable Runnable cancelListener) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.MuteDialog_mute_notifications);
builder.setItems(R.array.mute_durations, new DialogInterface.OnClickListener() {
@@ -43,6 +49,13 @@ public class MuteDialog extends AlertDialog {
}
});
if (cancelListener != null) {
builder.setOnCancelListener(dialog -> {
cancelListener.run();
dialog.dismiss();
});
}
builder.show();
}

View File

@@ -19,14 +19,16 @@ package org.thoughtcrime.securesms;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import org.thoughtcrime.securesms.conversation.ConversationActivity;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.whispersystems.libsignal.util.guava.Optional;
/**
* Activity container for starting a new conversation.
@@ -35,7 +37,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
*
*/
public class NewConversationActivity extends ContactSelectionActivity
implements ContactSelectionListFragment.InviteCallback
implements ContactSelectionListFragment.ListCallback
{
@SuppressWarnings("unused")
@@ -49,9 +51,18 @@ public class NewConversationActivity extends ContactSelectionActivity
}
@Override
public void onContactSelected(String number) {
Recipient recipient = Recipient.external(this, number);
public void onContactSelected(Optional<RecipientId> recipientId, String number) {
Recipient recipient;
if (recipientId.isPresent()) {
recipient = Recipient.resolved(recipientId.get());
} else {
Log.i(TAG, "[onContactSelected] Maybe creating a new recipient.");
recipient = Recipient.external(this, number);
}
launch(recipient);
}
private void launch(Recipient recipient) {
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.RECIPIENT_EXTRA, recipient.getId());
intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA));
@@ -85,7 +96,7 @@ public class NewConversationActivity extends ContactSelectionActivity
}
private void handleCreateGroup() {
startActivity(new Intent(this, GroupCreateActivity.class));
startActivity(CreateGroupActivity.newIntent(this));
}
private void handleInvite() {
@@ -93,10 +104,10 @@ public class NewConversationActivity extends ContactSelectionActivity
}
@Override
protected boolean onPrepareOptionsPanel(View view, Menu menu) {
MenuInflater inflater = this.getMenuInflater();
public boolean onPrepareOptionsMenu(Menu menu) {
menu.clear();
inflater.inflate(R.menu.new_conversation_activity, menu);
getMenuInflater().inflate(R.menu.new_conversation_activity, menu);
super.onPrepareOptionsMenu(menu);
return true;
}
@@ -104,5 +115,12 @@ public class NewConversationActivity extends ContactSelectionActivity
@Override
public void onInvite() {
handleInvite();
finish();
}
@Override
public void onNewGroup(boolean forceV1) {
handleCreateGroup();
finish();
}
}

View File

@@ -41,6 +41,8 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
public class PassphraseChangeActivity extends PassphraseActivity {
private static final String TAG = Log.tag(PassphraseChangeActivity.class);
private DynamicTheme dynamicTheme = new DynamicTheme();
private DynamicLanguage dynamicLanguage = new DynamicLanguage();
@@ -145,7 +147,7 @@ public class PassphraseChangeActivity extends PassphraseActivity {
return masterSecret;
} catch (InvalidPassphraseException e) {
Log.w(PassphraseChangeActivity.class.getSimpleName(), e);
Log.w(TAG, e);
return null;
}
}

View File

@@ -55,6 +55,7 @@ import org.thoughtcrime.securesms.components.AnimatingToggle;
import org.thoughtcrime.securesms.crypto.InvalidPassphraseException;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogActivity;
import org.thoughtcrime.securesms.util.DynamicIntroTheme;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -164,7 +165,7 @@ public class PassphrasePromptActivity extends PassphraseActivity {
}
private void handleLogSubmit() {
Intent intent = new Intent(this, LogSubmitActivity.class);
Intent intent = new Intent(this, SubmitDebugLogActivity.class);
startActivity(intent);
}
@@ -237,7 +238,7 @@ public class PassphrasePromptActivity extends PassphraseActivity {
EditorInfo.IME_ACTION_DONE);
fingerprintPrompt.setImageResource(R.drawable.ic_fingerprint_white_48dp);
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.signal_primary), PorterDuff.Mode.SRC_IN);
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.core_ultramarine), PorterDuff.Mode.SRC_IN);
lockScreenButton.setOnClickListener(v -> resumeScreenLock());
}
@@ -277,7 +278,7 @@ public class PassphrasePromptActivity extends PassphraseActivity {
fingerprintManager.authenticate(null, 0, fingerprintCancellationSignal, fingerprintListener, null);
} else if (Build.VERSION.SDK_INT >= 21){
Log.i(TAG, "firing intent...");
Intent intent = keyguardManager.createConfirmDeviceCredentialIntent("Unlock Signal", "");
Intent intent = keyguardManager.createConfirmDeviceCredentialIntent(getString(R.string.PassphrasePromptActivity_unlock_signal), "");
startActivityForResult(intent, 1);
} else {
Log.w(TAG, "Not compatible...");
@@ -357,7 +358,7 @@ public class PassphrasePromptActivity extends PassphraseActivity {
handleAuthenticated();
fingerprintPrompt.setImageResource(R.drawable.ic_fingerprint_white_48dp);
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.signal_primary), PorterDuff.Mode.SRC_IN);
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.core_ultramarine), PorterDuff.Mode.SRC_IN);
}
}).start();
}
@@ -380,7 +381,7 @@ public class PassphrasePromptActivity extends PassphraseActivity {
@Override
public void onAnimationEnd(Animation animation) {
fingerprintPrompt.setImageResource(R.drawable.ic_fingerprint_white_48dp);
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.signal_primary), PorterDuff.Mode.SRC_IN);
fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.core_ultramarine), PorterDuff.Mode.SRC_IN);
}
@Override

View File

@@ -0,0 +1,272 @@
package org.thoughtcrime.securesms;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.migrations.ApplicationMigrationActivity;
import org.thoughtcrime.securesms.migrations.ApplicationMigrations;
import org.thoughtcrime.securesms.pin.PinRestoreActivity;
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.registration.RegistrationNavigationActivity;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.util.Locale;
public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarActivity implements MasterSecretListener {
private static final String TAG = PassphraseRequiredActionBarActivity.class.getSimpleName();
public static final String LOCALE_EXTRA = "locale_extra";
public static final String NEXT_INTENT_EXTRA = "next_intent";
private static final int STATE_NORMAL = 0;
private static final int STATE_CREATE_PASSPHRASE = 1;
private static final int STATE_PROMPT_PASSPHRASE = 2;
private static final int STATE_UI_BLOCKING_UPGRADE = 3;
private static final int STATE_WELCOME_PUSH_SCREEN = 4;
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 SignalServiceNetworkAccess networkAccess;
private BroadcastReceiver clearKeyReceiver;
@Override
protected final void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "[" + Log.tag(getClass()) + "] onCreate()");
this.networkAccess = new SignalServiceNetworkAccess(this);
onPreCreate();
final boolean locked = KeyCachingService.isLocked(this);
routeApplicationState(locked);
super.onCreate(savedInstanceState);
if (!isFinishing()) {
initializeClearKeyReceiver();
onCreate(savedInstanceState, true);
}
}
protected void onPreCreate() {}
protected void onCreate(Bundle savedInstanceState, boolean ready) {}
@Override
protected void onResume() {
Log.d(TAG, "[" + Log.tag(getClass()) + "] onResume()");
super.onResume();
if (networkAccess.isCensored(this)) {
ApplicationDependencies.getJobManager().add(new PushNotificationReceiveJob(this));
}
}
@Override
protected void onStart() {
Log.d(TAG, "[" + Log.tag(getClass()) + "] onStart()");
super.onStart();
}
@Override
protected void onPause() {
Log.d(TAG, "[" + Log.tag(getClass()) + "] onPause()");
super.onPause();
}
@Override
protected void onStop() {
Log.d(TAG, "[" + Log.tag(getClass()) + "] onStop()");
super.onStop();
}
@Override
protected void onDestroy() {
Log.d(TAG, "[" + Log.tag(getClass()) + "] onDestroy()");
super.onDestroy();
removeClearKeyReceiver(this);
}
@Override
public void onMasterSecretCleared() {
Log.d(TAG, "onMasterSecretCleared()");
if (ApplicationContext.getInstance(this).isAppVisible()) routeApplicationState(true);
else finish();
}
protected <T extends Fragment> T initFragment(@IdRes int target,
@NonNull T fragment)
{
return initFragment(target, fragment, null);
}
protected <T extends Fragment> T initFragment(@IdRes int target,
@NonNull T fragment,
@Nullable Locale locale)
{
return initFragment(target, fragment, locale, null);
}
protected <T extends Fragment> T initFragment(@IdRes int target,
@NonNull T fragment,
@Nullable Locale locale,
@Nullable Bundle extras)
{
Bundle args = new Bundle();
args.putSerializable(LOCALE_EXTRA, locale);
if (extras != null) {
args.putAll(extras);
}
fragment.setArguments(args);
getSupportFragmentManager().beginTransaction()
.replace(target, fragment)
.commitAllowingStateLoss();
return fragment;
}
private void routeApplicationState(boolean locked) {
Intent intent = getIntentForState(getApplicationState(locked));
if (intent != null) {
startActivity(intent);
finish();
}
}
private Intent getIntentForState(int state) {
Log.d(TAG, "routeApplicationState(), state: " + state);
switch (state) {
case STATE_CREATE_PASSPHRASE: return getCreatePassphraseIntent();
case STATE_PROMPT_PASSPHRASE: return getPromptPassphraseIntent();
case STATE_UI_BLOCKING_UPGRADE: return getUiBlockingUpgradeIntent();
case STATE_WELCOME_PUSH_SCREEN: return getPushRegistrationIntent();
case STATE_ENTER_SIGNAL_PIN: return getEnterSignalPinIntent();
case STATE_CREATE_SIGNAL_PIN: return getCreateSignalPinIntent();
case STATE_CREATE_PROFILE_NAME: return getCreateProfileNameIntent();
default: return null;
}
}
private int getApplicationState(boolean locked) {
if (!MasterSecretUtil.isPassphraseInitialized(this)) {
return STATE_CREATE_PASSPHRASE;
} else if (locked) {
return STATE_PROMPT_PASSPHRASE;
} else if (ApplicationMigrations.isUpdate(this) && ApplicationMigrations.isUiBlockingMigrationRunning()) {
return STATE_UI_BLOCKING_UPGRADE;
} else if (!TextSecurePreferences.hasPromptedPushRegistration(this)) {
return STATE_WELCOME_PUSH_SCREEN;
} else if (SignalStore.storageServiceValues().needsAccountRestore()) {
return STATE_ENTER_SIGNAL_PIN;
} else if (userMustSetProfileName()) {
return STATE_CREATE_PROFILE_NAME;
} else if (userMustCreateSignalPin()) {
return STATE_CREATE_SIGNAL_PIN;
} else {
return STATE_NORMAL;
}
}
private boolean userMustCreateSignalPin() {
return !SignalStore.registrationValues().isRegistrationComplete() && !SignalStore.kbsValues().hasPin() && !SignalStore.kbsValues().lastPinCreateFailed();
}
private boolean userMustSetProfileName() {
return !SignalStore.registrationValues().isRegistrationComplete() && Recipient.self().getProfileName().isEmpty();
}
private Intent getCreatePassphraseIntent() {
return getRoutedIntent(PassphraseCreateActivity.class, getIntent());
}
private Intent getPromptPassphraseIntent() {
return getRoutedIntent(PassphrasePromptActivity.class, getIntent());
}
private Intent getUiBlockingUpgradeIntent() {
return getRoutedIntent(ApplicationMigrationActivity.class,
TextSecurePreferences.hasPromptedPushRegistration(this)
? getConversationListIntent()
: getPushRegistrationIntent());
}
private Intent getPushRegistrationIntent() {
return RegistrationNavigationActivity.newIntentForNewRegistration(this);
}
private Intent getEnterSignalPinIntent() {
return getRoutedIntent(PinRestoreActivity.class, getIntent());
}
private Intent getCreateSignalPinIntent() {
final Intent intent;
if (userMustSetProfileName()) {
intent = getCreateProfileNameIntent();
} else {
intent = getIntent();
}
return getRoutedIntent(CreateKbsPinActivity.class, intent);
}
private Intent getCreateProfileNameIntent() {
return getRoutedIntent(EditProfileActivity.class, getIntent());
}
private Intent getRoutedIntent(Class<?> destination, @Nullable Intent nextIntent) {
final Intent intent = new Intent(this, destination);
if (nextIntent != null) intent.putExtra("next_intent", nextIntent);
return intent;
}
private Intent getConversationListIntent() {
// TODO [greyson] Navigation
return new Intent(this, MainActivity.class);
}
private void initializeClearKeyReceiver() {
this.clearKeyReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "onReceive() for clear key event");
onMasterSecretCleared();
}
};
IntentFilter filter = new IntentFilter(KeyCachingService.CLEAR_KEY_EVENT);
registerReceiver(clearKeyReceiver, filter, KeyCachingService.KEY_PERMISSION, null);
}
private void removeClearKeyReceiver(Context context) {
if (clearKeyReceiver != null) {
context.unregisterReceiver(clearKeyReceiver);
clearKeyReceiver = null;
}
}
/**
* Puts an extra in {@code intent} so that {@code nextIntent} will be shown after it.
*/
public static @NonNull Intent chainIntent(@NonNull Intent intent, @NonNull Intent nextIntent) {
intent.putExtra(NEXT_INTENT_EXTRA, nextIntent);
return intent;
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms;
import android.content.Intent;
import android.os.Bundle;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.contacts.SelectedContact;
import org.thoughtcrime.securesms.recipients.RecipientId;
import java.util.ArrayList;
import java.util.List;
/**
* Activity container for selecting a list of contacts.
*
* @author Moxie Marlinspike
*
*/
public class PushContactSelectionActivity extends ContactSelectionActivity {
public static final String KEY_SELECTED_RECIPIENTS = "recipients";
@SuppressWarnings("unused")
private final static String TAG = PushContactSelectionActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle icicle, boolean ready) {
getIntent().putExtra(ContactSelectionListFragment.MULTI_SELECT, true);
super.onCreate(icicle, ready);
initializeToolbar();
}
protected void initializeToolbar() {
getToolbar().setNavigationIcon(R.drawable.ic_check_24);
getToolbar().setNavigationOnClickListener(v -> {
onFinishedSelection();
});
}
protected final void onFinishedSelection() {
Intent resultIntent = getIntent();
List<SelectedContact> selectedContacts = contactsFragment.getSelectedContacts();
List<RecipientId> recipients = Stream.of(selectedContacts).map(sc -> sc.getOrCreateRecipientId(this)).toList();
resultIntent.putParcelableArrayListExtra(KEY_SELECTED_RECIPIENTS, new ArrayList<>(recipients));
setResult(RESULT_OK, resultIntent);
finish();
}
}

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