Compare commits

...

742 Commits

Author SHA1 Message Date
Greyson Parrelli
6fdaef1f58 Bump version to 5.3.2 2021-01-22 23:44:10 -05:00
Greyson Parrelli
6db6c93295 Updated language translations. 2021-01-22 23:43:45 -05:00
Greyson Parrelli
4fb0f30d32 Add bubbles around additional elements in wallpaper mode. 2021-01-22 23:36:42 -05:00
Greyson Parrelli
7fa4eb079b Improve responsiveness of conversation update rendering. 2021-01-22 23:36:42 -05:00
Greyson Parrelli
c5392b8844 Fix highlighted bounds of conversation items. 2021-01-22 23:36:42 -05:00
Greyson Parrelli
e460973957 Various wallpaper UX fixes. 2021-01-22 23:36:42 -05:00
Greyson Parrelli
e1c6311a18 Fix bug where dates weren't rendered properly after wallpaper change. 2021-01-22 23:36:42 -05:00
Greyson Parrelli
ed11e2f05a Center-align About text in recipient settings. 2021-01-22 23:36:42 -05:00
Greyson Parrelli
3182e5af88 Fix spacing bug when About is set to only text or only emoji. 2021-01-22 23:36:42 -05:00
Greyson Parrelli
5cfdf626fe Reduce the font size of all update messages. 2021-01-22 23:36:42 -05:00
Alex Hart
e55834d523 Prevent NPE in PhoneNumberFormatter. 2021-01-22 23:36:42 -05:00
Greyson Parrelli
9d5a52a980 Made setting a profile photo a synchronous operation. 2021-01-22 23:36:42 -05:00
Alan Evans
5649c906a5 Do not bump group threads on leave. 2021-01-22 23:36:42 -05:00
Greyson Parrelli
ee548d27e5 Made setting profile name and About synchronous operations. 2021-01-22 23:36:42 -05:00
Greyson Parrelli
1dc737b5e5 Increase max About glyphs to 140. 2021-01-22 23:36:42 -05:00
Greyson Parrelli
427869d4ca Various improvements to About UI/UX. 2021-01-22 23:36:42 -05:00
Greyson Parrelli
36395ced89 Add About presets. 2021-01-22 23:36:42 -05:00
Greyson Parrelli
52a9f2c893 Add the ability to clear about and emoji. 2021-01-22 10:29:52 -05:00
Greyson Parrelli
bd88be2513 Auto-open keyboard on About screen. 2021-01-22 10:17:06 -05:00
Alan Evans
7107c1d6b2 Add Taqbaylit language support. 2021-01-22 10:59:42 -04:00
Alan Evans
51f4a343c9 Add Odia ଓଡ଼ିଆ language support. 2021-01-22 10:54:21 -04:00
Alex Hart
a12ee1b78b Update copy for reset all wallpapers dialog. 2021-01-22 09:42:24 -04:00
Alex Hart
4bf59a55da Distinguish clear copy between global and single recipient wallpapers. 2021-01-22 09:40:05 -04:00
Alex Hart
dbac9bf9f6 Enable 'dim in dark theme' by default. 2021-01-22 09:13:12 -04:00
Alex Hart
b95083fe92 Fix sticker support in multishare. 2021-01-22 09:01:38 -04:00
Alan Evans
e2d297eb8a Skip native LibSignal tests on unsupported and non-unix OS. 2021-01-22 00:34:41 -04:00
Greyson Parrelli
a3176bbb67 Bump version to 5.3.1 2021-01-21 22:52:31 -05:00
Greyson Parrelli
4203dde151 Updated language translations. 2021-01-21 22:52:08 -05:00
Greyson Parrelli
880661710f Fix possible NPE in group list item. 2021-01-21 22:47:30 -05:00
Greyson Parrelli
d844fa0fb5 Fix possible NPE on wallpaper change. 2021-01-21 22:47:30 -05:00
Greyson Parrelli
18ede2e900 Bump version to 5.3.0 2021-01-21 18:29:56 -05:00
Greyson Parrelli
06cc96bee7 Updated language translations. 2021-01-21 18:29:15 -05:00
Greyson Parrelli
43a12d2a81 Refine incognito keyboard setting string. 2021-01-21 18:22:05 -05:00
Michael Crenshaw
0a29ffcf4c Update incognito keyboard copy. 2021-01-21 18:17:35 -05:00
Thore Goebel
9c88532c21 Add ripple to review storage button. 2021-01-21 18:04:56 -05:00
Martin d'Allens
f3450b8f10 Center the terms link on the welcome fragment for long translations. 2021-01-21 18:02:20 -05:00
Alan Evans
a4d56e376f Allow clicking on typer avatar to bring up their bottom sheet details. 2021-01-21 18:02:20 -05:00
ascendingSun
24b5bac589 Fix camera crash when mic permission is granted.
Fixes #10642
2021-01-21 18:02:19 -05:00
Greyson Parrelli
762f17f1c1 Install a new animated sticker pack. 2021-01-21 18:02:19 -05:00
Greyson Parrelli
105c8c9745 Fix issue where forwarded link previews weren't marked uploaded. 2021-01-21 18:02:19 -05:00
Alan Evans
93d99287eb Wallpaper preview size respects device aspect ratio. 2021-01-21 18:02:19 -05:00
Jack Lloyd
ce156c3450 Bump libsignal-client to 0.2.2 2021-01-21 18:02:19 -05:00
Greyson Parrelli
7db16e6156 Add support for an 'About' field on your profile. 2021-01-21 18:02:19 -05:00
Alex Hart
e80033c287 Fix several issues with local expandable pip. 2021-01-21 18:02:19 -05:00
Alex Hart
1553f9b75d Upgrade libphonenumber to v8.12.16 2021-01-21 18:02:19 -05:00
Alex Hart
c244a98962 Finalize wallpaper UX.
Co-authored-by: Greyson Parrelli <greyson@signal.org>
Co-authored-by: Alan Evans <alan@signal.org>
2021-01-21 18:02:19 -05:00
Alan Evans
a8ad1e718e Wallpaper image selection and cropping. 2021-01-21 18:02:19 -05:00
Alex Hart
b5712f4bd1 Improve wallpaper settings screen, conversation rendering.
Co-authored-by: Greyson Parrelli <greyson@signal.org>
2021-01-21 18:02:19 -05:00
Greyson Parrelli
6bcb0de43d Add support for persisting wallpaper selection. 2021-01-21 18:02:19 -05:00
Alex Hart
80651d2425 Initial wallpaper settings screens. 2021-01-21 18:02:19 -05:00
Martin d'Allens
46492b8238 Remove ~200 unused English strings. 2021-01-21 18:02:19 -05:00
Alan Evans
1be561543c Fix API19 drawable. 2021-01-21 18:02:19 -05:00
PockelHockel
e430a46e20 Prevent screen rotation during voice record.
Fixes #8276
2021-01-21 18:02:19 -05:00
Alex Hart
8d187c8ba1 Add the ability to forward content to multiple chats at once. 2021-01-21 18:02:19 -05:00
Sicco van Sas
eacf03768f Create FUNDING.yml
This will add a 'sponsor' button at the top of the repository which links to Signals donate page https://signal.org/donate/
2021-01-19 17:09:42 -04:00
Alan Evans
b077c9b4f3 Only schedule one job per constraint set. 2021-01-19 17:07:32 -04:00
Alan Evans
893749fcab Prevent stripping of leading zeros in national numbers. 2021-01-19 17:06:49 -04:00
Alan Evans
848ead5e78 Include an English filter line in the support email body. 2021-01-19 17:06:23 -04:00
Jim Gustafson
9c47acb004 Update to RingRTC v2.8.10 2021-01-19 16:03:37 -05:00
Greyson Parrelli
8ca54bcc7b Create a new manage profile screen. 2021-01-19 10:39:49 -05:00
Greyson Parrelli
7e64d57ba8 Bump version to 5.2.3 2021-01-17 00:17:47 -05:00
Greyson Parrelli
a517fc4e15 Fix NPE in RecipientDatabase. 2021-01-17 00:17:26 -05:00
Greyson Parrelli
4f4aea22ce Bump version to 5.2.2 2021-01-16 21:27:38 -05:00
Greyson Parrelli
e0ea2bdde4 Updated language translations. 2021-01-16 21:27:14 -05:00
Greyson Parrelli
d40dc1d90b Bump signal-client-java version to 0.1.5 2021-01-16 21:11:42 -05:00
Greyson Parrelli
4571151e3c Revert "Remove reset session button."
This reverts commit f24020e7b7.
2021-01-16 21:11:42 -05:00
Greyson Parrelli
3e43963f67 Put receipts in the recipient's queue. 2021-01-16 21:11:42 -05:00
Greyson Parrelli
fe71d6ac41 Make outage banner color less aggressive. 2021-01-16 21:11:42 -05:00
Greyson Parrelli
0514950333 Feature flag OkHttp automatic network retry. 2021-01-16 21:11:42 -05:00
Greyson Parrelli
a2dc781840 Add an automatic session reset interval. 2021-01-16 21:11:42 -05:00
Greyson Parrelli
2c1c6fab35 Bump version to 5.2.1 2021-01-16 03:41:29 -05:00
Greyson Parrelli
3c2e428c54 Updated language translations. 2021-01-16 03:41:29 -05:00
Greyson Parrelli
8f7fe5c3ee Add jitter to job exponential backoff. 2021-01-16 03:41:29 -05:00
Greyson Parrelli
93e9dd6425 Feature flag the default max backoff interval. 2021-01-16 03:06:54 -05:00
Greyson Parrelli
c95f0fce6e Handle ServerRejectedException.
Handle an exception that indicates we should halt retries.
2021-01-16 02:32:09 -05:00
Greyson Parrelli
a3c7e7e552 Feature flag automatic session reset. 2021-01-16 02:05:43 -05:00
Greyson Parrelli
1e2590af49 Lock the threadId during message send.
Fixes #10659
2021-01-15 12:15:07 -05:00
Greyson Parrelli
562e608e1f Fix issue with previously-enqueued bad encrypted messages. 2021-01-15 11:50:50 -05:00
Greyson Parrelli
417d5a2804 Be extra safe when posting a notification during a migration. 2021-01-15 11:22:15 -05:00
Ewout ter Hoeven
c0c8d2caa7 Update issue template.
Fixes #10626

Co-authored-by: Greyson Parrelli <greyson@signal.org>
2021-01-15 11:17:38 -05:00
Greyson Parrelli
727175e4f4 Add 'constraints' and 'key preferences' sections to logs. 2021-01-15 11:17:38 -05:00
Ewout ter Hoeven
577d2b13ca CI: Update to checkout v2, remove install NDK
- Updates to action/checkout v2, which is faster
 - Remove install NDK step, since it's installed by default and speeds up the build
2021-01-14 12:44:08 -04:00
Greyson Parrelli
6ac2f922e2 Fix capitalization in some strings. 2021-01-14 10:47:42 -05:00
Greyson Parrelli
98297e55c1 Don't show menu actions for chat refresh messages. 2021-01-14 10:46:09 -05:00
Alan Evans
aa2094a2cc Fix group recipient showing in verify safety number change "learn more". 2021-01-14 10:19:50 -04:00
Alex Hart
f8c053cc96 Add 'on another device' to participants description 2021-01-14 07:03:19 -04:00
Alex Hart
790f8426ac Fix issue when single user leaves ParticipantCollection. 2021-01-14 06:53:18 -04:00
Greyson Parrelli
ff11609a82 Bump version to 5.2.0 2021-01-13 19:57:58 -05:00
Greyson Parrelli
94346033a8 Updated language translations. 2021-01-13 19:57:35 -05:00
Alan Evans
cb1401f556 Prompt to confirm number before SMS or call. 2021-01-13 19:43:35 -05:00
Alan Evans
ae676d7486 Fast job sorting. 2021-01-13 19:43:35 -05:00
Alan Evans
2d39e43677 Restrict group names to 32 graphemes.
Uses some code from #10132 hence co-author:

Co-authored-by: Fumiaki Yoshimatsu <fumiakiy@gmail.com>
2021-01-13 19:43:35 -05:00
Alex Hart
0ccc7e3c06 Distinguish between primary and secondary devices in participants list. 2021-01-13 19:43:23 -05:00
Alex Hart
2d20ceea01 Show contact profile photo instead of system contact. 2021-01-13 19:43:23 -05:00
Alex Hart
cee2702fdf Add expandable video pip to 1:1 conversations. 2021-01-13 19:43:23 -05:00
Greyson Parrelli
6c94be70dc Update safety number UI. 2021-01-13 19:43:23 -05:00
Greyson Parrelli
f24020e7b7 Remove reset session button. 2021-01-13 19:43:23 -05:00
Greyson Parrelli
728f1707b6 Automatically recover from bad encrypted messages. 2021-01-13 19:43:23 -05:00
Alan Evans
adea15df10 Recover from CDN 416 Range error on attachment download. 2021-01-13 19:43:23 -05:00
Alex Hart
be91f2396c Add toggle to control call bandwidth. 2021-01-13 19:43:23 -05:00
Alex Hart
8724d904b7 Add NotInCallConstraint, restrict auto-download of media and documents when on an active voice or video call. 2021-01-13 19:43:23 -05:00
Greyson Parrelli
ef95479157 Increase versionCode postFixSize from 10 to 100. 2021-01-13 19:43:23 -05:00
Greyson Parrelli
710cd23537 Fix typo in log. 2021-01-13 19:43:23 -05:00
Alex Hart
0af313a81f Add correct margin to in-call menu item. 2021-01-13 19:43:23 -05:00
Alex Hart
71be388989 Order grid by latest speakers and prevent any unnecessary shifts. 2021-01-13 19:43:23 -05:00
Alex Hart
db3098f633 Add immersive mode for calling. 2021-01-13 19:43:23 -05:00
Greyson Parrelli
ac197f42f2 Bump version to 5.1.9 2021-01-13 17:39:05 -05:00
Greyson Parrelli
d82882ba28 Updated language translations. 2021-01-13 17:38:35 -05:00
Greyson Parrelli
957a12875d Fix situations where we might not have detected first-ever-launch. 2021-01-13 17:33:14 -05:00
Greyson Parrelli
796eb5043c Bump version to 5.1.8 2021-01-12 12:47:57 -05:00
Greyson Parrelli
4f8d86828f Updated language translations. 2021-01-12 12:47:57 -05:00
Greyson Parrelli
5370605815 Control CDS refresh interval with a feature flag. 2021-01-12 12:47:57 -05:00
Greyson Parrelli
d5fb71b63f Prevent creating threads for remapped users.
Fixes #10538
2021-01-12 11:41:13 -05:00
Greyson Parrelli
2455c291d8 Bump version to 5.1.7 2021-01-12 02:06:59 -05:00
Greyson Parrelli
80ad28e9cc Updated language translations. 2021-01-12 02:06:00 -05:00
Greyson Parrelli
74552ba545 Fix possible crash with ProcessLifecycleOwner. 2021-01-12 02:06:00 -05:00
Greyson Parrelli
141cab1105 Perfom a migration to notify users of new contacts. 2021-01-11 23:22:01 -05:00
Greyson Parrelli
f012a41345 Fix issue with Signal join notifications. 2021-01-11 23:21:54 -05:00
Alan Evans
1f95df60d4 Fix style of approve new member switch in light bottom sheet. 2021-01-11 19:07:27 -04:00
Alan Evans
560c8c8cac Bump version to 5.1.6 2021-01-11 17:27:32 -04:00
Alan Evans
7cd79f8a94 Updated language translations. 2021-01-11 17:27:32 -04:00
Greyson Parrelli
667304c81e Cause LiveRecipient.refresh() to force a LiveData change. 2021-01-11 17:18:46 -04:00
Greyson Parrelli
2dd95c6ef6 Increase profile timeouts. 2021-01-11 17:18:46 -04:00
Greyson Parrelli
29e66e1d47 Fix the invite share button. 2021-01-11 17:18:46 -04:00
Alan Evans
5eb5af2f87 Bump version to 5.1.5 2021-01-11 14:13:02 -04:00
Alan Evans
e47b62805b Updated language translations. 2021-01-11 14:13:02 -04:00
Alan Evans
57adc73e95 Revert "Fast job sorting."
This reverts commit 373972f5dc.
2021-01-11 13:59:01 -04:00
Greyson Parrelli
8f4d64d37a Update link preview user agent. 2021-01-11 13:46:35 -04:00
Alan Evans
9ce3813044 Add "Enter your phone number" string for translation. 2021-01-11 13:46:35 -04:00
Alan Evans
6436e2836d No cell service hint during registration. 2021-01-11 13:46:35 -04:00
Alan Evans
77c83019d0 Smaller titles on small screen registration. 2021-01-11 13:46:35 -04:00
Greyson Parrelli
e6dfe96569 Add a gradient and background to the onboarding megaphone. 2021-01-11 13:46:35 -04:00
Alan Evans
5d515198e6 Fix initial state for update button. 2021-01-10 11:47:59 -04:00
Greyson Parrelli
1d912c0db2 Fix issue where conversation hero avatars didn't show up. 2021-01-10 10:01:31 -05:00
Greyson Parrelli
bac04dea8d Bump version to 5.1.4 2021-01-09 23:45:05 -05:00
Greyson Parrelli
3b39d13412 Fix possible crash with ProcessLifecycleObserver. 2021-01-09 23:41:31 -05:00
Greyson Parrelli
9838b2cf0a Fix crash in ContactSelectionListFragment. 2021-01-09 23:36:57 -05:00
Greyson Parrelli
0ac56ca571 Fix crash with ExpiringMessageManager. 2021-01-09 23:36:09 -05:00
Greyson Parrelli
12321bc2f0 Bump version to 5.1.3 2021-01-09 23:22:10 -05:00
Greyson Parrelli
3a55dfa32f Updated language translations. 2021-01-09 23:21:50 -05:00
Alan Evans
373972f5dc Fast job sorting. 2021-01-09 23:16:46 -05:00
Alan Evans
60a701f84f Fix missing dialog message on single user add confirm. 2021-01-09 20:12:10 -04:00
Greyson Parrelli
14f7c01fcb Only notify for actual recipient changes. 2021-01-09 18:45:22 -05:00
Greyson Parrelli
caf4f1a7ba Bump version to 5.1.2 2021-01-08 23:08:31 -05:00
Greyson Parrelli
eb55ac9a97 Updated language translations. 2021-01-08 23:07:17 -05:00
Greyson Parrelli
b9d8868aab Added a new onboarding megaphone. 2021-01-08 23:00:41 -05:00
Alex Hart
bec03534ef Animated skip button. 2021-01-08 21:10:40 -04:00
Alan Evans
565eab9dc1 Fix jumping "0 members". 2021-01-08 21:10:40 -04:00
Alan Evans
4d229862b6 Invite Friends bottom sheet. 2021-01-08 21:10:40 -04:00
Greyson Parrelli
3739eb7731 Add extra conditions for the SMS banner. 2021-01-08 21:01:13 -04:00
Alex Hart
ae5f9fb8ac Add empty state for members list in AddGroupDetailsFragment. 2021-01-08 21:01:13 -04:00
Alex Hart
4320a81846 Add invite friends action button and text. 2021-01-08 21:01:13 -04:00
Alan Evans
9fcf40fdc4 Allow empty group creation. 2021-01-08 12:53:23 -04:00
Greyson Parrelli
79d6ac100c Fix issue where megaphone display may be delayed. 2021-01-08 11:31:35 -05:00
Greyson Parrelli
a3e3153ee3 Add the Honor Play to the CameraX blacklist. 2021-01-08 10:13:50 -05:00
Alan Evans
0f525d2b07 Bump version to 5.1.1 2021-01-07 16:08:02 -04:00
Alan Evans
8de3f5045b Updated language translations. 2021-01-07 16:07:21 -04:00
Greyson Parrelli
fba4ae91e3 Fix issue where recipient observing could show stale data. 2021-01-07 16:07:21 -04:00
Alan Evans
dda68d6c95 Revert "Bump libsignal-client to 0.2.0"
This reverts commit e845fba8b3.
2021-01-07 16:07:04 -04:00
Greyson Parrelli
25af25cd19 Fix issue where button to go to archive was missing. 2021-01-07 16:07:04 -04:00
Alan Evans
dfd5b2c225 Ensure consistency and completeness of feature flag remote capable designation.
Make CustomVideoMuxer flag remote capable.
2021-01-07 16:07:04 -04:00
Greyson Parrelli
e850d8e917 Fix badge overlap in archive screen. 2021-01-07 09:54:02 -05:00
Alex Hart
677cf725a1 Fix bad screen lock behaviour. 2021-01-07 10:37:55 -04:00
Alan Evans
e95bb9cb0f Bump version to 5.1.0 2021-01-06 17:05:30 -04:00
Alan Evans
2c223a5826 Updated language translations. 2021-01-06 17:03:38 -04:00
Greyson Parrelli
bbc346bd7a Create a system for scheduling work post-initial-render. 2021-01-06 17:03:38 -04:00
Cody Henthorne
cf32b93269 Better error handling for group calls. 2021-01-06 17:03:38 -04:00
Cody Henthorne
84f1da76ad Fix bug where missing media keys would not always be shown on time. 2021-01-06 17:03:21 -04:00
Jack Lloyd
e845fba8b3 Bump libsignal-client to 0.2.0 2021-01-06 17:03:21 -04:00
Greyson Parrelli
01152ead61 Move the JobDatabase to a separate physical database.
Also removes maxInstancesPerFactory from DB, which was only used during job submission and had no need to be persisted.
2021-01-06 17:03:21 -04:00
Alex Hart
198281aa47 Show 'return to call' if local user is in the call group. 2021-01-06 17:03:21 -04:00
Jim Gustafson
8e8d86606b Update to RingRTC v2.8.9 2021-01-06 17:03:21 -04:00
Alan Evans
b4c2e21415 Custom streaming video muxer. 2021-01-06 17:03:21 -04:00
Alan Evans
6080e1f338 Ensure ProfileKeyCredentials match ProfileKey.
Fixes #10344
2021-01-06 17:03:20 -04:00
Alan Evans
6dd3fdaa55 Remove usages of deprecated Handler constructor. 2021-01-06 17:03:20 -04:00
Alan Evans
64312f9c7f Fix non-rendered previews when differ by trailing slash. 2021-01-06 17:03:20 -04:00
Greyson Parrelli
86542febf9 Move the MegaphoneDatabase to a separate physical database. 2021-01-06 17:03:20 -04:00
Alex Hart
9da49f9f8a Load correct recipient from thread record. 2021-01-06 17:03:20 -04:00
Alex Hart
ce3872ce1a Fix ACTION_OPEN_DOCUMENT_TREE crash when no file picker available.
Fixes #10131
2021-01-06 17:03:20 -04:00
Greyson Parrelli
c466dba8c4 Move the KeyValueDatabase to a separate physical database. 2021-01-06 17:03:20 -04:00
Alex Hart
46d412a6c3 UX update and slight stability fix. 2021-01-06 17:03:20 -04:00
Alex Hart
e2872d9af8 Add emdash instead of 0 if no callers are present and we haven't connected / loaded the group state. 2021-01-06 17:03:20 -04:00
Greyson Parrelli
3474b26f61 Don't include archived threads in recent conversation query. 2021-01-06 17:03:20 -04:00
Greyson Parrelli
740e934e5d Speed up the recipient warm-up phase. 2021-01-06 17:03:20 -04:00
Greyson Parrelli
61c5fc1057 Add shake-to-report for internal users. 2021-01-06 17:03:20 -04:00
Greyson Parrelli
7ef77bf16c Remove unbounded conversation list query. 2021-01-06 17:03:20 -04:00
Greyson Parrelli
aa3eb78956 Clean up and speed up conversation list item view. 2021-01-06 17:03:20 -04:00
Greyson Parrelli
cdd7b2deb9 Improve and streamline Application#onCreate. 2021-01-06 17:03:20 -04:00
Greyson Parrelli
c27300c19d Add a perf buildType for testing performance improvements. 2021-01-06 17:03:20 -04:00
Greyson Parrelli
8927971a19 Replace non-essential conversation list views with stubs. 2021-01-06 17:03:20 -04:00
Greyson Parrelli
1ced115b54 Only force a conversation list re-query for non-cold-starts. 2021-01-06 17:03:20 -04:00
Greyson Parrelli
fcbd594def Add a system to easily trace jobs. 2021-01-06 17:03:20 -04:00
Greyson Parrelli
4b8d02fdba Move Tracer to core-util. 2021-01-06 17:03:20 -04:00
Greyson Parrelli
e10284bd13 Remove Trace annotation. 2021-01-06 17:03:20 -04:00
Greyson Parrelli
4b5f1d64e6 Switch the conversation list to our own paging library. 2021-01-06 17:03:20 -04:00
Alex Hart
b7477d287b Reopen properly when we select launcher icon.
* Reopen properly when we select launcher icon.

* Reduce noise
2021-01-06 17:03:20 -04:00
Greyson Parrelli
6bab6c2454 Increase prekey archive age to 30 days. 2021-01-06 17:03:20 -04:00
Alex Hart
586c45616c Utilize ACTION_GET_CONTENT for one-time-access to backup.
Fixes #10312
2021-01-06 17:03:20 -04:00
Greyson Parrelli
ccd405fdce Don't double-isolate-bidi on phone numbers.
Fixes #10257
2021-01-06 17:03:20 -04:00
henry
dbf78d1b69 Show correct fragment layout preview. 2020-12-18 10:41:14 -04:00
Alex Hart
5f947ea2d6 Remove a few more instances of AsyncTask. 2020-12-18 10:41:14 -04:00
Alex Hart
73afa82147 Remove ViewUtil deprecated methods. 2020-12-18 10:41:14 -04:00
Alex Hart
744b79419b Swap out AsyncTask usage in notification action receivers with bounded threadpool. 2020-12-18 10:41:14 -04:00
Alex Hart
ce20dd97ff Fix bad compose input height. 2020-12-18 10:41:14 -04:00
Greyson Parrelli
3983d5aca4 Log the threadId of a log. 2020-12-18 10:41:14 -04:00
Greyson Parrelli
7b0de2d2a9 Force a feature flag refresh after a version change. 2020-12-18 10:41:14 -04:00
Cody Henthorne
2b65482abd Fix KitKat OOM when rendering rounded material buttons. 2020-12-18 10:41:14 -04:00
Cody Henthorne
fe01e80af5 Fix bug with mute states not dynamically updating in participants list. 2020-12-18 10:41:14 -04:00
Greyson Parrelli
fc43a0d8e9 Put log tag in brackets. 2020-12-18 10:41:14 -04:00
Alex Hart
e709cdc9d5 Remember the last position of emoji and sticker picker as you swap between them. 2020-12-18 10:41:14 -04:00
Jack Lloyd
d2d698f64e Don't rely on the SessionState protobuf.
Instead use the convenient deserialization constructor
2020-12-18 10:41:14 -04:00
Alan Evans
7f1e33be32 Fix not deselecting item that is too large to send. 2020-12-18 10:41:14 -04:00
Greyson Parrelli
443f1a1554 Bump version to 5.0.8 2020-12-17 17:55:40 -05:00
Greyson Parrelli
ebb025c40a Updated language translations. 2020-12-17 17:55:40 -05:00
Greyson Parrelli
f3ce582fa5 Inline GV1 auto-migration flag. 2020-12-17 17:55:33 -05:00
Greyson Parrelli
372744178e Bump version to 5.0.7 2020-12-15 20:24:51 -05:00
Greyson Parrelli
fc3aa96b5a Updated language translations. 2020-12-15 20:24:22 -05:00
Greyson Parrelli
f4c723cc60 Refactor how we handle GV1->GV2 migration suggestions. 2020-12-15 20:18:47 -05:00
Alan Evans
7864c8ceb4 Fix translations in group call screen when using in-app language. 2020-12-15 12:34:34 -04:00
Alan Evans
4c80aac4d6 Drop sync messages with bad GV1 lengths. 2020-12-15 12:10:42 -04:00
Greyson Parrelli
e2b6e85431 Bump version to 5.0.6 2020-12-14 22:48:57 -05:00
Greyson Parrelli
8587153ddd Updated language translations. 2020-12-14 22:48:18 -05:00
Greyson Parrelli
21956e400f Use a new DatabaseObserver system. 2020-12-14 22:43:34 -05:00
Alex Hart
fa7346f79b Add group calling tooltip and megaphone. 2020-12-14 22:43:34 -05:00
Alan Evans
7227b43bbe Remove conversation list datasource throttler. 2020-12-14 12:47:26 -04:00
Greyson Parrelli
e8c75249f1 Bump version to 5.0.5 2020-12-14 01:13:41 -05:00
Greyson Parrelli
cc5628cbce Updated language translations. 2020-12-14 01:12:46 -05:00
Greyson Parrelli
441808b1df Fix issue where client deprecation sometimes wasn't cleared. 2020-12-13 14:44:19 -05:00
Greyson Parrelli
42b0fe7853 Bump version to 5.0.4 2020-12-10 12:36:38 -05:00
Greyson Parrelli
7877f5db2f Updated language translations. 2020-12-10 12:36:38 -05:00
Alex Hart
b972e05660 Auto focus national number field after valid country code in delete fragment. 2020-12-10 12:36:38 -05:00
Greyson Parrelli
23579a9b1d Do not unnecessarily refresh known-unregistered users during migration. 2020-12-10 12:36:38 -05:00
Greyson Parrelli
af99753d47 Trace Application and Activity creates. 2020-12-10 11:45:15 -05:00
Greyson Parrelli
4b2366e537 Bump version to 5.0.3 2020-12-09 17:42:44 -05:00
Greyson Parrelli
bea72c2ee3 Updated language translations. 2020-12-09 17:42:44 -05:00
Greyson Parrelli
32a50fcfad Disable group calling for API 19. 2020-12-09 17:42:44 -05:00
Greyson Parrelli
30fa741365 Make group calling flag hot-swappable. 2020-12-09 17:39:02 -05:00
Greyson Parrelli
bed2544ff4 Don't try to update contacts if you have no permission.
Fixes #10271
2020-12-09 17:07:54 -05:00
Cody Henthorne
5a773de3b1 Handle group call update sync messages. 2020-12-09 16:33:47 -05:00
Alan Evans
924405c8ba Increase uncompressed video attachment size to 500 Mb. 2020-12-09 16:30:42 -04:00
Alan Evans
93e9de3932 Increase stream copy buffer size to 64K. 2020-12-09 16:29:08 -04:00
Alan Evans
a8dd81eace Return optional for telephone number region name for the unknown case to be localized. 2020-12-09 15:47:44 -04:00
Greyson Parrelli
ec8793c6fe Fix rendering issue when deleting the last message in a conversation. 2020-12-09 14:39:22 -05:00
Alex Hart
ffc0a230be Fix country code width on account deletion screen. 2020-12-09 14:09:21 -04:00
Cody Henthorne
5d4922ed8d Show accurate current group call participants in lobby header. 2020-12-09 11:53:59 -05:00
Alan Evans
974c33fe37 Directly reference activity for remove avatar confirmation prompt. 2020-12-09 11:15:48 -04:00
Alex Hart
3f2b4d60fd Fix voice note saves on API 28 and lower. 2020-12-09 10:22:31 -04:00
Greyson Parrelli
ca633b13af Bump version to 5.0.2 2020-12-08 18:23:07 -05:00
Greyson Parrelli
a671e152bd Updated language translations. 2020-12-08 18:22:44 -05:00
Cody Henthorne
a564aae80a Do not show speaker hint in pip. 2020-12-08 18:10:04 -05:00
Greyson Parrelli
9f8e31db78 Change WebsocketDrainedConstraint to DecryptionsDrainedConstraint. 2020-12-08 18:10:04 -05:00
Cody Henthorne
84e9282f87 Attempt to reduce number of peek jobs run after being offline. 2020-12-08 18:10:04 -05:00
Alan Evans
3949f4fd45 Hide join group call for inactive groups. 2020-12-08 18:10:04 -05:00
Greyson Parrelli
944a180b68 Ensure GV1->GV2 migrations work via group links. 2020-12-08 18:10:04 -05:00
Greyson Parrelli
9cd1a12b6a Fix NPE in FastJobStorage#getJobCountForQueue(). 2020-12-08 18:10:04 -05:00
Greyson Parrelli
a4a2d2fc0d Log out exception when a backup fails. 2020-12-08 18:10:04 -05:00
Artem Varaksa
6df839612d Fix "Advanced PIN settings" pushing wrong fragment. 2020-12-08 18:10:04 -05:00
Greyson Parrelli
dd630abd0e Fix issue where scrolling could get stuck.
The number of off-screen pages was too small, resulting in the
possibility of you still being offscreen after the pages loaded,
which could lead to loading more data, which could lead to you still
being offscreen, ad infinitum.

Simply increasing the number of buffer
pages resolves it.

Tested by adding an artificial 1 second delay to
loading a page.
2020-12-08 18:10:04 -05:00
Greyson Parrelli
6826c0ded5 Fix another scenario where search position was off. 2020-12-08 18:10:04 -05:00
Alex Hart
f1d0d4f81b Fix account deletion UI bugs. 2020-12-08 18:10:04 -05:00
Alex Hart
bfa56f771d Do not show join banner in pip mode. 2020-12-08 09:20:51 -04:00
Greyson Parrelli
167b9c13e5 Bump version to 5.0.1 2020-12-07 22:52:26 -05:00
Greyson Parrelli
4b7d9a3b9d Updated language translations. 2020-12-07 22:52:10 -05:00
Greyson Parrelli
c7585c5594 Fix issues with jumpToMessage behavior. 2020-12-07 22:40:43 -05:00
Greyson Parrelli
c3d7b88cf6 Add support for setting max instances per job queue. 2020-12-07 17:30:05 -05:00
Cody Henthorne
dc4ce234b7 Ensure proper group call history in chat after being offline.
Co-authored-by: Alan Evans <alan@signal.org>
2020-12-07 17:27:35 -05:00
Cody Henthorne
12330b0aff Bump RingRTC to 2.8.7 2020-12-07 16:43:47 -05:00
Alex Hart
edb2a17bcb Add ability to delete your Signal account from within the app. 2020-12-07 17:39:16 -04:00
Alan Evans
00b6416583 Prevent surplus notification sound when entering group. 2020-12-07 17:36:21 -04:00
Alex Hart
62297f1f98 Stabilize bluetooth a bit. 2020-12-07 16:33:14 -05:00
Cody Henthorne
c00b0727e3 Show call full UI when group call is full. 2020-12-07 16:17:39 -05:00
Greyson Parrelli
13616b9820 Fix preview of link previews with no thumbnails. 2020-12-07 15:54:16 -05:00
Greyson Parrelli
6530e1d937 Update CameraX blacklist. 2020-12-07 14:34:52 -05:00
Alex Hart
aff00615cb Fix bad theming on audio device selection popup. 2020-12-07 15:32:43 -04:00
Alex Hart
be53bfa88f Hide members list when user enters pip. 2020-12-07 14:50:11 -04:00
Alex Hart
5de50f1a8b Fix overflow presentation when active speaker changes. 2020-12-07 14:11:35 -04:00
Alex Hart
61886ea10a Display speaker in PiP. 2020-12-07 13:16:02 -04:00
Sgn-32
ea94f6bc91 Pretty print your phone number in advanced settings. 2020-12-07 11:15:13 -05:00
Greyson Parrelli
6080c18c90 Fix RTL display of formatted phone numbers.
Fixes #10261

Thank you to @Sgn-32 for finding that it can be solved with
StringUtil#isolateBidi()
2020-12-07 11:02:46 -05:00
Cody Henthorne
595d5dddbe Add Group Call speaker view hint. 2020-12-07 10:46:36 -05:00
Bastian Köcher
9b81e7f71b Removes deprecated Samsung multi-window support
This removes the deprecated Samsung multi-window support. Actually this
breaks multi-window support on newer Samsung devices. Android supports
multi-window since Android 7.0 and AFAIK Samsung switched to this as
well. There isn't even any reference anymore that mentions these lines
of code as required.
2020-12-07 10:39:54 -05:00
Cody Henthorne
bdc6c8c65a Fix a few minor group call UI issues. 2020-12-07 10:05:35 -05:00
Cody Henthorne
2dcc7d284f Update group membership for a group call when it changes. 2020-12-05 20:55:52 -05:00
Greyson Parrelli
234e4be924 Bump version to 5.0.0 2020-12-05 14:11:46 -05:00
Greyson Parrelli
1083e022cc Updated language translations. 2020-12-05 14:11:46 -05:00
Greyson Parrelli
cb1b4ec0b9 Rotate the GV1->GV2 auto migration flag. 2020-12-05 14:11:46 -05:00
Greyson Parrelli
40c46351e6 Update MMS description string. 2020-12-05 13:44:13 -05:00
Greyson Parrelli
3f75e4aeb3 Fix GV1->GV2 migration bug where users were incorrectly marked as dropped. 2020-12-05 13:44:13 -05:00
Greyson Parrelli
4321fabf0b Rotate the group calling feature flag. 2020-12-05 13:44:13 -05:00
Greyson Parrelli
8e93bf9075 Create a core-util module with some common utilities. 2020-12-05 13:44:13 -05:00
Greyson Parrelli
831cd2f297 Trace database methods. 2020-12-05 13:44:13 -05:00
Cody Henthorne
42d61518b3 Handle safety number changes in a group call context. 2020-12-05 13:44:13 -05:00
Alex Hart
112782ccaf Add join/leave banner for group calls. 2020-12-05 13:44:13 -05:00
Alan Evans
67a3a30d4c Run witness checksums in task and only when compiling. 2020-12-05 13:44:13 -05:00
Greyson Parrelli
898d92ba54 Fix issue where remote deletes screwed up jump positions.
Fixes #10171
2020-12-05 13:44:13 -05:00
Greyson Parrelli
323a405004 Don't format numbers unnecessarily.
Util.getFirstNonEmpty() requires calculating all input strings first,
but that's unnecessary and could result in lots of warning logs in the
case of calling PhoneNumberFormatter#prettyPrint with nulls or other
stuff.

Fixes #10246
2020-12-05 13:44:13 -05:00
Alex Hart
3f25609561 Add equals/hashcode to Key implementation. 2020-12-05 13:44:13 -05:00
Greyson Parrelli
97047bccde Remove job adds from database transactions. 2020-12-05 13:44:13 -05:00
Greyson Parrelli
31960b53a0 Use our own homemade paging library for conversation paging.
I made the lib, and Alan made the build actually work.

Co-authored-by: Alan Evans <alan@signal.org>
2020-12-05 13:44:13 -05:00
Cody Henthorne
ac41f3d662 Fix deadlock between group calling jobs. 2020-12-05 13:44:13 -05:00
Cody Henthorne
82eebbc3b0 Fix incorrect string resource usage for some activities. 2020-12-05 13:44:13 -05:00
Cody Henthorne
b1d74e21e2 Improve handling of 1:1 calls during group calls. 2020-12-05 13:44:13 -05:00
Greyson Parrelli
7868c3094b Make FastJobStorage synchronous again. 2020-12-05 13:44:13 -05:00
Sgn-32
ebaa4cee65 Pretty print phone numbers in recent chats on share screen.
Fixes #10204
2020-12-05 13:44:09 -05:00
Greyson Parrelli
141b22765e Pretty print your phone number in settings. 2020-12-05 13:43:25 -05:00
Cody Henthorne
050fad3114 Handle blocked users in group calls. 2020-12-05 13:43:25 -05:00
Cody Henthorne
01f143667f Add Group Call peeking in the Conversation view. 2020-12-05 13:43:25 -05:00
Alan Evans
2729eb9f5f Bump version to 4.79.3 2020-12-02 21:17:11 -04:00
Alan Evans
0a8e0d7889 Updated language translations. 2020-12-02 21:16:35 -04:00
Cody Henthorne
25bffa6d56 Put send viewed receipts behind a feature flag. 2020-12-02 20:15:26 -05:00
Greyson Parrelli
cf7fb7e1a2 Bump version to 4.79.2 2020-12-02 13:55:44 -05:00
Greyson Parrelli
4b7017580c Updated language translations. 2020-12-02 13:33:28 -05:00
Greyson Parrelli
90852b5715 Ensure we refresh our own profile after refreshing attributes. 2020-12-02 13:33:28 -05:00
Alex Hart
2103fd016b Return sane value if player is out of sync with data adapter. 2020-12-02 13:40:46 -04:00
Cody Henthorne
973ad55dfe Fix various UI issues with group calling PIPs. 2020-12-02 11:37:20 -05:00
Cody Henthorne
c3dea97857 Clear view cache properly after configuration change. 2020-12-02 10:27:20 -05:00
Cody Henthorne
0e37381179 Bump RingRTC to 2.8.5 2020-12-02 09:01:31 -05:00
Alex Hart
f7bc975534 Utilize left margin in drawing mask. 2020-12-02 09:44:51 -04:00
Greyson Parrelli
fab24bcd1e Bump version to 4.79.1 2020-12-01 17:11:12 -05:00
Greyson Parrelli
9be2e6b815 Updated language translations. 2020-12-01 17:11:12 -05:00
Greyson Parrelli
4037170b4a Rotate GV1 auto-migration feature flag. 2020-12-01 17:11:12 -05:00
Greyson Parrelli
b1974f31a9 Reduce number of optimistic migrations enqueued at once. 2020-12-01 16:52:19 -05:00
Greyson Parrelli
e6bf8f078d Use proper Recipient method in group sync job. 2020-12-01 16:52:19 -05:00
Alex Hart
cea4ee4ea9 Utilize GeneratedContactPhoto for named folk. 2020-12-01 16:17:36 -05:00
Alex Hart
283ff44da9 Cache conversation icon shortcuts. 2020-12-01 16:17:36 -05:00
Cody Henthorne
adee104899 Stop camera from turning on when returning to a group call. 2020-12-01 16:17:36 -05:00
Greyson Parrelli
1a844abcec Fix perf issues around shortcut updates. 2020-12-01 16:17:36 -05:00
Alan Evans
5f30745908 Reduce layout depth of conversation items. 2020-12-01 16:20:53 -04:00
Alex Hart
4ae0f3999c Apply better strategy for dependency init. 2020-12-01 16:01:47 -04:00
Alex Hart
dcb16378c8 Display PiP in Group Calls even if local video is disabled. 2020-12-01 14:53:34 -04:00
Alex Hart
b59a5c8609 Revert "Don't show members button if there are no remote people."
This reverts commit d4748efd42.
2020-12-01 12:19:03 -04:00
Cody Henthorne
55c9124c54 Prevent multiple taps from starting multiple calls. 2020-12-01 10:56:47 -05:00
henry
1376b4c0b8 Fix crash when enter long phone number on registration. 2020-12-01 10:13:12 -05:00
Sgn-32
9333e4fb68 Fix notification text for view-once videos.
Fixes #10141
2020-12-01 09:49:05 -05:00
Alan Evans
04d3faf057 Update rust lib to 0.1.5 to fix missing method log messages. 2020-12-01 10:43:58 -04:00
Greyson Parrelli
bcfbed9b3f Prevent error when user has no email client.
Fixes #10212

Thanks to @ali-khannakhjavani

Co-authored-by: ali-khannakhjavani
2020-11-30 18:59:42 -05:00
Jonah Beckford
dda51bf367 Complete update of reproducible build instructions 2020-11-30 18:14:35 -05:00
Greyson Parrelli
a324288d97 Bump version to 4.79.0 2020-11-30 15:32:27 -05:00
Greyson Parrelli
f21d2a2187 Updated language translations. 2020-11-30 15:31:56 -05:00
Cody Henthorne
5272fec948 Change group calling feature flag to boolean. 2020-11-30 15:13:56 -05:00
Alan Evans
fe11ebce55 Inline Group Invite Link feature flags. 2020-11-30 14:50:11 -04:00
Alan Evans
221cf56ddc Enqueue cached layout inflation on background thread. 2020-11-30 13:21:20 -04:00
Greyson Parrelli
7efd8be238 Inline max envelope size feature flag. 2020-11-30 11:47:54 -05:00
Greyson Parrelli
105862b524 Chunk read sync messages.
Same thing we do with read receipts we send to other people. Just missed
this part.
2020-11-30 11:36:04 -05:00
Alan Evans
cce8cdc7bf fixup! Clean up any invalid group V1 ids in database. 2020-11-30 12:32:01 -04:00
Cody Henthorne
834c2c2495 Bump RingRTC to 2.8.4 2020-11-30 11:02:54 -05:00
Greyson Parrelli
59f7ee6682 Remove aspectj for now. 2020-11-27 20:08:10 -05:00
Alan Evans
6cbd68fe9f Clean up any invalid group V1 ids in database. 2020-11-25 15:53:58 -04:00
Alex Hart
e1bf23251f Add support for Android 11 Conversation Bubbles. 2020-11-25 14:11:17 -04:00
Alan Evans
3aebadd90d Use protobuf's reserved keyword. 2020-11-25 13:58:06 -04:00
Alex Hart
e57a35ab3e Localize Conversation Intent creation. 2020-11-25 11:40:05 -04:00
Jack Lloyd
13c014215d Move to Signal Protocol written in Rust.
Co-authored-by: Alex Hart <alex@signal.org>
2020-11-25 11:40:05 -04:00
Alex Hart
02931f1826 Clear voice note media queues within synchronized block. 2020-11-25 11:40:05 -04:00
Cody Henthorne
a640d9e298 Improve group update copy and implement speaker indexing. 2020-11-25 11:40:05 -04:00
Alan Evans
ce68da1613 Reserve service field 20. 2020-11-25 11:40:05 -04:00
Greyson Parrelli
3599122ca6 Delete unnecessary artwork directory. 2020-11-25 11:40:05 -04:00
Greyson Parrelli
0003830a42 Cycle the GV1->GV2 auto migration flag. 2020-11-25 11:40:05 -04:00
Greyson Parrelli
3804a89619 Improve handling of membership changes during a GV1->GV2 migration. 2020-11-25 11:40:05 -04:00
Alex Hart
d4748efd42 Don't show members button if there are no remote people. 2020-11-25 11:40:05 -04:00
Alan Evans
0bda1d46a2 Allow setting local group names and avatars for MMS groups. 2020-11-25 11:40:05 -04:00
Greyson Parrelli
43e3ef2bee Refactor Message Request logic to fix some GV1->GV2 bugs. 2020-11-25 11:40:05 -04:00
Alex Hart
ce44e3949c Add new VIEWED item in RecieptMessage enumeration.
Also includes necessary Database changes for supporting this as well as View-Once receipt support.
2020-11-25 11:37:13 -04:00
Greyson Parrelli
7bb1262571 Upload trace file as separate debuglogs item. 2020-11-25 11:37:13 -04:00
Alex Hart
39f1aea8e3 Bump version to 4.78.5 2020-11-24 14:05:57 -04:00
Alex Hart
bda19d01ed Updated language translations. 2020-11-24 13:39:59 -04:00
Alex Hart
1f5364f01d Do not crash if RECIPIENT_EXTRA is null. 2020-11-24 13:22:41 -04:00
Alex Hart
65e88d2d1c Bump version to 4.78.4 2020-11-23 10:48:48 -04:00
Alex Hart
cef8aa67dd Updated language translations. 2020-11-23 10:43:51 -04:00
Alex Hart
5941b22eb6 Revert "Move to Signal Protocol written in Rust."
This reverts commit 907e8d93a3.
2020-11-23 10:22:53 -04:00
Alex Hart
9e7c55847e Bump version to 4.78.3 2020-11-20 16:54:53 -04:00
Alex Hart
5209b74605 Updated language translations. 2020-11-20 16:51:57 -04:00
Cody Henthorne
b90a74d26a Add additional Group Calling features. 2020-11-20 15:42:46 -05:00
Alan Evans
8c1737e597 Increase uncompressed video attachment size to 300 Mb. 2020-11-20 15:15:42 -04:00
Greyson Parrelli
2ea5bd2d44 Convert GV1->GV2 migration flags to booleans. 2020-11-20 13:50:14 -05:00
Greyson Parrelli
4166e7931e Fix membership diffs that occur during a GV1->GV2 migration.
Co-authored-by: Alan Evans <alan@signal.org>
2020-11-20 13:26:17 -05:00
Alan Evans
89f2c25d73 Display video file output size and duration during clipping.
Prevent video upscale, i.e. use input bit rate if lower than our normal target rates.
Do not time limit videos that are under the send file size.
Increase time limit to 10 minutes to match our lowest acceptable bitrate.
2020-11-20 13:27:58 -04:00
Alan Evans
abb1ca2afe Increase in-app recording duration to 60 seconds. 2020-11-20 13:11:48 -04:00
Greyson Parrelli
f7befd1593 Block GV1 creation if forced migrations are enabled. 2020-11-20 11:49:18 -05:00
Greyson Parrelli
28511de23c Ensure we properly detect update messages for migrations. 2020-11-20 11:39:55 -05:00
Greyson Parrelli
2ff3d1b7c5 Update phrasing on donate megaphone dismiss button. 2020-11-19 13:46:35 -05:00
Greyson Parrelli
fe6ae7e142 Don't show donate or research megaphones on new app installs. 2020-11-19 08:42:35 -05:00
Greyson Parrelli
0da6c83ce4 Bump version to 4.78.2 2020-11-18 19:52:48 -05:00
Greyson Parrelli
184b7db43c Updated language translations. 2020-11-18 19:51:15 -05:00
Greyson Parrelli
e442e34c1b More reliably setup initial preferences. 2020-11-18 19:47:27 -05:00
Greyson Parrelli
011efb0ce7 Request READ_PHONE_NUMBERS permission when necessary. 2020-11-18 19:47:27 -05:00
Alex Hart
63d00f87d8 Bump version to 4.78.1 2020-11-18 19:26:12 -04:00
Alex Hart
0323858145 Updated language translations. 2020-11-18 19:22:19 -04:00
Greyson Parrelli
a70e8ec7a7 Update reproducible build instructions. 2020-11-18 19:11:48 -04:00
Alex Hart
b306a3ef41 Update Dockerfile to utilize new commandline tools distributable. 2020-11-18 19:11:48 -04:00
Greyson Parrelli
ccd3467a61 Fix crash with MediaSendActivity progress dialog.
Co-authored-by: Alan Evans <alan@signal.org>
2020-11-18 17:31:43 -05:00
Alex Hart
40338afe7a Bump version to 4.78.0 2020-11-18 16:30:43 -04:00
Alex Hart
ff97f6af56 Updated language translations. 2020-11-18 16:30:43 -04:00
Alan Evans
6e7858e00f Restrict video send duration. 2020-11-18 16:30:43 -04:00
Greyson Parrelli
95468c85a8 Break large read receipt messages into chunks. 2020-11-18 14:19:28 -05:00
Cody Henthorne
f59e10d82c Fix read/unread conversation list colors. 2020-11-18 14:00:14 -05:00
Alex Hart
930370783e Implement ShortcutInfo for API 30. 2020-11-18 14:25:01 -04:00
Alex Hart
75062ada8a Upgrade SDK to 30. 2020-11-18 13:38:27 -04:00
Cody Henthorne
23618923d8 Attempt to recover from reoccurring exceptions when showing notifications. 2020-11-18 12:28:05 -05:00
Greyson Parrelli
f1d3a2f322 Fix Android 11 issue where keyboard wasn't auto-showing for PIN reminders. 2020-11-18 11:57:33 -05:00
Cody Henthorne
3b7fbbaf6e Fix crash when call concluded on non-existent remote peer. 2020-11-18 11:34:06 -05:00
Greyson Parrelli
725d793b20 Fix issue with link preview UI sizing. 2020-11-18 11:33:44 -05:00
Greyson Parrelli
5c3baca055 Add support for a donation megaphone. 2020-11-18 10:33:46 -05:00
Alan Evans
6e5abc92a0 Fix situation where group thread does not yet exist. 2020-11-17 15:52:38 -04:00
Alex Hart
8df6e95781 Stop proximity sensor on pause. 2020-11-17 15:15:13 -04:00
Alex Hart
2290a6c0df Synchronize voice note queue reads and writes. 2020-11-17 14:42:01 -04:00
Jack Lloyd
907e8d93a3 Move to Signal Protocol written in Rust.
Co-authored-by: Alex Hart <alex@signal.org>
2020-11-16 12:28:11 -05:00
Cody Henthorne
918497fb94 Bump version to 4.77.3 2020-11-16 11:49:35 -05:00
Cody Henthorne
3ccd6304c7 Updated language translations. 2020-11-16 11:47:55 -05:00
Greyson Parrelli
51d47adf57 Fix issue where FeatureFlags were triggering listeners for non-changes. 2020-11-16 11:27:58 -05:00
Cody Henthorne
f1e5206f56 Fix Invite Friend theming bug. 2020-11-16 10:17:42 -05:00
Cody Henthorne
f410635e2c Bump version to 4.77.2 2020-11-13 15:01:12 -05:00
Cody Henthorne
302d57bf19 Updated language translations. 2020-11-13 14:54:53 -05:00
Fumiaki Yoshimatsu
4c301a49b4 Fix appearance of small audio view to show correct background color and the progress circle. 2020-11-13 14:39:46 -05:00
Cody Henthorne
4ecfee292e Fix incorrect restarting and theming when system changes night mode. 2020-11-13 14:39:00 -05:00
Alex Hart
2a193ef455 Refactor with WindowUtil and correct some colors. 2020-11-13 14:43:58 -04:00
Cody Henthorne
96e241ef9c Fix RTL constraints for Help screen. 2020-11-13 12:32:55 -05:00
Cody Henthorne
e294a895e8 Bump version to 4.77.1 2020-11-12 12:54:33 -05:00
Cody Henthorne
003b9b1551 Updated language translations. 2020-11-12 12:49:45 -05:00
Alex Hart
a4e4af502e Retheme action modes. 2020-11-12 13:42:07 -04:00
Alex Hart
06aada20c1 Open keyboard when we open contact selection from blocked preference. 2020-11-12 13:39:38 -04:00
Greyson Parrelli
2dace38d43 Add support for GV1->GV2 forced migration. 2020-11-12 12:32:10 -05:00
Greyson Parrelli
554aa1ddf0 Trim message bodies at display time. 2020-11-12 12:18:20 -05:00
Greyson Parrelli
3b2a5f1ce3 Remove old profile sharing UI. 2020-11-12 12:01:43 -05:00
Cody Henthorne
3fc4b098e8 Show correct emojis for recipient names. 2020-11-12 11:22:00 -05:00
Fumiaki Yoshimatsu
a7d672f6b4 Apply locale updates correctly for appcompat-v1.2.0.
Fixes #9736

See https://developer.android.com/jetpack/androidx/releases/appcompat#1.2.0
for how the code is "correctly" applying a new configuration.

Co-authored-by: Cody Henthorne <cody@signal.org>
2020-11-12 09:56:07 -05:00
Greyson Parrelli
7e347f5cce Add support for manual initiation of GV1->GV2 migrations. 2020-11-12 09:52:21 -05:00
Cody Henthorne
4eaa6ebb47 Bump version to 4.77.0 2020-11-11 15:39:00 -05:00
Cody Henthorne
e85ef6881d Updated language translations. 2020-11-11 15:34:15 -05:00
Cody Henthorne
b1f6786392 Add initial support for Group Calling. 2020-11-11 15:29:02 -05:00
Cody Henthorne
696fffb603 Improve mention notifications by only showing alerting notifications once. 2020-11-11 15:29:02 -05:00
Alan Evans
3bb366ee04 Do not send typing indicator when deleting from the end & send stopped typing indicator when compose completely empty. 2020-11-11 15:29:02 -05:00
Alex Hart
6a59974f89 Add group settings UI polish. 2020-11-11 15:29:02 -05:00
Alan Evans
8e39267c42 Center number display vertically for non-signal contacts. 2020-11-11 15:29:02 -05:00
Jim Gustafson
b937534ce5 Update to RingRTC v2.8.0. 2020-11-11 15:29:01 -05:00
Alex Hart
f5b46f7356 Consolidate AnimatedDialog themes to single DayNight theme. 2020-11-11 15:29:01 -05:00
Greyson Parrelli
cd58c09be3 Proper handling of GV1->GV2 migrations in storage service. 2020-11-11 15:29:01 -05:00
Greyson Parrelli
e8f0038c36 Perform bulk receipt processing in a transaction. 2020-11-11 15:29:01 -05:00
Greyson Parrelli
0b77b33902 Add the ability to trace methods in internal builds.
Currently only for internal builds. Use the @Trace annotation to trace
methods for viewing in Perfetto.
2020-11-11 15:29:01 -05:00
Cody Henthorne
c3b5323010 Update assets and themes to leverage DayNight system. 2020-11-11 15:29:01 -05:00
Greyson Parrelli
81eaae4070 Update emoji to Unicode 13.0 2020-11-11 15:29:01 -05:00
Cody Henthorne
65461ce86f Fix incorrect reaction notification copy for various attachment types.
Fixes #10141. Thanks to @Sgn-32 for the initial PR.
2020-11-11 15:29:01 -05:00
Cody Henthorne
536e3139a2 Add foundation for using Android's DayNight theming system. 2020-11-11 15:29:01 -05:00
Alex Hart
e9c7b120a0 Improve contact blocking UX via settings. 2020-11-11 15:29:01 -05:00
Cody Henthorne
d6a230a235 Update AppCompat to 1.2 along with other Android UI libraries. 2020-11-11 15:29:01 -05:00
Alex Hart
6bf300ada8 Do not require write to read from single backup uri. 2020-11-11 15:29:01 -05:00
Greyson Parrelli
d307db8a95 Add the ability to add suggested members after a GV1 migration. 2020-11-11 15:29:01 -05:00
Alex Hart
c4c32d80b2 Update CameraX to 1.0.0-beta11. 2020-11-11 15:29:01 -05:00
Greyson Parrelli
f4c1e34402 Enforce max envelope size in more places. 2020-11-11 15:29:00 -05:00
Alan Evans
0068d62122 Bump version to 4.76.3 2020-11-09 14:39:30 -04:00
Alan Evans
3f1fa59e09 Updated language translations. 2020-11-09 14:38:11 -04:00
Greyson Parrelli
df5114c62c Fix website signing task. 2020-11-09 14:21:56 -04:00
Greyson Parrelli
956e3924ff Log the version in our PersistentLogger. 2020-11-09 12:47:10 -05:00
Greyson Parrelli
20ad166e0f Fix issue where we were double-logging job info. 2020-11-09 12:18:45 -05:00
Greyson Parrelli
12ea88f409 Improve logging around deletions. 2020-11-09 12:18:27 -05:00
Alan Evans
0c5648bfb1 Hide "Read More" when long message is remote deleted. 2020-11-09 10:24:11 -04:00
Greyson Parrelli
91ca19f294 Bump version to 4.76.2 2020-11-05 18:19:51 -05:00
Greyson Parrelli
71250afd2c Updated language translations. 2020-11-05 18:19:22 -05:00
Greyson Parrelli
cfdef7bca7 Only use the NATIONAL format for the US and UK. 2020-11-05 18:14:37 -05:00
Alan Evans
872f935fd5 Revert "Do not set or read quote author phone number."
This reverts commit 936e772ba0.
2020-11-05 18:56:17 -04:00
Alex Hart
0ed1f73990 Fix crash with multitouch in call screen pip. 2020-11-05 17:43:14 -04:00
Cody Henthorne
349a2f72cb Fix crash when handling call messaging failures. 2020-11-05 15:56:56 -05:00
Alex Hart
2b4a4d6109 Add support for Incoming / Outgoing Video Type. 2020-11-05 13:41:22 -04:00
Greyson Parrelli
9f882d2fbb Fix crash around creating MMS groups. 2020-11-05 10:57:31 -05:00
Greyson Parrelli
cb4a9730aa Bump version to 4.76.1 2020-11-04 20:11:55 -05:00
Greyson Parrelli
e0657d09d8 Fix issue where we weren't calling setTransactionSuccessful().
In a chain of events, this manifested by preventing the persistence of
media messages in group threads.
2020-11-04 20:07:57 -05:00
Alan Evans
01b9cb13b4 Bump version to 4.76.0 2020-11-04 16:51:23 -04:00
Alan Evans
2c7260557c Updated language translations. 2020-11-04 16:51:23 -04:00
Greyson Parrelli
9e5156ab73 Pretty-print phone numbers. 2020-11-04 16:51:23 -04:00
Alex Hart
3dc1614fbc Add basic profile spoofing detection. 2020-11-04 16:24:45 -04:00
Alan Evans
2f69a9c38e Share media from within Media Preview and share QR code image. 2020-11-04 16:05:35 -04:00
Greyson Parrelli
5e536c3fa5 Render GV1->GV2 migration event. 2020-11-04 16:05:35 -04:00
Greyson Parrelli
6bb9d27d4e Add the ability to migrate GV1 groups to GV2.
Co-authored-by: Alan Evans <alan@signal.org>
2020-11-04 16:05:35 -04:00
Greyson Parrelli
2d1bf33902 Update group table schema to support GV1->GV2 migration.
Also puts in protections to make sure we don't insert bad recipients or
groups.
2020-11-04 16:05:35 -04:00
Alan Evans
985a220fca Migrate GV1 to GV2 on to server. Allow query of group status. 2020-11-04 16:05:34 -04:00
Alex Hart
31e137cf6d Add support for MISSED_VIDEO_CALL type. 2020-11-04 16:05:34 -04:00
Alex Hart
f796447815 Add better error logging for single backup Uris. 2020-11-04 16:05:34 -04:00
Alan Evans
936e772ba0 Do not set or read quote author phone number. 2020-11-04 16:05:34 -04:00
Greyson Parrelli
ecee797d00 Always consider yourself a member of MMS groups.
Fixes #10162
2020-11-04 16:05:34 -04:00
Greyson Parrelli
357a8fc124 Remove name change for flipper and internal releases. 2020-11-04 16:02:11 -04:00
Alan Evans
1233af0ddd Add environment dimension. 2020-11-04 16:02:11 -04:00
Alex Hart
a264d10685 Fix issue with KitKat picture saves.
Fixes #10153
2020-11-04 16:01:58 -04:00
Alex Hart
ed17701a0a Remove look-behind and ding for single voice notes. 2020-11-02 11:50:37 -04:00
Greyson Parrelli
49e1ccea28 Allow more control over debug and staging signing. 2020-11-02 10:01:59 -05:00
Alan Evans
4c43b0d1e3 Update gradle plugin to 4.1.0, gradle to 6.5. 2020-11-02 10:01:59 -05:00
Greyson Parrelli
5ce09defca Include whether a user has a linked device in the debug log. 2020-11-02 10:01:59 -05:00
Greyson Parrelli
da9064b714 Bump version to 4.75.8 2020-11-02 10:00:23 -05:00
Greyson Parrelli
7bb53e4b06 Updated language translations. 2020-11-02 09:59:52 -05:00
Cody Henthorne
6a4ce1b658 Fix SMS role bug introduced for pre-Q devices. 2020-10-30 17:45:28 -04:00
Greyson Parrelli
f84595e1e8 Bump version to 4.75.7 2020-10-30 16:15:12 -04:00
Greyson Parrelli
41d5c54033 Updated language translations. 2020-10-30 16:14:30 -04:00
Greyson Parrelli
b9d6b63c09 Fix name of internal signing task. 2020-10-30 16:06:57 -04:00
Cody Henthorne
506ad0b3f1 Fix bug handling mentions in sync messages. 2020-10-30 15:13:54 -04:00
Cody Henthorne
c8302174a9 Fix mention suggestions for groups of 1.
Fixes #10152
2020-10-30 13:05:14 -04:00
Cody Henthorne
39cebfbb4e Fix SMS role request for Q+. 2020-10-30 12:34:47 -04:00
Cody Henthorne
d36ec9af47 Fix permission bug with avatar gallery selection. 2020-10-30 11:36:12 -04:00
Greyson Parrelli
5f6d971bf7 Bump version to 4.75.6 2020-10-30 08:24:14 -04:00
Greyson Parrelli
7a722d92a3 Updated language translations. 2020-10-30 08:23:25 -04:00
Greyson Parrelli
0bf0eba450 Fix NPE in BackupUtil. 2020-10-30 08:17:50 -04:00
Greyson Parrelli
d40783f794 Add signing task for internal builds. 2020-10-30 08:17:29 -04:00
Greyson Parrelli
88733473e2 Bump version to 4.75.5 2020-10-29 15:55:17 -04:00
Greyson Parrelli
7b65533095 Updated language translations. 2020-10-29 15:51:04 -04:00
Cody Henthorne
52b533c121 Add internal product flavor. 2020-10-29 15:33:15 -04:00
Greyson Parrelli
a4fa2e14fb Fix versioning issue with Dockerfile. 2020-10-29 15:31:05 -04:00
Cody Henthorne
6933f1d818 Fail call gracefully on turn server network error. 2020-10-29 13:51:30 -04:00
Greyson Parrelli
b5d6cb2a8d Notify about accidentally disabled backups. 2020-10-29 13:32:55 -04:00
Greyson Parrelli
d1478c5ce0 Reduce impact of CDS rate-limiting issues.
This will at least allow users with > RateLimit contacts to perform a successful sync. More work needs to be done here in the future to handle this better.
2020-10-29 10:16:21 -04:00
Greyson Parrelli
fbe62f0f3e Add more Huawei phones to the CameraX blacklist. 2020-10-29 08:04:29 -04:00
Greyson Parrelli
f84705b756 Include additional system properties in debuglog. 2020-10-28 17:01:34 -04:00
Cody Henthorne
cf2189c11a Ensure speakerphone is correctly enabled during call setup.
Race condition between handleStartOutgoingCall being enqueued from ringrtc and
handleSetEnableVideo being enqueued from the main thread.
2020-10-28 17:01:34 -04:00
Alex Hart
dfc4178252 Localize 'camera' folder title. 2020-10-28 17:01:34 -04:00
Greyson Parrelli
07952f2146 Bump version to 4.75.4.
Accidentally went the wrong direction with canonicalVersionCode in
4.75.3. So this release just fixes that and uses the correct
canonicalVersionCode.
2020-10-28 16:54:00 -04:00
Cody Henthorne
a90dad22a9 Bump version to 4.75.3 2020-10-28 16:22:16 -04:00
Cody Henthorne
64f7330609 Updated language translations. 2020-10-28 16:21:12 -04:00
Cody Henthorne
5e382c120b Fix security crash during directory refresh. 2020-10-28 16:14:45 -04:00
Greyson Parrelli
3eea568f5f Fix possible storage permission crash on camera. 2020-10-28 16:00:01 -04:00
Cody Henthorne
0077b29d6e Mitigate PSTN callback crash when service is in background. 2020-10-28 15:48:04 -04:00
Cody Henthorne
dfa6306b61 Bump version to 4.75.2 2020-10-26 16:08:44 -04:00
Cody Henthorne
a4bf075a1a Updated language translations. 2020-10-26 16:06:57 -04:00
Alex Hart
373d622535 Fix SMS, bad MODIFIED timestamp, and API19 beta crash. 2020-10-26 13:41:30 -03:00
Greyson Parrelli
ba1df58eb3 Do not show modern profile sharing on brand new conversations. 2020-10-26 12:08:01 -04:00
Greyson Parrelli
9fb85f7c76 Build log sections in series.
Doing them in parallel was causing possible bad blocked thread reports,
since the thread section could be built at the same time we were
building the jobs section.
2020-10-26 11:07:44 -04:00
Cody Henthorne
5e58f0a212 Bump version to 4.75.1 2020-10-23 15:45:20 -04:00
Cody Henthorne
8fa01f13e9 Updated language translations. 2020-10-23 15:44:07 -04:00
Alan Evans
4ce136be17 Fix missing message request on V2 re-invites. 2020-10-23 15:37:42 -04:00
Alan Evans
4099154dc0 Infer contact multi-select allowing assertion removal.
Hide count on invite friends.

Fixes #10125
2020-10-23 15:37:42 -04:00
Greyson Parrelli
3f983a5c82 Various UI adjustments to conversation updates. 2020-10-23 15:37:42 -04:00
Alex Hart
9743e3689a Add MimeType to MediaStore values. 2020-10-23 14:11:42 -03:00
Greyson Parrelli
1363f55f77 Fix back button behavior on OnePlus phones.
Couple things happened:
- Core issue: The device always thought the keyboard was open, so it was
always trying to dismiss the keyboard when you pressed back (instead of
actually going back)
- Big fix: Increase the tolerance of our view height differentialt that
detects if the keyboard is open
- Other fix: the getViewInset() method is always missing on Q, so as a
temp fix we fall back to the status bar height. Gets the calculation to
be closer, even if not truly correct.
2020-10-23 12:43:34 -04:00
Alex Hart
f1d98f6c7b Fix failed media saves on API < 29.
Fixes #10119
2020-10-23 13:12:07 -03:00
Alex Hart
9279a54d28 Fix bad voice note duration and listener breakage. 2020-10-23 13:00:46 -03:00
Alan Evans
81889d8130 Fix plural. 2020-10-23 11:13:37 -03:00
Cody Henthorne
6aecb8fbc1 Bump version to 4.75.0. 2020-10-22 17:04:24 -04:00
Cody Henthorne
8aa413032d Updated language translations. 2020-10-22 17:02:27 -04:00
Alan Evans
5bc4686eb8 Ignore some more ZKGroup dependent tests on mac. 2020-10-22 16:56:16 -04:00
Greyson Parrelli
f676d1c61c Enforce a configurable max envelope size. 2020-10-22 16:56:16 -04:00
Alex Hart
ac54b5cbdf Add polish to voice note bubbles. 2020-10-22 16:56:16 -04:00
Alan Evans
b4b1e5b605 Add feature flag driven group recommended size and hard size limits. 2020-10-22 16:56:16 -04:00
Greyson Parrelli
5eace49739 Improve PushProcessMessageJob logging. 2020-10-22 16:56:16 -04:00
Alex Hart
e93d7518f3 Add some polish to backups changes. 2020-10-22 16:56:16 -04:00
Greyson Parrelli
9c97cd8816 Improve conversation update message stylings. 2020-10-22 16:56:16 -04:00
Jim Gustafson
90f20c36c5 Update to RingRTC v2.7.3 2020-10-22 16:56:16 -04:00
Greyson Parrelli
9f8dd7992a Remove remote delete option for group updates. 2020-10-22 16:56:16 -04:00
Alex Hart
f4d3fe9176 Implement better backup failure notification strategy. 2020-10-22 16:56:16 -04:00
Alan Evans
ffc7c13717 Group GET 404 and PUT 409 handling. 2020-10-22 16:56:16 -04:00
Greyson Parrelli
daf93c473b Reduce verbosity of KeyboardAwareLinearLayout logs. 2020-10-22 16:56:16 -04:00
Greyson Parrelli
d21782696a Read the new GV1 Migration capability. 2020-10-22 15:55:18 -03:00
Greyson Parrelli
3357475fc4 Move capabilities into a single column. 2020-10-22 15:55:18 -03:00
Greyson Parrelli
ead64d92a5 Rename Recipient.isLocalNumber() to Recipient.isSelf() 2020-10-22 15:55:18 -03:00
Cody Henthorne
5eaac6cb17 Call handling state machine refactor. 2020-10-22 15:55:18 -03:00
Alex Hart
b3f0a44f10 Bump version to 4.74.3 2020-10-21 11:11:43 -03:00
Alex Hart
e4d0e2f730 Updated language translations. 2020-10-21 11:11:43 -03:00
Cody Henthorne
492a42883e Change Surveygizmo to Alchemer due to name change. 2020-10-21 11:11:43 -03:00
Alex Hart
b182f73415 Fix wakelock release exception. 2020-10-21 11:11:42 -03:00
Alan Evans
e766b9737e Do not enable admin approval on group links by default. 2020-10-20 19:39:51 -03:00
Alan Evans
2335f93579 Staging CDS enclave change. 2020-10-20 19:20:01 -03:00
Greyson Parrelli
1730260343 Bump version to 4.74.2 2020-10-19 17:34:08 -04:00
Greyson Parrelli
27506e9ed8 Updated language translations. 2020-10-19 17:33:25 -04:00
Alex Hart
dc64a186d5 Fix mediastore access for Android Q. 2020-10-19 18:16:29 -03:00
Alex Hart
3163e09b98 Fix issue with backup deletion. 2020-10-19 10:27:18 -03:00
Alex Hart
dcb9978bb1 Bump version to 4.74.1 2020-10-16 16:40:15 -03:00
Alex Hart
4a94a0a5c5 Updated language translations. 2020-10-16 16:36:47 -03:00
Alex Hart
8a2d20403e Add Proximity sensing back to voice note. 2020-10-16 16:23:04 -03:00
Alex Hart
ec706e95cc Backup style and copy tweak. 2020-10-16 16:17:34 -03:00
Alex Hart
bd3b14a27f Fix seeking voice notes that do not have waveforms. 2020-10-16 15:37:26 -03:00
Alex Hart
082d9e852c Voice Note Beta Feedback fixes. 2020-10-16 13:14:01 -03:00
Alex Hart
36da519b26 Bump version to 4.74.0 2020-10-15 17:43:35 -03:00
Alex Hart
06ffdde892 Updated language translations. 2020-10-15 17:42:28 -03:00
Greyson Parrelli
1ec57c080c Update targetSdk to 29. 2020-10-15 16:19:17 -04:00
Alan Evans
a635f27c68 Hide group link when not enabled. 2020-10-15 16:19:17 -04:00
Alex Hart
ee3d7a9a35 Implement new workflow for scoped storage backup selection. 2020-10-15 16:19:17 -04:00
Alex Hart
9a1c869efe Allow consecutive voice notes to be played as a playlist. 2020-10-15 16:19:17 -04:00
Alan Evans
837ed76f85 Show reminder banner to administrators for pending group join requests. 2020-10-15 16:19:17 -04:00
Cody Henthorne
b46589cd14 Remove mentions feature flag. 2020-10-15 16:19:17 -04:00
Alan Evans
d04e4606d2 Remove GV2 create flag. 2020-10-15 16:19:17 -04:00
Greyson Parrelli
385bd0eb8a Fix possible crash for unregistered devices. 2020-10-15 16:19:17 -04:00
Greyson Parrelli
089656e5c4 Add an application migration to do a CDS refresh. 2020-10-15 16:19:17 -04:00
Greyson Parrelli
84ec6dd458 Improve network reliability during resumable uploads. 2020-10-15 16:19:17 -04:00
Cody Henthorne
322c139c26 Fix bug of video showing on next call after cancel pre-join.
Fixes #10083
2020-10-15 16:19:17 -04:00
Alan Evans
babe1833bb Derive GV2 master key and group id from GV1. 2020-10-15 16:19:17 -04:00
Alex Hart
9effa47dd8 Allow voice notes to continue playback after leaving conversation. 2020-10-15 16:19:17 -04:00
Greyson Parrelli
7ef57cc0cf Add support for syncing pinned status with storage service. 2020-10-15 16:19:17 -04:00
Greyson Parrelli
97420aae1b Add a Github Action to test our docker build every day.
Runs at 5am UTC, which is ~midnight EST.
2020-10-15 16:19:17 -04:00
Greyson Parrelli
415e6309f9 Ensure CI runs on 5.x branches. 2020-10-15 16:19:17 -04:00
Greyson Parrelli
83e63ff854 Improve the reproducible build process.
* Moved stuff into it's own `reproducible-builds` directory.
* Improved reproducible build by using a debian snapshot and more clearly listing dependencies.
* Removed signing from assembleReelase.
* Updated README.
2020-10-15 16:19:17 -04:00
Greyson Parrelli
de7f103130 Add support for modern profile sharing. 2020-10-15 16:19:12 -04:00
Alan Evans
2cb912681d Bump version to 4.73.4 2020-10-13 15:18:00 -03:00
Alan Evans
04bdf94b78 Updated language translations. 2020-10-13 15:18:00 -03:00
Cody Henthorne
c7389ddaa7 Fix bug causing incorrect mention suggestions. 2020-10-13 15:18:00 -03:00
Greyson Parrelli
e778ab2e3a Fix issue with remote delete sent transcripts. 2020-10-13 13:50:21 -04:00
Alan Evans
533d86607f Bump version to 4.73.3 2020-10-12 15:24:34 -03:00
Alan Evans
cb2096670f Updated language translations. 2020-10-12 15:19:00 -03:00
Alan Evans
284f221a9d Handle no actual change to group. 2020-10-12 15:11:57 -03:00
Greyson Parrelli
bc639dd438 Show error message when unable to compute safety number. 2020-10-12 12:14:13 -04:00
Greyson Parrelli
1baddbb40e Fix some oddities with message request behavior.
There was a weird case where how our intent checking could behave
differently when coming from search. There's also some funny
interactions where backups, where because the 'time message requests was
enabled' is reset to System.currentTimeMillis() post-restore, we thought
there were always 'pre-message-request messages'. Only mattered when
profileSharing is also disabled, so impact isn't huge. Given a lot of
this UI is going away soon, rather than doing the complicated thing of
backing up the true timestamp, I just default it to 0 to mitigate
things.
2020-10-12 10:09:35 -04:00
Alan Evans
f784dab868 Bump version to 4.73.2 2020-10-09 17:46:21 -03:00
Alan Evans
85192aaa21 Updated language translations. 2020-10-09 17:46:21 -03:00
Alan Evans
054c705fe2 Respect the 206 paged response from the server group logs endpoint.
Prevent the deduplicate message logic firing and log it if it does.
2020-10-09 17:46:21 -03:00
Alan Evans
07b0d8cf6e Utilities for correctly handling json parsing errors on network responses. 2020-10-09 17:11:19 -03:00
Greyson Parrelli
597d16f566 Ensure one row per recipient in getRecipientSettingsForSync().
Technically there's no unique constraint in ThreadDatabase to guarantee
only one thread per recipient. We saw a crash that indicated that one
user has two threads for the same recipient. That's not true for any of
my devices. Still, best to play it safe here while we try to figure out
why this is happening.
2020-10-09 12:16:38 -04:00
Greyson Parrelli
0ca2c781c3 Only show the delivery status icon for 'sending' on remote deletes. 2020-10-08 16:29:13 -04:00
Greyson Parrelli
f642de9c41 Disable mention clicks in multi-select mode. 2020-10-08 14:09:44 -04:00
Greyson Parrelli
8965388d05 Fix rendering of remote-deleted view-once messages. 2020-10-08 14:04:00 -04:00
Alan Evans
58c4582f15 Bump version to 4.73.1 2020-10-08 12:53:17 -03:00
Alan Evans
44bc1b5cc0 Updated language translations. 2020-10-08 12:51:08 -03:00
Greyson Parrelli
714ebb3e08 Allow remote deletes of pending messages. 2020-10-08 10:58:55 -04:00
Greyson Parrelli
8f871c2e3a Don't allow quote-jumps to remote deleted messages. 2020-10-08 10:29:46 -04:00
Greyson Parrelli
5cdc5bc441 Ensure reactions are deleted for remote-deleted messages.
We were doing this for MmsDatabase, but not SmsDatabase. Includes a
migration to cleanup any existing bad state.
2020-10-08 10:21:57 -04:00
Cody Henthorne
8d060837ad Cleanup abandoned mentions during backup restore. 2020-10-08 09:46:26 -04:00
Greyson Parrelli
1d230d4cd6 Schedule another attribute refresh for GV2. 2020-10-07 20:29:40 -04:00
Greyson Parrelli
3636ae7667 Add the Pixel 4 back to the CameraX blacklist.
It's having pretty bad exposure problems.
2020-10-07 19:54:24 -04:00
Greyson Parrelli
9ffb5112c6 Bump version to 4.73.0 2020-10-07 17:22:05 -04:00
Greyson Parrelli
ca5d574cd7 Updated language translations. 2020-10-07 17:22:05 -04:00
Greyson Parrelli
c80283dbcc Inline remote delete feature flag. 2020-10-07 17:22:05 -04:00
Greyson Parrelli
3fcaddf2d3 Update delete for everyone education text. 2020-10-07 17:22:05 -04:00
Greyson Parrelli
6ecff5bce9 Ensure the storage manifest has all inserts and deletes.
A user hit a fishy case where not all inserts were present in the full
keyset. It's unclear how that would happen, so I'm being even more
explicit here.
2020-10-07 17:22:05 -04:00
Greyson Parrelli
a103c7dcb6 Apply storage service values for phone number privacy. 2020-10-07 17:22:05 -04:00
Greyson Parrelli
63746bbb47 Add support for syncing forced unread status. 2020-10-07 17:22:05 -04:00
Alan Evans
ed0be6fc9a Add dialog transitions to group manager. 2020-10-07 17:22:05 -04:00
Alan Evans
26404ff5d7 More descriptive copy for group link permission errors. 2020-10-07 17:22:05 -04:00
Alan Evans
adf1674877 Support sgnl://signal.group links. 2020-10-07 17:22:05 -04:00
Greyson Parrelli
ab2235fc88 Prefer remote value for profile sharing for groups during storage sync. 2020-10-07 17:22:05 -04:00
Cody Henthorne
441a6d3fe7 Fix start call resizing improperly with wrapping text. 2020-10-07 17:22:05 -04:00
Greyson Parrelli
e00397620a Simplify storing storage-service-specific recipient values.
This gives us the ability to separate things we need for the Recipient
class from things we only need for storage syncing.

Not only does this simplify the storage service model building code
(i.e. we no longer need to pass around a set of archived recipients),
but it also eliminates a join on the Identity table for building regular
recipients, which should help perf.
2020-10-07 17:22:05 -04:00
Alan Evans
38fa58c0a3 Write previous group state to the database for advanced change messages. 2020-10-06 11:21:56 -03:00
Alan Evans
b40fd7b243 Fix Audio slides reporting images.
Fixes #10063
2020-10-06 11:09:50 -03:00
Alan Evans
ae34877496 Use Emoji respecting textview in group member lists. 2020-10-06 10:36:48 -03:00
Greyson Parrelli
599cf1e5cb Ensure we refresh recipients after changing storage keys. 2020-10-06 10:32:03 -03:00
Greyson Parrelli
474963dcf1 Add the ability to migrate to new KBS enclaves. 2020-10-06 10:32:03 -03:00
Alan Evans
e22384b6b4 New copy for GV2 direct add message request. 2020-10-05 14:54:18 -03:00
Cody Henthorne
fb00652396 Fix incorrect UI for inactive groups. 2020-10-05 12:59:00 -04:00
Alan Evans
a5dbb5d91f Block unknown group messages from blocked senders. 2020-10-05 12:30:29 -03:00
Alan Evans
e75a03b6f8 Bump version to 4.72.6 2020-10-02 12:25:40 -03:00
Alan Evans
eb7fe7f3e0 Updated language translations. 2020-10-02 12:25:40 -03:00
Cody Henthorne
3179808f17 Cleanup mentions with bad thread ids or ranges, or duplicates. 2020-10-02 12:25:40 -03:00
Alan Evans
fde9f05bd0 Use GV2 change descriptions for invite events. 2020-10-02 10:40:57 -03:00
Alan Evans
8de4290c5b Fix can create backups when timed backup is waiting for charging constraint. 2020-10-02 10:32:04 -03:00
Alan Evans
19c74c8872 Fix English use of quantity zero string. 2020-10-02 10:31:11 -03:00
Alan Evans
50edb5d1f4 Bump version to 4.72.5 2020-09-30 17:38:38 -03:00
Cody Henthorne
c6ccfd7e75 Fix API19 crash when inflating new WebRTC UI. 2020-09-30 17:38:15 -03:00
Alan Evans
3796ce69e4 Clear auth cache on first verification failure. 2020-09-30 17:28:42 -03:00
Cody Henthorne
9835e31b46 Attempt to cleanup invalid mentions. 2020-09-30 15:56:23 -04:00
Alan Evans
a35040c909 Bump version to 4.72.4 2020-09-30 16:05:27 -03:00
Alan Evans
a4c94638ca Updated language translations. 2020-09-30 15:59:29 -03:00
Cody Henthorne
e70a8ae6a0 Drop messages with mentions not sent to V2 Groups. 2020-09-30 14:52:18 -04:00
Alan Evans
100359e38d Allow in notification reply to multi message if you can reply to latest. 2020-09-30 15:42:07 -03:00
Cody Henthorne
cd995aca56 Fix incorrect mention association when messages are deleted. 2020-09-30 14:35:02 -04:00
Alan Evans
3a4bae88ca Add network spinner to add members. 2020-09-30 13:59:39 -03:00
Cody Henthorne
e60eae27fb Tweak font sizes and PIP boundaries in call view. 2020-09-30 11:51:48 -04:00
Alan Evans
cd6c01e230 Fix spinner not disappearing when adding members with no network. 2020-09-30 12:25:35 -03:00
Alan Evans
0af264429f During GV2 storage sync, recover from recipient present but group not present. 2020-09-30 10:11:51 -03:00
Alan Evans
a6d3862350 Ignore bad messages from blocked senders. 2020-09-30 10:08:21 -03:00
Alan Evans
3fca4850dd Fix xml inflation crash. 2020-09-29 16:40:37 -03:00
Alan Evans
ba7e41d9a6 Fix missing Submit Debug Log loading progress spinner. 2020-09-29 15:23:31 -03:00
Alan Evans
fe33ce3413 Various groups V2 dialog copy changes. 2020-09-29 12:03:32 -03:00
Alan Evans
4e25e8aaa2 Ensure clock adjustments does not stop remote config refresh. 2020-09-29 11:10:25 -03:00
Alan Evans
91be826c7d Bump version to 4.72.3 2020-09-28 16:35:44 -03:00
Alan Evans
fdfe0cddb8 Updated language translations. 2020-09-28 16:32:18 -03:00
Alan Evans
e8ef62116f Write gv2-3 capability. 2020-09-28 14:15:19 -03:00
Alan Evans
caf8bb39d8 Fix desktop sync with body-less messages. 2020-09-28 11:53:27 -03:00
Alan Evans
222ba6ee53 Hide admin options on bottom sheet for members not currently in group. 2020-09-28 10:15:29 -03:00
Alan Evans
8dcda73072 Fix media preview crash. 2020-09-28 09:45:06 -03:00
Alan Evans
810365d334 Bump version to 4.72.2 2020-09-25 15:29:36 -03:00
Alan Evans
4b31510589 Updated language translations. 2020-09-25 15:24:47 -03:00
Alan Evans
dfce9a34b8 Fix leave group crash. 2020-09-25 15:18:34 -03:00
Alex Hart
dc9370c32b Fix false group name and avatar updates. 2020-09-25 15:18:34 -03:00
Cody Henthorne
8dbc721c08 Fix stale call preview state by finishing when leaving. 2020-09-25 15:18:34 -03:00
Cody Henthorne
6448b84430 Fix various mention issues.
Fixes #9960
2020-09-25 15:18:34 -03:00
Alan Evans
93d6ce40c3 GV2 learn more copy update. 2020-09-25 15:18:34 -03:00
Alan Evans
ce5be2c1be Share group link via signal to one recipient. 2020-09-25 12:33:14 -03:00
Alan Evans
20fe837022 Enable and disable group link options with first switch. 2020-09-25 12:32:48 -03:00
Greyson Parrelli
e3ce18fa3e Fix possible threading issues with attachment cleanup.
The way things were ordered, it was possible for us to create an
attachment file, but have it 'cleaned up' before we were able to link it
to an attachment row.
2020-09-24 16:51:20 -04:00
Greyson Parrelli
864a1d5e93 Prefer remote value for profile sharing during storage sync. 2020-09-24 12:41:31 -04:00
Greyson Parrelli
9cf7eec247 Log sent timestamps when hitting message processing errors. 2020-09-24 12:26:18 -04:00
Greyson Parrelli
d9c15621f6 Log more details around conversation fetch times. 2020-09-24 12:26:02 -04:00
Greyson Parrelli
fea14218a9 Don't allow borderless images to have quotes attached.
Fixes #9924
2020-09-24 12:11:40 -04:00
Greyson Parrelli
dbbded5250 Bump version to 4.72.1 2020-09-24 10:54:07 -04:00
Greyson Parrelli
d65cfc7981 Updated language translations. 2020-09-24 10:53:44 -04:00
Greyson Parrelli
dc9124f291 Fix crash in RetrieveProfileJob. 2020-09-24 10:46:56 -04:00
Cody Henthorne
4cd433b6bc Retain call start timestamp per peer to prevent race conditions. 2020-09-24 10:43:39 -04:00
Greyson Parrelli
f9a9ee6b0c Bump version to 4.72.0 2020-09-23 16:54:38 -04:00
Greyson Parrelli
1741f7ed58 Updated language translations. 2020-09-23 16:54:38 -04:00
Alan Evans
d459c751be Show linked device update message if we don't have the capability to join a group by link. 2020-09-23 16:54:38 -04:00
Alan Evans
34ef8b52f6 Display a loading message if group update message is taking a while to load. 2020-09-23 16:54:38 -04:00
Alan Evans
5ae96905bb Do not allow replying on reactions and messages without visible content. 2020-09-23 16:54:38 -04:00
Alan Evans
b1fdbc0151 Refresh own GV2 capability on group create. 2020-09-23 16:54:38 -04:00
Alan Evans
a5ad27b5f2 Hide "My contacts" phone number privacy option. 2020-09-23 16:54:38 -04:00
Greyson Parrelli
efcd5052a2 Remove Pixel 4 from the CameraX blacklist on Android 11. 2020-09-23 16:54:38 -04:00
Greyson Parrelli
f2b10c0ba8 Always include ourselves in optimistic profile fetches. 2020-09-23 16:54:38 -04:00
Greyson Parrelli
f182be2d79 Inline CDS feature flag. 2020-09-23 16:54:38 -04:00
Greyson Parrelli
41b10630bb Default to WEBP for sticker contentTypes. 2020-09-23 16:54:38 -04:00
Alan Evans
45915bed90 Inline GV2 feature flag. 2020-09-23 16:54:38 -04:00
Greyson Parrelli
a2c2ab428a Fallback to profile fetches for unlisted contacts. 2020-09-23 16:54:38 -04:00
Alan Evans
a05f74d302 Do not set color before profile name is known. 2020-09-23 16:54:38 -04:00
Alan Evans
74e94f3a97 Separate capability reads from writes and introduce gv2-2 write flag. 2020-09-23 16:54:38 -04:00
Christian Ascheberg
15ee8c6cac Fix timestamp of missed call record.
Fixes #7647
2020-09-23 16:54:38 -04:00
Alex Hart
18957b1f41 Remove members menu item for group message requests. 2020-09-23 16:54:38 -04:00
Cody Henthorne
29930cac41 Use mention-updated body for unread reaction notification text. 2020-09-23 16:54:38 -04:00
Cody Henthorne
e3338dc3ff Add MMS info to conversation settings. 2020-09-23 16:54:38 -04:00
Greyson Parrelli
97b7b4a501 Fix crash when receiving SMS before finishing registration.
If someone has set Signal as the default SMS but has cleared data or
otherwise reset the app's storage state, it can get into a weird
situation. Notably, it'll crash because SmsReceiveJob.onRun() expects
Recipient.self() to be available.

However, it also makes it impossible to get the registration SMS,
because the app won't post a notification for the code.

This change will post notifications and SmsRetriever broadcasts for
relevant SMS messages.
2020-09-23 16:54:38 -04:00
Greyson Parrelli
b471a72856 Don't show the link preview megaphone to new users. 2020-09-23 16:54:38 -04:00
Greyson Parrelli
fed7d911a3 Revert "Listen to the uiMode configuration changes."
This reverts commit dda98a474d.

This commit ended up causing spontaneous theme changes that have been
hard to track down. It's likely it just didn't fit into our theme system
well. We need to take a closer look, but in the meantime, a revert is in
order.
2020-09-23 16:54:38 -04:00
Cody Henthorne
ca442970a3 Add Research Megaphone. 2020-09-23 16:54:38 -04:00
Angus Turnbull
9dbb77c10a Remove some calls to GMS APIs for utility functions.
Fixes #9629
2020-09-23 16:54:38 -04:00
Dan
1116502bc0 Add vCard support for received MMS. 2020-09-23 16:54:38 -04:00
Cody Henthorne
edaf17bdd4 Fix invisible media controls and notch jank.
Fixes #9993.
2020-09-23 16:54:38 -04:00
Alan Evans
c61d731358 Allow side-by-side installation of staging build. 2020-09-23 16:54:38 -04:00
Cody Henthorne
a8415a3484 Add pre-join vanity view for 1:1 video calls. 2020-09-23 16:54:38 -04:00
Alan Evans
cd2467085e Correct storage query deleted filter argument. 2020-09-23 16:54:38 -04:00
Alan Evans
64efb3d2a4 Do not set or read reaction target phone number. 2020-09-23 16:54:38 -04:00
Alex Hart
e05f137bd8 Add animations to call screen. 2020-09-23 16:54:38 -04:00
Greyson Parrelli
0c73ddc08b Ensure we HTML-decode the <title> tag.
Fixes #10020
2020-09-23 16:54:38 -04:00
Greyson Parrelli
19cc43c442 Add a charging constraint to the backup job. 2020-09-23 16:54:38 -04:00
Greyson Parrelli
7108fc81a9 Prevent redundant JobScheduler jobs.
Some devices actually enforce a scheduling rate, and will crash if you
submit more than, say, 250 jobs in 1 minute. This can happen when
catching up with messages and scheduling a lot of
PushDecryptMessageJobs.

While it'd be tricky to limit jobs with constraints, this just does the
simple thing of not enqueueing unnecessary jobs for constraint-less
jobs.
2020-09-23 16:54:38 -04:00
Alex Hart
5943b9d7d6 Fix sending receipts.
Fixes #10016
2020-09-23 16:54:38 -04:00
Alex Hart
0271e4c918 Add lifecycle check in SnackbarAsyncTask. 2020-09-23 16:54:38 -04:00
Greyson Parrelli
9dc33eff3a Remove thumbnails from the AttachmentDatabase.
Glide can do everything for us now, including video thumbnails.
2020-09-23 16:54:38 -04:00
Jim Gustafson
5aef1c8a68 Update to RingRTC v2.7.0 2020-09-23 16:54:38 -04:00
Alan Evans
c608a05270 Prevent a resolve call in main. 2020-09-23 16:54:38 -04:00
Cody Henthorne
e2cfd247c3 Fix mention parsing for quotes. 2020-09-23 16:54:38 -04:00
Greyson Parrelli
97eb9154b2 Prevent NPE when setting sticker emoji. 2020-09-23 16:54:38 -04:00
Jim Gustafson
d7ff635445 RingRTC: Update to v2.6.0
Co-authored-by: Peter Thatcher <peter@signal.org>
2020-09-23 16:54:38 -04:00
Alan Evans
aff57fb54e Create the temporary backup file hidden in the final location.
Fixes #10003
2020-09-23 16:54:38 -04:00
Greyson Parrelli
e89285a219 Reduce log noise. 2020-09-23 16:54:38 -04:00
Greyson Parrelli
706f43caa8 Remove AttachmentsV3 feature flag. 2020-09-23 16:54:38 -04:00
Cody Henthorne
dc4faf57cb Add foundational UX and state support for Group Calling. 2020-09-23 16:54:38 -04:00
Alex Hart
7baf8052a2 Fix savedInstanceState crash. 2020-09-23 16:54:37 -04:00
Alan Evans
d3c59585fd Bump version to 4.71.5 2020-09-14 11:16:09 -03:00
Alan Evans
859bb8dc79 Updated language translations. 2020-09-14 11:12:30 -03:00
Alan Evans
58cd2e07ba Add some required face blurring models back.
Fixes #10009
2020-09-14 10:49:33 -03:00
Greyson Parrelli
a5a6fb590a Bump version to 4.71.4 2020-09-10 18:03:51 -04:00
Greyson Parrelli
3619993e68 Updated language translations. 2020-09-10 18:03:28 -04:00
Greyson Parrelli
88e12c78fa Disable mentions megaphone. 2020-09-10 17:54:57 -04:00
Alan Evans
5c285b4ac6 Cycle groups v2 feature flag. 2020-09-10 18:47:36 -03:00
Cody Henthorne
c6b729c470 Bump version to 4.71.3 2020-09-10 15:38:55 -04:00
Cody Henthorne
890014759e Updated language translations. 2020-09-10 15:38:12 -04:00
Cody Henthorne
68c1c43381 Update radio styling in storage settings. 2020-09-10 14:40:29 -04:00
Greyson Parrelli
d0dfcaaad5 Fix issue with storage key intersections.
- When doing the intersection, ignore keys that have type mismatches (same storageId, different types)
- If we detect that scenario, schedule a force push to happen afterwards
- Also schedule a force push afterwards if we detect that there's keys in the manifest that don't have any storage item on the service
2020-09-10 14:01:41 -04:00
Alan Evans
3cffaddc0a Validate incoming Group lengths and remote delete entries if wrong.
Ignore incoming messages with bad V1 group lengths.
2020-09-10 14:39:29 -03:00
Alex Hart
bf4cac0c82 Fix unarchive menu action. 2020-09-10 13:49:16 -03:00
Alex Hart
f680749a00 Use proper lifecycle for SimpleTask which touches fragment view. 2020-09-10 13:40:09 -03:00
Cody Henthorne
13a67980d9 Fix wrong timestamp being used when trimming by length. 2020-09-10 12:09:28 -04:00
Alan Evans
f110d595d2 Fix selection limit for add members GV1.
Fixes #10005
2020-09-10 09:59:57 -03:00
2403 changed files with 104360 additions and 43277 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
custom: https://signal.org/donate/

View File

@@ -1,3 +1,12 @@
---
name: 🛠️ Bug report
about: Let us know that something isn't working as intended
title: ''
labels: ''
assignees: ''
---
<!-- This is a bug report template. By following the instructions below and filling out the sections with your information, you will help the developers get all the necessary data to fix your issue.
You can also preview your report before submitting it. You may remove sections that aren't relevant to your particular case.

20
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
blank_issues_enabled: false
contact_links:
- name: 📃Support Center
url: https://support.signal.org/
about: Find answers to many common questions.
- name: ✨ Feature request
url: https://community.signalusers.org/c/feature-requests/
about: Missing something in Signal? Let us know.
- name: 💬 Community support
url: https://community.signalusers.org/c/support/
about: Feel free to ask anything.
- name: 📖 Developer documentation
url: https://signal.org/docs/
about: Official Signal developer documentation.
- name: 📚 Translation feedback.
url: https://community.signalusers.org/c/translation-feedback/
about: Share feedback on translations.
- name: ❓ Other issue?
url: https://community.signalusers.org/
about: Search on the community forums.

View File

@@ -6,6 +6,7 @@ on:
branches:
- 'master'
- '4.**'
- '5.**'
jobs:
build:
@@ -13,16 +14,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- 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

18
.github/workflows/docker.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: Reproducible Build Check
on:
schedule:
- cron: '0 5 * * *'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build image
run: cd reproducible-builds && docker build -t signal-android . && cd ..
- name: Test build
run: docker run --rm -v $(pwd):/project -w /project signal-android ./gradlew clean assembleRelease

4
.gitignore vendored
View File

@@ -1,6 +1,8 @@
.classpath
captures/
project.properties
keystore.debug.properties
keystore.staging.properties
.project
.settings
bin/
@@ -23,5 +25,5 @@ ffpr
test/androidTestEspresso/res/values/arrays.xml
obj/
jni/libspeex/.deps/
*.sh
pkcs11.password
dev.keystore

View File

@@ -1,25 +0,0 @@
FROM ubuntu:17.10
RUN dpkg --add-architecture i386 && \
apt-get update -y && \
apt-get install -y software-properties-common && \
apt-get update -y && \
apt-get install -y libc6:i386=2.26-0ubuntu2.1 libncurses5:i386=6.0+20160625-1ubuntu1 libstdc++6:i386=7.2.0-8ubuntu3.2 lib32z1=1:1.2.11.dfsg-0ubuntu2 wget openjdk-8-jdk=8u171-b11-0ubuntu0.17.10.1 git unzip opensc pcscd && \
rm -rf /var/lib/apt/lists/* && \
apt-get autoremove -y && \
apt-get clean
ENV ANDROID_SDK_FILENAME android-sdk_r24.4.1-linux.tgz
ENV ANDROID_SDK_URL https://dl.google.com/android/${ANDROID_SDK_FILENAME}
ENV ANDROID_API_LEVELS android-28
ENV ANDROID_BUILD_TOOLS_VERSION 28.0.3
ENV ANDROID_HOME /usr/local/android-sdk-linux
ENV PATH ${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools
RUN cd /usr/local/ && \
wget -q ${ANDROID_SDK_URL} && \
tar -xzf ${ANDROID_SDK_FILENAME} && \
rm ${ANDROID_SDK_FILENAME}
RUN echo y | android update sdk --no-ui -a --filter ${ANDROID_API_LEVELS}
RUN echo y | android update sdk --no-ui -a --filter extra-android-m2repository,extra-android-support,extra-google-google_play_services,extra-google-m2repository
RUN echo y | android update sdk --no-ui -a --filter tools,platform-tools,build-tools-${ANDROID_BUILD_TOOLS_VERSION}
RUN rm -rf ${ANDROID_HOME}/tools && unzip ${ANDROID_HOME}/temp/*.zip -d ${ANDROID_HOME}

View File

@@ -2,25 +2,6 @@ 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'
@@ -80,32 +61,47 @@ protobuf {
}
}
def canonicalVersionCode = 705
def canonicalVersionName = "4.71.2"
def canonicalVersionCode = 775
def canonicalVersionName = "5.3.2"
def postFixSize = 10
def postFixSize = 100
def abiPostFix = ['universal' : 0,
'armeabi-v7a' : 1,
'arm64-v8a' : 2,
'x86' : 3,
'x86_64' : 4]
def keystores = [ 'debug' : loadKeystoreProperties('keystore.debug.properties') ]
android {
flavorDimensions "none"
compileSdkVersion 28
buildToolsVersion '28.0.3'
buildToolsVersion BUILD_TOOL_VERSION
compileSdkVersion COMPILE_SDK
flavorDimensions 'distribution', 'environment'
useLibrary 'org.apache.http.legacy'
dexOptions {
javaMaxHeapSize "4g"
}
signingConfigs {
if (keystores.debug != null) {
debug {
storeFile file("${project.rootDir}/${keystores.debug.storeFile}")
storePassword keystores.debug.storePassword
keyAlias keystores.debug.keyAlias
keyPassword keystores.debug.keyPassword
}
}
}
defaultConfig {
versionCode canonicalVersionCode * postFixSize
versionName canonicalVersionName
minSdkVersion 19
targetSdkVersion 28
minSdkVersion MINIMUM_SDK
targetSdkVersion TARGET_SDK
multiDexEnabled true
vectorDrawables.useSupportLibrary = true
@@ -119,13 +115,15 @@ android {
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", "SIGNAL_SFU_URL", "\"https://sfu.voip.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", "\"c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15\""
buildConfigField "String", "KBS_ENCLAVE_NAME", "\"fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe\""
buildConfigField "String", "KBS_SERVICE_ID", "\"fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe\""
buildConfigField "String", "KBS_MRENCLAVE", "\"a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87\""
buildConfigField "KbsEnclave", "KBS_ENCLAVE", "new KbsEnclave(\"fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe\"," +
"\"fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe\", " +
"\"a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87\")";
buildConfigField "KbsEnclave[]", "KBS_FALLBACKS", "new KbsEnclave[0]"
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('", "') + '"}'
@@ -150,8 +148,8 @@ android {
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JAVA_VERSION
targetCompatibility JAVA_VERSION
}
packagingOptions {
@@ -165,11 +163,15 @@ android {
}
aaptOptions {
ignoreAssetsPattern '!contours.tfl:!LMprec_600.emd:!fssd_25_8bit_gray_v1.tflite:!fssd_25_8bit_v1.tflite:!blazeface.tfl'
ignoreAssetsPattern '!contours.tfl:!LMprec_600.emd:!blazeface.tfl'
}
buildTypes {
debug {
if (keystores['debug'] != null) {
signingConfig signingConfigs.debug
}
isDefault true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard/proguard-firebase-messaging.pro',
@@ -193,8 +195,57 @@ android {
testProguardFiles 'proguard/proguard-automation.pro',
'proguard/proguard.cfg'
}
staging {
flipper {
initWith debug
isDefault false
minifyEnabled false
matchingFallbacks = ['debug']
}
release {
minifyEnabled true
proguardFiles = buildTypes.debug.proguardFiles
}
perf {
initWith debug
isDefault false
debuggable false
matchingFallbacks = ['debug']
}
}
productFlavors {
play {
dimension 'distribution'
isDefault true
ext.websiteUpdateUrl = "null"
buildConfigField "boolean", "PLAY_STORE_DISABLED", "false"
buildConfigField "String", "NOPLAY_UPDATE_URL", "$ext.websiteUpdateUrl"
}
website {
dimension 'distribution'
ext.websiteUpdateUrl = "https://updates.signal.org/android"
buildConfigField "boolean", "PLAY_STORE_DISABLED", "true"
buildConfigField "String", "NOPLAY_UPDATE_URL", "\"$ext.websiteUpdateUrl\""
}
internal {
dimension 'distribution'
ext.websiteUpdateUrl = "null"
buildConfigField "boolean", "PLAY_STORE_DISABLED", "false"
buildConfigField "String", "NOPLAY_UPDATE_URL", "$ext.websiteUpdateUrl"
}
prod {
dimension 'environment'
isDefault true
}
staging {
dimension 'environment'
applicationIdSuffix ".staging"
buildConfigField "String", "SIGNAL_URL", "\"https://textsecure-service-staging.whispersystems.org\""
buildConfigField "String", "STORAGE_URL", "\"https://storage-staging.signal.org\""
@@ -202,36 +253,14 @@ android {
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", "\"bd123560b01c8fa92935bc5ae15cd2064e5c45215f23f0bd40364d521329d2ad\""
buildConfigField "String", "KBS_ENCLAVE_NAME", "\"823a3b2c037ff0cbe305cc48928cfcc97c9ed4a8ca6d49af6f7d6981fb60a4e9\""
buildConfigField "String", "KBS_SERVICE_ID", "\"038c40bbbacdc873caa81ac793bb75afde6dfe436a99ab1f15e3f0cbb7434ced\""
buildConfigField "String", "CDS_MRENCLAVE", "\"c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15\""
buildConfigField "KbsEnclave", "KBS_ENCLAVE", "new KbsEnclave(\"823a3b2c037ff0cbe305cc48928cfcc97c9ed4a8ca6d49af6f7d6981fb60a4e9\", " +
"\"038c40bbbacdc873caa81ac793bb75afde6dfe436a99ab1f15e3f0cbb7434ced\", " +
"\"a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87\")"
buildConfigField "KbsEnclave[]", "KBS_FALLBACKS", "new KbsEnclave[0]"
buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx\""
buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdls=\""
}
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 ->
@@ -262,31 +291,30 @@ android {
dependencies {
lintChecks project(':lintchecks')
implementation('androidx.appcompat:appcompat:1.1.0-beta01') {
implementation ('androidx.appcompat:appcompat:1.2.0') {
force = true
}
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'com.google.android.material:material:1.2.1'
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.constraintlayout:constraintlayout:2.0.4'
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.camera:camera-core:1.0.0-beta11"
implementation "androidx.camera:camera-camera2:1.0.0-beta11"
implementation "androidx.camera:camera-lifecycle:1.0.0-beta11"
implementation "androidx.camera:camera-view:1.0.0-alpha18"
implementation "androidx.concurrent:concurrent-futures:1.0.0"
implementation "androidx.autofill:autofill:1.0.0"
implementation "androidx.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'
@@ -301,16 +329,22 @@ dependencies {
implementation 'com.google.android.exoplayer:exoplayer-core:2.9.1'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.1'
implementation 'com.google.android.exoplayer:extension-mediasession: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 project(':paging')
implementation project(':core-util')
implementation project(':video')
implementation 'org.signal:zkgroup-android:0.7.0'
implementation 'org.whispersystems:signal-client-android:0.2.2'
implementation 'com.google.protobuf:protobuf-javalite:3.10.0'
implementation 'org.signal:argon2:13.1@aar'
implementation 'org.signal:ringrtc-android:2.5.1'
implementation 'org.signal:ringrtc-android:2.8.10'
implementation "me.leolin:ShortcutBadger:1.1.16"
implementation 'se.emilsjolander:stickylistheaders:2.7.0'
@@ -371,20 +405,20 @@ dependencies {
testImplementation 'org.powermock:powermock-classloading-xstream:1.7.4'
testImplementation 'androidx.test:core:1.2.0'
testImplementation ('org.robolectric:robolectric:4.2') {
testImplementation ('org.robolectric:robolectric:4.4') {
exclude group: 'com.google.protobuf', module: 'protobuf-java'
}
testImplementation 'org.robolectric:shadows-multidex:4.2'
testImplementation 'org.robolectric:shadows-multidex:4.4'
testImplementation 'org.hamcrest:hamcrest:2.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'
configuration = '(play|website)(Prod|Staging)(Debug|Release)RuntimeClasspath'
}
def assembleWebsiteDescriptor = { variant, file ->
if (file.exists()) {
MessageDigest md = MessageDigest.getInstance("SHA-256");
@@ -428,28 +462,24 @@ def signProductionRelease = { variant ->
task signProductionPlayRelease {
doLast {
signProductionRelease(android.applicationVariants.find { (it.name == 'playRelease') })
signProductionRelease(android.applicationVariants.find { (it.name == 'playProdRelease') })
}
}
task signProductionInternalRelease {
doLast {
signProductionRelease(android.applicationVariants.find { (it.name == 'internalProdRelease') })
}
}
task signProductionWebsiteRelease {
doLast {
def variant = android.applicationVariants.find { (it.name == 'websiteRelease') }
def variant = android.applicationVariants.find { (it.name == 'websiteProdRelease') }
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 {
@@ -471,3 +501,14 @@ tasks.withType(Test) {
showStackTraces true
}
}
def loadKeystoreProperties(filename) {
def keystorePropertiesFile = file("${project.rootDir}/${filename}")
if (keystorePropertiesFile.exists()) {
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
return keystoreProperties;
} else {
return null;
}
}

View File

@@ -5,5 +5,12 @@
<application
android:name=".FlipperApplicationContext"
tools:replace="android:name"/>
tools:replace="android:name">
<activity
android:name="com.facebook.flipper.android.diagnostics.FlipperDiagnosticActivity"
android:exported="true" />
</application>
</manifest>

View File

@@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.database;
import android.app.Application;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
@@ -14,8 +15,8 @@ import net.sqlcipher.DatabaseUtils;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteStatement;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.logging.Log;
import java.lang.reflect.Field;
import java.util.ArrayList;
@@ -24,6 +25,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* A lot of this code is taken from {@link com.facebook.flipper.plugins.databases.impl.SqliteDatabaseDriver}
@@ -42,8 +44,16 @@ public class FlipperSqlCipherAdapter extends DatabaseDriver<FlipperSqlCipherAdap
try {
Field databaseHelperField = DatabaseFactory.class.getDeclaredField("databaseHelper");
databaseHelperField.setAccessible(true);
SQLCipherOpenHelper sqlCipherOpenHelper = (SQLCipherOpenHelper) databaseHelperField.get(DatabaseFactory.getInstance(getContext()));
return Collections.singletonList(new Descriptor(sqlCipherOpenHelper));
SignalDatabase mainOpenHelper = Objects.requireNonNull((SQLCipherOpenHelper) databaseHelperField.get(DatabaseFactory.getInstance(getContext())));
SignalDatabase keyValueOpenHelper = KeyValueDatabase.getInstance((Application) getContext());
SignalDatabase megaphoneOpenHelper = MegaphoneDatabase.getInstance((Application) getContext());
SignalDatabase jobManagerOpenHelper = JobDatabase.getInstance((Application) getContext());
return Arrays.asList(new Descriptor(mainOpenHelper),
new Descriptor(keyValueOpenHelper),
new Descriptor(megaphoneOpenHelper),
new Descriptor(jobManagerOpenHelper));
} catch (Exception e) {
Log.i(TAG, "Unable to use reflection to access raw database.", e);
}
@@ -235,9 +245,9 @@ public class FlipperSqlCipherAdapter extends DatabaseDriver<FlipperSqlCipherAdap
}
static class Descriptor implements DatabaseDescriptor {
private final SQLCipherOpenHelper sqlCipherOpenHelper;
private final SignalDatabase sqlCipherOpenHelper;
Descriptor(@NonNull SQLCipherOpenHelper sqlCipherOpenHelper) {
Descriptor(@NonNull SignalDatabase sqlCipherOpenHelper) {
this.sqlCipherOpenHelper = sqlCipherOpenHelper;
}
@@ -247,11 +257,11 @@ public class FlipperSqlCipherAdapter extends DatabaseDriver<FlipperSqlCipherAdap
}
public @NonNull SQLiteDatabase getReadable() {
return sqlCipherOpenHelper.getReadableDatabase();
return sqlCipherOpenHelper.getSqlCipherDatabase();
}
public @NonNull SQLiteDatabase getWritable() {
return sqlCipherOpenHelper.getWritableDatabase();
return sqlCipherOpenHelper.getSqlCipherDatabase();
}
}
}

View File

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

View File

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

View File

@@ -3,9 +3,9 @@
xmlns:tools="http://schemas.android.com/tools"
package="org.thoughtcrime.securesms">
<uses-sdk tools:overrideLibrary="androidx.camera.core,androidx.camera.camera2,androidx.camera.lifecycle" />
<uses-sdk tools:overrideLibrary="androidx.camera.core,androidx.camera.camera2,androidx.camera.lifecycle,androidx.camera.view" />
<permission android:name="org.thoughtcrime.securesms.ACCESS_SECRETS"
<permission android:name="${applicationId}.ACCESS_SECRETS"
android:label="Access to TextSecure Secrets"
android:protectionLevel="signature" />
@@ -35,8 +35,10 @@
<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.READ_PHONE_NUMBERS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.CAMERA" />
@@ -113,8 +115,8 @@
<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"
<activity android:name=".WebRtcCallActivity"
android:theme="@style/TextSecure.DarkTheme.WebRTCCall"
android:excludeFromRecents="true"
android:screenOrientation="portrait"
android:supportsPictureInPicture="true"
@@ -127,25 +129,25 @@
android:theme="@style/TextSecure.DarkNoActionBar"
android:screenOrientation="portrait"
android:noHistory="true"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<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|uiMode">
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.thoughtcrime.securesms.MainActivity" />
android:value=".MainActivity" />
</activity>
<activity android:name=".PromptMmsActivity"
android:label="Configure MMS Settings"
android:windowSoftInputMode="stateUnchanged"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".DeviceProvisioningActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode">
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
@@ -155,16 +157,19 @@
</activity>
<activity android:name=".preferences.MmsPreferencesActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".sharing.interstitial.ShareInterstitialActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:windowSoftInputMode="adjustResize" />
<activity android:name=".sharing.ShareActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:noHistory="true"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode">
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT"/>
@@ -195,7 +200,7 @@
android:launchMode="singleTask"
android:noHistory="true"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode">
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
@@ -223,6 +228,25 @@
<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=".deeplinks.DeepLinkEntryActivity"
android:noHistory="true"
android:theme="@style/Signal.Transparent">
<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="signal.group" />
</intent-filter>
<intent-filter android:autoVerify="true"
tools:targetApi="23">
<action android:name="android.intent.action.VIEW" />
@@ -231,24 +255,23 @@
<data android:scheme="https"
android:host="signal.group"/>
</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>
<activity android:name=".conversation.ConversationActivity"
android:windowSoftInputMode="stateUnchanged"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"
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=".conversation.BubbleConversationActivity"
android:theme="@style/Signal.DayNight"
android:allowEmbedded="true"
android:resizeableActivity="true" />
<activity android:name=".longmessage.LongMessageActivity" />
<activity android:name=".conversation.ConversationPopupActivity"
@@ -257,17 +280,12 @@
android:taskAffinity=""
android:excludeFromRecents="true"
android:theme="@style/TextSecure.LightTheme.Popup"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode" />
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
<activity android:name=".messagedetails.MessageDetailsActivity"
android:label="@string/AndroidManifest__message_details"
android:windowSoftInputMode="stateHidden"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
<activity android:name=".groups.ui.pendingmemberinvites.PendingMemberInvitesActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"
android:theme="@style/Theme.Signal.DayNight.NoActionBar" />
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".groups.ui.invitesandrequests.ManagePendingAndRequestingMembersActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"
@@ -275,113 +293,122 @@
<activity android:name=".groups.ui.managegroup.ManageGroupActivity"
android:windowSoftInputMode="stateAlwaysHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".recipients.ui.managerecipient.ManageRecipientActivity"
android:windowSoftInputMode="stateAlwaysHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
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|uiMode"/>
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|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".PassphraseCreateActivity"
android:label="@string/AndroidManifest__create_passphrase"
android:windowSoftInputMode="stateUnchanged"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
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|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".NewConversationActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:windowSoftInputMode="stateAlwaysVisible"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
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|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".giph.ui.GiphyActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
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|uiMode"/>
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|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".VerifyIdentityActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ApplicationPreferencesActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode">
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=".wallpaper.ChatWallpaperActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:windowSoftInputMode="stateAlwaysHidden">
</activity>
<activity android:name=".wallpaper.ChatWallpaperPreviewActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:windowSoftInputMode="stateAlwaysHidden">
</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|uiMode"/>
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|uiMode"/>
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|uiMode"/>
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|uiMode"/>
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|uiMode"/>
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|uiMode"/>
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|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".mediaoverview.MediaOverviewActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".DummyActivity"
android:theme="@android:style/Theme.NoDisplay"
@@ -396,7 +423,7 @@
<activity android:name=".PlayServicesProblemActivity"
android:theme="@style/TextSecure.DialogActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".SmsSendtoActivity">
<intent-filter>
@@ -420,7 +447,7 @@
android:excludeFromRecents="true"
android:theme="@style/NoAnimation.Theme.BlackScreen"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode">
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@@ -432,41 +459,39 @@
<activity android:name=".mediasend.AvatarSelectionActivity"
android:theme="@style/TextSecure.FullScreenMedia"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".BlockedContactsActivity"
<activity android:name=".blocked.BlockedUsersActivity"
android:theme="@style/TextSecure.LightTheme"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
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|uiMode"/>
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=".profiles.manage.ManageProfileActivity"
android:theme="@style/TextSecure.LightTheme"
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|uiMode"/>
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|uiMode"/>
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|uiMode"
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=".ClearAvatarPromptActivity"
android:theme="@style/Theme.AppCompat.Dialog.Alert"
android:icon="@drawable/clear_profile_avatar"
android:label="@string/AndroidManifest_remove_photo"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".contacts.TurnOffContactJoinedNotificationsActivity"
android:theme="@style/Theme.AppCompat.Dialog.Alert" />
@@ -474,39 +499,38 @@
<activity android:name=".messagerequests.MessageRequestMegaphoneActivity"
android:theme="@style/TextSecure.LightRegistrationTheme"
android:windowSoftInputMode="adjustResize"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
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|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".contactshare.ContactNameEditActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".contactshare.SharedContactDetailsActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ShortcutLauncherActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:exported="true"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity
android:name=".maps.PlacePickerActivity"
android:label="@string/PlacePickerActivity_title"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".MainActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
<activity android:name=".pin.PinRestoreActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
<activity android:name=".groups.ui.creategroup.CreateGroupActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar" />
@@ -521,18 +545,39 @@
android:theme="@style/Theme.Signal.DayNight.NoActionBar" />
<activity android:name=".groups.ui.chooseadmin.ChooseNewAdminActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"/>
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
<activity android:name=".megaphone.ClientDeprecatedActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"
android:launchMode="singleTask" />
<service android:enabled="true" android:name="org.thoughtcrime.securesms.service.WebRtcCallService"/>
<activity android:name=".wallpaper.crop.WallpaperImageSelectionActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:theme="@style/TextSecure.FullScreenMedia" />
<activity android:name=".wallpaper.crop.WallpaperCropActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:screenOrientation="portrait"
android:theme="@style/Theme.Signal.WallpaperCropper" />
<service android:enabled="true" android:name=".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=".components.voice.VoiceNotePlaybackService">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
<receiver android:name="androidx.media.session.MediaButtonReceiver" >
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
<service android:name=".service.QuickResponseService"
android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
android:exported="true" >
@@ -650,15 +695,20 @@
<provider android:name=".providers.PartProvider"
android:grantUriPermissions="true"
android:exported="false"
android:authorities="org.thoughtcrime.provider.securesms" />
android:authorities="${applicationId}.part" />
<provider android:name=".providers.BlobContentProvider"
android:authorities="${applicationId}.blob"
android:exported="false"
android:grantUriPermissions="true" />
<provider android:name=".providers.MmsBodyProvider"
android:grantUriPermissions="true"
android:exported="false"
android:authorities="org.thoughtcrime.provider.securesms.mms" />
android:authorities="${applicationId}.mms" />
<provider android:name="androidx.core.content.FileProvider"
android:authorities="org.thoughtcrime.securesms.fileprovider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
@@ -667,23 +717,23 @@
</provider>
<provider android:name=".database.DatabaseContentProviders$Conversation"
android:authorities="org.thoughtcrime.securesms.database.conversation"
android:authorities="${applicationId}.database.conversation"
android:exported="false" />
<provider android:name=".database.DatabaseContentProviders$ConversationList"
android:authorities="org.thoughtcrime.securesms.database.conversationlist"
android:authorities="${applicationId}.database.conversationlist"
android:exported="false" />
<provider android:name=".database.DatabaseContentProviders$Attachment"
android:authorities="org.thoughtcrime.securesms.database.attachment"
android:authorities="${applicationId}.database.attachment"
android:exported="false" />
<provider android:name=".database.DatabaseContentProviders$Sticker"
android:authorities="org.thoughtcrime.securesms.database.sticker"
android:authorities="${applicationId}.database.sticker"
android:exported="false" />
<provider android:name=".database.DatabaseContentProviders$StickerPack"
android:authorities="org.thoughtcrime.securesms.database.stickerpack"
android:authorities="${applicationId}.database.stickerpack"
android:exported="false" />
<receiver android:name=".service.BootReceiver">
@@ -776,12 +826,5 @@
<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.

Before

Width:  |  Height:  |  Size: 225 KiB

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 KiB

After

Width:  |  Height:  |  Size: 421 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 KiB

After

Width:  |  Height:  |  Size: 365 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 395 KiB

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 622 KiB

After

Width:  |  Height:  |  Size: 664 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 599 KiB

After

Width:  |  Height:  |  Size: 608 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 559 KiB

After

Width:  |  Height:  |  Size: 552 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 643 KiB

After

Width:  |  Height:  |  Size: 631 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 647 KiB

After

Width:  |  Height:  |  Size: 653 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 602 KiB

After

Width:  |  Height:  |  Size: 652 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 589 KiB

After

Width:  |  Height:  |  Size: 531 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 531 KiB

After

Width:  |  Height:  |  Size: 685 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 589 KiB

After

Width:  |  Height:  |  Size: 603 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 KiB

After

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.thoughtcrime.securesms.mediasend.camerax;
package androidx.camera.view;
import android.Manifest.permission;
import android.annotation.SuppressLint;
@@ -45,43 +45,44 @@ import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.DisplayOrientedMeteringPointFactory;
import androidx.camera.core.FocusMeteringAction;
import androidx.camera.core.FocusMeteringResult;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCapture.OnImageCapturedCallback;
import androidx.camera.core.ImageCapture.OnImageSavedCallback;
import androidx.camera.core.ImageProxy;
import androidx.camera.core.Logger;
import androidx.camera.core.MeteringPoint;
import androidx.camera.core.MeteringPointFactory;
import androidx.camera.core.VideoCapture;
import androidx.camera.core.VideoCapture.OnVideoSavedCallback;
import androidx.camera.core.impl.LensFacingConverter;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.core.impl.utils.futures.FutureCallback;
import androidx.camera.core.impl.utils.futures.Futures;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import com.google.common.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.logging.Log;
import java.io.FileDescriptor;
import java.io.File;
import java.util.concurrent.Executor;
/**
* A {@link View} that displays a preview of the camera with methods {@link
* #takePicture(Executor, OnImageCapturedCallback)},
* {@link #startRecording(FileDescriptor, Executor, VideoCapture.OnVideoSavedCallback)} and {@link #stopRecording()}.
* {@link #takePicture(ImageCapture.OutputFileOptions, Executor, OnImageSavedCallback)},
* {@link #startRecording(File , Executor , OnVideoSavedCallback callback)}
* and {@link #stopRecording()}.
*
* <p>Because the Camera is a limited resource and consumes a high amount of power, CameraView must
* be opened/closed. CameraView will handle opening/closing automatically through use of a {@link
* LifecycleOwner}. Use {@link #bindToLifecycle(LifecycleOwner)} to start the camera.
*/
// Begin Signal Custom Code Block
@RequiresApi(21)
@SuppressLint("RestrictedApi")
// End Signal Custom Code Block
public final class CameraXView extends FrameLayout {
static final String TAG = CameraXView.class.getSimpleName();
static final boolean DEBUG = false;
public final class SignalCameraView extends FrameLayout {
static final String TAG = SignalCameraView.class.getSimpleName();
static final int INDEFINITE_VIDEO_DURATION = -1;
static final int INDEFINITE_VIDEO_SIZE = -1;
@@ -107,7 +108,7 @@ public final class CameraXView extends FrameLayout {
// For pinch-to-zoom
private PinchToZoomGestureDetector mPinchToZoomGestureDetector;
private boolean mIsPinchToZoomEnabled = true;
CameraXModule mCameraModule;
SignalCameraXModule mCameraModule;
private final DisplayManager.DisplayListener mDisplayListener =
new DisplayListener() {
@Override
@@ -124,26 +125,25 @@ public final class CameraXView extends FrameLayout {
}
};
private PreviewView mPreviewView;
private ScaleType mScaleType = ScaleType.CENTER_CROP;
// For accessibility event
private MotionEvent mUpEvent;
public CameraXView(@NonNull Context context) {
public SignalCameraView(@NonNull Context context) {
this(context, null);
}
public CameraXView(@NonNull Context context, @Nullable AttributeSet attrs) {
public SignalCameraView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CameraXView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
public SignalCameraView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
@RequiresApi(21)
public CameraXView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
public SignalCameraView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
@@ -172,23 +172,23 @@ public final class CameraXView extends FrameLayout {
private void init(Context context, @Nullable AttributeSet attrs) {
addView(mPreviewView = new PreviewView(getContext()), 0 /* view position */);
mCameraModule = new CameraXModule(this);
mCameraModule = new SignalCameraXModule(this);
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CameraXView);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CameraView);
setScaleType(
ScaleType.fromId(
a.getInteger(R.styleable.CameraXView_scaleType,
PreviewView.ScaleType.fromId(
a.getInteger(R.styleable.CameraView_scaleType,
getScaleType().getId())));
setPinchToZoomEnabled(
a.getBoolean(
R.styleable.CameraXView_pinchToZoomEnabled, isPinchToZoomEnabled()));
R.styleable.CameraView_pinchToZoomEnabled, isPinchToZoomEnabled()));
setCaptureMode(
CaptureMode.fromId(
a.getInteger(R.styleable.CameraXView_captureMode,
a.getInteger(R.styleable.CameraView_captureMode,
getCaptureMode().getId())));
int lensFacing = a.getInt(R.styleable.CameraXView_lensFacing, LENS_FACING_BACK);
int lensFacing = a.getInt(R.styleable.CameraView_lensFacing, LENS_FACING_BACK);
switch (lensFacing) {
case LENS_FACING_NONE:
setCameraLensFacing(null);
@@ -203,7 +203,7 @@ public final class CameraXView extends FrameLayout {
// Unhandled event.
}
int flashMode = a.getInt(R.styleable.CameraXView_flash, 0);
int flashMode = a.getInt(R.styleable.CameraView_flash, 0);
switch (flashMode) {
case FLASH_MODE_AUTO:
setFlash(ImageCapture.FLASH_MODE_AUTO);
@@ -265,7 +265,7 @@ public final class CameraXView extends FrameLayout {
if (savedState instanceof Bundle) {
Bundle state = (Bundle) savedState;
super.onRestoreInstanceState(state.getParcelable(EXTRA_SUPER));
setScaleType(ScaleType.fromId(state.getInt(EXTRA_SCALE_TYPE)));
setScaleType(PreviewView.ScaleType.fromId(state.getInt(EXTRA_SCALE_TYPE)));
setZoomRatio(state.getFloat(EXTRA_ZOOM_RATIO));
setPinchToZoomEnabled(state.getBoolean(EXTRA_PINCH_TO_ZOOM_ENABLED));
setFlash(FlashModeConverter.valueOf(state.getString(EXTRA_FLASH)));
@@ -298,6 +298,21 @@ public final class CameraXView extends FrameLayout {
dpyMgr.unregisterDisplayListener(mDisplayListener);
}
/**
* Gets the {@link LiveData} of the underlying {@link PreviewView}'s
* {@link PreviewView.StreamState}.
*
* @return A {@link LiveData} containing the {@link PreviewView.StreamState}. Apps can either
* get current value by {@link LiveData#getValue()} or register a observer by
* {@link LiveData#observe}.
* @see PreviewView#getPreviewStreamState()
*/
@NonNull
public LiveData<PreviewView.StreamState> getPreviewStreamState() {
return mPreviewView.getPreviewStreamState();
}
@NonNull
PreviewView getPreviewView() {
return mPreviewView;
}
@@ -347,11 +362,11 @@ public final class CameraXView extends FrameLayout {
/**
* Returns the scale type used to scale the preview.
*
* @return The current {@link ScaleType}.
* @return The current {@link PreviewView.ScaleType}.
*/
@NonNull
public ScaleType getScaleType() {
return mScaleType;
public PreviewView.ScaleType getScaleType() {
return mPreviewView.getScaleType();
}
/**
@@ -359,13 +374,10 @@ public final class CameraXView extends FrameLayout {
*
* <p>This controls how the view finder should be scaled and positioned within the view.
*
* @param scaleType The desired {@link ScaleType}.
* @param scaleType The desired {@link PreviewView.ScaleType}.
*/
public void setScaleType(@NonNull ScaleType scaleType) {
if (scaleType != mScaleType) {
mScaleType = scaleType;
requestLayout();
}
public void setScaleType(@NonNull PreviewView.ScaleType scaleType) {
mPreviewView.setScaleType(scaleType);
}
/**
@@ -401,8 +413,10 @@ public final class CameraXView extends FrameLayout {
}
/**
* Sets the maximum video duration before {@link VideoCapture.OnVideoSavedCallback#onVideoSaved(FileDescriptor)} is
* called automatically. Use {@link #INDEFINITE_VIDEO_DURATION} to disable the timeout.
* Sets the maximum video duration before
* {@link OnVideoSavedCallback#onVideoSaved(VideoCapture.OutputFileResults)} is called
* automatically.
* Use {@link #INDEFINITE_VIDEO_DURATION} to disable the timeout.
*/
private void setMaxVideoDuration(long duration) {
mCameraModule.setMaxVideoDuration(duration);
@@ -417,7 +431,8 @@ public final class CameraXView extends FrameLayout {
}
/**
* Sets the maximum video size in bytes before {@link VideoCapture.OnVideoSavedCallback#onVideoSaved(FileDescriptor)}
* Sets the maximum video size in bytes before
* {@link OnVideoSavedCallback#onVideoSaved(VideoCapture.OutputFileResults)}
* is called automatically. Use {@link #INDEFINITE_VIDEO_SIZE} to disable the size restriction.
*/
private void setMaxVideoSize(long size) {
@@ -435,28 +450,38 @@ public final class CameraXView extends FrameLayout {
mCameraModule.takePicture(executor, callback);
}
/**
* Takes a picture and calls
* {@link OnImageSavedCallback#onImageSaved(ImageCapture.OutputFileResults)} when done.
*
* <p> The value of {@link ImageCapture.Metadata#isReversedHorizontal()} in the
* {@link ImageCapture.OutputFileOptions} will be overwritten based on camera direction. For
* front camera, it will be set to true; for back camera, it will be set to false.
*
* @param outputFileOptions Options to store the newly captured image.
* @param executor The executor in which the callback methods will be run.
* @param callback Callback which will receive success or failure.
*/
public void takePicture(@NonNull ImageCapture.OutputFileOptions outputFileOptions,
@NonNull Executor executor,
@NonNull OnImageSavedCallback callback) {
mCameraModule.takePicture(outputFileOptions, executor, callback);
}
/**
* Takes a video and calls the OnVideoSavedCallback when done.
*
* @param file The destination.
* @param executor The executor in which the callback methods will be run.
* @param callback Callback which will receive success or failure.
* @param outputFileOptions Options to store the newly captured video.
* @param executor The executor in which the callback methods will be run.
* @param callback Callback which will receive success or failure.
*/
// Begin Signal Custom Code Block
@RequiresApi(26)
// End Signal Custom Code Block
public void startRecording(// Begin Signal Custom Code Block
@NonNull FileDescriptor file,
// End Signal Custom Code Block
public void startRecording(@NonNull VideoCapture.OutputFileOptions outputFileOptions,
@NonNull Executor executor,
@NonNull VideoCapture.OnVideoSavedCallback callback) {
mCameraModule.startRecording(file, executor, callback);
@NonNull OnVideoSavedCallback callback) {
mCameraModule.startRecording(outputFileOptions, executor, callback);
}
/** Stops an in progress video. */
// Begin Signal Custom Code Block
@RequiresApi(26)
// End Signal Custom Code Block
public void stopRecording() {
mCameraModule.stopRecording();
}
@@ -554,7 +579,8 @@ public final class CameraXView extends FrameLayout {
mDownEventTimestamp = System.currentTimeMillis();
break;
case MotionEvent.ACTION_UP:
if (delta() < ViewConfiguration.getLongPressTimeout()) {
if (delta() < ViewConfiguration.getLongPressTimeout()
&& mCameraModule.isBoundToLifecycle()) {
mUpEvent = event;
performClick();
}
@@ -578,19 +604,14 @@ public final class CameraXView extends FrameLayout {
final float y = (mUpEvent != null) ? mUpEvent.getY() : getY() + getHeight() / 2f;
mUpEvent = null;
CameraSelector cameraSelector =
new CameraSelector.Builder().requireLensFacing(
mCameraModule.getLensFacing()).build();
DisplayOrientedMeteringPointFactory pointFactory = new DisplayOrientedMeteringPointFactory(
getDisplay(), cameraSelector, mPreviewView.getWidth(), mPreviewView.getHeight());
float afPointWidth = 1.0f / 6.0f; // 1/6 total area
float aePointWidth = afPointWidth * 1.5f;
MeteringPoint afPoint = pointFactory.createPoint(x, y, afPointWidth);
MeteringPoint aePoint = pointFactory.createPoint(x, y, aePointWidth);
Camera camera = mCameraModule.getCamera();
if (camera != null) {
MeteringPointFactory pointFactory = mPreviewView.getMeteringPointFactory();
float afPointWidth = 1.0f / 6.0f; // 1/6 total area
float aePointWidth = afPointWidth * 1.5f;
MeteringPoint afPoint = pointFactory.createPoint(x, y, afPointWidth);
MeteringPoint aePoint = pointFactory.createPoint(x, y, aePointWidth);
ListenableFuture<FocusMeteringResult> future =
camera.getCameraControl().startFocusAndMetering(
new FocusMeteringAction.Builder(afPoint,
@@ -609,7 +630,7 @@ public final class CameraXView extends FrameLayout {
}, CameraXExecutors.directExecutor());
} else {
Log.d(TAG, "cannot access camera");
Logger.d(TAG, "cannot access camera");
}
return true;
@@ -711,45 +732,11 @@ public final class CameraXView extends FrameLayout {
return mCameraModule.isTorchOn();
}
/** Options for scaling the bounds of the view finder to the bounds of this view. */
public enum ScaleType {
/**
* Scale the view finder, maintaining the source aspect ratio, so the view finder fills the
* entire view. This will cause the view finder to crop the source image if the camera
* aspect ratio does not match the view aspect ratio.
*/
CENTER_CROP(0),
/**
* Scale the view finder, maintaining the source aspect ratio, so the view finder is
* entirely contained within the view.
*/
CENTER_INSIDE(1);
private final int mId;
int getId() {
return mId;
}
ScaleType(int id) {
mId = id;
}
static ScaleType fromId(int id) {
for (ScaleType st : values()) {
if (st.mId == id) {
return st;
}
}
throw new IllegalArgumentException();
}
}
/**
* The capture mode used by CameraView.
*
* <p>This enum can be used to determine which capture mode will be enabled for {@link
* CameraXView}.
* SignalCameraView}.
*/
public enum CaptureMode {
/** A mode where image capture is enabled. */
@@ -832,4 +819,4 @@ public final class CameraXView extends FrameLayout {
public void onScaleEnd(ScaleGestureDetector detector) {
}
}
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.thoughtcrime.securesms.mediasend.camerax;
package androidx.camera.view;
import android.Manifest.permission;
import android.annotation.SuppressLint;
@@ -28,16 +28,19 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RequiresPermission;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraInfoUnavailableException;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.CameraX;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCapture.OnImageCapturedCallback;
import androidx.camera.core.ImageCapture.OnImageSavedCallback;
import androidx.camera.core.Logger;
import androidx.camera.core.Preview;
import androidx.camera.core.TorchState;
import androidx.camera.core.UseCase;
import androidx.camera.core.VideoCapture;
import androidx.camera.core.VideoCapture.OnVideoSavedCallback;
import androidx.camera.core.impl.CameraInternal;
import androidx.camera.core.impl.LensFacingConverter;
import androidx.camera.core.impl.VideoCaptureConfig;
import androidx.camera.core.impl.utils.CameraOrientationUtil;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.core.impl.utils.futures.FutureCallback;
@@ -51,11 +54,10 @@ import androidx.lifecycle.OnLifecycleEvent;
import com.google.common.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mediasend.camerax.CameraXUtil;
import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.video.VideoUtil;
import java.io.FileDescriptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
@@ -68,10 +70,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
import static androidx.camera.core.ImageCapture.FLASH_MODE_OFF;
/** CameraX use case operation built on @{link androidx.camera.core}. */
// Begin Signal Custom Code Block
@RequiresApi(21)
// End Signal Custom Code Block
final class CameraXModule {
@SuppressLint("RestrictedApi")
final class SignalCameraXModule {
public static final String TAG = "CameraXModule";
private static final float UNITY_ZOOM_SCALE = 1f;
@@ -82,13 +83,13 @@ final class CameraXModule {
private static final Rational ASPECT_RATIO_3_4 = new Rational(3, 4);
private final Preview.Builder mPreviewBuilder;
private final VideoCaptureConfig.Builder mVideoCaptureConfigBuilder;
private final VideoCapture.Builder mVideoCaptureBuilder;
private final ImageCapture.Builder mImageCaptureBuilder;
private final CameraXView mCameraXView;
private final SignalCameraView mCameraView;
final AtomicBoolean mVideoIsRecording = new AtomicBoolean(false);
private CameraXView.CaptureMode mCaptureMode = CameraXView.CaptureMode.IMAGE;
private long mMaxVideoDuration = CameraXView.INDEFINITE_VIDEO_DURATION;
private long mMaxVideoSize = CameraXView.INDEFINITE_VIDEO_SIZE;
private SignalCameraView.CaptureMode mCaptureMode = SignalCameraView.CaptureMode.IMAGE;
private long mMaxVideoDuration = SignalCameraView.INDEFINITE_VIDEO_DURATION;
private long mMaxVideoSize = SignalCameraView.INDEFINITE_VIDEO_SIZE;
@ImageCapture.FlashMode
private int mFlash = FLASH_MODE_OFF;
@Nullable
@@ -110,7 +111,6 @@ final class CameraXModule {
public void onDestroy(LifecycleOwner owner) {
if (owner == mCurrentLifecycle) {
clearCurrentLifecycle();
mPreview.setSurfaceProvider(null);
}
}
};
@@ -123,8 +123,8 @@ final class CameraXModule {
@Nullable
ProcessCameraProvider mCameraProvider;
CameraXModule(CameraXView view) {
mCameraXView = view;
SignalCameraXModule(SignalCameraView view) {
mCameraView = view;
Futures.addCallback(ProcessCameraProvider.getInstance(view.getContext()),
new FutureCallback<ProcessCameraProvider>() {
@@ -149,14 +149,12 @@ final class CameraXModule {
mImageCaptureBuilder = new ImageCapture.Builder().setTargetName("ImageCapture");
// Begin Signal Custom Code Block
mVideoCaptureConfigBuilder =
new VideoCaptureConfig.Builder().setTargetName("VideoCapture")
.setAudioBitRate(VideoUtil.AUDIO_BIT_RATE)
.setVideoFrameRate(VideoUtil.VIDEO_FRAME_RATE)
.setBitRate(VideoUtil.VIDEO_BIT_RATE);
// End Signal Custom Code Block
mVideoCaptureBuilder = new VideoCapture.Builder().setTargetName("VideoCapture")
.setAudioBitRate(VideoUtil.AUDIO_BIT_RATE)
.setVideoFrameRate(VideoUtil.VIDEO_FRAME_RATE)
.setBitRate(VideoUtil.VIDEO_BIT_RATE);
}
@RequiresPermission(permission.CAMERA)
void bindToLifecycle(LifecycleOwner lifecycleOwner) {
mNewLifecycle = lifecycleOwner;
@@ -173,12 +171,15 @@ final class CameraXModule {
}
clearCurrentLifecycle();
if (mNewLifecycle.getLifecycle().getCurrentState() == Lifecycle.State.DESTROYED) {
// Lifecycle is already in a destroyed state. Since it may have been a valid
// lifecycle when bound, but became destroyed while waiting for layout, treat this as
// a no-op now that we have cleared the previous lifecycle.
mNewLifecycle = null;
return;
}
mCurrentLifecycle = mNewLifecycle;
mNewLifecycle = null;
if (mCurrentLifecycle.getLifecycle().getCurrentState() == Lifecycle.State.DESTROYED) {
mCurrentLifecycle = null;
throw new IllegalArgumentException("Cannot bind to lifecycle in a destroyed state.");
}
if (mCameraProvider == null) {
// try again once the camera provider is no longer null
@@ -188,18 +189,18 @@ final class CameraXModule {
Set<Integer> available = getAvailableCameraLensFacing();
if (available.isEmpty()) {
Log.w(TAG, "Unable to bindToLifeCycle since no cameras available");
Logger.w(TAG, "Unable to bindToLifeCycle since no cameras available");
mCameraLensFacing = null;
}
// Ensure the current camera exists, or default to another camera
if (mCameraLensFacing != null && !available.contains(mCameraLensFacing)) {
Log.w(TAG, "Camera does not exist with direction " + mCameraLensFacing);
Logger.w(TAG, "Camera does not exist with direction " + mCameraLensFacing);
// Default to the first available camera direction
mCameraLensFacing = available.iterator().next();
Log.w(TAG, "Defaulting to primary camera with direction " + mCameraLensFacing);
Logger.w(TAG, "Defaulting to primary camera with direction " + mCameraLensFacing);
}
// Do not attempt to create use cases for a null cameraLensFacing. This could occur if
@@ -216,14 +217,12 @@ final class CameraXModule {
boolean isDisplayPortrait = getDisplayRotationDegrees() == 0
|| getDisplayRotationDegrees() == 180;
Rational targetAspectRatio;
// Begin Signal Custom Code Block
int resolution = CameraXUtil.getIdealResolution(Resources.getSystem().getDisplayMetrics().widthPixels, Resources.getSystem().getDisplayMetrics().heightPixels);
// End Signal Custom Code Block
if (getCaptureMode() == CameraXView.CaptureMode.IMAGE) {
// mImageCaptureBuilder.setTargetAspectRatio(AspectRatio.RATIO_4_3);
Rational targetAspectRatio;
if (getCaptureMode() == SignalCameraView.CaptureMode.IMAGE) {
// Begin Signal Custom Code Block
mImageCaptureBuilder.setTargetResolution(CameraXUtil.buildResolutionForRatio(resolution, ASPECT_RATIO_4_3, isDisplayPortrait));
// End Signal Custom Code Block
@@ -232,7 +231,6 @@ final class CameraXModule {
// Begin Signal Custom Code Block
mImageCaptureBuilder.setTargetResolution(CameraXUtil.buildResolutionForRatio(resolution, ASPECT_RATIO_16_9, isDisplayPortrait));
// End Signal Custom Code Block
// mImageCaptureBuilder.setTargetAspectRatio(AspectRatio.RATIO_16_9);
targetAspectRatio = isDisplayPortrait ? ASPECT_RATIO_9_16 : ASPECT_RATIO_16_9;
}
@@ -245,15 +243,14 @@ final class CameraXModule {
// Begin Signal Custom Code Block
Size size = VideoUtil.getVideoRecordingSize();
mVideoCaptureConfigBuilder.setTargetResolution(size);
mVideoCaptureConfigBuilder.setMaxResolution(size);
mVideoCaptureBuilder.setTargetResolution(size);
mVideoCaptureBuilder.setMaxResolution(size);
// End Signal Custom Code Block
mVideoCaptureConfigBuilder.setTargetRotation(getDisplaySurfaceRotation());
mVideoCaptureBuilder.setTargetRotation(getDisplaySurfaceRotation());
// Begin Signal Custom Code Block
if (MediaConstraints.isVideoTranscodeAvailable()) {
mVideoCapture = new VideoCapture(mVideoCaptureConfigBuilder.getUseCaseConfig());
mVideoCapture = mVideoCaptureBuilder.build();
}
// End Signal Custom Code Block
@@ -262,15 +259,15 @@ final class CameraXModule {
mPreviewBuilder.setTargetResolution(new Size(getMeasuredWidth(), height));
mPreview = mPreviewBuilder.build();
mPreview.setSurfaceProvider(mCameraXView.getPreviewView().getPreviewSurfaceProvider());
mPreview.setSurfaceProvider(mCameraView.getPreviewView().getSurfaceProvider());
CameraSelector cameraSelector =
new CameraSelector.Builder().requireLensFacing(mCameraLensFacing).build();
if (getCaptureMode() == CameraXView.CaptureMode.IMAGE) {
if (getCaptureMode() == SignalCameraView.CaptureMode.IMAGE) {
mCamera = mCameraProvider.bindToLifecycle(mCurrentLifecycle, cameraSelector,
mImageCapture,
mPreview);
} else if (getCaptureMode() == CameraXView.CaptureMode.VIDEO) {
} else if (getCaptureMode() == SignalCameraView.CaptureMode.VIDEO) {
mCamera = mCameraProvider.bindToLifecycle(mCurrentLifecycle, cameraSelector,
mVideoCapture,
mPreview);
@@ -301,7 +298,7 @@ final class CameraXModule {
return;
}
if (getCaptureMode() == CameraXView.CaptureMode.VIDEO) {
if (getCaptureMode() == SignalCameraView.CaptureMode.VIDEO) {
throw new IllegalStateException("Can not take picture under VIDEO capture mode.");
}
@@ -312,17 +309,32 @@ final class CameraXModule {
mImageCapture.takePicture(executor, callback);
}
// Begin Signal Custom Code Block
@RequiresApi(26)
public void startRecording(FileDescriptor file,
// End Signal Custom Code Block
Executor executor,
final VideoCapture.OnVideoSavedCallback callback) {
public void takePicture(@NonNull ImageCapture.OutputFileOptions outputFileOptions,
@NonNull Executor executor, OnImageSavedCallback callback) {
if (mImageCapture == null) {
return;
}
if (getCaptureMode() == SignalCameraView.CaptureMode.VIDEO) {
throw new IllegalStateException("Can not take picture under VIDEO capture mode.");
}
if (callback == null) {
throw new IllegalArgumentException("OnImageSavedCallback should not be empty");
}
outputFileOptions.getMetadata().setReversedHorizontal(mCameraLensFacing != null
&& mCameraLensFacing == CameraSelector.LENS_FACING_FRONT);
mImageCapture.takePicture(outputFileOptions, executor, callback);
}
public void startRecording(VideoCapture.OutputFileOptions outputFileOptions,
Executor executor, final OnVideoSavedCallback callback) {
if (mVideoCapture == null) {
return;
}
if (getCaptureMode() == CameraXView.CaptureMode.IMAGE) {
if (getCaptureMode() == SignalCameraView.CaptureMode.IMAGE) {
throw new IllegalStateException("Can not record video under IMAGE capture mode.");
}
@@ -332,15 +344,14 @@ final class CameraXModule {
mVideoIsRecording.set(true);
mVideoCapture.startRecording(
file,
outputFileOptions,
executor,
new VideoCapture.OnVideoSavedCallback() {
@Override
// Begin Signal Custom Code Block
public void onVideoSaved(@NonNull FileDescriptor savedFile) {
// End Signal Custom Code Block
public void onVideoSaved(
@NonNull VideoCapture.OutputFileResults outputFileResults) {
mVideoIsRecording.set(false);
callback.onVideoSaved(savedFile);
callback.onVideoSaved(outputFileResults);
}
@Override
@@ -349,15 +360,12 @@ final class CameraXModule {
@NonNull String message,
@Nullable Throwable cause) {
mVideoIsRecording.set(false);
Log.e(TAG, message, cause);
Logger.e(TAG, message, cause);
callback.onError(videoCaptureError, message, cause);
}
});
}
// Begin Signal Custom Code Block
@RequiresApi(26)
// End Signal Custom Code Block
public void stopRecording() {
if (mVideoCapture == null) {
return;
@@ -388,14 +396,15 @@ final class CameraXModule {
@RequiresPermission(permission.CAMERA)
public boolean hasCameraWithLensFacing(@CameraSelector.LensFacing int lensFacing) {
String cameraId;
try {
cameraId = CameraX.getCameraWithLensFacing(lensFacing);
} catch (Exception e) {
throw new IllegalStateException("Unable to query lens facing.", e);
if (mCameraProvider == null) {
return false;
}
try {
return mCameraProvider.hasCamera(
new CameraSelector.Builder().requireLensFacing(lensFacing).build());
} catch (CameraInfoUnavailableException e) {
return false;
}
return cameraId != null;
}
@Nullable
@@ -454,7 +463,7 @@ final class CameraXModule {
}
}, CameraXExecutors.directExecutor());
} else {
Log.e(TAG, "Failed to set zoom ratio");
Logger.e(TAG, "Failed to set zoom ratio");
}
}
@@ -486,6 +495,10 @@ final class CameraXModule {
}
}
boolean isBoundToLifecycle() {
return mCamera != null;
}
int getRelativeCameraOrientation(boolean compensateForMirroring) {
int rotationDegrees = 0;
if (mCamera != null) {
@@ -520,6 +533,11 @@ final class CameraXModule {
if (!toUnbind.isEmpty()) {
mCameraProvider.unbind(toUnbind.toArray((new UseCase[0])));
}
// Remove surface provider once unbound.
if (mPreview != null) {
mPreview.setSurfaceProvider(null);
}
}
mCamera = null;
mCurrentLifecycle = null;
@@ -532,7 +550,7 @@ final class CameraXModule {
mImageCapture.setTargetRotation(getDisplaySurfaceRotation());
}
if (mVideoCapture != null && MediaConstraints.isVideoTranscodeAvailable()) {
if (mVideoCapture != null) {
mVideoCapture.setTargetRotation(getDisplaySurfaceRotation());
}
}
@@ -567,7 +585,7 @@ final class CameraXModule {
return false;
}
CameraInternal camera = mImageCapture.getBoundCamera();
CameraInternal camera = mImageCapture.getCamera();
if (camera == null) {
return false;
@@ -614,15 +632,15 @@ final class CameraXModule {
}
public Context getContext() {
return mCameraXView.getContext();
return mCameraView.getContext();
}
public int getWidth() {
return mCameraXView.getWidth();
return mCameraView.getWidth();
}
public int getHeight() {
return mCameraXView.getHeight();
return mCameraView.getHeight();
}
public int getDisplayRotationDegrees() {
@@ -630,15 +648,15 @@ final class CameraXModule {
}
protected int getDisplaySurfaceRotation() {
return mCameraXView.getDisplaySurfaceRotation();
return mCameraView.getDisplaySurfaceRotation();
}
private int getMeasuredWidth() {
return mCameraXView.getMeasuredWidth();
return mCameraView.getMeasuredWidth();
}
private int getMeasuredHeight() {
return mCameraXView.getMeasuredHeight();
return mCameraView.getMeasuredHeight();
}
@Nullable
@@ -647,11 +665,11 @@ final class CameraXModule {
}
@NonNull
public CameraXView.CaptureMode getCaptureMode() {
public SignalCameraView.CaptureMode getCaptureMode() {
return mCaptureMode;
}
public void setCaptureMode(@NonNull CameraXView.CaptureMode captureMode) {
public void setCaptureMode(@NonNull SignalCameraView.CaptureMode captureMode) {
this.mCaptureMode = captureMode;
rebindToLifecycle();
}

View File

@@ -7,8 +7,8 @@ package org.signal.glide.apng;
import android.content.Context;
import org.signal.glide.common.FrameAnimationDrawable;
import org.signal.glide.apng.decode.APNGDecoder;
import org.signal.glide.common.FrameAnimationDrawable;
import org.signal.glide.common.decode.FrameSeqDecoder;
import org.signal.glide.common.loader.AssetStreamLoader;
import org.signal.glide.common.loader.FileLoader;

View File

@@ -1,20 +1,22 @@
package org.thoughtcrime.securesms;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
import org.whispersystems.signalservice.api.account.AccountAttributes;
public final class AppCapabilities {
private AppCapabilities() {
}
private static final boolean UUID_CAPABLE = false;
private static final boolean UUID_CAPABLE = false;
private static final boolean GV2_CAPABLE = true;
private static final boolean GV1_MIGRATION = true;
/**
* @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);
public static AccountAttributes.Capabilities getCapabilities(boolean storageCapable) {
return new AccountAttributes.Capabilities(UUID_CAPABLE, GV2_CAPABLE, storageCapable, GV1_MIGRATION);
}
}

View File

@@ -4,12 +4,12 @@ import android.content.Context;
import androidx.annotation.NonNull;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.insights.InsightsOptOut;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobs.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;
@@ -34,10 +34,16 @@ public final class AppInitialization {
TextSecurePreferences.setJobManagerVersion(context, JobManager.CURRENT_VERSION);
TextSecurePreferences.setLastExperienceVersionCode(context, Util.getCanonicalVersionCode());
TextSecurePreferences.setHasSeenStickerIntroTooltip(context, true);
TextSecurePreferences.setPasswordDisabled(context, true);
TextSecurePreferences.setLastExperienceVersionCode(context, Util.getCanonicalVersionCode());
TextSecurePreferences.setReadReceiptsEnabled(context, true);
TextSecurePreferences.setTypingIndicatorsEnabled(context, true);
TextSecurePreferences.setHasSeenWelcomeScreen(context, false);
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.forInstall(BlessedPacks.DAY_BY_DAY.getPackId(), BlessedPacks.DAY_BY_DAY.getPackKey(), false));
ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forReference(BlessedPacks.SWOON_HANDS.getPackId(), BlessedPacks.SWOON_HANDS.getPackKey()));
ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forReference(BlessedPacks.SWOON_FACES.getPackId(), BlessedPacks.SWOON_FACES.getPackKey()));
}
@@ -47,8 +53,32 @@ public final class AppInitialization {
ApplicationDependencies.getMegaphoneRepository().onFirstEverAppLaunch();
SignalStore.onFirstEverAppLaunch();
SignalStore.onboarding().clearAll();
ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.ZOZO.getPackId(), BlessedPacks.ZOZO.getPackKey(), false));
ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.BANDIT.getPackId(), BlessedPacks.BANDIT.getPackKey(), false));
ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.DAY_BY_DAY.getPackId(), BlessedPacks.DAY_BY_DAY.getPackKey(), false));
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()));
}
/**
* Temporary migration method that does the safest bits of {@link #onFirstEverAppLaunch(Context)}
*/
public static void onRepairFirstEverAppLaunch(@NonNull Context context) {
Log.w(TAG, "onRepairFirstEverAppLaunch()");
InsightsOptOut.userRequestedOptOut(context);
TextSecurePreferences.setAppMigrationVersion(context, ApplicationMigrations.CURRENT_VERSION);
TextSecurePreferences.setJobManagerVersion(context, JobManager.CURRENT_VERSION);
TextSecurePreferences.setLastExperienceVersionCode(context, Util.getCanonicalVersionCode());
TextSecurePreferences.setHasSeenStickerIntroTooltip(context, true);
TextSecurePreferences.setPasswordDisabled(context, true);
TextSecurePreferences.setLastExperienceVersionCode(context, Util.getCanonicalVersionCode());
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.forInstall(BlessedPacks.DAY_BY_DAY.getPackId(), BlessedPacks.DAY_BY_DAY.getPackKey(), false));
ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forReference(BlessedPacks.SWOON_HANDS.getPackId(), BlessedPacks.SWOON_HANDS.getPackKey()));
ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forReference(BlessedPacks.SWOON_FACES.getPackId(), BlessedPacks.SWOON_FACES.getPackKey()));
}

View File

@@ -16,13 +16,13 @@
*/
package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.AsyncTask;
import android.hardware.SensorManager;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
@@ -33,27 +33,30 @@ import com.google.android.gms.security.ProviderInstaller;
import org.conscrypt.Conscrypt;
import org.signal.aesgcmprovider.AesGcmProvider;
import org.signal.core.util.ShakeDetector;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.AndroidLogger;
import org.signal.core.util.logging.Log;
import org.signal.core.util.logging.PersistentLogger;
import org.signal.core.util.tracing.Tracer;
import org.signal.glide.SignalGlideCodecs;
import org.signal.ringrtc.CallManager;
import org.thoughtcrime.securesms.components.TypingStatusRepository;
import org.thoughtcrime.securesms.components.TypingStatusSender;
import org.thoughtcrime.securesms.database.DatabaseFactory;
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.jobs.CreateSignedPreKeyJob;
import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
import org.thoughtcrime.securesms.jobs.GroupV1MigrationJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
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.SignalUncaughtExceptionHandler;
import org.thoughtcrime.securesms.logging.LogSecretProvider;
import org.thoughtcrime.securesms.migrations.ApplicationMigrations;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.providers.BlobProvider;
@@ -68,11 +71,15 @@ import org.thoughtcrime.securesms.service.LocalBackupListener;
import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
import org.thoughtcrime.securesms.shakereport.ShakeToReport;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.AppStartup;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.SignalUncaughtExceptionHandler;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.util.VersionTracker;
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;
import org.webrtc.voiceengine.WebRtcAudioManager;
import org.webrtc.voiceengine.WebRtcAudioUtils;
@@ -95,11 +102,9 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
private static final String TAG = ApplicationContext.class.getSimpleName();
private ExpiringMessageManager expiringMessageManager;
private ViewOnceMessageManager viewOnceMessageManager;
private TypingStatusRepository typingStatusRepository;
private TypingStatusSender typingStatusSender;
private PersistentLogger persistentLogger;
private ExpiringMessageManager expiringMessageManager;
private ViewOnceMessageManager viewOnceMessageManager;
private PersistentLogger persistentLogger;
private volatile boolean isAppVisible;
@@ -109,57 +114,81 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
@Override
public void onCreate() {
Tracer.getInstance().start("Application#onCreate()");
AppStartup.getInstance().onApplicationCreate();
long startTime = System.currentTimeMillis();
super.onCreate();
initializeSecurityProvider();
initializeLogging();
Log.i(TAG, "onCreate()");
initializeCrashHandling();
initializeAppDependencies();
initializeFirstEverAppLaunch();
initializeApplicationMigrations();
initializeMessageRetrieval();
initializeExpiringMessageManager();
initializeRevealableMessageManager();
initializeTypingStatusRepository();
initializeTypingStatusSender();
initializeGcmCheck();
initializeSignedPreKeyCheck();
initializePeriodicTasks();
initializeCircumvention();
initializeRingRtc();
initializePendingMessages();
initializeBlobProvider();
initializeCleanup();
initializeGlideCodecs();
FeatureFlags.init();
NotificationChannels.create(this);
RefreshPreKeysJob.scheduleIfNecessary();
StorageSyncHelper.scheduleRoutineSync();
RetrieveProfileJob.enqueueRoutineFetchIfNecessary(this);
RegistrationUtil.maybeMarkRegistrationComplete(this);
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
if (Build.VERSION.SDK_INT < 21) {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
if (FeatureFlags.internalUser()) {
Tracer.getInstance().setMaxBufferSize(35_000);
}
ApplicationDependencies.getJobManager().beginJobLoop();
super.onCreate();
AppStartup.getInstance().addBlocking("security-provider", this::initializeSecurityProvider)
.addBlocking("logging", () -> {
initializeLogging();
Log.i(TAG, "onCreate()");
})
.addBlocking("crash-handling", this::initializeCrashHandling)
.addBlocking("eat-db", () -> DatabaseFactory.getInstance(this))
.addBlocking("app-dependencies", this::initializeAppDependencies)
.addBlocking("first-launch", this::initializeFirstEverAppLaunch)
.addBlocking("app-migrations", this::initializeApplicationMigrations)
.addBlocking("ring-rtc", this::initializeRingRtc)
.addBlocking("mark-registration", () -> RegistrationUtil.maybeMarkRegistrationComplete(this))
.addBlocking("lifecycle-observer", () -> ProcessLifecycleOwner.get().getLifecycle().addObserver(this))
.addBlocking("message-retriever", this::initializeMessageRetrieval)
.addBlocking("dynamic-theme", () -> DynamicTheme.setDefaultDayNightMode(this))
.addBlocking("vector-compat", () -> {
if (Build.VERSION.SDK_INT < 21) {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
})
.addNonBlocking(this::initializeRevealableMessageManager)
.addNonBlocking(this::initializeGcmCheck)
.addNonBlocking(this::initializeSignedPreKeyCheck)
.addNonBlocking(this::initializePeriodicTasks)
.addNonBlocking(this::initializeCircumvention)
.addNonBlocking(this::initializePendingMessages)
.addNonBlocking(this::initializeCleanup)
.addNonBlocking(this::initializeGlideCodecs)
.addNonBlocking(FeatureFlags::init)
.addNonBlocking(RefreshPreKeysJob::scheduleIfNecessary)
.addNonBlocking(StorageSyncHelper::scheduleRoutineSync)
.addNonBlocking(() -> ApplicationDependencies.getJobManager().beginJobLoop())
.addPostRender(this::initializeExpiringMessageManager)
.addPostRender(this::initializeBlobProvider)
.addPostRender(() -> NotificationChannels.create(this))
.execute();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
Log.d(TAG, "onCreate() took " + (System.currentTimeMillis() - startTime) + " ms");
Tracer.getInstance().end("Application#onCreate()");
}
@Override
public void onStart(@NonNull LifecycleOwner owner) {
long startTime = System.currentTimeMillis();
isAppVisible = true;
Log.i(TAG, "App is now visible.");
FeatureFlags.refreshIfNecessary();
ApplicationDependencies.getRecipientCache().warmUp();
executePendingContactSync();
KeyCachingService.onAppForegrounded(this);
ApplicationDependencies.getFrameRateTracker().begin();
ApplicationDependencies.getMegaphoneRepository().onAppForegrounded();
checkBuildExpiration();
SignalExecutors.BOUNDED.execute(() -> {
FeatureFlags.refreshIfNecessary();
ApplicationDependencies.getRecipientCache().warmUp();
RetrieveProfileJob.enqueueRoutineFetchIfNecessary(this);
GroupV1MigrationJob.enqueueRoutineMigrationsIfNecessary(this);
executePendingContactSync();
KeyCachingService.onAppForegrounded(this);
ApplicationDependencies.getShakeToReport().enable();
checkBuildExpiration();
});
Log.d(TAG, "onStart() took " + (System.currentTimeMillis() - startTime) + " ms");
}
@Override
@@ -169,9 +198,13 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
KeyCachingService.onAppBackgrounded(this);
ApplicationDependencies.getMessageNotifier().clearVisibleThread();
ApplicationDependencies.getFrameRateTracker().end();
ApplicationDependencies.getShakeToReport().disable();
}
public ExpiringMessageManager getExpiringMessageManager() {
if (expiringMessageManager == null) {
initializeExpiringMessageManager();
}
return expiringMessageManager;
}
@@ -179,14 +212,6 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
return viewOnceMessageManager;
}
public TypingStatusRepository getTypingStatusRepository() {
return typingStatusRepository;
}
public TypingStatusSender getTypingStatusSender() {
return typingStatusSender;
}
public boolean isAppVisible() {
return isAppVisible;
}
@@ -227,8 +252,8 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
}
private void initializeLogging() {
persistentLogger = new PersistentLogger(this);
org.thoughtcrime.securesms.logging.Log.initialize(new AndroidLogger(), persistentLogger);
persistentLogger = new PersistentLogger(this, LogSecretProvider.getOrCreateAttachmentSecret(this), BuildConfig.VERSION_NAME);
org.signal.core.util.logging.Log.initialize(new AndroidLogger(), persistentLogger);
SignalProtocolLoggerProvider.setProvider(new CustomSignalProtocolLogger());
}
@@ -252,13 +277,16 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
private void initializeFirstEverAppLaunch() {
if (TextSecurePreferences.getFirstInstallVersion(this) == -1) {
if (!SQLCipherOpenHelper.databaseFileExists(this)) {
if (!SQLCipherOpenHelper.databaseFileExists(this) || VersionTracker.getDaysSinceFirstInstalled(this) < 365) {
Log.i(TAG, "First ever app launch!");
AppInitialization.onFirstEverAppLaunch(this);
}
Log.i(TAG, "Setting first install version to " + BuildConfig.CANONICAL_VERSION_CODE);
TextSecurePreferences.setFirstInstallVersion(this, BuildConfig.CANONICAL_VERSION_CODE);
} else if (!TextSecurePreferences.isPasswordDisabled(this) && VersionTracker.getDaysSinceFirstInstalled(this) < 90) {
Log.i(TAG, "Detected a new install that doesn't have passphrases disabled -- assuming bad initialization.");
AppInitialization.onRepairFirstEverAppLaunch(this);
}
}
@@ -286,14 +314,6 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
this.viewOnceMessageManager = new ViewOnceMessageManager(this);
}
private void initializeTypingStatusRepository() {
this.typingStatusRepository = new TypingStatusRepository();
}
private void initializeTypingStatusSender() {
this.typingStatusSender = new TypingStatusSender(this);
}
private void initializePeriodicTasks() {
RotateSignedPreKeyListener.schedule(this);
DirectoryRefreshListener.schedule(this);
@@ -341,23 +361,15 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
}
}
@SuppressLint("StaticFieldLeak")
@WorkerThread
private void initializeCircumvention() {
AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
if (new SignalServiceNetworkAccess(ApplicationContext.this).isCensored(ApplicationContext.this)) {
try {
ProviderInstaller.installIfNeeded(ApplicationContext.this);
} catch (Throwable t) {
Log.w(TAG, t);
}
}
return null;
if (new SignalServiceNetworkAccess(ApplicationContext.this).isCensored(ApplicationContext.this)) {
try {
ProviderInstaller.installIfNeeded(ApplicationContext.this);
} catch (Throwable t) {
Log.w(TAG, t);
}
};
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
private void executePendingContactSync() {
@@ -378,17 +390,15 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
}
}
@WorkerThread
private void initializeBlobProvider() {
SignalExecutors.BOUNDED.execute(() -> {
BlobProvider.getInstance().onSessionStart(this);
});
BlobProvider.getInstance().onSessionStart(this);
}
@WorkerThread
private void initializeCleanup() {
SignalExecutors.BOUNDED.execute(() -> {
int deleted = DatabaseFactory.getAttachmentDatabase(this).deleteAbandonedPreuploadedAttachments();
Log.i(TAG, "Deleted " + deleted + " abandoned attachments.");
});
int deleted = DatabaseFactory.getAttachmentDatabase(this).deleteAbandonedPreuploadedAttachments();
Log.i(TAG, "Deleted " + deleted + " abandoned attachments.");
}
private void initializeGlideCodecs() {
@@ -422,7 +432,8 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(base, TextSecurePreferences.getLanguage(base)));
DynamicLanguageContextWrapper.updateContext(base);
super.attachBaseContext(base);
}
private static class ProviderInitializationException extends RuntimeException {

View File

@@ -18,7 +18,6 @@
package org.thoughtcrime.securesms;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.PorterDuff;
@@ -27,9 +26,9 @@ import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.preference.Preference;
import org.thoughtcrime.securesms.help.HelpFragment;
@@ -37,22 +36,24 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.preferences.AdvancedPreferenceFragment;
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
import org.thoughtcrime.securesms.preferences.AppearancePreferenceFragment;
import org.thoughtcrime.securesms.preferences.BackupsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.ChatsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment;
import org.thoughtcrime.securesms.preferences.DataAndStoragePreferenceFragment;
import org.thoughtcrime.securesms.preferences.NotificationsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.SmsMmsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.StoragePreferenceFragment;
import org.thoughtcrime.securesms.preferences.widgets.ProfilePreference;
import org.thoughtcrime.securesms.preferences.widgets.UsernamePreference;
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity;
import org.thoughtcrime.securesms.profiles.manage.ManageProfileActivity;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.CachedInflater;
import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ThemeUtil;
/**
* The Activity for application preference display and management.
@@ -64,6 +65,9 @@ import org.thoughtcrime.securesms.util.ThemeUtil;
public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
implements SharedPreferences.OnSharedPreferenceChangeListener
{
public static final String LAUNCH_TO_BACKUPS_FRAGMENT = "launch.to.backups.fragment";
public static final String LAUNCH_TO_HELP_FRAGMENT = "launch.to.help.fragment";
@SuppressWarnings("unused")
private static final String TAG = ApplicationPreferencesActivity.class.getSimpleName();
@@ -80,9 +84,13 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
private static final String PREFERENCE_CATEGORY_ADVANCED = "preference_category_advanced";
private static final String PREFERENCE_CATEGORY_DONATE = "preference_category_donate";
private static final String WAS_CONFIGURATION_UPDATED = "was_configuration_updated";
private final DynamicTheme dynamicTheme = new DynamicTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private boolean wasConfigurationUpdated = false;
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
@@ -96,11 +104,23 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
if (getIntent() != null && getIntent().getCategories() != null && getIntent().getCategories().contains("android.intent.category.NOTIFICATION_PREFERENCES")) {
initFragment(android.R.id.content, new NotificationsPreferenceFragment());
} else if (getIntent() != null && getIntent().getBooleanExtra(LAUNCH_TO_BACKUPS_FRAGMENT, false)) {
initFragment(android.R.id.content, new BackupsPreferenceFragment());
} else if (getIntent() != null && getIntent().getBooleanExtra(LAUNCH_TO_HELP_FRAGMENT, false)) {
initFragment(android.R.id.content, new HelpFragment());
} else if (icicle == null) {
initFragment(android.R.id.content, new ApplicationPreferenceFragment());
} else {
wasConfigurationUpdated = icicle.getBoolean(WAS_CONFIGURATION_UPDATED);
}
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBoolean(WAS_CONFIGURATION_UPDATED, wasConfigurationUpdated);
super.onSaveInstanceState(outState);
}
@Override
public void onResume() {
super.onResume();
@@ -122,20 +142,29 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStack();
} else {
// TODO [greyson] Navigation
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
if (wasConfigurationUpdated) {
setResult(MainActivity.RESULT_CONFIG_CHANGED);
} else {
setResult(RESULT_OK);
}
finish();
}
return true;
}
@Override
public void onBackPressed() {
onSupportNavigateUp();
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(TextSecurePreferences.THEME_PREF)) {
DynamicTheme.setDefaultDayNightMode(this);
recreate();
} else if (key.equals(TextSecurePreferences.LANGUAGE_PREF)) {
CachedInflater.from(this).clear();
wasConfigurationUpdated = true;
recreate();
Intent intent = new Intent(this, KeyCachingService.class);
@@ -190,7 +219,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
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);
preference.getIcon().setColorFilter(ContextCompat.getColor(requireContext(), R.color.signal_icon_tint_primary), PorterDuff.Mode.SRC_IN);
}
@Override
@@ -284,7 +313,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
fragment = new ChatsPreferenceFragment();
break;
case PREFERENCE_CATEGORY_STORAGE:
fragment = new StoragePreferenceFragment();
fragment = new DataAndStoragePreferenceFragment();
break;
case PREFERENCE_CATEGORY_DEVICES:
Intent intent = new Intent(getActivity(), DeviceActivity.class);
@@ -317,7 +346,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
private class ProfileClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
requireActivity().startActivity(EditProfileActivity.getIntentForUserProfileEdit(preference.getContext()));
requireActivity().startActivity(ManageProfileActivity.getIntent(requireActivity()));
return true;
}
}
@@ -325,7 +354,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
private class UsernameClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
requireActivity().startActivity(EditProfileActivity.getIntentForUsernameEdit(preference.getContext()));
requireActivity().startActivity(ManageProfileActivity.getIntentForUsernameEdit(preference.getContext()));
return true;
}
}

View File

@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@@ -10,12 +9,7 @@ import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.transition.TransitionInflater;
import android.view.DisplayCutout;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import androidx.annotation.NonNull;
@@ -32,14 +26,15 @@ import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.request.transition.Transition;
import org.signal.core.util.logging.Log;
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;
import org.thoughtcrime.securesms.util.FullscreenHelper;
/**
* Activity for displaying avatars full screen.
@@ -81,26 +76,16 @@ public final class AvatarPreviewActivity extends PassphraseRequiredActivity {
setSupportActionBar(toolbar);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
if (Build.VERSION.SDK_INT >= 28) {
getWindow().getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
toolbar.getViewTreeObserver().addOnGlobalLayoutListener(new DisplayCutoutAdjuster(toolbar, findViewById(R.id.toolbar_cutout_spacer)));
}
showSystemUI();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
requireSupportActionBar().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();
ContactPhoto contactPhoto = recipient.isSelf() ? new ProfileContactPhoto(recipient, recipient.getProfileAvatar())
: recipient.getContactPhoto();
FallbackContactPhoto fallbackPhoto = recipient.isSelf() ? new ResourceContactPhoto(R.drawable.ic_profile_outline_40, R.drawable.ic_profile_outline_20, R.drawable.ic_person_large)
: recipient.getFallbackContactPhoto();
Resources resources = this.getResources();
@@ -140,47 +125,13 @@ public final class AvatarPreviewActivity extends PassphraseRequiredActivity {
toolbar.setTitle(recipient.getDisplayName(context));
});
avatar.setOnClickListener(v -> toggleUiVisibility());
FullscreenHelper fullscreenHelper = new FullscreenHelper(this);
showAndHideWithSystemUI(getWindow(), findViewById(R.id.toolbar_layout));
}
findViewById(android.R.id.content).setOnClickListener(v -> fullscreenHelper.toggleUiVisibility());
private static void showAndHideWithSystemUI(@NonNull Window window, @NonNull View... views) {
window.getDecorView().setOnSystemUiVisibilityChangeListener(visibility -> {
boolean hide = (visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
fullscreenHelper.configureToolbarSpacer(findViewById(R.id.toolbar_cutout_spacer));
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 );
fullscreenHelper.showAndHideWithSystemUI(getWindow(), findViewById(R.id.toolbar_layout));
}
@Override
@@ -188,36 +139,4 @@ public final class AvatarPreviewActivity extends PassphraseRequiredActivity {
onBackPressed();
return true;
}
/**
* Adjust a spacer for the toolbar when a display cutout is detected. Runs within
* a layout listener because the activity delays view attachment due to the transitions
* and needs to update on device rotation.
*/
@TargetApi(28)
private static class DisplayCutoutAdjuster implements ViewTreeObserver.OnGlobalLayoutListener {
private final View view;
private final View spacer;
private DisplayCutoutAdjuster(@NonNull View view, @NonNull View spacer) {
this.view = view;
this.spacer = spacer;
}
@Override
public void onGlobalLayout() {
if (view.getRootWindowInsets() == null) {
return;
}
DisplayCutout cutout = view.getRootWindowInsets().getDisplayCutout();
if (cutout != null) {
ViewGroup.LayoutParams params = spacer.getLayoutParams();
params.height = cutout.getSafeInsetTop();
spacer.setLayoutParams(params);
spacer.setVisibility(View.VISIBLE);
}
}
}
}

View File

@@ -1,23 +1,24 @@
package org.thoughtcrime.securesms;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.content.res.Configuration;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.core.app.ActivityCompat;
import androidx.core.app.ActivityOptionsCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.view.WindowManager;
import org.thoughtcrime.securesms.logging.Log;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.app.ActivityCompat;
import androidx.core.app.ActivityOptionsCompat;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.util.AppStartup;
import org.thoughtcrime.securesms.util.ConfigurationUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageActivityHelper;
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;
import java.util.Objects;
@@ -32,20 +33,22 @@ public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
AppStartup.getInstance().onCriticalRenderEventStart();
logEvent("onCreate()");
super.onCreate(savedInstanceState);
AppStartup.getInstance().onCriticalRenderEventEnd();
}
@Override
protected void onResume() {
super.onResume();
initializeScreenshotSecurity();
DynamicLanguageActivityHelper.recreateIfNotInCorrectLanguage(this, TextSecurePreferences.getLanguage(this));
}
@Override
protected void onStart() {
logEvent("onStart()");
ApplicationDependencies.getShakeToReport().registerActivity(this);
super.onStart();
}
@@ -75,16 +78,23 @@ public abstract class BaseActivity extends AppCompatActivity {
ActivityCompat.startActivity(this, intent, bundle);
}
@TargetApi(21)
protected void setStatusBarColor(int color) {
if (Build.VERSION.SDK_INT >= 21) {
getWindow().setStatusBarColor(color);
}
@Override
protected void attachBaseContext(@NonNull Context newBase) {
super.attachBaseContext(newBase);
Configuration configuration = new Configuration(newBase.getResources().getConfiguration());
int appCompatNightMode = getDelegate().getLocalNightMode() != AppCompatDelegate.MODE_NIGHT_UNSPECIFIED ? getDelegate().getLocalNightMode()
: AppCompatDelegate.getDefaultNightMode();
configuration.uiMode = (configuration.uiMode & ~Configuration.UI_MODE_NIGHT_MASK) | mapNightModeToConfigurationUiMode(newBase, appCompatNightMode);
applyOverrideConfiguration(configuration);
}
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(newBase, TextSecurePreferences.getLanguage(newBase)));
public void applyOverrideConfiguration(@NonNull Configuration overrideConfiguration) {
DynamicLanguageContextWrapper.prepareOverrideConfiguration(this, overrideConfiguration);
super.applyOverrideConfiguration(overrideConfiguration);
}
private void logEvent(@NonNull String event) {
@@ -94,4 +104,13 @@ public abstract class BaseActivity extends AppCompatActivity {
public final @NonNull ActionBar requireSupportActionBar() {
return Objects.requireNonNull(getSupportActionBar());
}
private static int mapNightModeToConfigurationUiMode(@NonNull Context context, @AppCompatDelegate.NightMode int appCompatNightMode) {
if (appCompatNightMode == AppCompatDelegate.MODE_NIGHT_YES) {
return Configuration.UI_MODE_NIGHT_YES;
} else if (appCompatNightMode == AppCompatDelegate.MODE_NIGHT_NO) {
return Configuration.UI_MODE_NIGHT_NO;
}
return ConfigurationUtil.getNightModeConfiguration(context.getApplicationContext());
}
}

View File

@@ -1,15 +1,20 @@
package org.thoughtcrime.securesms;
import android.net.Uri;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.Observer;
import org.thoughtcrime.securesms.components.voice.VoiceNotePlaybackState;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.conversation.ConversationMessage;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -22,7 +27,8 @@ import java.util.Locale;
import java.util.Set;
public interface BindableConversationItem extends Unbindable {
void bind(@NonNull ConversationMessage messageRecord,
void bind(@NonNull LifecycleOwner lifecycleOwner,
@NonNull ConversationMessage messageRecord,
@NonNull Optional<MessageRecord> previousMessageRecord,
@NonNull Optional<MessageRecord> nextMessageRecord,
@NonNull GlideRequests glideRequests,
@@ -30,7 +36,8 @@ public interface BindableConversationItem extends Unbindable {
@NonNull Set<ConversationMessage> batchSelected,
@NonNull Recipient recipients,
@Nullable String searchQuery,
boolean pulseMention);
boolean pulseMention,
boolean hasWallpaper);
ConversationMessage getConversationMessage();
@@ -49,6 +56,16 @@ public interface BindableConversationItem extends Unbindable {
void onReactionClicked(@NonNull View reactionTarget, long messageId, boolean isMms);
void onGroupMemberClicked(@NonNull RecipientId recipientId, @NonNull GroupId groupId);
void onMessageWithErrorClicked(@NonNull MessageRecord messageRecord);
void onRegisterVoiceNoteCallbacks(@NonNull Observer<VoiceNotePlaybackState> onPlaybackStartObserver);
void onUnregisterVoiceNoteCallbacks(@NonNull Observer<VoiceNotePlaybackState> onPlaybackStartObserver);
void onVoiceNotePause(@NonNull Uri uri);
void onVoiceNotePlay(@NonNull Uri uri, long messageId, double position);
void onVoiceNoteSeekTo(@NonNull Uri uri, double position);
void onGroupMigrationLearnMoreClicked(@NonNull GroupMigrationMembershipChange membershipChange);
void onDecryptionFailedLearnMoreClicked();
void onSafetyNumberLearnMoreClicked(@NonNull Recipient recipient);
void onJoinGroupCallClicked();
void onInviteFriendsToGroupClicked(@NonNull GroupId.V2 groupId);
/** @return true if handled, false if you want to let the normal url handling continue */
boolean onUrlClicked(@NonNull String url);

View File

@@ -11,9 +11,6 @@ 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;
/**

View File

@@ -1,139 +0,0 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
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;
import org.thoughtcrime.securesms.mms.GlideRequests;
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.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.DynamicTheme;
public class BlockedContactsActivity extends PassphraseRequiredActivity {
private final DynamicTheme dynamicTheme = new DynamicTheme();
@Override
public void onPreCreate() {
dynamicTheme.onCreate(this);
}
@Override
public void onCreate(Bundle bundle, boolean ready) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle(R.string.BlockedContactsActivity_blocked_contacts);
initFragment(android.R.id.content, new BlockedContactsFragment());
}
@Override
public void onResume() {
super.onResume();
dynamicTheme.onResume(this);
}
@Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
public static class BlockedContactsFragment
extends ListFragment
implements LoaderManager.LoaderCallbacks<Cursor>, ListView.OnItemClickListener
{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
return inflater.inflate(R.layout.blocked_contacts_fragment, container, false);
}
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setListAdapter(new BlockedContactAdapter(requireActivity(), GlideApp.with(this), null));
LoaderManager.getInstance(this).initLoader(0, null, this);
}
@Override
public void onStart() {
super.onStart();
LoaderManager.getInstance(this).restartLoader(0, null, this);
}
@Override
public void onActivityCreated(Bundle bundle) {
super.onActivityCreated(bundle);
getListView().setOnItemClickListener(this);
}
@Override
public @NonNull Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new BlockedContactsLoader(getActivity());
}
@Override
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor data) {
if (getListAdapter() != null) {
((CursorAdapter) getListAdapter()).changeCursor(data);
}
}
@Override
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
if (getListAdapter() != null) {
((CursorAdapter) getListAdapter()).changeCursor(null);
}
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Recipient recipient = ((BlockedContactListItem)view).getRecipient();
BlockUnblockDialog.showUnblockFor(requireContext(), getLifecycle(), recipient, () -> {
RecipientUtil.unblock(requireContext(), recipient);
LoaderManager.getInstance(this).restartLoader(0, null, this);
});
}
private static class BlockedContactAdapter extends CursorAdapter {
private final GlideRequests glideRequests;
BlockedContactAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests, @Nullable Cursor c) {
super(context, c);
this.glideRequests = glideRequests;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(context)
.inflate(R.layout.blocked_contact_list_item, parent, false);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
RecipientId recipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RecipientDatabase.ID)));
LiveRecipient recipient = Recipient.live(recipientId);
((BlockedContactListItem) view).set(glideRequests, recipient);
}
}
}
}

View File

@@ -3,26 +3,25 @@ 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.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.ThemeUtil;
public class ClearProfileAvatarActivity extends Activity {
public final class ClearAvatarPromptActivity extends Activity {
private static final String ARG_TITLE = "arg_title";
public static Intent createForUserProfilePhoto() {
return new Intent("org.thoughtcrime.securesms.action.CLEAR_PROFILE_PHOTO");
Intent intent = new Intent(ApplicationDependencies.getApplication(), ClearAvatarPromptActivity.class);
intent.putExtra(ARG_TITLE, R.string.ClearProfileActivity_remove_profile_photo);
return intent;
}
public static Intent createForGroupProfilePhoto() {
Intent intent = new Intent("org.thoughtcrime.securesms.action.CLEAR_PROFILE_PHOTO");
Intent intent = new Intent(ApplicationDependencies.getApplication(), ClearAvatarPromptActivity.class);
intent.putExtra(ARG_TITLE, R.string.ClearProfileActivity_remove_group_photo);
return intent;
}
@@ -31,10 +30,10 @@ public class ClearProfileAvatarActivity extends Activity {
public void onResume() {
super.onResume();
int titleId = getIntent().getIntExtra(ARG_TITLE, R.string.ClearProfileActivity_remove_profile_photo);
int message = getIntent().getIntExtra(ARG_TITLE, 0);
new AlertDialog.Builder(new ContextThemeWrapper(this, DynamicTheme.isDarkTheme(this) ? R.style.TextSecure_DarkTheme : R.style.TextSecure_LightTheme))
.setMessage(titleId)
.setMessage(message)
.setNegativeButton(android.R.string.cancel, (dialog, which) -> finish())
.setPositiveButton(R.string.ClearProfileActivity_remove, (dialog, which) -> {
Intent result = new Intent();

View File

@@ -14,9 +14,7 @@ import androidx.appcompat.app.AlertDialog;
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.PushDatabase;
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;

View File

@@ -22,10 +22,10 @@ import android.os.Bundle;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
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;
@@ -99,7 +99,6 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActivit
private void initializeResources() {
contactsFragment = (ContactSelectionListFragment) getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
contactsFragment.setOnContactSelectedListener(this);
contactsFragment.setOnRefreshListener(this);
}

View File

@@ -21,6 +21,7 @@ import android.Manifest;
import android.animation.LayoutTransition;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -29,7 +30,6 @@ 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;
@@ -55,7 +55,9 @@ import com.annimon.stream.Stream;
import com.google.android.material.chip.ChipGroup;
import com.pnikosis.materialishprogress.ProgressWheel;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.components.RecyclerViewFastScroller;
import org.thoughtcrime.securesms.components.emoji.WarningTextView;
import org.thoughtcrime.securesms.contacts.ContactChip;
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter;
import org.thoughtcrime.securesms.contacts.ContactSelectionListItem;
@@ -63,7 +65,8 @@ 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.groups.SelectionLimits;
import org.thoughtcrime.securesms.groups.ui.GroupLimitDialog;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.permissions.Permissions;
@@ -82,7 +85,6 @@ 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;
/**
@@ -103,35 +105,42 @@ public final class ContactSelectionListFragment extends LoggingFragment
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 SELECTION_LIMITS = "selection_limits";
public static final String CURRENT_SELECTION = "current_selection";
public static final String HIDE_COUNT = "hide_count";
public static final String CAN_SELECT_SELF = "can_select_self";
public static final String DISPLAY_CHIPS = "display_chips";
private ConstraintLayout constraintLayout;
private TextView emptyText;
private OnContactSelectedListener onContactSelectedListener;
private SwipeRefreshLayout swipeRefresh;
private View showContactsLayout;
private Button showContactsButton;
private TextView showContactsDescription;
private ProgressWheel showContactsProgress;
private String cursorFilter;
private RecyclerView recyclerView;
private RecyclerViewFastScroller fastScroller;
private ContactSelectionListAdapter cursorRecyclerViewAdapter;
private ChipGroup chipGroup;
private HorizontalScrollView chipGroupScrollContainer;
private WarningTextView groupLimit;
private OnSelectionLimitReachedListener onSelectionLimitReachedListener;
private ConstraintLayout constraintLayout;
private TextView emptyText;
private OnContactSelectedListener onContactSelectedListener;
private SwipeRefreshLayout swipeRefresh;
private View showContactsLayout;
private Button showContactsButton;
private TextView showContactsDescription;
private ProgressWheel showContactsProgress;
private String cursorFilter;
private RecyclerView recyclerView;
private RecyclerViewFastScroller fastScroller;
private ContactSelectionListAdapter cursorRecyclerViewAdapter;
private ChipGroup chipGroup;
private HorizontalScrollView chipGroupScrollContainer;
private 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 SelectionLimits selectionLimit = SelectionLimits.NO_LIMITS;
private Set<RecipientId> currentSelection;
private boolean isMulti;
private boolean hideCount;
private boolean canSelectSelf;
@Override
public void onAttach(@NonNull Context context) {
@@ -144,6 +153,14 @@ public final class ContactSelectionListFragment extends LoggingFragment
if (context instanceof ScrollCallback) {
scrollCallback = (ScrollCallback) context;
}
if (context instanceof OnContactSelectedListener) {
onContactSelectedListener = (OnContactSelectedListener) context;
}
if (context instanceof OnSelectionLimitReachedListener) {
onSelectionLimitReachedListener = (OnSelectionLimitReachedListener) context;
}
}
@Override
@@ -206,9 +223,19 @@ public final class ContactSelectionListFragment extends LoggingFragment
}
});
swipeRefresh.setEnabled(requireActivity().getIntent().getBooleanExtra(REFRESHABLE, true));
Intent intent = requireActivity().getIntent();
swipeRefresh.setEnabled(intent.getBooleanExtra(REFRESHABLE, true));
hideCount = intent.getBooleanExtra(HIDE_COUNT, false);
selectionLimit = intent.getParcelableExtra(SELECTION_LIMITS);
isMulti = selectionLimit != null;
canSelectSelf = intent.getBooleanExtra(CAN_SELECT_SELF, !isMulti);
if (!isMulti) {
selectionLimit = SelectionLimits.NO_LIMITS;
}
selectionLimit = requireActivity().getIntent().getIntExtra(TOTAL_CAPACITY, NO_LIMIT);
currentSelection = getCurrentSelection();
updateGroupLimit(getChipCount());
@@ -217,12 +244,10 @@ public final class ContactSelectionListFragment extends LoggingFragment
}
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);
}
int members = currentSelection.size() + chipCount;
groupLimit.setText(getResources().getQuantityString(R.plurals.ContactSelectionListFragment_d_members, members, members));
groupLimit.setVisibility(isMulti && !hideCount ? View.VISIBLE : View.GONE);
groupLimit.setWarning(selectionWarningLimitExceeded());
}
@Override
@@ -254,7 +279,7 @@ public final class ContactSelectionListFragment extends LoggingFragment
}
public boolean isMulti() {
return requireActivity().getIntent().getBooleanExtra(MULTI_SELECT, false);
return isMulti;
}
private void initializeCursor() {
@@ -264,7 +289,7 @@ public final class ContactSelectionListFragment extends LoggingFragment
glideRequests,
null,
new ListClickListener(),
isMulti(),
isMulti,
currentSelection);
RecyclerViewConcatenateAdapterStickyHeader concatenateAdapter = new RecyclerViewConcatenateAdapterStickyHeader();
@@ -284,7 +309,7 @@ public final class ContactSelectionListFragment extends LoggingFragment
}
recyclerView.setAdapter(concatenateAdapter);
recyclerView.addItemDecoration(new StickyHeaderDecoration(concatenateAdapter, true, true));
recyclerView.addItemDecoration(new StickyHeaderDecoration(concatenateAdapter, true, true, 0));
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
@@ -437,8 +462,11 @@ public final class ContactSelectionListFragment extends LoggingFragment
swipeRefresh.setVisibility(View.VISIBLE);
reset();
} else {
Toast.makeText(getContext(), R.string.ContactSelectionListFragment_error_retrieving_contacts_check_your_network_connection, Toast.LENGTH_LONG).show();
initializeNoContactsPermission();
Context context = getContext();
if (context != null) {
Toast.makeText(getContext(), R.string.ContactSelectionListFragment_error_retrieving_contacts_check_your_network_connection, Toast.LENGTH_LONG).show();
initializeNoContactsPermission();
}
}
}
}.execute();
@@ -450,15 +478,18 @@ public final class ContactSelectionListFragment extends LoggingFragment
SelectedContact selectedContact = contact.isUsernameType() ? SelectedContact.forUsername(contact.getRecipientId().orNull(), contact.getNumber())
: SelectedContact.forPhone(contact.getRecipientId().orNull(), contact.getNumber());
if (isMulti() && Recipient.self().getId().equals(selectedContact.getOrCreateRecipientId(requireContext()))) {
if (!canSelectSelf && Recipient.self().getId().equals(selectedContact.getOrCreateRecipientId(requireContext()))) {
Toast.makeText(requireContext(), R.string.ContactSelectionListFragment_you_do_not_need_to_add_yourself_to_the_group, Toast.LENGTH_SHORT).show();
return;
}
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();
if (!isMulti || !cursorRecyclerViewAdapter.isSelectedContact(selectedContact)) {
if (selectionHardLimitReached()) {
if (onSelectionLimitReachedListener != null) {
onSelectionLimitReachedListener.onHardLimitReached(selectionLimit.getHardLimit());
} else {
GroupLimitDialog.showHardLimitMessage(requireContext());
}
return;
}
@@ -476,17 +507,17 @@ public final class ContactSelectionListFragment extends LoggingFragment
if (onContactSelectedListener != null) {
if (onContactSelectedListener.onBeforeContactSelected(Optional.of(recipient.getId()), null)) {
markContactSelected(selected);
cursorRecyclerViewAdapter.notifyItemChanged(recyclerView.getChildAdapterPosition(contact), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
cursorRecyclerViewAdapter.notifyItemRangeChanged(0, cursorRecyclerViewAdapter.getItemCount(), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
}
} else {
markContactSelected(selected);
cursorRecyclerViewAdapter.notifyItemChanged(recyclerView.getChildAdapterPosition(contact), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
cursorRecyclerViewAdapter.notifyItemRangeChanged(0, cursorRecyclerViewAdapter.getItemCount(), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
}
} 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())
.setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.dismiss())
.show();
}
});
@@ -494,30 +525,39 @@ public final class ContactSelectionListFragment extends LoggingFragment
if (onContactSelectedListener != null) {
if (onContactSelectedListener.onBeforeContactSelected(contact.getRecipientId(), contact.getNumber())) {
markContactSelected(selectedContact);
cursorRecyclerViewAdapter.notifyItemChanged(recyclerView.getChildAdapterPosition(contact), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
cursorRecyclerViewAdapter.notifyItemRangeChanged(0, cursorRecyclerViewAdapter.getItemCount(), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
}
} else {
markContactSelected(selectedContact);
cursorRecyclerViewAdapter.notifyItemChanged(recyclerView.getChildAdapterPosition(contact), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
cursorRecyclerViewAdapter.notifyItemRangeChanged(0, cursorRecyclerViewAdapter.getItemCount(), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
}
}
} else {
markContactUnselected(selectedContact);
cursorRecyclerViewAdapter.notifyItemChanged(recyclerView.getChildAdapterPosition(contact), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
cursorRecyclerViewAdapter.notifyItemRangeChanged(0, cursorRecyclerViewAdapter.getItemCount(), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
if (onContactSelectedListener != null) {
onContactSelectedListener.onContactDeselected(contact.getRecipientId(), contact.getNumber());
}
}}
}
}
}
private boolean selectionLimitReached() {
return getChipCount() + currentSelection.size() >= selectionLimit;
private boolean selectionHardLimitReached() {
return getChipCount() + currentSelection.size() >= selectionLimit.getHardLimit();
}
private boolean selectionWarningLimitReachedExactly() {
return getChipCount() + currentSelection.size() == selectionLimit.getRecommendedLimit();
}
private boolean selectionWarningLimitExceeded() {
return getChipCount() + currentSelection.size() > selectionLimit.getRecommendedLimit();
}
private void markContactSelected(@NonNull SelectedContact selectedContact) {
cursorRecyclerViewAdapter.addSelectedContact(selectedContact);
if (isMulti()) {
if (isMulti) {
addChipForSelectedContact(selectedContact);
}
}
@@ -588,6 +628,13 @@ public final class ContactSelectionListFragment extends LoggingFragment
private void addChip(@NonNull ContactChip chip) {
chipGroup.addView(chip);
updateGroupLimit(getChipCount());
if (selectionWarningLimitReachedExactly()) {
if (onSelectionLimitReachedListener != null) {
onSelectionLimitReachedListener.onSuggestedLimitReached(selectionLimit.getRecommendedLimit());
} else {
GroupLimitDialog.showRecommendedLimitMessage(requireContext());
}
}
}
private int getChipCount() {
@@ -608,6 +655,10 @@ public final class ContactSelectionListFragment extends LoggingFragment
}
private void setChipGroupVisibility(int visibility) {
if (!requireActivity().getIntent().getBooleanExtra(DISPLAY_CHIPS, true)) {
return;
}
TransitionManager.beginDelayedTransition(constraintLayout, new AutoTransition().setDuration(CHIP_GROUP_REVEAL_DURATION_MS));
ConstraintSet constraintSet = new ConstraintSet();
@@ -616,10 +667,6 @@ public final class ContactSelectionListFragment extends LoggingFragment
constraintSet.applyTo(constraintLayout);
}
public void setOnContactSelectedListener(OnContactSelectedListener onContactSelectedListener) {
this.onContactSelectedListener = onContactSelectedListener;
}
public void setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener onRefreshListener) {
this.swipeRefresh.setOnRefreshListener(onRefreshListener);
}
@@ -635,6 +682,11 @@ public final class ContactSelectionListFragment extends LoggingFragment
void onContactDeselected(Optional<RecipientId> recipientId, String number);
}
public interface OnSelectionLimitReachedListener {
void onSuggestedLimitReached(int limit);
void onHardLimitReached(int limit);
}
public interface ListCallback {
void onInvite();
void onNewGroup(boolean forceV1);

View File

@@ -9,6 +9,7 @@ import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Parcelable;
import android.view.View;
@@ -150,7 +151,7 @@ public class DatabaseMigrationActivity extends PassphraseRequiredActivity {
startActivity((Intent)getIntent().getParcelableExtra("next_intent"));
} else {
// TODO [greyson] Navigation
startActivity(new Intent(this, MainActivity.class));
startActivity(MainActivity.clearTop(this));
}
}
@@ -158,6 +159,11 @@ public class DatabaseMigrationActivity extends PassphraseRequiredActivity {
}
private class ImportStateHandler extends Handler {
public ImportStateHandler() {
super(Looper.getMainLooper());
}
@Override
public void handleMessage(Message message) {
switch (message.what) {

View File

@@ -18,10 +18,10 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import org.signal.core.util.logging.Log;
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.qr.ScanListener;
import org.thoughtcrime.securesms.util.Base64;

View File

@@ -5,8 +5,6 @@ import android.annotation.TargetApi;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewAnimationUtils;
@@ -15,6 +13,8 @@ import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.components.camera.CameraView;
import org.thoughtcrime.securesms.qr.ScanListener;
import org.thoughtcrime.securesms.qr.ScanningThread;
@@ -32,9 +32,9 @@ public class DeviceAddFragment extends LoggingFragment {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) {
this.container = ViewUtil.inflate(inflater, viewGroup, R.layout.device_add_fragment);
this.overlay = ViewUtil.findById(this.container, R.id.overlay);
this.scannerView = ViewUtil.findById(this.container, R.id.scanner);
this.devicesImage = ViewUtil.findById(this.container, R.id.devices);
this.overlay = this.container.findViewById(R.id.overlay);
this.scannerView = this.container.findViewById(R.id.scanner);
this.devicesImage = this.container.findViewById(R.id.devices);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
this.overlay.setOrientation(LinearLayout.HORIZONTAL);

View File

@@ -3,13 +3,14 @@ package org.thoughtcrime.securesms;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
public class DeviceLinkFragment extends Fragment implements View.OnClickListener {
private LinearLayout container;

View File

@@ -6,15 +6,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.ListFragment;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.appcompat.app.AlertDialog;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.devicelist.Device;
import org.thoughtcrime.securesms.logging.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -24,12 +15,20 @@ import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.ListFragment;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import com.melnykov.fab.FloatingActionButton;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.loaders.DeviceListLoader;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.devicelist.Device;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import java.io.IOException;
@@ -68,7 +67,7 @@ public class DeviceListFragment extends ListFragment
this.empty = view.findViewById(R.id.empty);
this.progressContainer = view.findViewById(R.id.progress_container);
this.addDeviceButton = ViewUtil.findById(view, R.id.add_device);
this.addDeviceButton = view.findViewById(R.id.add_device);
this.addDeviceButton.setOnClickListener(this);
return view;

View File

@@ -2,9 +2,10 @@ package org.thoughtcrime.securesms;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AlertDialog;
import android.view.Window;
import androidx.appcompat.app.AlertDialog;
public class DeviceProvisioningActivity extends PassphraseRequiredActivity {
@SuppressWarnings("unused")

View File

@@ -28,7 +28,7 @@ public final class GroupMembersDialog {
public void display() {
AlertDialog dialog = new AlertDialog.Builder(fragmentActivity)
.setTitle(R.string.ConversationActivity_group_members)
.setIconAttribute(R.attr.group_members_dialog_icon)
.setIcon(R.drawable.ic_group_24)
.setCancelable(true)
.setView(R.layout.dialog_group_members)
.setPositiveButton(android.R.string.ok, null)

View File

@@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChange
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.SelectedContact;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.groups.SelectionLimits;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.MessageSender;
@@ -34,6 +35,7 @@ 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.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.WindowUtil;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture.Listener;
@@ -62,7 +64,8 @@ public class InviteActivity extends PassphraseRequiredActivity implements Contac
@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.SELECTION_LIMITS, SelectionLimits.NO_LIMITS);
getIntent().putExtra(ContactSelectionListFragment.HIDE_COUNT, true);
getIntent().putExtra(ContactSelectionListFragment.REFRESHABLE, false);
setContentView(R.layout.invite_activity);
@@ -91,26 +94,32 @@ public class InviteActivity extends PassphraseRequiredActivity implements Contac
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);
View shareButton = findViewById(R.id.share_button);
Button smsButton = findViewById(R.id.sms_button);
Button smsCancelButton = findViewById(R.id.cancel_sms_button);
ContactFilterToolbar contactFilter = findViewById(R.id.contact_filter);
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);
inviteText = findViewById(R.id.invite_text);
smsSendFrame = findViewById(R.id.sms_send_frame);
smsSendButton = findViewById(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.getSelectedContacts().size());
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);
if (Util.isDefaultSmsProvider(this)) {
shareButton.setOnClickListener(new ShareClickListener());
smsButton.setOnClickListener(new SmsClickListener());
} else {
shareButton.setVisibility(View.GONE);
smsButton.setOnClickListener(new ShareClickListener());
smsButton.setText(R.string.InviteActivity_share);
}
}
private Animation loadAnimation(@AnimRes int animResId) {
@@ -162,10 +171,10 @@ public class InviteActivity extends PassphraseRequiredActivity implements Contac
private void setPrimaryColorsToolbarNormal() {
primaryToolbar.setBackgroundColor(0);
primaryToolbar.getNavigationIcon().setColorFilter(null);
primaryToolbar.setTitleTextColor(ThemeUtil.getThemedColor(this, R.attr.title_text_color_primary));
primaryToolbar.setTitleTextColor(ContextCompat.getColor(this, R.color.signal_text_primary));
if (Build.VERSION.SDK_INT >= 23) {
getWindow().setStatusBarColor(ThemeUtil.getThemedColor(this, android.R.attr.statusBarColor));
WindowUtil.setStatusBarColor(getWindow(), ThemeUtil.getThemedColor(this, android.R.attr.statusBarColor));
getWindow().setNavigationBarColor(ThemeUtil.getThemedColor(this, android.R.attr.navigationBarColor));
WindowUtil.setLightStatusBarFromTheme(this);
}
@@ -175,11 +184,11 @@ public class InviteActivity extends PassphraseRequiredActivity implements Contac
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));
primaryToolbar.getNavigationIcon().setColorFilter(ContextCompat.getColor(this, R.color.signal_text_toolbar_subtitle), PorterDuff.Mode.SRC_IN);
primaryToolbar.setTitleTextColor(ContextCompat.getColor(this, R.color.signal_text_toolbar_title));
if (Build.VERSION.SDK_INT >= 23) {
getWindow().setStatusBarColor(ContextCompat.getColor(this, R.color.core_ultramarine));
WindowUtil.setStatusBarColor(getWindow(), ContextCompat.getColor(this, R.color.core_ultramarine));
WindowUtil.clearLightStatusBar(getWindow());
}

View File

@@ -0,0 +1,49 @@
package org.thoughtcrime.securesms;
import androidx.annotation.NonNull;
import java.util.Objects;
/**
* Used in our {@link BuildConfig} to tie together the various attributes of a KBS instance. This
* is sitting in the root directory so it can be accessed by the build config.
*/
public final class KbsEnclave {
private final String enclaveName;
private final String serviceId;
private final String mrEnclave;
public KbsEnclave(@NonNull String enclaveName, @NonNull String serviceId, @NonNull String mrEnclave) {
this.enclaveName = enclaveName;
this.serviceId = serviceId;
this.mrEnclave = mrEnclave;
}
public @NonNull String getMrEnclave() {
return mrEnclave;
}
public @NonNull String getEnclaveName() {
return enclaveName;
}
public @NonNull String getServiceId() {
return serviceId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
KbsEnclave enclave = (KbsEnclave) o;
return enclaveName.equals(enclave.enclaveName) &&
serviceId.equals(enclave.serviceId) &&
mrEnclave.equals(enclave.mrEnclave);
}
@Override
public int hashCode() {
return Objects.hash(enclaveName, serviceId, mrEnclave);
}
}

View File

@@ -6,7 +6,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import org.thoughtcrime.securesms.logging.Log;
import org.signal.core.util.logging.Log;
/**
* Simply logs out lifecycle events.

View File

@@ -1,28 +1,55 @@
package org.thoughtcrime.securesms;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.util.AppStartup;
import org.thoughtcrime.securesms.util.CachedInflater;
import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
public class MainActivity extends PassphraseRequiredActivity {
public static final int RESULT_CONFIG_CHANGED = Activity.RESULT_FIRST_USER + 901;
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
private final MainNavigator navigator = new MainNavigator(this);
public static @NonNull Intent clearTop(@NonNull Context context) {
Intent intent = new Intent(context, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
return intent;
}
@Override
protected void onCreate(Bundle savedInstanceState, boolean ready) {
AppStartup.getInstance().onCriticalRenderEventStart();
super.onCreate(savedInstanceState, ready);
setContentView(R.layout.main_activity);
navigator.onCreate(savedInstanceState);
handleGroupLinkInIntent(getIntent());
CachedInflater.from(this).clear();
}
@Override
public Intent getIntent() {
return super.getIntent().setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
}
@Override
@@ -50,6 +77,14 @@ public class MainActivity extends PassphraseRequiredActivity {
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == MainNavigator.REQUEST_CONFIG_CHANGES && resultCode == RESULT_CONFIG_CHANGED) {
recreate();
}
}
public @NonNull MainNavigator getNavigator() {
return navigator;
}

View File

@@ -9,7 +9,7 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import org.thoughtcrime.securesms.conversation.ConversationActivity;
import org.thoughtcrime.securesms.conversation.ConversationIntents;
import org.thoughtcrime.securesms.conversationlist.ConversationListArchiveFragment;
import org.thoughtcrime.securesms.conversationlist.ConversationListFragment;
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity;
@@ -18,6 +18,8 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
public class MainNavigator {
public static final int REQUEST_CONFIG_CHANGES = 901;
private final MainActivity activity;
public MainNavigator(@NonNull MainActivity activity) {
@@ -57,7 +59,10 @@ public class MainNavigator {
}
public void goToConversation(@NonNull RecipientId recipientId, long threadId, int distributionType, int startingPosition) {
Intent intent = ConversationActivity.buildIntent(activity, recipientId, threadId, distributionType, startingPosition);
Intent intent = ConversationIntents.createBuilder(activity, recipientId, threadId)
.withDistributionType(distributionType)
.withStartingPosition(startingPosition)
.build();
activity.startActivity(intent);
activity.overridePendingTransition(R.anim.slide_from_end, R.anim.fade_scale_out);
@@ -65,10 +70,9 @@ public class MainNavigator {
public void goToAppSettings() {
Intent intent = new Intent(activity, ApplicationPreferencesActivity.class);
activity.startActivity(intent);
activity.startActivityForResult(intent, REQUEST_CONFIG_CHANGES);
}
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)

View File

@@ -18,12 +18,14 @@ package org.thoughtcrime.securesms;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
@@ -31,14 +33,14 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.app.ShareCompat;
import androidx.core.util.Pair;
import androidx.core.view.ViewCompat;
import androidx.fragment.app.Fragment;
@@ -51,26 +53,29 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.ViewPager;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.animation.DepthPageTransformer;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.components.viewpager.ExtendedOnPageChangedListener;
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;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.PartAuthority;
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.FullscreenHelper;
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment;
import org.thoughtcrime.securesms.util.StorageUtil;
import java.util.HashMap;
import java.util.Locale;
@@ -119,6 +124,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
private boolean cameFromAllMedia;
private boolean showThread;
private MediaDatabase.Sorting sorting;
private FullscreenHelper fullscreenHelper;
private @Nullable Cursor cursor = null;
@@ -133,10 +139,16 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, attachment.getSize());
intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, attachment.getCaption());
intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, leftIsRecent);
intent.setDataAndType(attachment.getDataUri(), mediaRecord.getContentType());
intent.setDataAndType(attachment.getUri(), mediaRecord.getContentType());
return intent;
}
@Override
protected void attachBaseContext(@NonNull Context newBase) {
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);
super.attachBaseContext(newBase);
}
@SuppressWarnings("ConstantConditions")
@Override
protected void onCreate(Bundle bundle, boolean ready) {
@@ -147,10 +159,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
viewModel = ViewModelProviders.of(this).get(MediaPreviewViewModel.class);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
showSystemUI();
fullscreenHelper = new FullscreenHelper(this);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@@ -196,7 +205,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
if (threadRecipient != null) {
if (mediaItem.outgoing || threadRecipient.isGroup()) {
if (threadRecipient.isLocalNumber()) {
if (threadRecipient.isSelf()) {
from = getString(R.string.note_to_self);
} else {
to = threadRecipient.getDisplayName(this);
@@ -261,6 +270,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
albumRail = findViewById(R.id.media_preview_album_rail);
albumRailAdapter = new MediaRailAdapter(GlideApp.with(this), this, false);
albumRail.setItemAnimator(null); // Or can crash when set to INVISIBLE while animating by FullscreenHelper https://issuetracker.google.com/issues/148720682
albumRail.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
albumRail.setAdapter(albumRailAdapter);
@@ -273,9 +283,9 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
anchorMarginsToBottomInsets(detailsContainer);
anchorMarginsToTopInsets(toolbarLayout);
fullscreenHelper.configureToolbarSpacer(findViewById(R.id.toolbar_cutout_spacer));
showAndHideWithSystemUI(getWindow(), detailsContainer, toolbarLayout);
fullscreenHelper.showAndHideWithSystemUI(getWindow(), detailsContainer, toolbarLayout);
}
private void initializeResources() {
@@ -379,6 +389,27 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
}
}
private void share() {
MediaItem mediaItem = getCurrentMediaItem();
if (mediaItem != null) {
Uri publicUri = PartAuthority.getAttachmentPublicUri(mediaItem.uri);
String mimeType = Intent.normalizeMimeType(mediaItem.type);
Intent shareIntent = ShareCompat.IntentBuilder.from(this)
.setStream(publicUri)
.setType(mimeType)
.createChooserIntent()
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
startActivity(shareIntent);
} catch (ActivityNotFoundException e) {
Log.w(TAG, "No activity existed to share the media.", e);
Toast.makeText(this, R.string.MediaPreviewActivity_cant_find_an_app_able_to_share_this_media, Toast.LENGTH_LONG).show();
}
}
}
@SuppressWarnings("CodeBlock2Expr")
@SuppressLint("InlinedApi")
private void saveToDisk() {
@@ -386,21 +417,30 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
if (mediaItem != null) {
SaveAttachmentTask.showWarningDialog(this, (dialogInterface, i) -> {
if (StorageUtil.canWriteToMediaStore()) {
performSavetoDisk(mediaItem);
return;
}
Permissions.with(this)
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.ifNecessary()
.withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied))
.onAnyDenied(() -> Toast.makeText(this, R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show())
.onAllGranted(() -> {
SaveAttachmentTask saveTask = new SaveAttachmentTask(MediaPreviewActivity.this);
long saveDate = (mediaItem.date > 0) ? mediaItem.date : System.currentTimeMillis();
saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new Attachment(mediaItem.uri, mediaItem.type, saveDate, null));
performSavetoDisk(mediaItem);
})
.execute();
});
}
}
private void performSavetoDisk(@NonNull MediaItem mediaItem) {
SaveAttachmentTask saveTask = new SaveAttachmentTask(MediaPreviewActivity.this);
long saveDate = (mediaItem.date > 0) ? mediaItem.date : System.currentTimeMillis();
saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new Attachment(mediaItem.uri, mediaItem.type, saveDate, null));
}
@SuppressLint("StaticFieldLeak")
private void deleteMedia() {
MediaItem mediaItem = getCurrentMediaItem();
@@ -409,7 +449,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setIconAttribute(R.attr.dialog_alert_icon);
builder.setIcon(R.drawable.ic_warning);
builder.setTitle(R.string.MediaPreviewActivity_media_delete_confirmation_title);
builder.setMessage(R.string.MediaPreviewActivity_media_delete_confirmation_message);
builder.setCancelable(true);
@@ -447,6 +487,9 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
menu.findItem(R.id.delete).setVisible(false);
}
// Restricted to API26 because of MemoryFileUtil not supporting lower API levels well
menu.findItem(R.id.media_preview__share).setVisible(Build.VERSION.SDK_INT >= 26);
if (cameFromAllMedia) {
menu.findItem(R.id.media_preview__overview).setVisible(false);
}
@@ -456,16 +499,17 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.media_preview__overview: showOverview(); return true;
case R.id.media_preview__forward: forward(); return true;
case R.id.save: saveToDisk(); return true;
case R.id.delete: deleteMedia(); return true;
case android.R.id.home: finish(); return true;
}
int itemId = item.getItemId();
if (itemId == R.id.media_preview__overview) { showOverview(); return true; }
if (itemId == R.id.media_preview__forward) { forward(); return true; }
if (itemId == R.id.media_preview__share) { share(); return true; }
if (itemId == R.id.save) { saveToDisk(); return true; }
if (itemId == R.id.delete) { deleteMedia(); return true; }
if (itemId == android.R.id.home) { finish(); return true; }
return false;
}
@@ -546,7 +590,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
@Override
public boolean singleTapOnMedia() {
toggleUiVisibility();
fullscreenHelper.toggleUiVisibility();
return true;
}
@@ -556,32 +600,6 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
finish();
}
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 );
}
private class ViewPagerListener extends ExtendedOnPageChangedListener {
@Override
@@ -697,33 +715,6 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
});
}
private static void anchorMarginsToTopInsets(@NonNull View viewToAnchor) {
ViewCompat.setOnApplyWindowInsetsListener(viewToAnchor, (view, insets) -> {
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
layoutParams.setMargins(insets.getSystemWindowInsetLeft(),
insets.getSystemWindowInsetTop(),
insets.getSystemWindowInsetRight(),
layoutParams.bottomMargin);
view.setLayoutParams(layoutParams);
return insets;
});
}
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 static class CursorPagerAdapter extends FragmentStatePagerAdapter implements MediaItemAdapter {
@SuppressLint("UseSparseArrays")
@@ -801,7 +792,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
return new MediaItem(Recipient.live(recipientId).get(),
Recipient.live(threadRecipientId).get(),
attachment,
Objects.requireNonNull(attachment.getDataUri()),
Objects.requireNonNull(attachment.getUri()),
mediaRecord.getContentType(),
mediaRecord.getDate(),
mediaRecord.isOutgoing());

View File

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

View File

@@ -23,16 +23,15 @@ import android.view.MenuItem;
import androidx.appcompat.app.AlertDialog;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
import org.thoughtcrime.securesms.conversation.ConversationActivity;
import org.thoughtcrime.securesms.conversation.ConversationIntents;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
import org.whispersystems.libsignal.util.guava.Optional;
@@ -65,16 +64,17 @@ public class NewConversationActivity extends ContactSelectionActivity
launch(Recipient.resolved(recipientId.get()));
} else {
Log.i(TAG, "[onContactSelected] Maybe creating a new recipient.");
if (FeatureFlags.cds() && NetworkConstraint.isMet(this)) {
Log.i(TAG, "[onContactSelected] CDS enabled. Doing contact refresh.");
if (TextSecurePreferences.isPushRegistered(this) && NetworkConstraint.isMet(this)) {
Log.i(TAG, "[onContactSelected] Doing contact refresh.");
AlertDialog progress = SimpleProgressDialog.show(this);
SimpleTask.run(getLifecycle(), () -> {
Recipient resolved = Recipient.external(this, number);
if (!resolved.isRegistered()) {
Log.i(TAG, "[onContactSelected] Not registered. Doing a directory refresh.");
if (!resolved.isRegistered() || !resolved.hasUuid()) {
Log.i(TAG, "[onContactSelected] Not registered or no UUID. Doing a directory refresh.");
try {
DirectoryHelper.refreshDirectoryFor(this, resolved, false);
resolved = Recipient.resolved(resolved.getId());
@@ -97,15 +97,13 @@ public class NewConversationActivity extends ContactSelectionActivity
}
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));
intent.setDataAndType(getIntent().getData(), getIntent().getType());
long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient.getId());
Intent intent = ConversationIntents.createBuilder(this, recipient.getId(), existingThread)
.withDraftText(getIntent().getStringExtra(Intent.EXTRA_TEXT))
.withDataUri(getIntent().getData())
.withDataType(getIntent().getType())
.build();
long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, existingThread);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
startActivity(intent);
finish();
}

View File

@@ -21,8 +21,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import org.thoughtcrime.securesms.logging.Log;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.service.KeyCachingService;

View File

@@ -20,12 +20,12 @@ import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import org.thoughtcrime.securesms.logging.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.crypto.InvalidPassphraseException;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;

View File

@@ -22,8 +22,6 @@ import android.os.Bundle;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.VersionTracker;
/**
@@ -66,12 +64,6 @@ public class PassphraseCreateActivity extends PassphraseActivity {
IdentityKeyUtil.generateIdentityKeys(PassphraseCreateActivity.this);
VersionTracker.updateLastSeenVersion(PassphraseCreateActivity.this);
TextSecurePreferences.setLastExperienceVersionCode(PassphraseCreateActivity.this, Util.getCanonicalVersionCode());
TextSecurePreferences.setPasswordDisabled(PassphraseCreateActivity.this, true);
TextSecurePreferences.setReadReceiptsEnabled(PassphraseCreateActivity.this, true);
TextSecurePreferences.setTypingIndicatorsEnabled(PassphraseCreateActivity.this, true);
TextSecurePreferences.setHasSeenWelcomeScreen(PassphraseCreateActivity.this, false);
return null;
}

View File

@@ -24,16 +24,12 @@ import android.content.Intent;
import android.graphics.PorterDuff;
import android.os.Build;
import android.os.Bundle;
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
import androidx.core.os.CancellationSignal;
import androidx.appcompat.widget.Toolbar;
import android.text.Editable;
import android.text.InputType;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.RelativeSizeSpan;
import android.text.style.TypefaceSpan;
import org.thoughtcrime.securesms.logging.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
@@ -50,6 +46,11 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.widget.Toolbar;
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
import androidx.core.os.CancellationSignal;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
import org.thoughtcrime.securesms.components.AnimatingToggle;
import org.thoughtcrime.securesms.crypto.InvalidPassphraseException;

View File

@@ -11,12 +11,13 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import org.signal.core.util.logging.Log;
import org.signal.core.util.tracing.Tracer;
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;
@@ -25,6 +26,7 @@ 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.AppStartup;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.util.Locale;
@@ -49,6 +51,8 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
@Override
protected final void onCreate(Bundle savedInstanceState) {
Tracer.getInstance().start(Log.tag(getClass()) + "#onCreate()");
AppStartup.getInstance().onCriticalRenderEventStart();
this.networkAccess = new SignalServiceNetworkAccess(this);
onPreCreate();
@@ -61,6 +65,9 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
initializeClearKeyReceiver();
onCreate(savedInstanceState, true);
}
AppStartup.getInstance().onCriticalRenderEventEnd();
Tracer.getInstance().end(Log.tag(getClass()) + "#onCreate()");
}
protected void onPreCreate() {}
@@ -218,15 +225,17 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
private Intent getConversationListIntent() {
// TODO [greyson] Navigation
return new Intent(this, MainActivity.class);
return MainActivity.clearTop(this);
}
private void initializeClearKeyReceiver() {
this.clearKeyReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "onReceive() for clear key event");
onMasterSecretCleared();
Log.i(TAG, "onReceive() for clear key event. PasswordDisabled: " + TextSecurePreferences.isPasswordDisabled(context) + ", ScreenLock: " + TextSecurePreferences.isScreenLockEnabled(context));
if (TextSecurePreferences.isScreenLockEnabled(context) || !TextSecurePreferences.isPasswordDisabled(context)) {
onMasterSecretCleared();
}
}
};

View File

@@ -17,6 +17,7 @@
package org.thoughtcrime.securesms;
import android.os.Bundle;
import androidx.fragment.app.FragmentActivity;
public class PlayServicesProblemActivity extends FragmentActivity {

View File

@@ -21,10 +21,11 @@ import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import com.google.android.gms.common.GoogleApiAvailability;

View File

@@ -42,7 +42,6 @@ public class PushContactSelectionActivity extends ContactSelectionActivity {
@Override
protected void onCreate(Bundle icicle, boolean ready) {
getIntent().putExtra(ContactSelectionListFragment.MULTI_SELECT, true);
super.onCreate(icicle, ready);
initializeToolbar();

View File

@@ -3,11 +3,12 @@ package org.thoughtcrime.securesms;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.TaskStackBuilder;
import androidx.appcompat.app.AppCompatActivity;
import android.widget.Toast;
import androidx.core.app.TaskStackBuilder;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
@@ -34,7 +35,7 @@ public class ShortcutLauncherActivity extends AppCompatActivity {
if (rawId == null) {
Toast.makeText(this, R.string.ShortcutLauncherActivity_invalid_shortcut, Toast.LENGTH_SHORT).show();
// TODO [greyson] Navigation
startActivity(new Intent(this, MainActivity.class));
startActivity(MainActivity.clearTop(this));
finish();
return;
}
@@ -42,7 +43,7 @@ public class ShortcutLauncherActivity extends AppCompatActivity {
Recipient recipient = Recipient.live(RecipientId.from(rawId)).get();
// TODO [greyson] Navigation
TaskStackBuilder backStack = TaskStackBuilder.create(this)
.addNextIntent(new Intent(this, MainActivity.class));
.addNextIntent(MainActivity.clearTop(this));
CommunicationActions.startConversation(this, recipient, null, backStack);
finish();

View File

@@ -5,13 +5,13 @@ import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import androidx.annotation.NonNull;
import android.text.TextUtils;
import org.thoughtcrime.securesms.conversation.ConversationActivity;
import org.thoughtcrime.securesms.logging.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.conversation.ConversationIntents;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Rfc5724Uri;
@@ -44,16 +44,15 @@ public class SmsSendtoActivity extends Activity {
if (TextUtils.isEmpty(destination.destination)) {
nextIntent = new Intent(this, NewConversationActivity.class);
nextIntent.putExtra(ConversationActivity.TEXT_EXTRA, destination.getBody());
nextIntent.putExtra(Intent.EXTRA_TEXT, destination.getBody());
Toast.makeText(this, R.string.ConversationActivity_specify_recipient, Toast.LENGTH_LONG).show();
} else {
Recipient recipient = Recipient.external(this, destination.getDestination());
long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient);
long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient.getId());
nextIntent = new Intent(this, ConversationActivity.class);
nextIntent.putExtra(ConversationActivity.TEXT_EXTRA, destination.getBody());
nextIntent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
nextIntent.putExtra(ConversationActivity.RECIPIENT_EXTRA, recipient.getId());
nextIntent = ConversationIntents.createBuilder(this, recipient.getId(), threadId)
.withDraftText(destination.getBody())
.build();
}
return nextIntent;
}

View File

@@ -2,9 +2,10 @@ package org.thoughtcrime.securesms;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import android.text.TextUtils;
import org.thoughtcrime.securesms.util.CharacterCalculator;
import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState;

View File

@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms;
import android.Manifest;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

View File

@@ -2,7 +2,6 @@ package org.thoughtcrime.securesms;
import android.content.Context;
import android.graphics.PorterDuff.Mode;
import androidx.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -10,7 +9,7 @@ import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import org.thoughtcrime.securesms.util.ViewUtil;
import androidx.annotation.NonNull;
import java.util.List;
@@ -54,9 +53,9 @@ public class TransportOptionsAdapter extends BaseAdapter {
}
TransportOption transport = (TransportOption) getItem(position);
ImageView imageView = ViewUtil.findById(convertView, R.id.icon);
TextView textView = ViewUtil.findById(convertView, R.id.text);
TextView subtextView = ViewUtil.findById(convertView, R.id.subtext);
ImageView imageView = convertView.findViewById(R.id.icon);
TextView textView = convertView.findViewById(R.id.text);
TextView subtextView = convertView.findViewById(R.id.subtext);
imageView.getBackground().setColorFilter(transport.getBackgroundColor(), Mode.MULTIPLY);
imageView.setImageResource(transport.getDrawable());

View File

@@ -1,12 +1,13 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.ListPopupWindow;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.ListPopupWindow;
import java.util.LinkedList;
import java.util.List;

View File

@@ -31,7 +31,6 @@ import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Vibrator;
import android.text.Html;
@@ -56,10 +55,12 @@ import android.widget.Toast;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SwitchCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.components.camera.CameraView;
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
@@ -69,7 +70,6 @@ import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.qr.QrCode;
import org.thoughtcrime.securesms.qr.ScanListener;
@@ -85,6 +85,7 @@ import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.WindowUtil;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.fingerprint.Fingerprint;
import org.whispersystems.libsignal.fingerprint.FingerprintParsingException;
@@ -224,9 +225,7 @@ public class VerifyIdentityActivity extends PassphraseRequiredActivity implement
private void setActionBarNotificationBarColor(MaterialColor color) {
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(color.toActionBarColor(this)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setStatusBarColor(color.toStatusBarColor(this));
}
WindowUtil.setStatusBarColor(getWindow(), color.toStatusBarColor(this));
}
public static class VerifyDisplayFragment extends Fragment implements CompoundButton.OnCheckedChangeListener {
@@ -259,24 +258,24 @@ public class VerifyIdentityActivity extends PassphraseRequiredActivity implement
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) {
this.container = ViewUtil.inflate(inflater, viewGroup, R.layout.verify_display_fragment);
this.numbersContainer = ViewUtil.findById(container, R.id.number_table);
this.qrCode = ViewUtil.findById(container, R.id.qr_code);
this.verified = ViewUtil.findById(container, R.id.verified_switch);
this.qrVerified = ViewUtil.findById(container, R.id.qr_verified);
this.description = ViewUtil.findById(container, R.id.description);
this.tapLabel = ViewUtil.findById(container, R.id.tap_label);
this.codes[0] = ViewUtil.findById(container, R.id.code_first);
this.codes[1] = ViewUtil.findById(container, R.id.code_second);
this.codes[2] = ViewUtil.findById(container, R.id.code_third);
this.codes[3] = ViewUtil.findById(container, R.id.code_fourth);
this.codes[4] = ViewUtil.findById(container, R.id.code_fifth);
this.codes[5] = ViewUtil.findById(container, R.id.code_sixth);
this.codes[6] = ViewUtil.findById(container, R.id.code_seventh);
this.codes[7] = ViewUtil.findById(container, R.id.code_eighth);
this.codes[8] = ViewUtil.findById(container, R.id.code_ninth);
this.codes[9] = ViewUtil.findById(container, R.id.code_tenth);
this.codes[10] = ViewUtil.findById(container, R.id.code_eleventh);
this.codes[11] = ViewUtil.findById(container, R.id.code_twelth);
this.numbersContainer = container.findViewById(R.id.number_table);
this.qrCode = container.findViewById(R.id.qr_code);
this.verified = container.findViewById(R.id.verified_switch);
this.qrVerified = container.findViewById(R.id.qr_verified);
this.description = container.findViewById(R.id.description);
this.tapLabel = container.findViewById(R.id.tap_label);
this.codes[0] = container.findViewById(R.id.code_first);
this.codes[1] = container.findViewById(R.id.code_second);
this.codes[2] = container.findViewById(R.id.code_third);
this.codes[3] = container.findViewById(R.id.code_fourth);
this.codes[4] = container.findViewById(R.id.code_fifth);
this.codes[5] = container.findViewById(R.id.code_sixth);
this.codes[6] = container.findViewById(R.id.code_seventh);
this.codes[7] = container.findViewById(R.id.code_eighth);
this.codes[8] = container.findViewById(R.id.code_ninth);
this.codes[9] = container.findViewById(R.id.code_tenth);
this.codes[10] = container.findViewById(R.id.code_eleventh);
this.codes[11] = container.findViewById(R.id.code_twelth);
this.qrCode.setOnClickListener(clickListener);
this.registerForContextMenu(numbersContainer);
@@ -307,16 +306,26 @@ public class VerifyIdentityActivity extends PassphraseRequiredActivity implement
byte[] localId;
byte[] remoteId;
if (FeatureFlags.verifyV2() && recipient.resolve().getUuid().isPresent()) {
Recipient resolved = recipient.resolve();
if (FeatureFlags.verifyV2() && resolved.getUuid().isPresent()) {
Log.i(TAG, "Using UUID (version 2).");
version = 2;
localId = UuidUtil.toByteArray(TextSecurePreferences.getLocalUuid(requireContext()));
remoteId = UuidUtil.toByteArray(recipient.resolve().getUuid().get());
} else {
remoteId = UuidUtil.toByteArray(resolved.getUuid().get());
} else if (!FeatureFlags.verifyV2() && resolved.getE164().isPresent()) {
Log.i(TAG, "Using E164 (version 1).");
version = 1;
localId = TextSecurePreferences.getLocalNumber(requireContext()).getBytes();
remoteId = recipient.resolve().requireE164().getBytes();
remoteId = resolved.requireE164().getBytes();
} else {
Log.w(TAG, String.format(Locale.ENGLISH, "Could not show proper verification! verifyV2: %s, hasUuid: %s, hasE164: %s", FeatureFlags.verifyV2(), resolved.getUuid().isPresent(), resolved.getE164().isPresent()));
new AlertDialog.Builder(requireContext())
.setMessage(getString(R.string.VerifyIdentityActivity_you_must_first_exchange_messages_in_order_to_view, resolved.getDisplayName(requireContext())))
.setPositiveButton(android.R.string.ok, (dialog, which) -> requireActivity().finish())
.setOnDismissListener(dialog -> requireActivity().finish())
.show();
return;
}
this.recipient.observe(this, this::setRecipientText);
@@ -655,7 +664,7 @@ public class VerifyIdentityActivity extends PassphraseRequiredActivity implement
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) {
this.container = ViewUtil.inflate(inflater, viewGroup, R.layout.verify_scan_fragment);
this.cameraView = ViewUtil.findById(container, R.id.scanner);
this.cameraView = container.findViewById(R.id.scanner);
return container;
}

View File

@@ -19,8 +19,7 @@ package org.thoughtcrime.securesms;
import android.Manifest;
import android.app.PictureInPictureParams;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
@@ -28,42 +27,56 @@ import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Rational;
import android.view.View;
import android.view.Window;
import android.view.WindowInsetsController;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProviders;
import androidx.transition.Transition;
import androidx.transition.TransitionListenerAdapter;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.components.TooltipPopup;
import org.thoughtcrime.securesms.components.webrtc.CallParticipantsListUpdatePopupWindow;
import org.thoughtcrime.securesms.components.webrtc.CallParticipantsState;
import org.thoughtcrime.securesms.components.webrtc.GroupCallSafetyNumberChangeNotificationUtil;
import org.thoughtcrime.securesms.components.webrtc.WebRtcAudioOutput;
import org.thoughtcrime.securesms.components.webrtc.WebRtcCallView;
import org.thoughtcrime.securesms.components.webrtc.WebRtcCallViewModel;
import org.thoughtcrime.securesms.components.webrtc.participantslist.CallParticipantsListDialog;
import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog;
import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.messagerequests.CalleeMustAcceptMessageRequestActivity;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.ringrtc.RemotePeer;
import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.EllapsedTimeFormatter;
import org.thoughtcrime.securesms.util.FullscreenHelper;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.WindowUtil;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.signalservice.api.messages.calls.HangupMessage;
import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumberChangeDialog.Callback {
import java.util.List;
public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChangeDialog.Callback {
private static final String TAG = WebRtcCallActivity.class.getSimpleName();
private static final String TAG = Log.tag(WebRtcCallActivity.class);
private static final int STANDARD_DELAY_FINISH = 1000;
private static final int STANDARD_DELAY_FINISH = 1000;
public static final String ANSWER_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".ANSWER_ACTION";
public static final String DENY_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".DENY_ACTION";
@@ -71,11 +84,20 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
public static final String EXTRA_ENABLE_VIDEO_IF_AVAILABLE = WebRtcCallActivity.class.getCanonicalName() + ".ENABLE_VIDEO_IF_AVAILABLE";
private CallParticipantsListUpdatePopupWindow participantUpdateWindow;
private FullscreenHelper fullscreenHelper;
private WebRtcCallView callScreen;
private TooltipPopup videoTooltip;
private WebRtcCallViewModel viewModel;
private boolean enableVideoIfAvailable;
@Override
protected void attachBaseContext(@NonNull Context newBase) {
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);
super.attachBaseContext(newBase);
}
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate()");
@@ -85,7 +107,8 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.webrtc_call_activity);
getSupportActionBar().hide();
fullscreenHelper = new FullscreenHelper(this);
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
@@ -110,7 +133,7 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
}
@Override
public void onNewIntent(Intent intent){
public void onNewIntent(Intent intent) {
Log.i(TAG, "onNewIntent");
super.onNewIntent(intent);
processIntent(intent);
@@ -124,6 +147,13 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
if (!isInPipMode()) {
EventBus.getDefault().unregister(this);
}
if (!viewModel.isCallStarting()) {
CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
if (state != null && state.getCallState().isPreJoinOrNetworkUnavailable()) {
finish();
}
}
}
@Override
@@ -132,11 +162,15 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
super.onStop();
EventBus.getDefault().unregister(this);
}
@Override
public void onConfigurationChanged(Configuration newConfiguration) {
super.onConfigurationChanged(newConfiguration);
if (!viewModel.isCallStarting()) {
CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
if (state != null && state.getCallState().isPreJoinOrNetworkUnavailable()) {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_CANCEL_PRE_JOIN_CALL);
startService(intent);
}
}
}
@Override
@@ -159,14 +193,17 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
viewModel.setIsInPipMode(isInPictureInPictureMode);
participantUpdateWindow.setEnabled(!isInPictureInPictureMode);
}
private boolean enterPipModeIfPossible() {
if (isSystemPipEnabledAndAvailable()) {
if (viewModel.canEnterPipMode() && isSystemPipEnabledAndAvailable()) {
PictureInPictureParams params = new PictureInPictureParams.Builder()
.setAspectRatio(new Rational(9, 16))
.build();
enterPictureInPictureMode(params);
CallParticipantsListDialog.dismiss(getSupportFragmentManager());
return true;
}
return false;
@@ -196,48 +233,67 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
}
private void initializeResources() {
callScreen = ViewUtil.findById(this, R.id.callScreen);
callScreen = findViewById(R.id.callScreen);
callScreen.setControlsListener(new ControlsListener());
participantUpdateWindow = new CallParticipantsListUpdatePopupWindow(callScreen);
}
private void initializeViewModel() {
viewModel = ViewModelProviders.of(this).get(WebRtcCallViewModel.class);
viewModel.setIsInPipMode(isInPipMode());
viewModel.getRemoteVideoEnabled().observe(this,callScreen::setRemoteVideoEnabled);
viewModel.getMicrophoneEnabled().observe(this, callScreen::setMicEnabled);
viewModel.getCameraDirection().observe(this, callScreen::setCameraDirection);
viewModel.getLocalRenderState().observe(this, callScreen::setLocalRenderState);
viewModel.getWebRtcControls().observe(this, callScreen::setWebRtcControls);
viewModel.getEvents().observe(this, this::handleViewModelEvent);
viewModel.getCallTime().observe(this, this::handleCallTime);
viewModel.displaySquareCallCard().observe(this, callScreen::showCallCard);
viewModel.getCallParticipantsState().observe(this, callScreen::updateCallParticipants);
viewModel.getCallParticipantListUpdate().observe(this, participantUpdateWindow::addCallParticipantListUpdate);
viewModel.getSafetyNumberChangeEvent().observe(this, this::handleSafetyNumberChangeEvent);
viewModel.getGroupMembers().observe(this, unused -> updateGroupMembersForGroupCall());
viewModel.shouldShowSpeakerHint().observe(this, this::updateSpeakerHint);
callScreen.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
if (state != null) {
if (state.needsNewRequestSizes()) {
Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_GROUP_UPDATE_RENDERED_RESOLUTIONS);
startService(intent);
}
}
});
}
private void handleViewModelEvent(@NonNull WebRtcCallViewModel.Event event) {
if (event instanceof WebRtcCallViewModel.Event.StartCall) {
startCall(((WebRtcCallViewModel.Event.StartCall)event).isVideoCall());
return;
} else if (event instanceof WebRtcCallViewModel.Event.ShowGroupCallSafetyNumberChange) {
SafetyNumberChangeDialog.showForGroupCall(getSupportFragmentManager(), ((WebRtcCallViewModel.Event.ShowGroupCallSafetyNumberChange) event).getIdentityRecords());
return;
}
if (isInPipMode()) {
return;
}
switch (event) {
case SHOW_VIDEO_TOOLTIP:
if (videoTooltip == null) {
videoTooltip = TooltipPopup.forTarget(callScreen.getVideoTooltipTarget())
.setBackgroundTint(ContextCompat.getColor(this, R.color.core_ultramarine))
.setTextColor(ContextCompat.getColor(this, R.color.core_white))
.setText(R.string.WebRtcCallActivity__tap_here_to_turn_on_your_video)
.setOnDismissListener(() -> viewModel.onDismissedVideoTooltip())
.show(TooltipPopup.POSITION_ABOVE);
return;
}
break;
case DISMISS_VIDEO_TOOLTIP:
if (videoTooltip != null) {
videoTooltip.dismiss();
videoTooltip = null;
}
break;
default:
throw new IllegalArgumentException("Unknown event: " + event);
if (event instanceof WebRtcCallViewModel.Event.ShowVideoTooltip) {
if (videoTooltip == null) {
videoTooltip = TooltipPopup.forTarget(callScreen.getVideoTooltipTarget())
.setBackgroundTint(ContextCompat.getColor(this, R.color.core_ultramarine))
.setTextColor(ContextCompat.getColor(this, R.color.core_white))
.setText(R.string.WebRtcCallActivity__tap_here_to_turn_on_your_video)
.setOnDismissListener(() -> viewModel.onDismissedVideoTooltip())
.show(TooltipPopup.POSITION_ABOVE);
return;
}
} else if (event instanceof WebRtcCallViewModel.Event.DismissVideoTooltip) {
if (videoTooltip != null) {
videoTooltip.dismiss();
videoTooltip = null;
}
} else {
throw new IllegalArgumentException("Unknown event: " + event);
}
}
@@ -375,19 +431,17 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
startService(intent);
}
private void handleIncomingCall(@NonNull WebRtcViewModel event) {
callScreen.setRecipient(event.getRecipient());
}
private void handleOutgoingCall(@NonNull WebRtcViewModel event) {
callScreen.setRecipient(event.getRecipient());
callScreen.setStatus(getString(R.string.WebRtcCallActivity__calling));
if (event.getGroupState().isNotIdle()) {
callScreen.setStatusFromGroupCallState(event.getGroupState());
} else {
callScreen.setStatus(getString(R.string.WebRtcCallActivity__calling));
}
}
private void handleTerminate(@NonNull Recipient recipient, @NonNull HangupMessage.Type hangupType) {
Log.i(TAG, "handleTerminate called: " + hangupType.name());
callScreen.setRecipient(recipient);
callScreen.setStatusFromHangupType(hangupType);
EventBus.getDefault().removeStickyEvent(WebRtcViewModel.class);
@@ -398,62 +452,49 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
delayedFinish();
}
private void handleCallRinging(@NonNull WebRtcViewModel event) {
callScreen.setRecipient(event.getRecipient());
private void handleCallRinging() {
callScreen.setStatus(getString(R.string.RedPhone_ringing));
}
private void handleCallBusy(@NonNull WebRtcViewModel event) {
private void handleCallBusy() {
EventBus.getDefault().removeStickyEvent(WebRtcViewModel.class);
callScreen.setRecipient(event.getRecipient());
callScreen.setStatus(getString(R.string.RedPhone_busy));
delayedFinish(WebRtcCallService.BUSY_TONE_LENGTH);
}
private void handleCallConnected(@NonNull WebRtcViewModel event) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES);
callScreen.setRecipient(event.getRecipient());
if (event.getGroupState().isNotIdleOrConnected()) {
callScreen.setStatusFromGroupCallState(event.getGroupState());
}
}
private void handleRecipientUnavailable(@NonNull WebRtcViewModel event) {
private void handleRecipientUnavailable() {
EventBus.getDefault().removeStickyEvent(WebRtcViewModel.class);
callScreen.setRecipient(event.getRecipient());
callScreen.setStatus(getString(R.string.RedPhone_recipient_unavailable));
delayedFinish();
}
private void handleServerFailure(@NonNull WebRtcViewModel event) {
private void handleServerFailure() {
EventBus.getDefault().removeStickyEvent(WebRtcViewModel.class);
callScreen.setRecipient(event.getRecipient());
callScreen.setStatus(getString(R.string.RedPhone_network_failed));
delayedFinish();
}
private void handleNoSuchUser(final @NonNull WebRtcViewModel event) {
if (isFinishing()) return; // XXX Stuart added this check above, not sure why, so I'm repeating in ignorance. - moxie
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setTitle(R.string.RedPhone_number_not_registered);
dialog.setIconAttribute(R.attr.dialog_alert_icon);
dialog.setMessage(R.string.RedPhone_the_number_you_dialed_does_not_support_secure_voice);
dialog.setCancelable(true);
dialog.setPositiveButton(R.string.RedPhone_got_it, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
WebRtcCallActivity.this.handleTerminate(event.getRecipient(), HangupMessage.Type.NORMAL);
}
});
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
WebRtcCallActivity.this.handleTerminate(event.getRecipient(), HangupMessage.Type.NORMAL);
}
});
dialog.show();
new AlertDialog.Builder(this)
.setTitle(R.string.RedPhone_number_not_registered)
.setIcon(R.drawable.ic_warning)
.setMessage(R.string.RedPhone_the_number_you_dialed_does_not_support_secure_voice)
.setCancelable(true)
.setPositiveButton(R.string.RedPhone_got_it, (d, w) -> handleTerminate(event.getRecipient(), HangupMessage.Type.NORMAL))
.setOnCancelListener(d -> handleTerminate(event.getRecipient(), HangupMessage.Type.NORMAL))
.show();
}
private void handleUntrustedIdentity(@NonNull WebRtcViewModel event) {
final IdentityKey theirKey = event.getIdentityKey();
final Recipient recipient = event.getRecipient();
final IdentityKey theirKey = event.getRemoteParticipants().get(0).getIdentityKey();
final Recipient recipient = event.getRemoteParticipants().get(0).getRecipient();
if (theirKey == null) {
handleTerminate(recipient, HangupMessage.Type.NORMAL);
@@ -462,13 +503,40 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
SafetyNumberChangeDialog.showForCall(getSupportFragmentManager(), recipient.getId());
}
@Override
public void onSendAnywayAfterSafetyNumberChange() {
Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL)
.putExtra(WebRtcCallService.EXTRA_REMOTE_PEER, new RemotePeer(viewModel.getRecipient().getId()));
public void handleSafetyNumberChangeEvent(@NonNull WebRtcCallViewModel.SafetyNumberChangeEvent safetyNumberChangeEvent) {
if (Util.hasItems(safetyNumberChangeEvent.getRecipientIds())) {
if (safetyNumberChangeEvent.isInPipMode()) {
GroupCallSafetyNumberChangeNotificationUtil.showNotification(this, viewModel.getRecipient().get());
} else {
GroupCallSafetyNumberChangeNotificationUtil.cancelNotification(this, viewModel.getRecipient().get());
SafetyNumberChangeDialog.showForDuringGroupCall(getSupportFragmentManager(), safetyNumberChangeEvent.getRecipientIds());
}
}
}
startService(intent);
private void updateGroupMembersForGroupCall() {
startService(new Intent(this, WebRtcCallService.class).setAction(WebRtcCallService.ACTION_GROUP_REQUEST_UPDATE_MEMBERS));
}
private void updateSpeakerHint(boolean showSpeakerHint) {
if (showSpeakerHint) {
callScreen.showSpeakerViewHint();
} else {
callScreen.hideSpeakerViewHint();
}
}
@Override
public void onSendAnywayAfterSafetyNumberChange(@NonNull List<RecipientId> changedRecipients) {
CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
if (state.getGroupCallState().isConnected()) {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_GROUP_APPROVE_SAFETY_CHANGE)
.putExtra(WebRtcCallService.EXTRA_RECIPIENT_IDS, RecipientId.toSerializedList(changedRecipients));
startService(intent);
} else {
viewModel.startCall(state.getLocalParticipant().isVideoEnabled());
}
}
@Override
@@ -476,7 +544,19 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
@Override
public void onCanceled() {
handleTerminate(viewModel.getRecipient().get(), HangupMessage.Type.NORMAL);
CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
if (state != null && state.getGroupCallState().isNotIdle()) {
if (state.getCallState().isPreJoinOrNetworkUnavailable()) {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_CANCEL_PRE_JOIN_CALL);
startService(intent);
finish();
} else {
handleEndCall();
}
} else {
handleTerminate(viewModel.getRecipient().get(), HangupMessage.Type.NORMAL);
}
}
private boolean isSystemPipEnabledAndAvailable() {
@@ -493,32 +573,30 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventMainThread(final WebRtcViewModel event) {
public void onEventMainThread(@NonNull WebRtcViewModel event) {
Log.i(TAG, "Got message from service: " + event);
viewModel.setRecipient(event.getRecipient());
callScreen.setRecipient(event.getRecipient());
switch (event.getState()) {
case CALL_PRE_JOIN: handleCallPreJoin(event); break;
case CALL_CONNECTED: handleCallConnected(event); break;
case NETWORK_FAILURE: handleServerFailure(event); break;
case CALL_RINGING: handleCallRinging(event); break;
case NETWORK_FAILURE: handleServerFailure(); break;
case CALL_RINGING: handleCallRinging(); break;
case CALL_DISCONNECTED: handleTerminate(event.getRecipient(), HangupMessage.Type.NORMAL); break;
case CALL_ACCEPTED_ELSEWHERE: handleTerminate(event.getRecipient(), HangupMessage.Type.ACCEPTED); break;
case CALL_DECLINED_ELSEWHERE: handleTerminate(event.getRecipient(), HangupMessage.Type.DECLINED); break;
case CALL_ONGOING_ELSEWHERE: handleTerminate(event.getRecipient(), HangupMessage.Type.BUSY); break;
case CALL_NEEDS_PERMISSION: handleTerminate(event.getRecipient(), HangupMessage.Type.NEED_PERMISSION); break;
case NO_SUCH_USER: handleNoSuchUser(event); break;
case RECIPIENT_UNAVAILABLE: handleRecipientUnavailable(event); break;
case CALL_INCOMING: handleIncomingCall(event); break;
case RECIPIENT_UNAVAILABLE: handleRecipientUnavailable(); break;
case CALL_OUTGOING: handleOutgoingCall(event); break;
case CALL_BUSY: handleCallBusy(event); break;
case CALL_BUSY: handleCallBusy(); break;
case UNTRUSTED_IDENTITY: handleUntrustedIdentity(event); break;
}
callScreen.setLocalRenderer(event.getLocalRenderer());
callScreen.setRemoteRenderer(event.getRemoteRenderer());
boolean enableVideo = event.getLocalCameraState().getCameraCount() > 0 && enableVideoIfAvailable;
boolean enableVideo = event.getLocalParticipant().getCameraState().getCameraCount() > 0 && enableVideoIfAvailable;
viewModel.updateFromWebRtcViewModel(event, enableVideo);
@@ -528,8 +606,36 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
}
}
private void handleCallPreJoin(@NonNull WebRtcViewModel event) {
if (event.getGroupState().isNotIdle()) {
callScreen.setStatusFromGroupCallState(event.getGroupState());
}
}
private void startCall(boolean isVideoCall) {
enableVideoIfAvailable = isVideoCall;
Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL)
.putExtra(WebRtcCallService.EXTRA_REMOTE_PEER, new RemotePeer(viewModel.getRecipient().getId()))
.putExtra(WebRtcCallService.EXTRA_OFFER_TYPE, (isVideoCall ? OfferMessage.Type.VIDEO_CALL : OfferMessage.Type.AUDIO_CALL).getCode());
startService(intent);
MessageSender.onMessageSent();
}
private final class ControlsListener implements WebRtcCallView.ControlsListener {
@Override
public void onStartCall(boolean isVideoCall) {
viewModel.startCall(isVideoCall);
}
@Override
public void onCancelStartCall() {
finish();
}
@Override
public void onControlsFadeOut() {
if (videoTooltip != null) {
@@ -537,6 +643,16 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
}
}
@Override
public void showSystemUI() {
fullscreenHelper.showSystemUI();
}
@Override
public void hideSystemUI() {
fullscreenHelper.hideSystemUI();
}
@Override
public void onAudioOutputChanged(@NonNull WebRtcAudioOutput audioOutput) {
switch (audioOutput) {
@@ -594,9 +710,18 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
}
@Override
public void onDownCaretPressed() {
public void onShowParticipantsList() {
CallParticipantsListDialog.show(getSupportFragmentManager());
}
@Override
public void onPageChanged(@NonNull CallParticipantsState.SelectedPage page) {
viewModel.setIsViewingFocusedParticipant(page);
}
@Override
public void onLocalPictureInPictureClicked() {
viewModel.onLocalPictureInPictureClicked();
}
}
}

View File

@@ -0,0 +1,50 @@
package org.thoughtcrime.securesms.animation;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import androidx.annotation.NonNull;
public class ResizeAnimation extends Animation {
private final View target;
private final int targetWidthPx;
private final int targetHeightPx;
private int startWidth;
private int startHeight;
public ResizeAnimation(@NonNull View target, int targetWidthPx, int targetHeightPx) {
this.target = target;
this.targetWidthPx = targetWidthPx;
this.targetHeightPx = targetHeightPx;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
int newWidth = (int) (startWidth + (targetWidthPx - startWidth) * interpolatedTime);
int newHeight = (int) (startHeight + (targetHeightPx - startHeight) * interpolatedTime);
ViewGroup.LayoutParams params = target.getLayoutParams();
params.width = newWidth;
params.height = newHeight;
target.setLayoutParams(params);
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
this.startWidth = width;
this.startHeight = height;
}
@Override
public boolean willChangeBounds() {
return true;
}
}

View File

@@ -106,10 +106,7 @@ public abstract class Attachment {
}
@Nullable
public abstract Uri getDataUri();
@Nullable
public abstract Uri getThumbnailUri();
public abstract Uri getUri();
public int getTransferState() {
return transferState;

View File

@@ -57,7 +57,7 @@ public class DatabaseAttachment extends Attachment {
@Override
@Nullable
public Uri getDataUri() {
public Uri getUri() {
if (hasData) {
return PartAuthority.getAttachmentDataUri(attachmentId);
} else {
@@ -65,16 +65,6 @@ public class DatabaseAttachment extends Attachment {
}
}
@Override
@Nullable
public Uri getThumbnailUri() {
if (hasThumbnail) {
return PartAuthority.getAttachmentThumbnailUri(attachmentId);
} else {
return null;
}
}
public AttachmentId getAttachmentId() {
return attachmentId;
}

View File

@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.attachments;
import android.net.Uri;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
@@ -15,13 +16,7 @@ public class MmsNotificationAttachment extends Attachment {
@Nullable
@Override
public Uri getDataUri() {
return null;
}
@Nullable
@Override
public Uri getThumbnailUri() {
public Uri getUri() {
return null;
}

View File

@@ -1,10 +1,10 @@
package org.thoughtcrime.securesms.attachments;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.audio.AudioHash;
import org.thoughtcrime.securesms.blurhash.BlurHash;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.stickers.StickerLocator;
@@ -42,17 +42,10 @@ public class PointerAttachment extends Attachment {
@Nullable
@Override
public Uri getDataUri() {
public Uri getUri() {
return null;
}
@Nullable
@Override
public Uri getThumbnailUri() {
return null;
}
public static List<Attachment> forPointers(Optional<List<SignalServiceAttachment>> pointers) {
List<Attachment> results = new LinkedList<>();

View File

@@ -20,12 +20,7 @@ public class TombstoneAttachment extends Attachment {
}
@Override
public @Nullable Uri getDataUri() {
return null;
}
@Override
public @Nullable Uri getThumbnailUri() {
public @Nullable Uri getUri() {
return null;
}
}

View File

@@ -13,7 +13,6 @@ import org.thoughtcrime.securesms.stickers.StickerLocator;
public class UriAttachment extends Attachment {
private final @NonNull Uri dataUri;
private final @Nullable Uri thumbnailUri;
public UriAttachment(@NonNull Uri uri,
@NonNull String contentType,
@@ -29,11 +28,10 @@ public class UriAttachment extends Attachment {
@Nullable AudioHash audioHash,
@Nullable TransformProperties transformProperties)
{
this(uri, uri, contentType, transferState, size, 0, 0, fileName, null, voiceNote, borderless, quote, caption, stickerLocator, blurHash, audioHash, transformProperties);
this(uri, contentType, transferState, size, 0, 0, fileName, null, voiceNote, borderless, quote, caption, stickerLocator, blurHash, audioHash, transformProperties);
}
public UriAttachment(@NonNull Uri dataUri,
@Nullable Uri thumbnailUri,
@NonNull String contentType,
int transferState,
long size,
@@ -51,22 +49,15 @@ public class UriAttachment extends Attachment {
@Nullable TransformProperties transformProperties)
{
super(contentType, transferState, size, fileName, 0, null, null, null, null, fastPreflightId, voiceNote, borderless, width, height, quote, 0, caption, stickerLocator, blurHash, audioHash, transformProperties);
this.dataUri = dataUri;
this.thumbnailUri = thumbnailUri;
this.dataUri = dataUri;
}
@Override
@NonNull
public Uri getDataUri() {
public Uri getUri() {
return dataUri;
}
@Override
@Nullable
public Uri getThumbnailUri() {
return thumbnailUri;
}
@Override
public boolean equals(Object other) {
return other != null && other instanceof UriAttachment && ((UriAttachment) other).dataUri.equals(this.dataUri);

View File

@@ -8,8 +8,9 @@ import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaRecorder;
import android.os.Build;
import org.thoughtcrime.securesms.logging.Log;
import org.signal.core.util.StreamUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.util.Util;
import java.io.IOException;
@@ -81,7 +82,7 @@ public class AudioCodec {
mediaCodec.release();
audioRecord.release();
Util.close(outputStream);
StreamUtil.close(outputStream);
setFinished();
}
}

View File

@@ -5,15 +5,16 @@ import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.logging.Log;
import androidx.annotation.NonNull;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.whispersystems.libsignal.util.Pair;
import java.io.IOException;

View File

@@ -1,378 +0,0 @@
package org.thoughtcrime.securesms.audio;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.util.Pair;
import android.widget.Toast;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.LoadControl;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.AudioSlide;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.video.exo.AttachmentDataSourceFactory;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
import java.lang.ref.WeakReference;
public class AudioSlidePlayer implements SensorEventListener {
private static final String TAG = AudioSlidePlayer.class.getSimpleName();
private static @NonNull Optional<AudioSlidePlayer> playing = Optional.absent();
private final @NonNull Context context;
private final @NonNull AudioSlide slide;
private final @NonNull Handler progressEventHandler;
private final @NonNull AudioManager audioManager;
private final @NonNull SensorManager sensorManager;
private final @NonNull Sensor proximitySensor;
private final @Nullable WakeLock wakeLock;
private @NonNull WeakReference<Listener> listener;
private @Nullable SimpleExoPlayer mediaPlayer;
private long startTime;
public synchronized static AudioSlidePlayer createFor(@NonNull Context context,
@NonNull AudioSlide slide,
@NonNull Listener listener)
{
if (playing.isPresent() && playing.get().getAudioSlide().equals(slide)) {
playing.get().setListener(listener);
return playing.get();
} else {
return new AudioSlidePlayer(context, slide, listener);
}
}
private AudioSlidePlayer(@NonNull Context context,
@NonNull AudioSlide slide,
@NonNull Listener listener)
{
this.context = context;
this.slide = slide;
this.listener = new WeakReference<>(listener);
this.progressEventHandler = new ProgressEventHandler(this);
this.audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
this.sensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
this.proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
if (Build.VERSION.SDK_INT >= 21) {
this.wakeLock = ServiceUtil.getPowerManager(context).newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);
} else {
this.wakeLock = null;
}
}
public void play(final double progress) throws IOException {
play(progress, false);
}
private void play(final double progress, boolean earpiece) throws IOException {
if (this.mediaPlayer != null) {
return;
}
if (slide.getUri() == null) {
throw new IOException("Slide has no URI!");
}
LoadControl loadControl = new DefaultLoadControl.Builder().setBufferDurationsMs(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE).createDefaultLoadControl();
this.mediaPlayer = ExoPlayerFactory.newSimpleInstance(context, new DefaultRenderersFactory(context), new DefaultTrackSelector(), loadControl);
this.startTime = System.currentTimeMillis();
mediaPlayer.prepare(createMediaSource(slide.getUri()));
mediaPlayer.setPlayWhenReady(true);
mediaPlayer.setAudioAttributes(new AudioAttributes.Builder()
.setContentType(earpiece ? C.CONTENT_TYPE_SPEECH : C.CONTENT_TYPE_MUSIC)
.setUsage(earpiece ? C.USAGE_VOICE_COMMUNICATION : C.USAGE_MEDIA)
.build());
mediaPlayer.addListener(new Player.EventListener() {
boolean started = false;
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
Log.d(TAG, "onPlayerStateChanged(" + playWhenReady + ", " + playbackState + ")");
switch (playbackState) {
case Player.STATE_READY:
Log.i(TAG, "onPrepared() " + mediaPlayer.getBufferedPercentage() + "% buffered");
synchronized (AudioSlidePlayer.this) {
if (mediaPlayer == null) return;
if (started) {
Log.d(TAG, "Already started. Ignoring.");
return;
}
started = true;
if (progress > 0) {
mediaPlayer.seekTo((long) (mediaPlayer.getDuration() * progress));
}
sensorManager.registerListener(AudioSlidePlayer.this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
setPlaying(AudioSlidePlayer.this);
}
notifyOnStart();
progressEventHandler.sendEmptyMessage(0);
break;
case Player.STATE_ENDED:
Log.i(TAG, "onComplete");
synchronized (AudioSlidePlayer.this) {
mediaPlayer = null;
sensorManager.unregisterListener(AudioSlidePlayer.this);
if (wakeLock != null && wakeLock.isHeld()) {
if (Build.VERSION.SDK_INT >= 21) {
wakeLock.release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY);
}
}
}
notifyOnStop();
progressEventHandler.removeMessages(0);
}
}
@Override
public void onPlayerError(ExoPlaybackException error) {
Log.w(TAG, "MediaPlayer Error: " + error);
Toast.makeText(context, R.string.AudioSlidePlayer_error_playing_audio, Toast.LENGTH_SHORT).show();
synchronized (AudioSlidePlayer.this) {
mediaPlayer = null;
sensorManager.unregisterListener(AudioSlidePlayer.this);
if (wakeLock != null && wakeLock.isHeld()) {
if (Build.VERSION.SDK_INT >= 21) {
wakeLock.release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY);
}
}
}
notifyOnStop();
progressEventHandler.removeMessages(0);
}
});
}
private MediaSource createMediaSource(@NonNull Uri uri) {
DefaultDataSourceFactory defaultDataSourceFactory = new DefaultDataSourceFactory(context, "GenericUserAgent", null);
AttachmentDataSourceFactory attachmentDataSourceFactory = new AttachmentDataSourceFactory(context, defaultDataSourceFactory, null);
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);
return new ExtractorMediaSource.Factory(attachmentDataSourceFactory)
.setExtractorsFactory(extractorsFactory)
.createMediaSource(uri);
}
public synchronized void stop() {
Log.i(TAG, "Stop called!");
removePlaying(this);
if (this.mediaPlayer != null) {
this.mediaPlayer.stop();
this.mediaPlayer.release();
}
sensorManager.unregisterListener(AudioSlidePlayer.this);
this.mediaPlayer = null;
}
public synchronized static void stopAll() {
if (playing.isPresent()) {
playing.get().stop();
}
}
public void setListener(@NonNull Listener listener) {
this.listener = new WeakReference<>(listener);
if (this.mediaPlayer != null && this.mediaPlayer.getPlaybackState() == Player.STATE_READY) {
notifyOnStart();
}
}
public @NonNull AudioSlide getAudioSlide() {
return slide;
}
private Pair<Double, Integer> getProgress() {
if (mediaPlayer == null || mediaPlayer.getCurrentPosition() <= 0 || mediaPlayer.getDuration() <= 0) {
return new Pair<>(0D, 0);
} else {
return new Pair<>((double) mediaPlayer.getCurrentPosition() / (double) mediaPlayer.getDuration(),
(int) mediaPlayer.getCurrentPosition());
}
}
private void notifyOnStart() {
Util.runOnMain(new Runnable() {
@Override
public void run() {
getListener().onStart();
}
});
}
private void notifyOnStop() {
Util.runOnMain(new Runnable() {
@Override
public void run() {
getListener().onStop();
}
});
}
private void notifyOnProgress(final double progress, final long millis) {
Util.runOnMain(new Runnable() {
@Override
public void run() {
getListener().onProgress(progress, millis);
}
});
}
private @NonNull Listener getListener() {
Listener listener = this.listener.get();
if (listener != null) return listener;
else return new Listener() {
@Override
public void onStart() {}
@Override
public void onStop() {}
@Override
public void onProgress(double progress, long millis) {}
};
}
private synchronized static void setPlaying(@NonNull AudioSlidePlayer player) {
if (playing.isPresent() && playing.get() != player) {
playing.get().notifyOnStop();
playing.get().stop();
}
playing = Optional.of(player);
}
private synchronized static void removePlaying(@NonNull AudioSlidePlayer player) {
if (playing.isPresent() && playing.get() == player) {
playing = Optional.absent();
}
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() != Sensor.TYPE_PROXIMITY) return;
if (mediaPlayer == null || mediaPlayer.getPlaybackState() != Player.STATE_READY) return;
int streamType;
if (event.values[0] < 5f && event.values[0] != proximitySensor.getMaximumRange()) {
streamType = AudioManager.STREAM_VOICE_CALL;
} else {
streamType = AudioManager.STREAM_MUSIC;
}
if (streamType == AudioManager.STREAM_VOICE_CALL &&
mediaPlayer.getAudioStreamType() != streamType &&
!audioManager.isWiredHeadsetOn())
{
double position = mediaPlayer.getCurrentPosition();
double duration = mediaPlayer.getDuration();
double progress = position / duration;
if (wakeLock != null) wakeLock.acquire();
stop();
try {
play(progress, true);
} catch (IOException e) {
Log.w(TAG, e);
}
} else if (streamType == AudioManager.STREAM_MUSIC &&
mediaPlayer.getAudioStreamType() != streamType &&
System.currentTimeMillis() - startTime > 500)
{
if (wakeLock != null) wakeLock.release();
stop();
notifyOnStop();
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
public interface Listener {
void onStart();
void onStop();
void onProgress(double progress, long millis);
}
private static class ProgressEventHandler extends Handler {
private final WeakReference<AudioSlidePlayer> playerReference;
private ProgressEventHandler(@NonNull AudioSlidePlayer player) {
this.playerReference = new WeakReference<>(player);
}
@Override
public void handleMessage(Message msg) {
AudioSlidePlayer player = playerReference.get();
if (player == null || player.mediaPlayer == null || !isPlayerActive(player.mediaPlayer)) {
return;
}
Pair<Double, Integer> progress = player.getProgress();
player.notifyOnProgress(progress.first, progress.second);
sendEmptyMessageDelayed(0, 50);
}
private boolean isPlayerActive(@NonNull SimpleExoPlayer player) {
return player.getPlaybackState() == Player.STATE_READY || player.getPlaybackState() == Player.STATE_BUFFERING;
}
}
}

View File

@@ -16,18 +16,18 @@ import androidx.core.util.Consumer;
import com.google.protobuf.ByteString;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.model.databaseprotos.AudioWaveFormData;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.media.DecryptableUriMediaInput;
import org.thoughtcrime.securesms.media.MediaInput;
import org.thoughtcrime.securesms.mms.AudioSlide;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.SerialExecutor;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import java.io.IOException;
import java.nio.ByteBuffer;

View File

@@ -1,9 +1,14 @@
package org.thoughtcrime.securesms.backup;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.DocumentsContract;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
@@ -13,10 +18,14 @@ import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.registration.fragments.RestoreBackupFragment;
import org.thoughtcrime.securesms.service.LocalBackupListener;
import org.thoughtcrime.securesms.util.BackupUtil;
@@ -26,25 +35,49 @@ import org.thoughtcrime.securesms.util.text.AfterTextChanged;
public class BackupDialog {
public static void showEnableBackupDialog(@NonNull Context context, @NonNull SwitchPreferenceCompat preference) {
private static final String TAG = Log.tag(BackupDialog.class);
public static void showEnableBackupDialog(@NonNull Context context,
@Nullable Intent backupDirectorySelectionIntent,
@Nullable String backupDirectoryDisplayName,
@NonNull Runnable onBackupsEnabled)
{
String[] password = BackupUtil.generateBackupPassphrase();
AlertDialog dialog = new AlertDialog.Builder(context)
.setTitle(R.string.BackupDialog_enable_local_backups)
.setView(R.layout.backup_enable_dialog)
.setView(backupDirectorySelectionIntent != null ? R.layout.backup_enable_dialog_v29 : R.layout.backup_enable_dialog)
.setPositiveButton(R.string.BackupDialog_enable_backups, null)
.setNegativeButton(android.R.string.cancel, null)
.create();
dialog.setOnShowListener(created -> {
if (backupDirectoryDisplayName != null) {
TextView folderName = dialog.findViewById(R.id.backup_enable_dialog_folder_name);
if (folderName != null) {
folderName.setText(backupDirectoryDisplayName);
}
}
Button button = ((AlertDialog) created).getButton(AlertDialog.BUTTON_POSITIVE);
button.setOnClickListener(v -> {
CheckBox confirmationCheckBox = dialog.findViewById(R.id.confirmation_check);
if (confirmationCheckBox.isChecked()) {
if (backupDirectorySelectionIntent != null && backupDirectorySelectionIntent.getData() != null) {
Uri backupDirectoryUri = backupDirectorySelectionIntent.getData();
int takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
SignalStore.settings().setSignalBackupDirectory(backupDirectoryUri);
context.getContentResolver()
.takePersistableUriPermission(backupDirectoryUri, takeFlags);
}
BackupPassphrase.set(context, Util.join(password, " "));
TextSecurePreferences.setNextBackupTime(context, 0);
TextSecurePreferences.setBackupEnabled(context, true);
LocalBackupListener.schedule(context);
preference.setChecked(true);
onBackupsEnabled.run();
created.dismiss();
} else {
Toast.makeText(context, R.string.BackupDialog_please_acknowledge_your_understanding_by_marking_the_confirmation_check_box, Toast.LENGTH_LONG).show();
@@ -75,16 +108,47 @@ public class BackupDialog {
}
public static void showDisableBackupDialog(@NonNull Context context, @NonNull SwitchPreferenceCompat preference) {
@RequiresApi(29)
public static void showChooseBackupLocationDialog(@NonNull Fragment fragment, int requestCode) {
new AlertDialog.Builder(fragment.requireContext())
.setView(R.layout.backup_choose_location_dialog)
.setCancelable(true)
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {
dialog.dismiss();
})
.setPositiveButton(R.string.BackupDialog_choose_folder, ((dialog, which) -> {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
if (Build.VERSION.SDK_INT >= 26) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, SignalStore.settings().getLatestSignalBackupDirectory());
}
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
fragment.startActivityForResult(intent, requestCode);
} catch (ActivityNotFoundException e) {
Toast.makeText(fragment.requireContext(), R.string.BackupDialog_no_file_picker_available, Toast.LENGTH_LONG)
.show();
}
dialog.dismiss();
}))
.create()
.show();
}
public static void showDisableBackupDialog(@NonNull Context context, @NonNull Runnable onBackupsDisabled) {
new AlertDialog.Builder(context)
.setTitle(R.string.BackupDialog_delete_backups)
.setMessage(R.string.BackupDialog_disable_and_delete_all_local_backups)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.BackupDialog_delete_backups_statement, (dialog, which) -> {
BackupPassphrase.set(context, null);
TextSecurePreferences.setBackupEnabled(context, false);
BackupUtil.deleteAllBackups();
preference.setChecked(false);
BackupUtil.disableBackups(context);
onBackupsDisabled.run();
})
.create()
.show();

View File

@@ -0,0 +1,78 @@
package org.thoughtcrime.securesms.backup;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.notifications.NotificationCancellationHelper;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import java.io.IOException;
public enum BackupFileIOError {
ACCESS_ERROR(R.string.LocalBackupJobApi29_backup_failed, R.string.LocalBackupJobApi29_your_backup_directory_has_been_deleted_or_moved),
FILE_TOO_LARGE(R.string.LocalBackupJobApi29_backup_failed, R.string.LocalBackupJobApi29_your_backup_file_is_too_large),
NOT_ENOUGH_SPACE(R.string.LocalBackupJobApi29_backup_failed, R.string.LocalBackupJobApi29_there_is_not_enough_space),
UNKNOWN(R.string.LocalBackupJobApi29_backup_failed, R.string.LocalBackupJobApi29_tap_to_manage_backups);
private static final short BACKUP_FAILED_ID = 31321;
private final @StringRes int titleId;
private final @StringRes int messageId;
BackupFileIOError(@StringRes int titleId, @StringRes int messageId) {
this.titleId = titleId;
this.messageId = messageId;
}
public static void clearNotification(@NonNull Context context) {
NotificationCancellationHelper.cancelLegacy(context, BACKUP_FAILED_ID);
}
public void postNotification(@NonNull Context context) {
Intent intent = new Intent(context, ApplicationPreferencesActivity.class);
intent.putExtra(ApplicationPreferencesActivity.LAUNCH_TO_BACKUPS_FRAGMENT, true);
PendingIntent pendingIntent = PendingIntent.getActivity(context, -1, intent, 0);
Notification backupFailedNotification = new NotificationCompat.Builder(context, NotificationChannels.FAILURES)
.setSmallIcon(R.drawable.ic_signal_backup)
.setContentTitle(context.getString(titleId))
.setContentText(context.getString(messageId))
.setContentIntent(pendingIntent)
.build();
NotificationManagerCompat.from(context)
.notify(BACKUP_FAILED_ID, backupFailedNotification);
}
public static void postNotificationForException(@NonNull Context context, @NonNull IOException e, int runAttempt) {
BackupFileIOError error = getFromException(e);
if (error != null) {
error.postNotification(context);
}
if (error == null && runAttempt > 0) {
UNKNOWN.postNotification(context);
}
}
private static @Nullable BackupFileIOError getFromException(@NonNull IOException e) {
if (e.getMessage() != null) {
if (e.getMessage().contains("EFBIG")) return FILE_TOO_LARGE;
else if (e.getMessage().contains("ENOSPC")) return NOT_ENOUGH_SPACE;
}
return null;
}
}

View File

@@ -6,8 +6,8 @@ import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.crypto.KeyStoreHelper;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
/**

View File

@@ -3,18 +3,22 @@ package org.thoughtcrime.securesms.backup;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import androidx.annotation.RequiresApi;
import androidx.documentfile.provider.DocumentFile;
import com.annimon.stream.function.Consumer;
import com.annimon.stream.function.Predicate;
import com.google.android.collect.Sets;
import com.google.protobuf.ByteString;
import net.sqlcipher.database.SQLiteDatabase;
import org.greenrobot.eventbus.EventBus;
import org.signal.core.util.Conversions;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.attachments.AttachmentId;
import org.thoughtcrime.securesms.crypto.AttachmentSecret;
import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream;
@@ -32,9 +36,8 @@ import org.thoughtcrime.securesms.database.SessionDatabase;
import org.thoughtcrime.securesms.database.SignedPreKeyDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.StickerDatabase;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.util.Conversions;
import org.thoughtcrime.securesms.util.SetUtil;
import org.thoughtcrime.securesms.util.Stopwatch;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.kdf.HKDFv3;
@@ -50,6 +53,7 @@ import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import javax.crypto.BadPaddingException;
@@ -65,16 +69,12 @@ public class FullBackupExporter extends FullBackupBase {
@SuppressWarnings("unused")
private static final String TAG = FullBackupExporter.class.getSimpleName();
private static final Set<String> BLACKLISTED_TABLES = Sets.newHashSet(
private static final Set<String> BLACKLISTED_TABLES = SetUtil.newHashSet(
SignedPreKeyDatabase.TABLE_NAME,
OneTimePreKeyDatabase.TABLE_NAME,
SessionDatabase.TABLE_NAME,
SearchDatabase.SMS_FTS_TABLE_NAME,
SearchDatabase.MMS_FTS_TABLE_NAME,
JobDatabase.JOBS_TABLE_NAME,
JobDatabase.CONSTRAINTS_TABLE_NAME,
JobDatabase.DEPENDENCIES_TABLE_NAME,
KeyValueDatabase.TABLE_NAME
SearchDatabase.MMS_FTS_TABLE_NAME
);
public static void export(@NonNull Context context,
@@ -84,7 +84,32 @@ public class FullBackupExporter extends FullBackupBase {
@NonNull String passphrase)
throws IOException
{
BackupFrameOutputStream outputStream = new BackupFrameOutputStream(output, passphrase);
try (OutputStream outputStream = new FileOutputStream(output)) {
internalExport(context, attachmentSecret, input, outputStream, passphrase);
}
}
@RequiresApi(29)
public static void export(@NonNull Context context,
@NonNull AttachmentSecret attachmentSecret,
@NonNull SQLiteDatabase input,
@NonNull DocumentFile output,
@NonNull String passphrase)
throws IOException
{
try (OutputStream outputStream = Objects.requireNonNull(context.getContentResolver().openOutputStream(output.getUri()))) {
internalExport(context, attachmentSecret, input, outputStream, passphrase);
}
}
private static void internalExport(@NonNull Context context,
@NonNull AttachmentSecret attachmentSecret,
@NonNull SQLiteDatabase input,
@NonNull OutputStream fileOutputStream,
@NonNull String passphrase)
throws IOException
{
BackupFrameOutputStream outputStream = new BackupFrameOutputStream(fileOutputStream, passphrase);
int count = 0;
try {
@@ -322,7 +347,7 @@ public class FullBackupExporter extends FullBackupBase {
private byte[] iv;
private int counter;
private BackupFrameOutputStream(@NonNull File output, @NonNull String passphrase) throws IOException {
private BackupFrameOutputStream(@NonNull OutputStream output, @NonNull String passphrase) throws IOException {
try {
byte[] salt = Util.getSecretBytes(32);
byte[] key = getBackupKey(passphrase, salt);
@@ -334,7 +359,7 @@ public class FullBackupExporter extends FullBackupBase {
this.cipher = Cipher.getInstance("AES/CTR/NoPadding");
this.mac = Mac.getInstance("HmacSHA256");
this.outputStream = new FileOutputStream(output);
this.outputStream = output;
this.iv = Util.getSecretBytes(16);
this.counter = Conversions.byteArrayToInt(iv);

View File

@@ -6,12 +6,17 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import androidx.annotation.NonNull;
import android.net.Uri;
import android.util.Pair;
import androidx.annotation.NonNull;
import net.sqlcipher.database.SQLiteDatabase;
import org.greenrobot.eventbus.EventBus;
import org.signal.core.util.Conversions;
import org.signal.core.util.StreamUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.backup.BackupProtos.Attachment;
import org.thoughtcrime.securesms.backup.BackupProtos.BackupFrame;
import org.thoughtcrime.securesms.backup.BackupProtos.DatabaseVersion;
@@ -23,20 +28,16 @@ import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.SearchDatabase;
import org.thoughtcrime.securesms.database.StickerDatabase;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Conversions;
import org.thoughtcrime.securesms.util.BackupUtil;
import org.thoughtcrime.securesms.util.SqlUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.kdf.HKDFv3;
import org.whispersystems.libsignal.util.ByteUtil;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -46,6 +47,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
@@ -61,13 +63,14 @@ public class FullBackupImporter extends FullBackupBase {
private static final String TAG = FullBackupImporter.class.getSimpleName();
public static void importFile(@NonNull Context context, @NonNull AttachmentSecret attachmentSecret,
@NonNull SQLiteDatabase db, @NonNull File file, @NonNull String passphrase)
@NonNull SQLiteDatabase db, @NonNull Uri uri, @NonNull String passphrase)
throws IOException
{
BackupRecordInputStream inputStream = new BackupRecordInputStream(file, passphrase);
int count = 0;
int count = 0;
try (InputStream is = getInputStream(context, uri)) {
BackupRecordInputStream inputStream = new BackupRecordInputStream(is, passphrase);
try {
db.beginTransaction();
dropAllTables(db);
@@ -93,6 +96,14 @@ public class FullBackupImporter extends FullBackupBase {
EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.FINISHED, count));
}
private static @NonNull InputStream getInputStream(@NonNull Context context, @NonNull Uri uri) throws IOException{
if (BackupUtil.isUserSelectionRequired(context)) {
return Objects.requireNonNull(context.getContentResolver().openInputStream(uri));
} else {
return new FileInputStream(new File(Objects.requireNonNull(uri.getPath())));
}
}
private static void processVersion(@NonNull SQLiteDatabase db, DatabaseVersion version) throws IOException {
if (version.getVersion() > db.getVersion()) {
throw new DatabaseDowngradeException(db.getVersion(), version.getVersion());
@@ -138,13 +149,11 @@ public class FullBackupImporter extends FullBackupBase {
inputStream.readAttachmentTo(output.second, attachment.getLength());
contentValues.put(AttachmentDatabase.DATA, dataFile.getAbsolutePath());
contentValues.put(AttachmentDatabase.THUMBNAIL, (String)null);
contentValues.put(AttachmentDatabase.DATA_RANDOM, output.first);
} catch (BadMacException e) {
Log.w(TAG, "Bad MAC for attachment " + attachment.getAttachmentId() + "! Can't restore it.", e);
dataFile.delete();
contentValues.put(AttachmentDatabase.DATA, (String) null);
contentValues.put(AttachmentDatabase.THUMBNAIL, (String) null);
contentValues.put(AttachmentDatabase.DATA_RANDOM, (String) null);
}
@@ -223,16 +232,16 @@ public class FullBackupImporter extends FullBackupBase {
private byte[] iv;
private int counter;
private BackupRecordInputStream(@NonNull File file, @NonNull String passphrase) throws IOException {
private BackupRecordInputStream(@NonNull InputStream in, @NonNull String passphrase) throws IOException {
try {
this.in = new FileInputStream(file);
this.in = in;
byte[] headerLengthBytes = new byte[4];
Util.readFully(in, headerLengthBytes);
StreamUtil.readFully(in, headerLengthBytes);
int headerLength = Conversions.byteArrayToInt(headerLengthBytes);
byte[] headerFrame = new byte[headerLength];
Util.readFully(in, headerFrame);
StreamUtil.readFully(in, headerFrame);
BackupFrame frame = BackupFrame.parseFrom(headerFrame);
@@ -304,7 +313,7 @@ public class FullBackupImporter extends FullBackupBase {
byte[] theirMac = new byte[10];
try {
Util.readFully(in, theirMac);
StreamUtil.readFully(in, theirMac);
} catch (IOException e) {
throw new IOException(e);
}
@@ -320,10 +329,10 @@ public class FullBackupImporter extends FullBackupBase {
private BackupFrame readFrame(InputStream in) throws IOException {
try {
byte[] length = new byte[4];
Util.readFully(in, length);
StreamUtil.readFully(in, length);
byte[] frame = new byte[Conversions.byteArrayToInt(length)];
Util.readFully(in, frame);
StreamUtil.readFully(in, frame);
byte[] theirMac = new byte[10];
System.arraycopy(frame, frame.length - 10, theirMac, 0, theirMac.length);

View File

@@ -0,0 +1,169 @@
package org.thoughtcrime.securesms.blocked;
import android.app.AlertDialog;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.ViewSwitcher;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
import com.google.android.material.snackbar.Snackbar;
import org.thoughtcrime.securesms.ContactSelectionListFragment;
import org.thoughtcrime.securesms.PassphraseRequiredActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.whispersystems.libsignal.util.guava.Optional;
public class BlockedUsersActivity extends PassphraseRequiredActivity implements BlockedUsersFragment.Listener, ContactSelectionListFragment.OnContactSelectedListener {
private static final String CONTACT_SELECTION_FRAGMENT = "Contact.Selection.Fragment";
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
private BlockedUsersViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState, boolean ready) {
super.onCreate(savedInstanceState, ready);
dynamicTheme.onCreate(this);
setContentView(R.layout.blocked_users_activity);
BlockedUsersRepository repository = new BlockedUsersRepository(this);
BlockedUsersViewModel.Factory factory = new BlockedUsersViewModel.Factory(repository);
viewModel = ViewModelProviders.of(this, factory).get(BlockedUsersViewModel.class);
ViewSwitcher viewSwitcher = findViewById(R.id.toolbar_switcher);
Toolbar toolbar = findViewById(R.id.toolbar);
ContactFilterToolbar contactFilterToolbar = findViewById(R.id.filter_toolbar);
View container = findViewById(R.id.fragment_container);
toolbar.setNavigationOnClickListener(unused -> onBackPressed());
contactFilterToolbar.setNavigationOnClickListener(unused -> onBackPressed());
contactFilterToolbar.setOnFilterChangedListener(query -> {
Fragment fragment = getSupportFragmentManager().findFragmentByTag(CONTACT_SELECTION_FRAGMENT);
if (fragment != null) {
((ContactSelectionListFragment) fragment).setQueryFilter(query);
}
});
contactFilterToolbar.setHint(R.string.BlockedUsersActivity__add_blocked_user);
//noinspection CodeBlock2Expr
getSupportFragmentManager().addOnBackStackChangedListener(() -> {
viewSwitcher.setDisplayedChild(getSupportFragmentManager().getBackStackEntryCount());
if (getSupportFragmentManager().getBackStackEntryCount() == 1) {
contactFilterToolbar.focusAndShowKeyboard();
}
});
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, new BlockedUsersFragment())
.commit();
viewModel.getEvents().observe(this, event -> handleEvent(container, event));
}
@Override
protected void onResume() {
super.onResume();
dynamicTheme.onResume(this);
}
@Override
public boolean onBeforeContactSelected(Optional<RecipientId> recipientId, String number) {
final String displayName = recipientId.transform(id -> Recipient.resolved(id).getDisplayName(this)).or(number);
AlertDialog confirmationDialog = new AlertDialog.Builder(BlockedUsersActivity.this)
.setTitle(R.string.BlockedUsersActivity__block_user)
.setMessage(getString(R.string.BlockedUserActivity__s_will_not_be_able_to, displayName))
.setPositiveButton(R.string.BlockedUsersActivity__block, (dialog, which) -> {
if (recipientId.isPresent()) {
viewModel.block(recipientId.get());
} else {
viewModel.createAndBlock(number);
}
dialog.dismiss();
onBackPressed();
})
.setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.dismiss())
.setCancelable(true)
.create();
confirmationDialog.setOnShowListener(dialog -> {
confirmationDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(Color.RED);
});
confirmationDialog.show();
return false;
}
@Override
public void onContactDeselected(Optional<RecipientId> recipientId, String number) {
}
@Override
public void handleAddUserToBlockedList() {
ContactSelectionListFragment fragment = new ContactSelectionListFragment();
Intent intent = getIntent();
intent.putExtra(ContactSelectionListFragment.REFRESHABLE, false);
intent.putExtra(ContactSelectionListFragment.SELECTION_LIMITS, 1);
intent.putExtra(ContactSelectionListFragment.HIDE_COUNT, true);
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE,
ContactsCursorLoader.DisplayMode.FLAG_PUSH |
ContactsCursorLoader.DisplayMode.FLAG_SMS |
ContactsCursorLoader.DisplayMode.FLAG_ACTIVE_GROUPS |
ContactsCursorLoader.DisplayMode.FLAG_INACTIVE_GROUPS |
ContactsCursorLoader.DisplayMode.FLAG_BLOCK);
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, fragment, CONTACT_SELECTION_FRAGMENT)
.addToBackStack(null)
.commit();
}
private void handleEvent(@NonNull View view, @NonNull BlockedUsersViewModel.Event event) {
final String displayName;
if (event.getRecipient() == null) {
displayName = event.getNumber();
} else {
displayName = event.getRecipient().getDisplayName(this);
}
final @StringRes int messageResId;
switch (event.getEventType()) {
case BLOCK_SUCCEEDED:
messageResId = R.string.BlockedUsersActivity__s_has_been_blocked;
break;
case BLOCK_FAILED:
messageResId = R.string.BlockedUsersActivity__failed_to_block_s;
break;
case UNBLOCK_SUCCEEDED:
messageResId = R.string.BlockedUsersActivity__s_has_been_unblocked;
break;
default:
throw new IllegalArgumentException("Unsupported event type " + event);
}
Snackbar.make(view, getString(messageResId, displayName), Snackbar.LENGTH_SHORT).show();
}
}

View File

@@ -0,0 +1,96 @@
package org.thoughtcrime.securesms.blocked;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.util.Consumer;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.recipients.Recipient;
import java.util.Objects;
final class BlockedUsersAdapter extends ListAdapter<Recipient, BlockedUsersAdapter.ViewHolder> {
private final RecipientClickedListener recipientClickedListener;
BlockedUsersAdapter(@NonNull RecipientClickedListener recipientClickedListener) {
super(new RecipientDiffCallback());
this.recipientClickedListener = recipientClickedListener;
}
@Override
public @NonNull ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.blocked_users_adapter_item, parent, false),
position -> recipientClickedListener.onRecipientClicked(Objects.requireNonNull(getItem(position))));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.bind(Objects.requireNonNull(getItem(position)));
}
static final class ViewHolder extends RecyclerView.ViewHolder {
private final AvatarImageView avatar;
private final TextView displayName;
private final TextView numberOrUsername;
public ViewHolder(@NonNull View itemView, Consumer<Integer> clickConsumer) {
super(itemView);
this.avatar = itemView.findViewById(R.id.avatar);
this.displayName = itemView.findViewById(R.id.display_name);
this.numberOrUsername = itemView.findViewById(R.id.number_or_username);
itemView.setOnClickListener(unused -> {
if (getAdapterPosition() != RecyclerView.NO_POSITION) {
clickConsumer.accept(getAdapterPosition());
}
});
}
public void bind(@NonNull Recipient recipient) {
avatar.setAvatar(recipient);
displayName.setText(recipient.getDisplayName(itemView.getContext()));
if (recipient.hasAUserSetDisplayName(itemView.getContext())) {
String identifier = recipient.getE164().or(recipient.getUsername()).orNull();
if (identifier != null) {
numberOrUsername.setText(identifier);
numberOrUsername.setVisibility(View.VISIBLE);
} else {
numberOrUsername.setVisibility(View.GONE);
}
} else {
numberOrUsername.setVisibility(View.GONE);
}
}
}
private static final class RecipientDiffCallback extends DiffUtil.ItemCallback<Recipient> {
@Override
public boolean areItemsTheSame(@NonNull Recipient oldItem, @NonNull Recipient newItem) {
return oldItem.equals(newItem);
}
@Override
public boolean areContentsTheSame(@NonNull Recipient oldItem, @NonNull Recipient newItem) {
return oldItem.equals(newItem);
}
}
interface RecipientClickedListener {
void onRecipientClicked(@NonNull Recipient recipient);
}
}

View File

@@ -0,0 +1,100 @@
package org.thoughtcrime.securesms.blocked;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.RecyclerView;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.recipients.Recipient;
public class BlockedUsersFragment extends Fragment {
private BlockedUsersViewModel viewModel;
private Listener listener;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof Listener) {
listener = (Listener) context;
} else {
throw new ClassCastException("Expected context to implement Listener");
}
}
@Override
public void onDetach() {
super.onDetach();
listener = null;
}
@Override
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.blocked_users_fragment, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
View addUser = view.findViewById(R.id.add_blocked_user_touch_target);
RecyclerView recycler = view.findViewById(R.id.blocked_users_recycler);
View empty = view.findViewById(R.id.no_blocked_users);
BlockedUsersAdapter adapter = new BlockedUsersAdapter(this::handleRecipientClicked);
recycler.setAdapter(adapter);
addUser.setOnClickListener(unused -> {
if (listener != null) {
listener.handleAddUserToBlockedList();
}
});
viewModel = ViewModelProviders.of(requireActivity()).get(BlockedUsersViewModel.class);
viewModel.getRecipients().observe(getViewLifecycleOwner(), list -> {
if (list.isEmpty()) {
empty.setVisibility(View.VISIBLE);
} else {
empty.setVisibility(View.GONE);
}
adapter.submitList(list);
});
}
private void handleRecipientClicked(@NonNull Recipient recipient) {
AlertDialog confirmationDialog = new AlertDialog.Builder(requireContext())
.setTitle(R.string.BlockedUsersActivity__unblock_user)
.setMessage(getString(R.string.BlockedUsersActivity__do_you_want_to_unblock_s, recipient.getDisplayName(requireContext())))
.setPositiveButton(R.string.BlockedUsersActivity__unblock, (dialog, which) -> {
viewModel.unblock(recipient.getId());
dialog.dismiss();
})
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {
dialog.dismiss();
})
.setCancelable(true)
.create();
confirmationDialog.setOnShowListener(dialog -> {
confirmationDialog.getButton(DialogInterface.BUTTON_POSITIVE).setTextColor(Color.RED);
});
confirmationDialog.show();
}
interface Listener {
void handleAddUserToBlockedList();
}
}

View File

@@ -0,0 +1,76 @@
package org.thoughtcrime.securesms.blocked;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.core.util.Consumer;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.groups.GroupChangeBusyException;
import org.thoughtcrime.securesms.groups.GroupChangeFailedException;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class BlockedUsersRepository {
private static final String TAG = Log.tag(BlockedUsersRepository.class);
private final Context context;
BlockedUsersRepository(@NonNull Context context) {
this.context = context;
}
void getBlocked(@NonNull Consumer<List<Recipient>> blockedUsers) {
SignalExecutors.BOUNDED.execute(() -> {
RecipientDatabase db = DatabaseFactory.getRecipientDatabase(context);
try (RecipientDatabase.RecipientReader reader = db.readerForBlocked(db.getBlocked())) {
int count = reader.getCount();
if (count == 0) {
blockedUsers.accept(Collections.emptyList());
} else {
List<Recipient> recipients = new ArrayList<>();
while (reader.getNext() != null) {
recipients.add(reader.getCurrent());
}
blockedUsers.accept(recipients);
}
}
});
}
void block(@NonNull RecipientId recipientId, @NonNull Runnable success, @NonNull Runnable failure) {
SignalExecutors.BOUNDED.execute(() -> {
try {
RecipientUtil.block(context, Recipient.resolved(recipientId));
success.run();
} catch (IOException | GroupChangeFailedException | GroupChangeBusyException e) {
Log.w(TAG, "block: failed to block recipient: ", e);
failure.run();
}
});
}
void createAndBlock(@NonNull String number, @NonNull Runnable success) {
SignalExecutors.BOUNDED.execute(() -> {
RecipientUtil.blockNonGroup(context, Recipient.external(context, number));
success.run();
});
}
void unblock(@NonNull RecipientId recipientId, @NonNull Runnable success) {
SignalExecutors.BOUNDED.execute(() -> {
RecipientUtil.unblock(context, Recipient.resolved(recipientId));
success.run();
});
}
}

View File

@@ -0,0 +1,115 @@
package org.thoughtcrime.securesms.blocked;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.SingleLiveEvent;
import java.util.List;
import java.util.Objects;
public class BlockedUsersViewModel extends ViewModel {
private final BlockedUsersRepository repository;
private final MutableLiveData<List<Recipient>> recipients;
private final SingleLiveEvent<Event> events = new SingleLiveEvent<>();
private BlockedUsersViewModel(@NonNull BlockedUsersRepository repository) {
this.repository = repository;
this.recipients = new MutableLiveData<>();
loadRecipients();
}
public LiveData<List<Recipient>> getRecipients() {
return recipients;
}
public LiveData<Event> getEvents() {
return events;
}
void block(@NonNull RecipientId recipientId) {
repository.block(recipientId,
() -> {
loadRecipients();
events.postValue(new Event(EventType.BLOCK_SUCCEEDED, Recipient.resolved(recipientId)));
},
() -> events.postValue(new Event(EventType.BLOCK_FAILED, Recipient.resolved(recipientId))));
}
void createAndBlock(@NonNull String number) {
repository.createAndBlock(number, () -> {
loadRecipients();
events.postValue(new Event(EventType.BLOCK_SUCCEEDED, number));
});
}
void unblock(@NonNull RecipientId recipientId) {
repository.unblock(recipientId, () -> {
loadRecipients();
events.postValue(new Event(EventType.UNBLOCK_SUCCEEDED, Recipient.resolved(recipientId)));
});
}
private void loadRecipients() {
repository.getBlocked(recipients::postValue);
}
enum EventType {
BLOCK_SUCCEEDED,
BLOCK_FAILED,
UNBLOCK_SUCCEEDED
}
public static final class Event {
private final EventType eventType;
private final Recipient recipient;
private final String number;
private Event(@NonNull EventType eventType, @NonNull Recipient recipient) {
this.eventType = eventType;
this.recipient = recipient;
this.number = null;
}
private Event(@NonNull EventType eventType, @NonNull String number) {
this.eventType = eventType;
this.recipient = null;
this.number = number;
}
public @Nullable Recipient getRecipient() {
return recipient;
}
public @Nullable String getNumber() {
return number;
}
public @NonNull EventType getEventType() {
return eventType;
}
}
public static class Factory implements ViewModelProvider.Factory {
private final BlockedUsersRepository repository;
public Factory(@NonNull BlockedUsersRepository repository) {
this.repository = repository;
}
@Override
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
return Objects.requireNonNull(modelClass.cast(new BlockedUsersViewModel(repository)));
}
}
}

View File

@@ -20,8 +20,6 @@
*/
package org.thoughtcrime.securesms.blurhash;
import android.text.TextUtils;
import androidx.annotation.Nullable;
final class Base83 {

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