Compare commits

...

1675 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
Cody Henthorne
9c8857352b Bump version to 4.71.2 2020-09-09 16:09:49 -04:00
Cody Henthorne
c09a1fdba8 Updated language translations. 2020-09-09 16:04:27 -04:00
Greyson Parrelli
cdc7033a51 Update CDS enclave. 2020-09-09 15:38:42 -04:00
Alex Hart
fa30c759d7 Fix PIP positioning in video calls. 2020-09-09 13:06:38 -03:00
Fumiaki Yoshimatsu
d040be2df0 Use the light styles in the action bar style in the light theme, but keep the dark theme version of it in the action mode.
Fixes #9932
2020-09-09 12:24:45 -03:00
Alan Evans
935c831a7f Fix equality comparison causing blank updates and "The group was updated" messages. 2020-09-09 12:16:09 -03:00
Cody Henthorne
867e95eef1 Re-download sticker if backing file data no longer exists. 2020-09-09 11:15:34 -04:00
Alan Evans
2ee04bd1b6 Insert placeholder group on GV2 storage service sync. 2020-09-09 11:59:09 -03:00
Greyson Parrelli
75d567e555 Implement new client deprecation UI. 2020-09-09 10:22:22 -04:00
Alex Hart
d8a489971c Fix missing reply arrows. 2020-09-09 10:42:14 -03:00
Greyson Parrelli
19ce5b5c76 Reduce APNGParser logging. 2020-09-08 18:08:40 -04:00
Greyson Parrelli
7c70ea4d3e Change directory refresh interval to every 24 hours. 2020-09-08 18:06:09 -04:00
Greyson Parrelli
2784285d47 Add support for fetching remote deprecation. 2020-09-08 18:03:56 -04:00
Cody Henthorne
c946a7a1d5 Bump version to 4.71.1 2020-09-08 14:30:22 -04:00
Greyson Parrelli
3e60b49b8b Updated language translations. 2020-09-08 14:25:34 -04:00
Cody Henthorne
4e7331bbb8 Fix typo in trim message history copy. 2020-09-08 14:15:10 -04:00
Cody Henthorne
b8c7e86223 Fix improper deletion of stickers when restored from backup. 2020-09-08 14:07:56 -04:00
Alex Hart
3b925f8674 Add in-app donate button to preferences screen. 2020-09-08 12:48:52 -03:00
Cody Henthorne
f1f6d41c73 Bumped version to 4.71.0 2020-09-08 09:47:58 -04:00
Alan Evans
29ef1cb1be Updated language translations. 2020-09-08 09:47:58 -04:00
Alan Evans
4296085d65 Show no notification actions when the message content is hidden.
Fixes #9928
2020-09-08 09:47:57 -04:00
Alan Evans
c797b09228 Set profile sharing based on who added you to the group. 2020-09-08 09:47:57 -04:00
Greyson Parrelli
a870ef0030 Set isRecipientUpdate based on delivery status, not address count.
We were setting isRecipientUpdate to `true` incorrectly if there were
unregistered people in the group, resulting in the message not being
rendered on linked devices. Instead of using the address count, we can
just look at the current receipt status of the message.

Fixes #9981
2020-09-08 09:47:57 -04:00
Alan Evans
43ed9e7310 Set discoverable account attribute. 2020-09-08 09:47:57 -04:00
Cody Henthorne
bcd27355f9 Add trim conversations by time option. 2020-09-08 09:47:57 -04:00
Alan Evans
6a14dc69c0 Make Group V2 creation driven by version flag. 2020-09-03 20:23:26 -04:00
Jim Gustafson
ed9acd25f9 Ensure serial handling of calling events and improve busy UX. 2020-09-03 20:23:26 -04:00
Alan Evans
7b24e66ed3 Phone number privacy settings and certificate support behind feature flag. 2020-09-03 20:23:26 -04:00
Alan Evans
abd3d4b546 Group link copy changes. 2020-09-03 20:23:26 -04:00
Alan Evans
4040c4240a Lighter weight mentions membership query. 2020-09-03 20:23:26 -04:00
Alan Evans
1ee747f3ef Always share profile as part of unblocking. 2020-09-03 20:23:26 -04:00
Alan Evans
f88874bec8 Default values for member level and admin when no UUID. 2020-09-03 20:23:26 -04:00
Greyson Parrelli
ed440a2150 Do not clear UUID for unregistered users.
Otherwise, a number could be unregistered and re-registered by a
different person, assigning a new UUID to an existing RecipientId,
which we never want to do.
2020-09-03 20:23:26 -04:00
Greyson Parrelli
2fd46b196b Show sticker emoji in notification. 2020-09-03 20:23:26 -04:00
Greyson Parrelli
12dfcaf7e7 Log sent timestamp with message sends. 2020-09-03 20:23:26 -04:00
Greyson Parrelli
f4a199f621 Add support for animated stickers. 2020-09-03 20:23:26 -04:00
Alan Evans
bb708e0aa3 Ignore link preview descriptions that match the title. 2020-09-03 20:23:26 -04:00
Alan Evans
d625740ca4 Ensure feature flag is string before cast. 2020-09-03 20:23:26 -04:00
Greyson Parrelli
250402e9b9 Add support for rendering APNGs. 2020-09-03 20:23:26 -04:00
Jim Gustafson
1d2ffe56fb Update to RingRTC v2.5.1 2020-09-01 15:43:07 -04:00
Alan Evans
d16c0d2887 Prevent autofill for username editor. 2020-09-01 15:43:07 -04:00
Cody Henthorne
b3555f2f94 Use updated Safety Number Change dialog for calls.
Fixes [#9940](https://github.com/signalapp/Signal-Android/issues/9940)
2020-09-01 15:43:07 -04:00
Greyson Parrelli
83a638fc6d Bump version to 4.70.5 2020-09-01 14:56:32 -04:00
Greyson Parrelli
f1534a710f Updated language translations. 2020-09-01 14:56:08 -04:00
Greyson Parrelli
a16845340b Update CDS enclave. 2020-09-01 14:56:08 -04:00
Alan Evans
ffa4725f8e Bump version to 4.70.4 2020-08-31 12:54:22 -03:00
Alan Evans
7792c66c64 Updated language translations. 2020-08-31 12:50:08 -03:00
Alan Evans
1a3985d709 Add QR group link share. 2020-08-31 12:35:38 -03:00
Greyson Parrelli
4714895c59 Do not attempt to send to unregistered users when using CDS flag.
CDS is slow, and unregistered users will always trigger a CDS lookup on
send (since we can't get their UUID).

This starts skipping sends to unregistered users and shortens the time
window to do a full CDS lookup from every 12 hours to every 6 hours.
2020-08-31 11:33:57 -04:00
Fumiaki Yoshimatsu
1e37951701 Use onCreateOptionsMenu when to inflate a menu in order for menu items to appear correctly in RTL languages.
The bug was reported in
https://community.signalusers.org/t/beta-feedback-for-the-upcoming-android-4-70-release/16449/20?u=alan-signal, but it was not necessarily a regression caused by the commit suggested in the forum post. It is more like that the bug was finally exposed by the commit. Before the commit the menu items were not properly aligned nor translated upon configuration changes in RTL languages.
2020-08-31 12:12:15 -03:00
Alan Evans
e8be1ad752 Handle GV2 sync messages. 2020-08-31 12:07:03 -03:00
Alan Evans
e316a70b6c Fix group limit enforcement and display. 2020-08-31 12:02:50 -03:00
Alan Evans
40a8d21c15 Fix to allow send of Signal invitation SMS to a single person.
Fixes #9970
2020-08-31 11:33:50 -03:00
Alan Evans
28d5ca7ed9 Bump version to 4.70.3 2020-08-28 10:49:31 -03:00
Alan Evans
110b18545f Updated language translations. 2020-08-28 10:44:03 -03:00
Alan Evans
a478605da4 Remove requesting members if they are directly added to the group. 2020-08-28 10:32:20 -03:00
Alan Evans
f5f1589813 Fix class cast exception when member is approved. 2020-08-28 10:32:20 -03:00
Greyson Parrelli
0c332b6adb Fix corner cases with LinkPreviewViewModel enabled state. 2020-08-28 09:30:03 -04:00
Greyson Parrelli
ba712ce357 Fix crash with link preview date formatting on Android < 7.
The 'X' wasn't supported until Android 7.
2020-08-28 09:30:03 -04:00
Alan Evans
2d2395accf Hide block options if recipient is not blockable. 2020-08-28 10:13:23 -03:00
Alan Evans
8634289b7a Bump version to 4.70.2 2020-08-27 17:39:17 -03:00
Alan Evans
45043fb9a8 Updated language translations. 2020-08-27 17:38:23 -03:00
Cody Henthorne
0449795725 Make top gradient disappear with call controls.
Fixes [#9951](https://github.com/signalapp/Signal-Android/issues/9951)
2020-08-27 16:26:15 -04:00
Alan Evans
a96093f1b7 Exclude unused facial models from APK. 2020-08-27 17:01:10 -03:00
Alex Hart
bd4f7691e9 Add proper background color to camera icon.
Fixes #9945
2020-08-27 17:00:05 -03:00
Alex Hart
e12acbae70 Add @ to username in preferences. 2020-08-27 16:34:21 -03:00
Alan Evans
48dc4eac10 Bump version to 4.70.1 2020-08-27 12:25:39 -03:00
Alan Evans
a869c92eee Updated language translations. 2020-08-27 12:23:00 -03:00
Greyson Parrelli
4fefd14538 Add unit test to prevent shipping forced feature flags. 2020-08-27 11:14:20 -04:00
Greyson Parrelli
c09dbfa47c Prevent corner-case where link previews were generated for SMS.
Also added some hardening to make sure that it's impossible for any link
previews to be fetched if the setting is disabled (this was already the
case in practice, we just have some assertions in there now).

Fixes #9956
2020-08-27 12:12:44 -03:00
Alan Evans
d3c9f66de6 Prevent simple dialog flicker. 2020-08-27 12:12:44 -03:00
Alan Evans
01d7694108 Add reset confirmation dialog and copy to group link management screen. 2020-08-27 12:12:44 -03:00
Alex Hart
1425b651d4 Update username UX and UI. 2020-08-27 12:12:44 -03:00
Greyson Parrelli
b1befbeefc Add additional LinkPreviewUtil unit tests.
Also updated the date format -- funnily enough Android will work with
either Z or X in the format, but the test JVM will fail if it doesn't
use X. X is definitely the correct thing to use based on the Javadoc, I
think Android's implementation is just a little more lenient.
2020-08-27 09:32:33 -04:00
Panagiotis Vasilopoulos
3a9a84a0b1 Do not attempt to create link previews for .i2p links 2020-08-27 10:01:50 -03:00
Alan Evans
368284cccc Enable auto verify for signal.group links. 2020-08-26 20:48:42 -03:00
Alan Evans
ef777f4db9 Make group links remote capable. 2020-08-26 18:02:42 -03:00
Alan Evans
a8e4e8e882 Bump version to 4.70.0 2020-08-26 17:25:54 -03:00
Alan Evans
cf93760d00 Updated language translations. 2020-08-26 17:25:54 -03:00
Greyson Parrelli
dd8b9ff8fb Add support for article dates in link previews. 2020-08-26 17:25:54 -03:00
Alan Evans
bfed03b7b5 Manage group links behind feature flag. 2020-08-26 17:25:54 -03:00
Alan Evans
860f06ec9e Join group via invite link. 2020-08-26 12:51:25 -03:00
Alex Hart
b58376920f Order pinned conversations in "first added" order instead of reordering as messages come in. 2020-08-26 11:13:01 -03:00
Alan Evans
4ace075ddf Display membership count in link preview description field. 2020-08-26 09:26:25 -03:00
Fumiaki Yoshimatsu
dda98a474d Listen to the uiMode configuration changes.
Fixes #9736
Fixes #9922
2020-08-25 17:11:29 -03:00
Alan Evans
f1c0df7d87 Update KBS Service Id on staging. 2020-08-25 17:10:07 -03:00
Greyson Parrelli
c78e098cb4 Add support for link preview descriptions. 2020-08-25 16:05:39 -04:00
Alex Hart
a3438c4f8d Change where edit profile screen requests camera permission. 2020-08-25 16:35:16 -03:00
Alan Evans
92ecf2d5de Add group link join version feature flag. 2020-08-25 16:35:06 -03:00
Alex Hart
f18b653725 Fix crash when scrolling to the top of a conversation. 2020-08-25 15:17:21 -03:00
Alex Hart
5128438cfb Fix action bar usability in vertical screen split. 2020-08-25 09:33:11 -03:00
Greyson Parrelli
f29f25822b Have DatabaseFactory.getMmsDatabase() return MessageDatabase. 2020-08-24 16:40:47 -04:00
Greyson Parrelli
ecfe218840 Bump version to 4.69.6 2020-08-24 14:34:53 -04:00
Greyson Parrelli
dd33d2b5d0 Updated language translations. 2020-08-24 14:34:28 -04:00
Alex Hart
12a8d4e10b Fix crash on multi-archive. 2020-08-24 14:34:28 -04:00
Alex Hart
c5c2fb31b1 Fix CREATE statement for RecipientDatabase. 2020-08-24 14:25:37 -04:00
Alex Hart
343b7faf98 Bumped version to 4.69.5 2020-08-24 11:16:41 -03:00
Alex Hart
18aa8bbf60 Updated language translations. 2020-08-24 11:16:41 -03:00
Greyson Parrelli
a358d1630f Rotate the CDS feature flag. 2020-08-24 11:16:41 -03:00
Alan Evans
01375b321c Don't release bitmaps managed by Glide, and don't cache group preview avatars. 2020-08-24 11:16:41 -03:00
Alex Hart
d2739d52e0 Remember position in react-with-any-emoji picker. 2020-08-24 11:16:41 -03:00
Alex Hart
4668510106 Fix crash when archiving multiple conversations. 2020-08-24 11:16:41 -03:00
Alex Hart
ffcd311c90 Fix strange long press behavior in convo list.
Fixes #9944
2020-08-24 11:16:41 -03:00
Alex Hart
b94a636542 Apply Content-Range and Content-Length headers to resumable upload request. 2020-08-24 11:16:41 -03:00
Jim Gustafson
a7aec6bfbc Update to RingRTC v2.5.0 2020-08-24 11:16:41 -03:00
Greyson Parrelli
190ca9eddd Have DatabaseFactory.getSmsDatabase() return MessageDatabase.
Slowly moving towards a single interface.
2020-08-24 11:16:41 -03:00
Greyson Parrelli
2cf9eb69eb Add support for handling unknown protobuf fields. 2020-08-24 11:16:41 -03:00
Alan Evans
ffcb90da52 Accept any length group link password. 2020-08-24 11:16:41 -03:00
Alan Evans
878b0c9275 Change group invite link host. 2020-08-24 11:16:41 -03:00
Evan Hahn
5505cb0dea Update donation link in contribution instructions. 2020-08-24 11:16:41 -03:00
Alex Hart
7ac14dccda Refresh username in onResume and utilize imeAction. 2020-08-24 11:16:41 -03:00
Greyson Parrelli
6cffd0a723 Update link preview sync settings.
We need to rotate the link preview setting to avoid newer desktops with
older mobile clients from generating proxy-less previews.
2020-08-24 11:16:41 -03:00
Alan Evans
220ebf93c7 During registration, persist time that call me is available.
Fixes #9926
2020-08-19 16:32:01 -04:00
Greyson Parrelli
d0681a5592 Make calling status strings consistent.
Fixes #9904
2020-08-19 16:32:01 -04:00
Alan Evans
09d167c16d Group link preview and info display bottom sheet. 2020-08-19 16:32:01 -04:00
Alan Evans
477bb45df7 Group invite link epoch support. 2020-08-19 16:32:01 -04:00
Alex Hart
e006306036 Utilize ItemCallback for ReactWithAnyAdapter.
Fixes #9918
2020-08-19 16:32:01 -04:00
Greyson Parrelli
065cbcf0f9 Bump version to 4.69.4 2020-08-19 16:08:07 -04:00
Greyson Parrelli
7a6b958bbe Updated language translations. 2020-08-19 16:07:46 -04:00
Cody Henthorne
ef6a5b6599 Fix bug causing call requests to not be handled properly. 2020-08-19 15:49:16 -04:00
Greyson Parrelli
cdae919b5e Bump version to 4.69.3 2020-08-19 10:03:26 -04:00
Greyson Parrelli
12889f4549 Updated language translations. 2020-08-19 10:03:04 -04:00
Greyson Parrelli
089d59b691 Properly mark local note-to-self attachments as uploaded. 2020-08-19 09:59:37 -04:00
Alex Hart
b3e247e9cc Fix crash when loading vector from typed array.
Fixes #9933
2020-08-19 10:45:01 -03:00
Greyson Parrelli
56392b87f7 Bump version to 4.69.2 2020-08-18 19:22:42 -04:00
Greyson Parrelli
1b1a4aeb38 Updated language translations. 2020-08-18 19:22:18 -04:00
Greyson Parrelli
16147e0c08 Ensure link preview fetches are canceled on message send. 2020-08-18 18:34:18 -04:00
Cody Henthorne
139317cf1b Improve various aspects of mentions. 2020-08-18 18:13:45 -04:00
Cody Henthorne
72b94127fb Stop muted threads from triggering full notification updates. 2020-08-18 14:15:55 -04:00
Alan Evans
1f1fc94d22 Fix flakey robolectric test. 2020-08-18 11:57:35 -03:00
Greyson Parrelli
a574fe026c Bump version to 4.69.1 2020-08-17 12:04:41 -04:00
Greyson Parrelli
aa82083d30 Updated language translations. 2020-08-17 12:04:41 -04:00
Greyson Parrelli
08d5df70c2 Don't show the link preview megaphone if previously disabled. 2020-08-17 12:04:41 -04:00
Greyson Parrelli
29b8fa5897 Keep pinned chats at the top of the 'recent' chat section. 2020-08-17 11:12:10 -04:00
Alex Hart
e96faf31d4 Fix browser opening on long-press of debug log links. 2020-08-17 11:54:41 -03:00
Greyson Parrelli
157a73aa99 Fix title of conversation pin menu item. 2020-08-17 10:37:17 -04:00
Greyson Parrelli
bdd298c8a0 Prevent swipe actions on the 'Pinned' header. 2020-08-17 10:31:28 -04:00
Greyson Parrelli
3f7dd21186 Do not attempt to create link previews for .onion links. 2020-08-17 10:27:30 -04:00
Greyson Parrelli
086b708cf7 Fix NPE when double-tapping the conversation pinning icon. 2020-08-17 10:07:58 -04:00
Alan Evans
57e0e57f48 Fix NPE when link preview image cannot be decoded. 2020-08-15 10:10:15 -03:00
Greyson Parrelli
4b7efbfdc0 Bump version to 4.69.0 2020-08-14 15:54:06 -04:00
Greyson Parrelli
7dc2653042 Updated language translations. 2020-08-14 15:54:06 -04:00
Cody Henthorne
e428453835 Fix conversation list bug with pinned chats.
Co-authored-by: Alex Hart <alex@signal.org>
2020-08-14 15:54:06 -04:00
Greyson Parrelli
f84c8229de Revert "Replace a call to a deprecated method to update context with the new one."
This reverts commit 5f0d384c9e.

Introduced a bug where the system theme wasn't changing until app
restart.
2020-08-14 15:54:06 -04:00
Alex Hart
a73427d68d Fix issues with conversation list position. 2020-08-14 15:54:05 -04:00
Alan Evans
e4456bb236 Handle GV2 addresses. 2020-08-14 15:54:05 -04:00
Alex Hart
06eadd0c15 Add mentions unread counter. 2020-08-14 15:54:05 -04:00
Alan Evans
3c90dfa660 Ensure a GV2 update message mentioning you as a new member is first in the list. 2020-08-14 15:54:05 -04:00
Greyson Parrelli
ace1b8ee71 Update link preview settings and add some UI polish. 2020-08-14 15:54:05 -04:00
Cody Henthorne
676356e800 Add Mentions Megaphone. 2020-08-14 15:54:05 -04:00
Greyson Parrelli
f732e54c22 Update group size flag. 2020-08-14 15:54:05 -04:00
Cody Henthorne
cdc2e74f68 Stop conversations without meaningful messages from showing in list. 2020-08-14 15:54:05 -04:00
Cody Henthorne
724f3e872b Update Mention UI/UX to match latest designs. 2020-08-14 15:54:05 -04:00
Alex Hart
d63e5165eb Add ability to pin up to 4 conversations. 2020-08-14 15:54:05 -04:00
Cody Henthorne
9892c4392e Fix janky avatar preview transition for notched devices. 2020-08-14 15:54:05 -04:00
Cody Henthorne
5ced1a775c Fix bug where SN change dialog appeared unnecessarily. 2020-08-14 15:54:05 -04:00
Cody Henthorne
761de1318e Update mention data during recipient merge. 2020-08-14 15:54:05 -04:00
Cody Henthorne
02508512d5 Fix incorrect snippet generation by ignoring profile name change messages. 2020-08-14 15:54:05 -04:00
Greyson Parrelli
6e6105af05 Open up link previews to work with all sites. 2020-08-14 15:54:05 -04:00
Jared Andrews
d569419e13 Fixes conversation overflow menu items not being tappable.
Fixes #9908
2020-08-13 19:47:46 -04:00
Greyson Parrelli
93f1641803 Bump version to 4.68.8 2020-08-10 21:13:19 -04:00
Greyson Parrelli
ff52bf93fa Make the CDS flag remote capable. 2020-08-10 13:27:11 -04:00
Greyson Parrelli
a039275a0c Bump version to 4.68.7 2020-08-10 11:40:37 -04:00
Greyson Parrelli
a98d10104d Updated language translations. 2020-08-10 11:39:30 -04:00
Alan Evans
8924bc59b1 Hide legacy group warning when GV2 create feature flag is off or MMS is forced.
Fixes #9913
2020-08-08 17:43:07 -03:00
Greyson Parrelli
eefe60a9c9 Bump version to 4.68.6 2020-08-07 19:37:05 -04:00
Greyson Parrelli
fe1cb3d904 Updated language translations. 2020-08-07 19:36:26 -04:00
Greyson Parrelli
0448278a78 Include a recipient in sent transcripts when possible. 2020-08-07 19:20:35 -04:00
Greyson Parrelli
99c0c2ff4c Fix crash when opening debuglogs during registration. 2020-08-07 19:20:35 -04:00
Greyson Parrelli
b369b734ca Improve storage service insert recovery. 2020-08-07 19:20:35 -04:00
Greyson Parrelli
57150a20fd Make verificationV2 a separate flag. 2020-08-07 19:20:35 -04:00
Cody Henthorne
1634d7d531 Show mention picker immediately after @ entered. 2020-08-07 15:27:15 -04:00
Cody Henthorne
d563de4207 Add mention detection to search flows. 2020-08-07 15:18:40 -04:00
Greyson Parrelli
5cd4b82ed0 Bump version to 4.68.5 2020-08-06 21:03:31 -04:00
Greyson Parrelli
5f728d348c Updated language translations. 2020-08-06 21:02:22 -04:00
Greyson Parrelli
596c4b6e40 Don't include inactive groups when listing groups in common. 2020-08-06 20:57:50 -04:00
Alex Hart
36d1e7c44a Disable Contact Join Notification via Action. 2020-08-06 20:57:50 -04:00
Alan Evans
25c17082f2 Share a common groups v2 capacity flag across clients. 2020-08-06 20:57:50 -04:00
Alan Evans
810ccf8e94 Improve GV2 Invitation revoke experience. 2020-08-06 20:57:50 -04:00
Alex Hart
c8ed0b19f0 Do not update thread on profile name change. 2020-08-06 20:57:50 -04:00
Alan Evans
9e09444c65 Increment the Groups V2 feature flags version. 2020-08-06 20:57:50 -04:00
Greyson Parrelli
5923fa0cd5 Block sends on CDS lookups. 2020-08-06 20:57:50 -04:00
Cody Henthorne
b2d4c5d14b Add mentions for v2 group chats. 2020-08-06 20:57:50 -04:00
Alex Hart
0bb9c1d650 Add light and dark spinner lotties with correct coloring. 2020-08-06 20:57:50 -04:00
Alan Evans
fbfa3abffd Skip delete actions where the removed member/pending member is not in the group. 2020-08-06 20:57:50 -04:00
Alan Evans
b5656aa5dd Exclude non-translatable multiline blocks. 2020-08-06 20:57:50 -04:00
Alan Evans
d53fd6a109 Change invite cancel to invite revoke. 2020-08-06 20:57:50 -04:00
Alan Evans
b0650b926b Fix pending member group edit rights. 2020-08-06 20:57:50 -04:00
Alan Evans
845f6a0a93 Notify user during group create of members that do not support GV2. 2020-08-06 20:57:50 -04:00
Alex Hart
d8daa83c79 Remove autoLink from conversation update items. 2020-08-06 20:57:50 -04:00
Alex Hart
7bb0199e83 Change additional groups copy to match iOS. 2020-08-06 20:57:50 -04:00
Alex Hart
f014dadf06 Adjust Zoom levels and transition duration. 2020-08-06 20:57:50 -04:00
Alex Hart
393e54ce91 Update how we mark messages as read. 2020-08-06 20:57:50 -04:00
Alan Evans
fdf4ad9543 Remove the GV2 "anyone" access level. 2020-08-06 20:57:50 -04:00
Fumiaki Yoshimatsu
5f0d384c9e Replace a call to a deprecated method to update context with the new one.
Fixes #9736
2020-08-06 20:57:50 -04:00
Christian Ascheberg
4271700046 Do not collapse list to hide only one entry. 2020-08-06 20:57:50 -04:00
Niko Lockenvitz
e153b0ab78 Fix message compose hint on fullscreen.
Fixes #5294
Closes #5348
2020-08-06 20:57:50 -04:00
Alan Evans
26868ae668 Get authoritative profile keys from group changes only. 2020-08-06 20:57:50 -04:00
Greyson Parrelli
17c0364eda Ensure group avatars have V2 attachmentIds. 2020-08-06 20:57:50 -04:00
Alan Evans
b28ac7af8c Additional tests around rigid Groups V2 change application. 2020-08-06 20:57:50 -04:00
Greyson Parrelli
2dcaa21a44 Remove UuidRecipientError. 2020-08-04 19:12:25 -04:00
Greyson Parrelli
33cc8363f9 Add internal setting to see recipient details. 2020-08-04 19:12:25 -04:00
Greyson Parrelli
9b61e1c85c Show a message request for certain GV2 adds. 2020-08-04 19:12:25 -04:00
Greyson Parrelli
6f53fdc02d Clean up log statement in FcmFetchService. 2020-08-04 19:12:25 -04:00
Greyson Parrelli
6f850f5a55 Bump version to 4.68.4 2020-08-04 17:53:22 -04:00
Greyson Parrelli
a482a4b1f4 Updated language translations. 2020-08-04 17:46:11 -04:00
Greyson Parrelli
3664e6f96d Fix processing of unsupported messages. 2020-08-04 17:37:25 -04:00
Greyson Parrelli
dda8808173 Bump version to 4.68.3 2020-08-03 12:30:51 -04:00
Greyson Parrelli
63a24c23cc Updated language translations. 2020-08-03 12:29:52 -04:00
Greyson Parrelli
1ec3a72f79 Fix issue with thread summaries being updated after message deletion.
Fixes #9902
2020-08-03 10:36:02 -04:00
Greyson Parrelli
566285ec0e Fix crash in MMS group creation.
Fixes #9901
2020-08-03 10:03:45 -04:00
Greyson Parrelli
d5ba82338d Fix issue with text rendering in search results. 2020-08-03 09:47:27 -04:00
Greyson Parrelli
cbecd2a2fc Bump version to 4.68.2 2020-07-31 16:47:55 -04:00
Greyson Parrelli
3772dd40ac Updated language translations. 2020-07-31 16:46:01 -04:00
Alex Hart
f69a0f0261 Refine reaction details fragment. 2020-07-31 16:49:52 -03:00
Alex Hart
cb323ffb84 Fix reaction overlay toolbar and status bar. 2020-07-31 15:51:41 -03:00
Alex Hart
0db73e71a0 Remove sticky header on list reinitailization.
When we forward a message or share into the app, it is possible that we are going to reuse the same activity. In this case, when the adapter was reinitialized, we were just adding a new ItemDecoration every time.

This fix checks if we've already added one and removes it if necessary, just like the last seen decorator.
2020-07-31 14:26:31 -03:00
Alex Hart
eeb0c838db Fix masking when attachment keyboard is visible. 2020-07-31 11:34:46 -03:00
Greyson Parrelli
dc48ee5aed Bump version to 4.68.1 2020-07-30 23:32:20 -04:00
Greyson Parrelli
c0acfa57a9 Updated language translations. 2020-07-30 23:32:19 -04:00
Greyson Parrelli
3e166ef927 Fix issue where group updates were mis-rendered. 2020-07-30 23:32:19 -04:00
Greyson Parrelli
4942d83de5 Properly render reset session update messages. 2020-07-30 23:32:19 -04:00
Alex Hart
4c30b39e71 Add section to recent reactions page listing emoji already applied to message. 2020-07-30 23:32:19 -04:00
Alex Hart
e55f4fe6b6 Save preference on emoji send. 2020-07-30 22:26:59 -04:00
Greyson Parrelli
aff74cffa0 Fix crash with UnknownSenderView.
The listener was being called on a background thread, but it was doing
UI work.
2020-07-30 13:31:51 -04:00
Alex Hart
8b29bb8664 Fix info icon in light mode. 2020-07-30 10:48:45 -03:00
Greyson Parrelli
3cee57b6c2 Bump version to 4.68.0 2020-07-29 23:54:46 -04:00
Greyson Parrelli
857f4a4fc8 Updated language translations. 2020-07-29 23:54:09 -04:00
Jim Gustafson
a942293a74 RingRTC v2.4.0 Release Integration.
Co-authored-by: Peter Thatcher <peter@signal.org>
2020-07-29 23:43:06 -04:00
Greyson Parrelli
550b121990 Prevent UUID-only contacts from being added to GV1 groups. 2020-07-29 23:43:06 -04:00
Alex Hart
cc84901a49 Add dropshadow to emoji variation popup. 2020-07-29 23:43:06 -04:00
Alex Hart
9d3764c5d9 Reactions UX polish. 2020-07-29 23:43:06 -04:00
Greyson Parrelli
0950235ccd Fix typo in RemappedRecords. 2020-07-29 23:19:21 -04:00
Greyson Parrelli
8ed7fc894e Improve handling of partially bi-directional text. 2020-07-29 23:19:21 -04:00
Greyson Parrelli
e504ffa225 Clean up conversation list data loading sequence.
- The Paging library was giving us empty paged lists when loading was
invalidated, but only *sometimes*. This library, man. Fixed it by
ignoring invalid lists, which you'd think the library would do for us...
- Noticed we were doing a ton of list refreshes because of how we were
listening to archive count. Switched from combine to switchMap.
- Noticed that we could become double-subscribed to LiveDatas in the
ConversationListFragment if you went to archived. Fixed by observing on
the fragment's view lifecycle.

Fixes #9803
2020-07-29 23:19:21 -04:00
Cody Henthorne
9c63b37bb4 Refactor use of MessageRecord to increase flexibility of ConversationAdapter. 2020-07-29 23:19:21 -04:00
Greyson Parrelli
5c110ca359 Remove UUIDs from GV1 membership lists. 2020-07-29 23:19:21 -04:00
Cody Henthorne
1ab61beeb9 Add initial Mentions UI/UX for picker and compose edit. 2020-07-28 15:20:20 -04:00
Alan Evans
8e45a546c9 Fix NPE on Group multi-invite. 2020-07-28 15:20:20 -04:00
Alan Evans
745a7f76ea Change position of GroupsV2 leave update message. 2020-07-28 15:20:20 -04:00
Alan Evans
8cb9ab3204 Fetch newly found profiles on Groups V2 inline. 2020-07-28 15:20:20 -04:00
Alan Evans
12533d1414 Ensure profile key is up to date on Group V2 conversation open. 2020-07-28 15:20:20 -04:00
Alan Evans
bd1c164d57 Live group update messages on conversation list and conversation. 2020-07-28 15:20:20 -04:00
Greyson Parrelli
7446c2096d Don't ellipsize multi-line text in conversation list.
Instead, basically convert newlines to spaces.
2020-07-28 15:19:52 -04:00
Greyson Parrelli
8ce5c4b885 Cleanup naming of RecipientDatabase GLOB search. 2020-07-28 15:19:52 -04:00
Alan Evans
ab76112f5f Prevent leading and trailing whitespace in group names. 2020-07-28 15:19:52 -04:00
Alan Evans
9c54e39eae Adjust scope of Groups V2 feature flag. 2020-07-28 15:19:52 -04:00
Greyson Parrelli
61eab44474 Bump version to 4.67.3 2020-07-27 18:04:05 -04:00
Greyson Parrelli
f6285ec710 Updated language translations. 2020-07-27 18:02:31 -04:00
Alex Hart
ed878ec4b4 Add more generic SMS verification code pattern. 2020-07-27 17:57:56 -04:00
Greyson Parrelli
e38d41d67a Reduce the number of cats in giphy sticker search results. 2020-07-27 15:25:26 -04:00
Greyson Parrelli
3d237d72bd Fix issue where feature flag fetches weren't limited. 2020-07-27 15:25:01 -04:00
Cody Henthorne
8044d2390c Fix bug causing profile updates to unarchive threads. 2020-07-27 13:32:38 -04:00
Greyson Parrelli
6b82e6b5ac Bump version to 4.67.2 2020-07-24 14:31:06 -04:00
Greyson Parrelli
842e6a93e2 Updated language translations. 2020-07-24 14:31:06 -04:00
Alan Evans
f140f054e5 Ignore typing indicators from blocked group members. 2020-07-24 14:31:06 -04:00
Greyson Parrelli
5cd4726e23 Do not show profile name changes if names are visually identical.
Fixes #9860
2020-07-24 14:30:58 -04:00
Greyson Parrelli
bccc58d693 Bump version to 4.67.1 2020-07-22 22:58:21 -04:00
Greyson Parrelli
e25f1c1481 Updated language translations. 2020-07-22 22:58:21 -04:00
Greyson Parrelli
fc4e690996 Revert "Ensure GV1 length is exactly the length expected."
This reverts commit 8e962bf992.
2020-07-22 22:58:21 -04:00
Greyson Parrelli
dadb2f9d37 Allow auto-downloads from groups you've accepted. 2020-07-22 22:58:21 -04:00
Greyson Parrelli
5bf15b0587 Fix casing issues with non-ASCII characters in contact search.
SQLite's case-related stuff is ASCII-only. That means that even though LIKE is supposed to be case-insensitive, it fails when used on non-ASCII characters.

There appears to be no relief in SQLite itself, so I swapped our contact search to use GLOB instead of LIKE and wrote a little thing to convert query strings into a case-insensitive unicode-compatible patterns. Didn't see any noticeable performance difference.
2020-07-22 22:58:21 -04:00
Cody Henthorne
5f9c0c3204 Fix bug with skipping resend message on safety number change. 2020-07-22 22:58:21 -04:00
Alan Evans
dfa4f0c309 Fix group change failure reason display logic. 2020-07-22 22:58:21 -04:00
Greyson Parrelli
f0063b4b0d Sync ContactRecords as whitelisted if they're a system contact. 2020-07-22 22:58:21 -04:00
Alan Evans
5dc51c34ea Fix recipient resolution during add to Groups V2. 2020-07-22 22:58:21 -04:00
Greyson Parrelli
5bf7a55bfa Bump version to 4.67.0 2020-07-21 16:11:45 -04:00
Greyson Parrelli
eb9ae8d5dc Updated language translations. 2020-07-21 16:11:45 -04:00
Greyson Parrelli
2a133587cc Add a flag for recipientTrust. 2020-07-21 16:11:45 -04:00
Greyson Parrelli
0e4a19c368 Improve exception stack traces in OptimizedMessageNotifier. 2020-07-21 15:31:53 -04:00
Greyson Parrelli
813c820227 Fix issue with GV1 avatars using attachmentsV3. 2020-07-21 15:31:53 -04:00
Greyson Parrelli
870cee5707 Remove uuidOnlyContacts feature flag. 2020-07-21 15:31:53 -04:00
Alan Evans
4e55d2d941 Tint pending group invites menu icon. 2020-07-21 15:31:53 -04:00
Alan Evans
8e962bf992 Ensure GV1 length is exactly the length expected. 2020-07-21 15:31:53 -04:00
Cody Henthorne
0815715f7b Enable call requests always. 2020-07-21 15:31:53 -04:00
Alan Evans
85e4697b7f Increment the Groups V2 feature flags version. 2020-07-21 15:31:53 -04:00
Alan Evans
16fdb9bf4c Make identity record list immutable. 2020-07-21 12:53:25 -03:00
Greyson Parrelli
46f3d50a54 Increment the attachmentsV3 feature flag version. 2020-07-21 10:49:19 -04:00
Alan Evans
3a38240fb2 Groups V2 group manager copy updates. 2020-07-21 11:47:11 -03:00
Greyson Parrelli
662f0b8fb6 Improve detection of websocket drained status.
Will now work when you lose and regain network. Also removes the
unnecessary InitialMessageRetriever.
2020-07-21 10:38:42 -04:00
Alan Evans
96ce42ae91 Legacy group learn more badge and info bottom sheet. 2020-07-21 06:05:16 -03:00
Alan Evans
93f587b851 For atomic Groups V2 block and leave, block after leaving group. 2020-07-21 06:04:44 -03:00
Greyson Parrelli
89a940ec81 Fix issue with contact syncing with attachmentsV3. 2020-07-20 17:57:22 -04:00
Alan Evans
a33771b15d Added progress feedback to leave and block group actions and additional group v2 error handling. 2020-07-20 15:20:56 -03:00
Greyson Parrelli
9a566e5559 Group together skin tone variations of the same reaction. 2020-07-20 10:26:39 -04:00
Greyson Parrelli
6e75d42a92 Enable skin tone selection for emoji reactions. 2020-07-20 10:26:39 -04:00
Alan Evans
575413cac9 Wait for message queue to drain before updating v2 groups. 2020-07-20 11:09:42 -03:00
Greyson Parrelli
6a9476c6d0 Fix retry issues with RotateProfileKeyJob. 2020-07-19 10:45:20 -04:00
Greyson Parrelli
5468f1705c Ensure we refresh attributes if key changes from storage service. 2020-07-19 10:45:20 -04:00
Greyson Parrelli
5ea132e712 Delay directory refresh until registration is complete. 2020-07-19 10:22:05 -04:00
Cody Henthorne
8128fcf8bc Hide compose for inactive groups. 2020-07-19 09:32:16 -04:00
Greyson Parrelli
e89655f793 Resolve newly-entered numbers before starting a conversation. 2020-07-19 09:32:16 -04:00
Cody Henthorne
2db2b068c4 Do not show typing indicators for inactive groups. 2020-07-19 09:32:16 -04:00
Alan Evans
a59e214317 Show Group V2 invited member dialog explaining invites on new group and add to group. 2020-07-19 09:32:16 -04:00
Cody Henthorne
ae2b6e4d7a Prevent last admin from leaving without selecting new admin. 2020-07-19 09:32:16 -04:00
Alan Evans
b10fc6a0b0 Support Groups v2 Change Epochs. 2020-07-19 09:32:16 -04:00
Cody Henthorne
70977e5228 Show expiration time exactly as set instead of rounding. 2020-07-19 09:32:16 -04:00
Greyson Parrelli
4482391574 Update libphonenumber to v8.12.6 2020-07-19 09:32:16 -04:00
Greyson Parrelli
bd078fc883 Handle UUID-only recipients and merging. 2020-07-19 09:32:16 -04:00
Alan Evans
644af87782 Groups V2 invite decline. 2020-07-19 09:32:16 -04:00
Greyson Parrelli
1ce36c1069 Bump version to 4.66.8 2020-07-17 17:32:33 -04:00
Greyson Parrelli
0a71005ecc Updated language translations. 2020-07-17 17:32:07 -04:00
Cody Henthorne
698618a4b3 Only show profile updates in active groups. 2020-07-17 17:32:07 -04:00
Alan Evans
f9642dd79f Reduce scrim overlap when scrolling new manage screens. 2020-07-17 17:32:07 -04:00
Cody Henthorne
85d1a3c016 Add system contact indicator to recipient bottom sheet. 2020-07-17 17:32:07 -04:00
Alan Evans
38c74c81a6 Add qa to translate task. 2020-07-17 17:32:07 -04:00
Greyson Parrelli
4c04991b70 Refresh recipient after viewing system contact details.
They might have changed the name or otherwise edited the contact, so we
want to try to keep things in sync.
2020-07-17 17:32:07 -04:00
Cody Henthorne
293a339fed Only show delete action when long pressing on profile change update. 2020-07-17 17:32:07 -04:00
Greyson Parrelli
5255a527f9 Do not show profile name changes for blocked users. 2020-07-17 17:32:07 -04:00
Cody Henthorne
9440dfb66c Do not show profile name changes on first update. 2020-07-17 09:42:13 -04:00
Alan Evans
7a019eee19 Updated language translations. 2020-07-16 16:21:02 -03:00
Greyson Parrelli
93f56a5dc8 Bump version to 4.66.7 2020-07-16 10:40:04 -04:00
Greyson Parrelli
68264228b8 Updated language translations. 2020-07-16 10:33:33 -04:00
Greyson Parrelli
66c1b8e26c Fix contact icon tint issues on older android versions. 2020-07-16 10:27:23 -04:00
Cody Henthorne
5776c048ea Do not update threads that do not exist. 2020-07-16 09:27:41 -04:00
Greyson Parrelli
76dd09bc50 Handle null profile names better. 2020-07-16 08:34:53 -04:00
Greyson Parrelli
73d18d3abd Bump version to 4.66.6 2020-07-15 17:12:37 -04:00
Greyson Parrelli
c1c9d0c8a3 Updated language translations. 2020-07-15 17:12:09 -04:00
Cody Henthorne
64420ead7c Show Profile Name Change update messages. 2020-07-15 16:15:15 -04:00
Alan Evans
6d035c6888 Allow sending of group v2 updates to inactive groups. 2020-07-15 12:31:59 -03:00
Alan Evans
833ca8cce9 Add disable GV2 creation option to internal preferences UI. 2020-07-15 12:28:47 -03:00
Ehren Kret
d02d506b13 Add force refresh of remote values to internal preferences UI. 2020-07-15 12:16:07 -03:00
Alan Evans
f306056e5d Enable lint StopShip comments. 2020-07-15 12:04:05 -03:00
Greyson Parrelli
58ec669d15 Fix quote attachmentV3 usage. 2020-07-14 19:43:17 -04:00
Greyson Parrelli
d1b61bfed3 Add indicator for system contacts. 2020-07-14 10:37:09 -04:00
Greyson Parrelli
325e0c6781 Bump version to 4.66.5 2020-07-14 10:26:15 -04:00
Greyson Parrelli
8d66cd52b5 Updated language translations. 2020-07-14 10:25:46 -04:00
Greyson Parrelli
4b9277629c Fix issue with tracking registration state. 2020-07-13 19:00:44 -04:00
Greyson Parrelli
6515a6188b Bump version to 4.66.4 2020-07-13 11:01:18 -04:00
Greyson Parrelli
8b3ca52502 Updated language translations. 2020-07-13 11:00:34 -04:00
Alan Evans
fae003e085 Do not sync group v2 recipients that we do not have the master key for. 2020-07-13 11:52:06 -03:00
Greyson Parrelli
4b961d2d8f Simplify PIN opt-out code. 2020-07-13 09:29:17 -04:00
Greyson Parrelli
e27fc512b4 Add a migration for users of the previous PIN opt-out flow. 2020-07-13 08:53:02 -04:00
Greyson Parrelli
8f0f600b6b Bump version to 4.66.3 2020-07-11 11:42:51 -04:00
Greyson Parrelli
5950610690 Updated language translations. 2020-07-11 11:42:51 -04:00
Greyson Parrelli
fce3df0c82 Update pin opt-out strings and behavior. 2020-07-11 11:42:51 -04:00
Greyson Parrelli
e2021231c6 Bump version to 4.66.2 2020-07-10 17:23:50 -04:00
Greyson Parrelli
f61dd7509e Updated language translations. 2020-07-10 17:23:50 -04:00
Greyson Parrelli
db2b64e58c Update PIN opt-out strings. 2020-07-10 17:23:50 -04:00
Alan Evans
d70999c386 Add storage force push internal option. 2020-07-10 17:23:50 -04:00
Alan Evans
eb6ecc59ab Consolidate duplicated group send job logic. 2020-07-10 17:23:50 -04:00
Cody Henthorne
1e0e2fadfd Improve scroll to last position behavior. 2020-07-10 17:23:50 -04:00
Alan Evans
4325f714b9 Silent group update send job for profile key rotation. 2020-07-10 17:23:50 -04:00
Alan Evans
137cd45497 Hide "Add to a group" if you don't have any groups. 2020-07-10 17:23:50 -04:00
Alan Evans
f3dbe4416f Add lint to detect non-numeric version code checks. 2020-07-10 17:23:50 -04:00
Greyson Parrelli
7fb55c0f51 Keep borderless property when forwarding media. 2020-07-10 17:23:50 -04:00
Greyson Parrelli
fdc6cbc507 Bump version to 4.66.1 2020-07-09 19:10:27 -04:00
Greyson Parrelli
072085ae82 Updated language translations. 2020-07-09 19:09:54 -04:00
Greyson Parrelli
04a8996348 Add the ability to opt out of PINs. 2020-07-09 19:07:21 -04:00
Cody Henthorne
c26dcc2618 Fix theming issues with snackbars and alert dialogs. 2020-07-09 19:07:21 -04:00
Alan Evans
a4dc340bbc Handle empty group change byte array. 2020-07-09 19:07:21 -04:00
Cody Henthorne
3c069fb588 Enable Media Preview to respond to media changes. 2020-07-09 19:07:21 -04:00
Fumiaki Yoshimatsu
1fe38f5ed1 Fix pen/highlighter tool single tap.
Fixes #9745
2020-07-09 11:25:10 -03:00
Greyson Parrelli
841c9424e9 Remove GV2 flag requirement for WakeGroupV2Job. 2020-07-09 10:02:59 -04:00
Greyson Parrelli
9c44a0c7d3 Don't run ProfileUploadJob if you're not registered. 2020-07-09 07:57:37 -04:00
Greyson Parrelli
2883d2eb31 Enable video call PiP. 2020-07-09 07:50:38 -04:00
Greyson Parrelli
f5aade943e Bump version to 4.66.0 2020-07-08 17:15:10 -04:00
Greyson Parrelli
d17c3f39d0 Updated language translations. 2020-07-08 17:12:19 -04:00
Alan Evans
9ac9ace6b8 Groups V2 state comparison and gap handling. 2020-07-08 17:12:19 -04:00
Greyson Parrelli
c9d2cef58d Add support for sending borderless keyboard stickers. 2020-07-08 16:51:30 -04:00
Alan Evans
a9e30eefdc Prevent adding self to group by number.
Fixes #9821
2020-07-08 16:51:30 -04:00
Cody Henthorne
1a895db9bd Finalize support for calling with system PIP. 2020-07-08 16:51:30 -04:00
Alan Evans
a955bc3b9b Fix single line text input for group names. 2020-07-08 16:51:30 -04:00
Alan Evans
96e888a4f5 Remove versioned profiles feature flag. 2020-07-08 16:51:30 -04:00
Alan Evans
99ff0c1e3c Ensure direct add members to a group removes any matching pending. 2020-07-08 16:51:30 -04:00
Alan Evans
599e89b1f9 Fix audio waveform RTL rendering.
Fixes #9823
2020-07-08 16:51:30 -04:00
Greyson Parrelli
33c527f15e Remove the final KBS feature flags. 2020-07-08 16:51:30 -04:00
Greyson Parrelli
eb02dacfdc Convert HEIC/HEIF to JPEG. 2020-07-08 16:51:30 -04:00
Alan Evans
e6a0e5b858 Add internal preferences under Advanced behind feature flag.
Initially for GV2 testing.
2020-07-08 16:51:30 -04:00
Greyson Parrelli
545ba80697 Add support for borderless images.
Added support for 'borderless' images. Basically images that we'd like to render 
as if they were stickers, even though they're not stickers. On iOS, this will be 
stuff like memoji and bitmoji. On Android, in my initial pass, I've just added 
support for Giphy stickers. However, we can also detect bitmoji and keyboard 
stickers in the future. This is kind of a 'best effort' thing, so as long as we 
support receiving, we can just add sending support for more things as we go.
2020-07-08 16:51:30 -04:00
Cody Henthorne
1e250ee95c Add Calling Requests. 2020-07-08 16:51:30 -04:00
Greyson Parrelli
5a12eedc2c Prevent possible deadlock with LiveRecipientCache.
Thread A: DirectoryHelper#updateContactsDatabase() acquires database lock
Thread B: LiveRecipientCache#getSelf() acquires lock on LiveRecipientCache
Thread A: DirectoryHelper#updateContactsDatabase() calls Recipient.externalContact(), which eventually needs LiveRecipientCache lock
Thread B: Needs to read the database (e.g. line 120) to get information about itself

So A has the DB lock but needs the LiveRecipientCache lock, and B has
the LiveRecipientCache lock but needs the DB lock.

In general, we need to avoid acquiring any new locks in a transaction,
but for now, this specific instance looks like it could be solved by
using a unique lock for LiveRecipientCache#getSelf().
2020-07-08 16:51:30 -04:00
Greyson Parrelli
5605fde777 Rename the UUID flag to be more explicit. 2020-07-08 16:51:30 -04:00
Greyson Parrelli
9ac142688a Increase the max PIN reminder interval to 4 weeks. 2020-07-08 16:51:30 -04:00
Greyson Parrelli
2791790bf5 Implement new CDS changes. 2020-07-08 16:51:30 -04:00
Cody Henthorne
1752972be9 Update delete for everyone functionality to match requirements. 2020-07-08 16:51:30 -04:00
Greyson Parrelli
c877aba09f Use resolved recipients in the conversation list. 2020-07-08 16:51:30 -04:00
Greyson Parrelli
70e33518a9 Do registration checks for new numbers during group creation. 2020-07-08 16:51:30 -04:00
Greyson Parrelli
cb81a9f783 Disallow 'visually empty' profile names. 2020-07-08 16:51:30 -04:00
Greyson Parrelli
b6b499d865 Refresh recipients outside of a transaction for storage service. 2020-07-08 16:51:30 -04:00
Alan Evans
6704ad8193 Do not show update messages for profile key updates. 2020-07-08 16:51:30 -04:00
Greyson Parrelli
942628a261 Improve ConversationListDataSource logging. 2020-07-08 16:51:30 -04:00
Greyson Parrelli
4ea8bac10d Re-enable view prefetching. 2020-07-08 16:51:30 -04:00
Alan Evans
eafccc5721 Add GV2 copy for the unknown editor. 2020-06-30 14:46:10 -03:00
Greyson Parrelli
a01bec3a11 Bump version to 4.65.2 2020-06-30 11:38:26 -04:00
Greyson Parrelli
3868175b85 Updated language translations. 2020-06-30 11:37:44 -04:00
Greyson Parrelli
904cb01067 Use the BlobProvider in the contact and group sync jobs. 2020-06-30 11:17:29 -04:00
Alan Evans
5c0cb425a6 Only sync V1 groups with linked devices. 2020-06-30 10:17:42 -03:00
Cody Henthorne
9dbb2ef630 Ensure user knows Safety Number Change Dialog list is scrollable when necessary. 2020-06-26 16:36:01 -04:00
Alan Evans
bafd2817ee Fix pending member activity background color. 2020-06-26 17:29:05 -03:00
Greyson Parrelli
3380293923 Bump version to 4.65.1 2020-06-26 15:40:23 -04:00
Greyson Parrelli
a549c1ec8b Updated language translations. 2020-06-26 15:38:48 -04:00
Greyson Parrelli
ad84997ce0 Fix display of quotes in 'All Media' view. 2020-06-26 15:33:08 -04:00
Alan Evans
42e2576813 Prevent repeat attempts when waveforms cannot be generated. 2020-06-26 16:18:27 -03:00
Cody Henthorne
31b995fa98 Retrieve profiles on mismatch to notify user of updates quicker. 2020-06-26 14:25:39 -03:00
Greyson Parrelli
0364bec995 Allow skipping if you hit a network error during PIN restore. 2020-06-26 14:25:39 -03:00
Alan Evans
aa39f3d0a3 Fix create new pin option in registration flow. 2020-06-26 13:29:00 -03:00
Greyson Parrelli
db545f43ea Remove profile name reminder megaphone. 2020-06-26 11:52:00 -04:00
Cody Henthorne
bbe003a454 Improve messaging and UX around safety number changes. 2020-06-26 11:10:54 -04:00
Greyson Parrelli
819f0f68f6 Fix issue with some search results returning empty. 2020-06-26 10:46:44 -04:00
Greyson Parrelli
8c0160937b Fix crash with 'select all' in conversation list.
Fixes #9790
2020-06-26 10:12:16 -04:00
Cody Henthorne
6de789dfe3 Prevent attachment download button re-animation. 2020-06-26 10:10:34 -04:00
Greyson Parrelli
afa2bb3bf5 Disallow swipe actions in search mode.
Fixes #9771
2020-06-26 10:08:01 -04:00
Greyson Parrelli
89e66c0741 Bump version to 4.65.0 2020-06-25 18:14:54 -04:00
Greyson Parrelli
0dc4afba99 Updated language translations. 2020-06-25 18:14:54 -04:00
Greyson Parrelli
152578e576 Add reserved job runners for inbound and outbound messages. 2020-06-25 18:14:54 -04:00
Greyson Parrelli
63d6ab6fa7 Throttle conversation list update frequency.
This helps fast phones process messages faster by reducing contention on
the database while processing a large batch of messages.
2020-06-25 18:14:54 -04:00
Greyson Parrelli
75c8c59d78 Reduce notification update interval. 2020-06-25 18:14:54 -04:00
Greyson Parrelli
87a59b6a9b Add support for memory-only jobs. 2020-06-25 18:14:54 -04:00
Alan Evans
2001fa86cf Log capabilities. 2020-06-25 18:14:54 -04:00
Alan Evans
52747782a7 Full screen avatar circle to square shape transition. 2020-06-25 18:14:54 -04:00
Fumiaki Yoshimatsu
66f2668326 Do not cache locale in each conversation object.
Fixes #9751
2020-06-25 18:14:54 -04:00
Cody Henthorne
b262efc24c Clear up warnings in string resource file. 2020-06-25 18:14:54 -04:00
Alan Evans
ce7ad76447 Cycle Versioned Profiles feature flag. 2020-06-25 08:29:48 -04:00
Greyson Parrelli
9e98b6616e Log job run time. 2020-06-25 08:29:48 -04:00
Alan Evans
f4c9eaa904 Remove some unused resources. 2020-06-25 08:29:48 -04:00
Greyson Parrelli
f8a0988e5f Various JobManager performance improvements. 2020-06-25 08:29:48 -04:00
Greyson Parrelli
bf919207ed Various logging improvements.
* Improve lifecycle logging.
* Remove 'action bar' from base activity names.
* Remove some unnecessary glide logs.
2020-06-25 08:29:48 -04:00
Greyson Parrelli
dac6b5c992 Bump version to 4.64.7 2020-06-24 20:09:31 -04:00
Greyson Parrelli
7f8043777e Updated language translations. 2020-06-24 20:09:00 -04:00
Greyson Parrelli
854b3feb36 Reduce verbosity of job logs. 2020-06-24 20:00:42 -04:00
Greyson Parrelli
22447e6ddb Fix theming issue with snackbar. 2020-06-24 20:00:42 -04:00
Alan Evans
be2ec36e1f Fix clipping issues with archive icon.
Fixes #8344
2020-06-24 20:00:12 -04:00
Greyson Parrelli
98cf16479d Bump version to 4.64.6 2020-06-24 10:58:13 -04:00
Greyson Parrelli
584735cbd0 Updated language translations. 2020-06-24 10:57:45 -04:00
Alan Evans
3741493cb7 Remove frame rate reporter and unused FPS ringbuffer. 2020-06-24 11:44:35 -03:00
Greyson Parrelli
4ea861fe5c Improve 'mark all read' performance. 2020-06-24 10:34:52 -04:00
Jim Gustafson
cd3df4d3c1 Update to ringrtc v2.2.0 2020-06-24 09:50:43 -04:00
Alan Evans
881a1edccb Bump version to 4.64.5 2020-06-22 10:53:52 -03:00
Alan Evans
1b7b574289 Updated language translations. 2020-06-22 10:50:27 -03:00
Alan Evans
d1d7498447 Fix text colors when system theme doesn't match. 2020-06-22 10:02:18 -03:00
Greyson Parrelli
50c18727e7 Bump version to 4.64.4 2020-06-21 12:23:31 -04:00
Greyson Parrelli
e9bfde470a Updated language translations. 2020-06-21 12:23:10 -04:00
Greyson Parrelli
68f718a210 Fix issue with conversation list times not updating.
Just started calling notifyDataSetChanged() in onResume() to provide
some sort of time update regularity.
2020-06-21 12:20:18 -04:00
Greyson Parrelli
c3e528ad4b Bump version to 4.64.3 2020-06-19 19:17:16 -04:00
Greyson Parrelli
28af97c400 Updated language translations. 2020-06-19 19:17:16 -04:00
Jim Gustafson
c2e4c343ab Update to ringrtc v2.1.1 2020-06-19 19:12:59 -04:00
Cody Henthorne
8a78589c2f Fix light navigation buttons in conversation settings screens. 2020-06-19 16:53:38 -04:00
Alan Evans
841ee18435 Add default option to message vibrate for pre API26. 2020-06-19 13:08:54 -03:00
Greyson Parrelli
71f54701d2 Add additional safeguards around disappearing messages. 2020-06-19 10:17:23 -04:00
Alan Evans
1c99939dfa Bump version to 4.64.2 2020-06-18 17:30:38 -03:00
Alan Evans
50462cecd0 Updated language translations. 2020-06-18 17:29:20 -03:00
Cody Henthorne
aa6a32f023 Make conversation footer always show. 2020-06-18 16:14:38 -04:00
Alan Evans
c4dc9064e3 Handle Attachment Keyboard selection of a too large item. 2020-06-18 15:55:26 -03:00
Alan Evans
bc5be10a0e Respect emoji config on conversation banner title. 2020-06-18 15:39:02 -03:00
Alan Evans
98d9b57379 Add copy to bottom sheet for Note to Self. 2020-06-18 14:34:30 -03:00
Cody Henthorne
021a16050a Stop back transition jank from avatar viewer to settings. 2020-06-18 13:16:08 -04:00
Alan Evans
555104aff0 Make message button navigate back if launched from the conversation. 2020-06-18 14:00:06 -03:00
Alan Evans
95d63b78f4 Add call and message buttons to recipient bottom sheet.
And insecure call button for non-registered contacts.
2020-06-18 13:23:46 -03:00
Alan Evans
80f9e1f4f1 Fix not able to get to archived conversations when all archived. 2020-06-18 12:23:20 -03:00
Alan Evans
a77997a4de Fix margins for "No groups in common" & unregistered case. 2020-06-18 09:49:22 -03:00
Alan Evans
ec4eb8e2a9 Bump version to 4.64.1 2020-06-17 17:54:58 -03:00
Alan Evans
1bdeade71e Updated language translations. 2020-06-17 17:53:19 -03:00
Greyson Parrelli
629ba105cb Detect real age of call request by using server timestamps. 2020-06-17 17:53:18 -03:00
Alan Evans
891a1af995 Show Note to Self for local number recipient preferences. 2020-06-17 17:49:44 -03:00
Cody Henthorne
0fbc6ac151 Revert improperly removed code for Message Request footer. 2020-06-17 17:49:43 -03:00
Alan Evans
a6384d1b73 Add insecure call ability to recipient settings. 2020-06-17 17:49:43 -03:00
Alan Evans
2fb9514890 Respect emoji setting in profile/group name editing. 2020-06-17 17:49:43 -03:00
Alan Evans
fe89794505 Hide recipient subtitle if no name/username set. 2020-06-17 17:49:43 -03:00
Cody Henthorne
08800c9faf Make Message Details update views in more situations. 2020-06-17 17:49:43 -03:00
Cody Henthorne
469a4700d2 Fix improper tinting on screens when using FallbackPhoto. 2020-06-17 17:49:43 -03:00
Alan Evans
6707f974a5 Remove NewGroupUI FeatureFlag. 2020-06-17 17:49:43 -03:00
Alan Evans
c122cada2b Change call button shade. 2020-06-17 17:49:43 -03:00
Alan Evans
96f02d8c95 Hide some views for Note to Self conversation. 2020-06-17 17:49:43 -03:00
Greyson Parrelli
dd717b60b8 Bump version to 4.64.0 2020-06-16 23:47:15 -04:00
Greyson Parrelli
3c20c7f4b4 Updated language translations. 2020-06-16 23:46:41 -04:00
Cody Henthorne
1a09e70a04 Remove old Message Details. 2020-06-16 19:30:35 -04:00
Alan Evans
027453bbd2 Prevent IllegalStateException on recipient bottom sheet. 2020-06-16 19:30:35 -04:00
Greyson Parrelli
b621efa4a5 Don't prefetch views for the conversation list. 2020-06-16 19:30:35 -04:00
Cody Henthorne
2915e4698c Show registration rate limit error messaging. 2020-06-16 19:30:35 -04:00
Cody Henthorne
b687b1a4c5 Fix repeat alerts by using explicit reminder intent. 2020-06-16 19:30:35 -04:00
Alan Evans
b53827f32b Manage recipient activity. 2020-06-16 19:30:35 -04:00
Cody Henthorne
d9641128a8 Refresh Message Details screen. 2020-06-16 19:30:35 -04:00
Alan Evans
dfb5562142 Use group manager for MMS groups. 2020-06-16 19:30:35 -04:00
Jim Gustafson
d467c04749 Ensure speaker off at start of any call 2020-06-16 19:30:35 -04:00
Greyson Parrelli
3d7cffef2b Remove Message Requests feature flag. 2020-06-16 19:30:35 -04:00
Alex Hart
f2fe81d9b5 Fix conversation jumping when loading at last scroll position. 2020-06-16 19:30:35 -04:00
Greyson Parrelli
cf98a22269 Add placeholder support for ConversationListAdapter. 2020-06-16 19:30:35 -04:00
Alex Hart
49f75d7036 Migrate ConversationList to paging library and apply abstractions to conversation. 2020-06-16 19:30:35 -04:00
Greyson Parrelli
ce940235b0 Optimistically fetch profiles. 2020-06-16 19:30:35 -04:00
Alan Evans
f5626f678d Make CustomNotificationsDialogFragment work with recipients. 2020-06-16 19:30:35 -04:00
Alan Evans
b3a59c3946 Use recipient display name in recipient bottom sheet. 2020-06-16 19:30:35 -04:00
Fumiaki Yoshimatsu
93c390c4fc Don't send a read receipt when the recipient is blocked.
Fixes #9610
2020-06-16 19:30:35 -04:00
Cody Henthorne
941ab5a98f Prevent avatar from showing a start of outgoing video call. 2020-06-16 19:30:35 -04:00
Jim Gustafson
2ecdf803c0 Update to ringrtc v2.1.0 2020-06-16 19:30:35 -04:00
Cody Henthorne
5b2a399392 Return to previous scroll position when returning to a conversation. 2020-06-16 19:30:35 -04:00
Alex Hart
a9ea1d7606 Utilize DayNight theme when launching the app. 2020-06-12 11:36:15 -03:00
Greyson Parrelli
1ce8ac2de6 Light refactor of SignalStore. 2020-06-12 11:36:15 -03:00
Greyson Parrelli
e2019579fb Bump version to 4.63.3 2020-06-12 10:09:20 -04:00
Greyson Parrelli
fb3c6e56ee Updated language translations. 2020-06-12 10:08:51 -04:00
Greyson Parrelli
3fad007ae0 Cancel typing jobs when you send a group message. 2020-06-12 10:06:20 -04:00
Greyson Parrelli
8891b6c930 Properly throw UnregisteredUserException in SignalServicePipe. 2020-06-11 12:08:40 -04:00
Alan Evans
400c592acf Display 'Unknown group' for groups with no name. 2020-06-10 17:17:47 -03:00
Alex Hart
e13f3254ad Fix message jump-to-position. 2020-06-10 17:06:40 -03:00
Greyson Parrelli
bf40a07bb9 Bump version to 4.63.2 2020-06-10 14:43:24 -04:00
Greyson Parrelli
8f3a6b8479 Update unblock string. 2020-06-10 14:37:03 -04:00
Greyson Parrelli
7642b7cc72 Fix issue with typing indicators in blocked groups. 2020-06-10 14:28:12 -04:00
Greyson Parrelli
e12ea60d85 Bump version to 4.63.1 2020-06-10 12:48:15 -04:00
Greyson Parrelli
0b13c4aed6 Updated language translations. 2020-06-10 12:48:15 -04:00
Alan Evans
47919382e9 Show 'Add to another group' when launched from a group context. 2020-06-10 12:59:57 -03:00
Greyson Parrelli
d60d67ee7e Set contact colors more aggressively. 2020-06-10 10:49:22 -04:00
Alan Evans
559aa687a5 Show group participants menu item on a MMS group. 2020-06-10 11:32:50 -03:00
Cody Henthorne
bc0761f002 Fix navigate up behavior for Conversations. 2020-06-10 10:28:34 -04:00
Alan Evans
c0c2fc0eba When there are no recipients left on group create screen toast and return to list. 2020-06-10 09:07:12 -03:00
Alan Evans
44fe43c74c Hide 'Add to a group' for non-registered users. 2020-06-10 08:54:57 -03:00
Alan Evans
53a2a5d693 Prevent highlighter opacity affecting blur tool. 2020-06-09 23:56:03 -03:00
Greyson Parrelli
2334c26cbb Bump version to 4.63.0 2020-06-09 16:56:57 -04:00
Greyson Parrelli
0b6dde46d9 Updated language translations. 2020-06-09 16:55:50 -04:00
Greyson Parrelli
98d9d81aff Insert receipts in a transaction. 2020-06-09 15:11:37 -04:00
Greyson Parrelli
736a62b632 Update strings related to message requests. 2020-06-09 14:12:52 -04:00
Cody Henthorne
cea6a83d8a Show member count in contact selection list. 2020-06-09 13:32:48 -04:00
Greyson Parrelli
2751fd7efc Retrieve profiles in parallel. 2020-06-09 12:47:11 -04:00
Cody Henthorne
2822042eeb Show recent groups in Add to Groups screen. 2020-06-09 12:13:13 -04:00
Cody Henthorne
dc46d88ddd Provide two ways of listening for thread/message db updates. 2020-06-09 11:52:58 -04:00
Alex Hart
e04f76b558 Fix issue where invalid PagedList objects were passed to ConversationAdapter. 2020-06-09 12:37:19 -03:00
Alan Evans
a758056494 Take highlighter down from 50% to 37.5% opacity. 2020-06-09 12:35:53 -03:00
Alan Evans
1ecdea5db3 Reinstate highlighter under drawing menu. 2020-06-09 12:10:40 -03:00
Alan Evans
e1bb773d85 Add 'Add to a group' button to bottom sheet. 2020-06-09 12:09:59 -03:00
Alan Evans
7e934eff5d Make quotes not hold strong references to attachments. 2020-06-09 12:07:41 -03:00
Greyson Parrelli
cfdf5603af Bump version to 4.62.4 2020-06-09 00:39:55 -04:00
Greyson Parrelli
45bfb8c6b6 Updated language translations. 2020-06-09 00:38:19 -04:00
Alex Hart
65608a51b8 Fix API 19 crash by using different resource. 2020-06-09 00:33:38 -04:00
Greyson Parrelli
b6314597fe Bump version to 4.62.3 2020-06-08 16:32:08 -04:00
Greyson Parrelli
20a588199a Updated language translations. 2020-06-08 16:32:00 -04:00
Greyson Parrelli
59916f1e95 Add 'Add to contacts' button to bottom sheet. 2020-06-08 16:07:14 -04:00
Alan Evans
8b91f8f9e7 Disable disappearing messages option and remove from menu. 2020-06-08 12:31:55 -03:00
Alex Hart
cbc3cce66f Fix API 19 drawable crash in ManageGroupFragment. 2020-06-08 11:34:39 -03:00
Alex Hart
b4b63b5860 Add auto-mirroring to ic_forward_outline. 2020-06-08 10:23:43 -03:00
Alex Hart
b9ae15a890 Fix group name RTL alignment. 2020-06-08 10:21:55 -03:00
Greyson Parrelli
d955389c46 Bump version to 4.62.2 2020-06-07 22:05:02 -04:00
Greyson Parrelli
975eb885c1 Updated language translations. 2020-06-07 22:05:02 -04:00
Alan Evans
a3aed96757 Sort contacts without names after contacts with names. 2020-06-07 22:05:02 -04:00
Alan Evans
dc70bfabaf Lighter ultramarine + in dark mode. 2020-06-07 22:05:02 -04:00
Greyson Parrelli
6932340671 Add ability to copy a number via long-press. 2020-06-07 19:59:42 -04:00
Alan Evans
f6637b7caf Restore mute in conversation menu. 2020-06-07 19:59:42 -04:00
Alan Evans
4f4be44caa Load identities in transaction. 2020-06-07 19:59:42 -04:00
Greyson Parrelli
7832497ba7 Shorten logging in ConversationActivity. 2020-06-07 19:59:42 -04:00
Alex Hart
7d06e2395f Rework how ConversationFragment RecyclerView responds to data updates. 2020-06-07 19:59:42 -04:00
Greyson Parrelli
3a479d7eef Reduce database notifications for disappearing conversations. 2020-06-07 19:59:42 -04:00
Greyson Parrelli
8fe8a1e9ee Put refresh and upload profile jobs in the same queue. 2020-06-07 19:59:42 -04:00
Alan Evans
2d8b2e7fb0 Transitions for group settings. 2020-06-07 19:59:42 -04:00
Alan Evans
9c0365f92c Open group settings from group avatar click. 2020-06-07 19:59:42 -04:00
Greyson Parrelli
b48abb08d2 Show custom notifications for API < 26. 2020-06-07 19:59:42 -04:00
Alan Evans
d8f3e032c7 Fix group name clearing after avatar change. 2020-06-07 19:59:42 -04:00
Alan Evans
8dbcb255ad Hide Block and Leave options when not available in group settings, add unblock. 2020-06-07 19:59:42 -04:00
Greyson Parrelli
db06cbbc86 Remove unnecessary recipient refreshes. 2020-06-07 19:59:42 -04:00
Cody Henthorne
98ab23c1a3 Make Custom Notification dialog dismiss itself on up press. 2020-06-07 10:23:41 -04:00
Cody Henthorne
d0ca9ba6a6 Make text button color responsive to theme. 2020-06-07 09:43:14 -04:00
Alan Evans
b242368675 Remove group members button. 2020-06-07 09:31:18 -03:00
Alan Evans
664527ce63 Fix sort order for group members. 2020-06-07 08:25:31 -03:00
Alan Evans
99e4f80be0 Allow whole row selection for Shared media in group settings. 2020-06-07 08:12:25 -03:00
Alan Evans
702dae9fcd Fix double tap required for "See all" media in group settings. 2020-06-07 07:47:28 -03:00
Alan Evans
48fe1ba559 Fix group settings divider shade in dark mode. 2020-06-07 07:42:28 -03:00
Greyson Parrelli
382ac7ba0d Bump version to 4.62.1 2020-06-06 20:28:18 -04:00
Greyson Parrelli
a46f47f352 Updated language translations. 2020-06-06 20:27:48 -04:00
Greyson Parrelli
e984d8a42c Change Gif -> GIF. 2020-06-06 20:27:22 -04:00
Greyson Parrelli
554bad6b8d Improve DB access in group sends. 2020-06-06 20:25:02 -04:00
Jim Gustafson
ed13c97ad7 Handle legacy hangup properly. 2020-06-06 20:25:02 -04:00
Greyson Parrelli
d33873d59a Fix possible crash with null thread body. 2020-06-06 20:25:02 -04:00
Greyson Parrelli
1234899ea1 Add support for non-blocking media sends. 2020-06-06 20:25:02 -04:00
Cody Henthorne
13027dc44b Fix leaking MessageDetailsActivity via list items. 2020-06-06 20:25:02 -04:00
Cody Henthorne
5b4d74b7fe Move group resolution for conversations to background LiveData. 2020-06-06 20:25:02 -04:00
Alan Evans
18c7bc2b5b Prevent edit of a group post leave. 2020-06-06 20:25:02 -04:00
Alan Evans
bbbee0f372 Fix group create arrow for RTL. 2020-06-06 20:25:02 -04:00
Alex Hart
cf9d090154 Start Paging @ Unread count instead of -1. 2020-06-06 20:25:02 -04:00
Alan Evans
718471917f Separate text only message layouts. 2020-06-06 20:25:02 -04:00
Greyson Parrelli
bb97407cde Bump version to 4.62.0 2020-06-05 22:04:18 -04:00
Greyson Parrelli
92ce678e29 Updated language translations. 2020-06-05 22:04:16 -04:00
Cody Henthorne
e100aea2c7 Preserve scroll position in Message Details on update. 2020-06-05 21:46:04 -04:00
Greyson Parrelli
fea3b6cb4a Don't show 'conversation settings' for groups. 2020-06-05 21:46:04 -04:00
Cody Henthorne
afbc132faa Fix conversation item and data source memory leaks. 2020-06-05 21:46:04 -04:00
Alan Evans
b27198286d MMS proof new group UI. 2020-06-05 21:46:04 -04:00
Greyson Parrelli
ac93d81032 Remove pins4all feature flag. 2020-06-05 21:46:04 -04:00
Alan Evans
9981e5ca76 Enable new group UI. 2020-06-05 20:19:03 -03:00
Cody Henthorne
7dd3efeb53 Remove listeners when detaching conversation item views. 2020-06-05 19:29:55 -03:00
Greyson Parrelli
d38d702adf Parallelize group sends. 2020-06-05 18:10:50 -04:00
Alex Hart
04a000a8a8 Always display labels in contact search. 2020-06-05 15:16:55 -03:00
Fumiaki Yoshimatsu
3bbf0741ee Use localized string for Phone number.
Fixes #9626
2020-06-05 15:01:20 -03:00
Fumiaki Yoshimatsu
e9a336100b Display backup date in users locale on restore.
Fixes #9693
2020-06-05 15:01:20 -03:00
Cody Henthorne
fb600e9829 Update SMS/MMS as sending when retrying failed send.
This was only impacting SMS/MMS as Push already reset the status.
2020-06-05 13:46:25 -04:00
Alex Hart
4a455ff958 Implement new Add Members UI. 2020-06-05 13:44:02 -03:00
Cody Henthorne
707e238e5c Make borderless button style responsive to theme. 2020-06-05 12:01:00 -04:00
Alan Evans
90f22a4b66 Include face position and projection matrix into elements matrix. 2020-06-04 19:49:22 -03:00
Alex Hart
b4f134adf7 Add more descriptive messages for media notifications and chat previews. 2020-06-04 13:13:42 -03:00
Greyson Parrelli
1e00fc6149 Bump version to 4.61.6 2020-06-04 10:21:23 -04:00
Greyson Parrelli
f52133a69c Updated language translations. 2020-06-04 10:21:23 -04:00
Alan Evans
91b142e0d9 Fix waveform array out of bounds. 2020-06-04 10:21:10 -04:00
Greyson Parrelli
26a9dd98c1 Bump version to 4.61.5 2020-06-03 19:01:03 -04:00
Greyson Parrelli
99e38e1d23 Updated language translations. 2020-06-03 18:58:34 -04:00
Greyson Parrelli
a2d8a25fd9 Blur UI tweaks. 2020-06-03 18:51:38 -04:00
Alan Evans
d86d625bcc Smoother blur rendering. 2020-06-03 19:47:51 -03:00
Greyson Parrelli
18e3fb6609 Fix string format. 2020-06-03 17:19:32 -04:00
Greyson Parrelli
da33ba0ed5 Update blur UI. 2020-06-03 17:12:47 -04:00
Greyson Parrelli
66f021d01a Fix issue where rail wasn't showing in some situations. 2020-06-03 17:12:47 -04:00
Greyson Parrelli
40231ea45f Fix issue with view-once toggle and face blurring. 2020-06-03 17:12:42 -04:00
Alex Hart
cd80a47c04 Made edit profile save button move with the keyboard. 2020-06-03 17:12:27 -04:00
Alan Evans
1033bd7bda Blur faces rotation and crop and zoom support. 2020-06-03 14:02:24 -03:00
Greyson Parrelli
b4f60f3acb Bump version to 4.61.4 2020-06-03 06:40:09 -04:00
Greyson Parrelli
bed3b571cc Updated language translations. 2020-06-03 06:39:34 -04:00
Greyson Parrelli
c8dd4e5254 Added support for blurring faces.
Co-authored-by: Alan Evans <alan@signal.org>
2020-06-03 06:39:20 -04:00
Alan Evans
514048171b Add Image Editor support for blur mask layer. 2020-06-03 03:33:06 -03:00
Greyson Parrelli
32e9901592 Bump version to 4.61.3 2020-06-02 19:22:22 -04:00
Greyson Parrelli
d83f86a469 Revert "Make notifications and chat previews for media messages more descriptive."
This reverts commit a3f9737e63.
2020-06-02 19:19:30 -04:00
Greyson Parrelli
403d53586c Bump version to 4.61.2 2020-06-02 17:40:56 -04:00
Greyson Parrelli
6acae58694 Updated language translations. 2020-06-02 17:33:41 -04:00
Alex Hart
a3f9737e63 Make notifications and chat previews for media messages more descriptive. 2020-06-02 17:34:50 -03:00
Cody Henthorne
263af7c139 Add registration lock status to support email. 2020-06-02 16:14:19 -04:00
Alex Hart
7f2439f1e9 Fix contact selection behavior when searching and clear search on selection. 2020-06-02 16:27:04 -03:00
Alex Hart
ae87d23003 Always use the new group settings screen if the flag is enabled. 2020-06-02 16:09:48 -03:00
Alex Hart
3192cc0aac Add outlined view-once close icon. 2020-06-02 16:05:16 -03:00
Alex Hart
6102e9aa72 Apply better coordinatorlayout animation and RTL support. 2020-06-02 15:02:35 -03:00
Alan Evans
f4a152b0fe Fetch own profile after GV2 feature flag is enabled, improve GV2 capability check. 2020-06-02 11:48:40 -03:00
Greyson Parrelli
2b11bca7dc Guard against possible invalid conversation data loads. 2020-06-02 10:20:55 -04:00
Artem Varaksa
07d19f38e3 Fix typos in logging for remote delete. 2020-06-02 10:22:29 -03:00
Greyson Parrelli
cd228c439e Be more explicit with the ID we use for account updates. 2020-06-02 09:03:54 -04:00
Alan Evans
7a859c8961 For smaller width devices, use original 210dp for audio messages. 2020-06-02 09:32:59 -03:00
Alan Evans
543f38c75d Fix Wave form IOException thread issue. 2020-06-02 07:38:15 -03:00
Greyson Parrelli
f7b150f2d2 Bump version to 4.61.1 2020-06-01 17:43:05 -04:00
Greyson Parrelli
11328f643f Updated language translations. 2020-06-01 17:43:05 -04:00
Greyson Parrelli
f270a6b8c4 Fix potential crash by removing an unnecessary column.
The column I removed is already in the recipient half of the projection.
Having two representations of the groupId made reading the groupId out
of the cursor non-deterministic, and when compounded with another bug,
could cause a crash if one of them was null.
2020-06-01 17:43:05 -04:00
Alan Evans
3fec23fd36 Show remaining time on wave form view and cache wave form in database. 2020-06-01 17:43:05 -04:00
Alex Hart
e01838e996 Fix text size for pending members. 2020-06-01 17:43:05 -04:00
Greyson Parrelli
f70e41e7cd Don't allow account record updates to delete our profile key. 2020-06-01 17:43:05 -04:00
Greyson Parrelli
c4ec0c9897 Handle devices disallowing start of FcmFetchService.
Some devices are overzealous with battery management and disallow
starting services even when they're in response to a high-priority FCM
message (which should be allowed). So in these situations, we just
fall back to what we were doing before.
2020-06-01 17:43:05 -04:00
Greyson Parrelli
989b071a6d Ignore contacts that don't have a phone number. 2020-06-01 17:43:05 -04:00
Greyson Parrelli
c39751f9db Add info about play services to the debug log. 2020-06-01 17:43:05 -04:00
jimio-signal
dbf74a2234 Update copyright in README.md 2020-05-31 10:39:19 -07:00
Greyson Parrelli
837230d72d Bump version to 4.61.0 2020-05-29 19:18:55 -04:00
Greyson Parrelli
f544ec4126 Updated language translations. 2020-05-29 19:18:02 -04:00
Greyson Parrelli
79dbf85c1e Improve local encrypted PIN storage. 2020-05-29 19:15:56 -04:00
Greyson Parrelli
61fe6cc961 Enable the ability to react with any emoji. 2020-05-29 19:14:37 -04:00
Greyson Parrelli
70c88b68e2 Store recent reactions separately from keyboard emoji. 2020-05-29 19:14:37 -04:00
Greyson Parrelli
d70c33d20f Add support for mark as unread. 2020-05-29 19:14:37 -04:00
Greyson Parrelli
6b2e000e61 Prevent waiting for old queues in our retrieval strategies. 2020-05-29 19:14:37 -04:00
Alan Evans
b9f11dafff New internal testing flag and V1 group creation button. All menus create GV1. 2020-05-29 19:14:37 -04:00
Alan Evans
9b32eaeb8a Do not log URLs. 2020-05-29 19:14:37 -04:00
Alan Evans
a99c0d438e Rename GV2 "version" to "revision". 2020-05-29 19:14:37 -04:00
Alex Hart
c634c24afb Utilize Wrapper instead of dynamic theme. 2020-05-29 19:14:37 -04:00
Alex Hart
2ddd1437cf Utilize exclusive AudioFocus. 2020-05-29 19:14:37 -04:00
Alan Evans
9da309ca48 Enforce a local GV2 capacity limit driven by a feature flag. 2020-05-29 19:14:37 -04:00
henry
cfcd451db7 Fix crash on unlink device when offline. 2020-05-29 09:51:21 -04:00
Alex Hart
5ab72fd1a9 Ask for permission before launching avatar sheet. 2020-05-29 09:51:21 -04:00
Alan Evans
daace9bd1a Audio wave forms on voice notes. 2020-05-29 09:51:21 -04:00
Alan Evans
69adcd1d69 Tap avatar in chat preferences or group management to see full screen. 2020-05-29 09:51:21 -04:00
Alex Hart
0711a22188 Add overflow toast and fix edit menu option. 2020-05-29 09:51:21 -04:00
Greyson Parrelli
3a06412cd8 Throttle notifications when doing the intial message fetch. 2020-05-29 09:51:21 -04:00
Alan Evans
51c82702e2 Remove expectation of ActionBar in DeviceProvisioningActivity.
Fixes #9661
2020-05-29 09:51:21 -04:00
Greyson Parrelli
1b01196ec6 Refactor ThreadRecord. 2020-05-29 09:51:21 -04:00
Greyson Parrelli
1cd6b58ece Don't enqueue duplicate PushDecryptMessageJobs. 2020-05-29 09:51:21 -04:00
Greyson Parrelli
ea8e13b1c8 Create a WebsocketDrainedConstraint. 2020-05-29 09:51:21 -04:00
Greyson Parrelli
f392229393 Extract MessageNotifier interface. 2020-05-29 09:51:21 -04:00
Greyson Parrelli
a299bafe89 Create a new system for fetching the intial batch of messages. 2020-05-29 09:51:21 -04:00
Alex Hart
d2bf539504 Clear sticky WebRtcViewModel events when initiating a new call. 2020-05-29 09:51:21 -04:00
Alex Hart
903c3989b9 Fix chip jank and other groups v2 ux issues. 2020-05-29 09:51:21 -04:00
Alan Evans
00996f0d7a Rename back to build.gradle 2020-05-29 09:51:21 -04:00
Alan Evans
4aded3a436 Close keyboard on contact list scroll. 2020-05-29 09:51:20 -04:00
Alan Evans
9acdc37729 Alphabetical member order. 2020-05-29 09:51:20 -04:00
Greyson Parrelli
d4cdcbe54f Improve logging around group sends. 2020-05-29 09:51:20 -04:00
Alex Hart
6fa2a0f411 Polish UX for groups v2 management. 2020-05-29 09:51:20 -04:00
Alex Hart
558a8e4a14 Add polish to groups v2 creation flow. 2020-05-29 09:51:20 -04:00
Alan Evans
8947b82034 Make GV2 feature flags remote capable. 2020-05-29 09:51:20 -04:00
Alan Evans
56551025e9 Detect if group v2 is active from membership. 2020-05-29 09:51:20 -04:00
Alan Evans
befb4939d5 Restore groups from storage service. 2020-05-29 09:51:20 -04:00
Alan Evans
289f7aba63 Add versioned profiles feature flag. 2020-05-29 09:51:20 -04:00
Alan Evans
28bd245b96 While testing GV2 without UUID, fail jobs that hit UuidRecipientError. 2020-05-29 09:51:20 -04:00
Alan Evans
c5e7300df2 Fix matches logic in contact selection. 2020-05-29 09:51:20 -04:00
Greyson Parrelli
fe25d941bb Prevent FCM bottlenecking. 2020-05-29 09:51:20 -04:00
Alan Evans
4cda267f3b Show pending count and allow view of zero pending screen. 2020-05-29 09:51:20 -04:00
Alex Hart
82ba7e2b8b Display "You" at end of members list in ConversationTitleView. 2020-05-29 09:51:20 -04:00
Alex Hart
41ebaf3938 Clean up Overflow menu for GV2 groups. 2020-05-29 09:51:20 -04:00
Alex Hart
090c400037 Collapse title into toolbar on scroll in ManageGroupFragment. 2020-05-29 09:51:20 -04:00
Alan Evans
12b1232ac0 Fix groups v2 patch response handler. 2020-05-29 09:51:20 -04:00
Alex Hart
204a84c522 Apply proper spacing to RecipientBottomSheetDialogFragment. 2020-05-29 09:51:20 -04:00
Alan Evans
526afd539b Fix avatar tap in conversation multi-select mode. 2020-05-29 09:51:20 -04:00
Greyson Parrelli
d708984abd Require users be a system contact or whitelisted to appear in the contact list. 2020-05-29 09:51:20 -04:00
Greyson Parrelli
9d39db6428 Add additional account restore logging, prevent double avatar fetch. 2020-05-29 09:51:20 -04:00
Alan Evans
67a8ec0d39 Only admin can cancel any invite. 2020-05-29 09:51:20 -04:00
Alan Evans
297a7d0ef8 Handle absent change during invite. 2020-05-29 09:51:20 -04:00
Bastian Köcher
4712833853 Always convert HEIC images to JPEG.
This pr changes the behavior of sending HEIC images to always convert
them to JPEG. This conversion is required to support image inline
viewing accross different devices and operating systems. This follows
the same strategy as on IOS: https://github.com/signalapp/Signal-iOS/pull/2511

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

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

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

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

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

Note: We still need to change the registration flow to allow non-reglock
users to enter their PIN.
2020-04-21 13:33:33 -04:00
Ehren Kret
3c6a7b76ca Send increased protocol version number if CDN key or attachment are used. 2020-04-21 13:33:33 -04:00
Alan Evans
428128651e Move database protos to separate files. 2020-04-21 13:33:33 -04:00
Alan Evans
326678f214 Add support for GV2 group update messages. 2020-04-21 13:33:29 -04:00
Alex Hart
1f994495f8 Clear search if user sends message. 2020-04-21 13:33:29 -04:00
Greyson Parrelli
fb1637006d Include screen size details in debuglogs. 2020-04-21 13:33:29 -04:00
Ehren Kret
37a35e8f70 Add initial support for send/receive on CDN2. 2020-04-21 13:33:29 -04:00
Alan Evans
1290d0ead9 Add pending member activity. 2020-04-21 13:33:25 -04:00
Greyson Parrelli
ef0f26b64c Remove borders from images in the gallery picker. 2020-04-03 12:20:15 -04:00
Greyson Parrelli
485d211768 Remove border from images in the attachment keyboard. 2020-04-03 12:20:15 -04:00
Greyson Parrelli
f1ea035197 Re-enable and clean up Signal PINs.
- Require PINs during registration agian.
- Change min length to 4.
- Allow the full-screen megaphone to be enabled remotely.
- Clean up and remove some code.
2020-04-03 12:20:15 -04:00
Martijn van den Hoek
6f961ade74 Fix crash when importing vcf after exporting it.
Fixes #9465
2020-04-03 12:20:15 -04:00
Alan Evans
b8e17e0116 Enable video trimming feature by default. 2020-04-03 12:20:15 -04:00
Alan Evans
040e1fe8f6 Apply dark theme to scroll to bottom button. 2020-04-03 12:20:15 -04:00
Alan Evans
e9c92bdf51 Show unblock dialog when tap blocked contact. 2020-04-03 12:20:15 -04:00
Alan Evans
48c33f3dcd GroupsV2 service changes. 2020-04-03 12:20:15 -04:00
Alex Hart
6b2bc924dd Prefer profile photo over system contact photo. 2020-04-03 12:20:15 -04:00
Ehren Kret
a65c4f90f4 Avoid potential race condition in attachment uploads. 2020-04-03 12:19:13 -04:00
Alan Evans
04bb4b351a Refactor group leave dialog out of conversation. 2020-04-03 12:19:13 -04:00
Alan Evans
e02e4d52b4 Prevent empty message processing. 2020-04-03 12:19:13 -04:00
Alex Hart
6f3c4434f6 Add animation when replying to a message. 2020-04-03 12:19:13 -04:00
Greyson Parrelli
711715ca1e Add DNS fallback system. 2020-04-03 12:19:12 -04:00
Greyson Parrelli
d6000af843 Re-use recently-acquired attachment pointers. 2020-04-03 12:19:12 -04:00
Greyson Parrelli
9b0954a898 Bump version to 4.58.5 2020-04-03 11:18:39 -04:00
Greyson Parrelli
42a2c33fd7 Updated language translations. 2020-04-03 11:18:13 -04:00
Greyson Parrelli
a4d18a18d9 Don't use vector assets for notification icon. 2020-04-03 11:14:36 -04:00
Alex Hart
bf32409d4e Split drawable into light and dark. 2020-04-03 12:14:09 -03:00
Greyson Parrelli
e38aec225f Bump version to 4.58.4 2020-04-01 14:48:47 -04:00
Greyson Parrelli
995b7a4712 Updated language translations. 2020-04-01 14:48:25 -04:00
Alex Hart
9fe3026941 Fix AdaptiveActionsToolbar sizing algorithm. 2020-04-01 11:26:19 -03:00
3158 changed files with 208397 additions and 60251 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,12 +14,15 @@ 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: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
- name: Build with Gradle
run: ./gradlew qa

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

@@ -83,7 +83,7 @@ There are several other ways to get involved:
* Try to reproduce issues and help with troubleshooting.
* Discover solutions to open issues and post any relevant findings.
* Test other people's pull requests.
* Contribute to Signal via the [Freedom of the Press Foundation's donation page](https://freedom.press/crowdfunding/signal/).
* [Donate to Signal.](https://signal.org/donate/)
* Share Signal with your friends and family.
Signal is made for you. Thank you for your feedback and support.

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

@@ -59,9 +59,7 @@ The form and manner of this distribution makes it eligible for export under the
## License
Copyright 2011 Whisper Systems
Copyright 2013-2020 Open Whisper Systems
Copyright 2013-2020 Signal
Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html

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.5.1'
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 = 617
def canonicalVersionName = "4.58.3"
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
@@ -115,17 +111,21 @@ android {
buildConfigField "String", "SIGNAL_URL", "\"https://textsecure-service.whispersystems.org\""
buildConfigField "String", "STORAGE_URL", "\"https://storage.signal.org\""
buildConfigField "String", "SIGNAL_CDN_URL", "\"https://cdn.signal.org\""
buildConfigField "String", "SIGNAL_CDN2_URL", "\"https://cdn2.signal.org\""
buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"https://api.directory.signal.org\""
buildConfigField "String", "SIGNAL_SERVICE_STATUS_URL", "\"uptime.signal.org\""
buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api.backup.signal.org\""
buildConfigField "String", "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", "MRENCLAVE", "\"cd6cfc342937b23b1bdd3bbf9721aa5615ac9ff50a75c5527d441cd3276826c9\""
buildConfigField "String", "KEY_BACKUP_ENCLAVE_NAME", "\"fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe\""
buildConfigField "String", "KEY_BACKUP_MRENCLAVE", "\"a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87\""
buildConfigField "String", "CDS_MRENCLAVE", "\"c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15\""
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", "\"\""
buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X0=\""
buildConfigField "String[]", "LANGUAGES", "new String[]{\"" + autoResConfig().collect { s -> s.replace('-r', '_') }.join('", "') + '"}'
buildConfigField "int", "CANONICAL_VERSION_CODE", "$canonicalVersionCode"
@@ -148,8 +148,8 @@ android {
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JAVA_VERSION
targetCompatibility JAVA_VERSION
}
packagingOptions {
@@ -160,11 +160,18 @@ android {
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
exclude 'META-INF/proguard/androidx-annotations.pro'
exclude 'lib/*/libzkgroup.so' // TODO: GV2 Remove line to include .so when used
}
aaptOptions {
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',
@@ -188,43 +195,72 @@ android {
testProguardFiles 'proguard/proguard-automation.pro',
'proguard/proguard.cfg'
}
staging {
initWith debug
buildConfigField "String", "SIGNAL_URL", "\"https://textsecure-service-staging.whispersystems.org\""
buildConfigField "String", "STORAGE_URL", "\"https://storage-staging.signal.org\""
buildConfigField "String", "SIGNAL_CDN_URL", "\"https://cdn-staging.signal.org\""
buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"https://api-staging.directory.signal.org\""
buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api-staging.backup.signal.org\""
buildConfigField "String", "MRENCLAVE", "\"ba4ebb438bc07713819ee6c98d94037747006d7df63fc9e44d2d6f1fec962a79\""
buildConfigField "String", "KEY_BACKUP_ENCLAVE_NAME", "\"a1e9c1d3f352b5c4f0fc7a421b98119e60e5ff703c28fbea85c66bfa7306deab\""
buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx\""
buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"\""
}
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 "none"
dimension 'distribution'
isDefault true
ext.websiteUpdateUrl = "null"
buildConfigField "boolean", "PLAY_STORE_DISABLED", "false"
buildConfigField "String", "NOPLAY_UPDATE_URL", "$ext.websiteUpdateUrl"
}
website {
dimension "none"
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\""
buildConfigField "String", "SIGNAL_CDN_URL", "\"https://cdn-staging.signal.org\""
buildConfigField "String", "SIGNAL_CDN2_URL", "\"https://cdn2-staging.signal.org\""
buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"https://api-staging.directory.signal.org\""
buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api-staging.backup.signal.org\""
buildConfigField "String", "CDS_MRENCLAVE", "\"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=\""
}
}
android.applicationVariants.all { variant ->
@@ -253,28 +289,36 @@ android {
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0-beta01'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'com.google.android.material:material:1.0.0'
lintChecks project(':lintchecks')
implementation ('androidx.appcompat:appcompat:1.2.0') {
force = true
}
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'com.google.android.material:material:1.2.1'
implementation '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 'com.google.firebase:firebase-ml-vision:24.0.3'
implementation 'com.google.firebase:firebase-ml-vision-face-model:20.0.1'
implementation('com.google.firebase:firebase-messaging:17.3.4') {
implementation ('com.google.firebase:firebase-messaging:20.2.0') {
exclude group: 'com.google.firebase', module: 'firebase-core'
exclude group: 'com.google.firebase', module: 'firebase-analytics'
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
@@ -285,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.4.1'
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:1.2.0'
implementation 'org.signal:ringrtc-android:2.8.10'
implementation "me.leolin:ShortcutBadger:1.1.16"
implementation 'se.emilsjolander:stickylistheaders:2.7.0'
@@ -341,33 +391,34 @@ dependencies {
exclude group: 'com.fasterxml.jackson.core'
exclude group: 'org.freemarker'
}
implementation 'dnsjava:dnsjava:2.1.9'
flipperImplementation 'com.facebook.flipper:flipper:0.32.2'
flipperImplementation 'com.facebook.soloader:soloader:0.8.2'
testImplementation 'junit:junit:4.12'
testImplementation 'org.assertj:assertj-core:3.11.1'
testImplementation 'org.mockito:mockito-core:1.9.5'
testImplementation 'org.powermock:powermock-api-mockito:1.6.1'
testImplementation 'org.powermock:powermock-module-junit4:1.6.1'
testImplementation 'org.powermock:powermock-module-junit4-rule:1.6.1'
testImplementation 'org.powermock:powermock-classloading-xstream:1.6.1'
testImplementation 'org.mockito:mockito-core:2.8.9'
testImplementation 'org.powermock:powermock-api-mockito2:1.7.4'
testImplementation 'org.powermock:powermock-module-junit4:1.7.4'
testImplementation 'org.powermock:powermock-module-junit4-rule:1.7.4'
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");
@@ -411,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 {
@@ -444,3 +491,24 @@ def getLastCommitTimestamp() {
return os.toString() + "000"
}
}
tasks.withType(Test) {
testLogging {
events "failed"
exceptionFormat "full"
showCauses true
showExceptions true
showStackTraces true
}
}
def loadKeystoreProperties(filename) {
def keystorePropertiesFile = file("${project.rootDir}/${filename}")
if (keystorePropertiesFile.exists()) {
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
return keystoreProperties;
} else {
return null;
}
}

View File

@@ -1,15 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<!-- Wont pass lint or qa with a STOPSHIP in a comment -->
<issue id="StopShip" severity="fatal" />
<!-- L10N errors -->
<!-- This is a runtime crash so we don't want to ship with this. -->
<issue id="StringFormatMatches" severity="error" />
<!-- L10N warnings -->
<issue id="MissingTranslation" severity="warning" />
<issue id="MissingTranslation" severity="ignore" />
<issue id="MissingQuantity" severity="warning" />
<issue id="MissingDefaultResource" severity="error">
<ignore path="*/res/values-*/strings.xml" /> <!-- Ignore for non-English, excludeNonTranslatables task will remove these -->
</issue>
<issue id="ExtraTranslation" severity="warning" />
<issue id="ImpliedQuantity" severity="warning" />
<issue id="TypographyDashes" severity="error" >
<ignore path="*/res/values-*/strings.xml" /> <!-- Ignore for non-English -->
</issue>
<issue id="CanvasSize" severity="error" />
<issue id="HardcodedText" severity="error" />
@@ -17,9 +26,17 @@
<issue id="ButtonOrder" severity="error" />
<issue id="ExtraTranslation" severity="warning" />
<!-- Custom lints -->
<issue id="LogNotSignal" severity="error" />
<issue id="LogNotAppSignal" severity="error" />
<issue id="LogTagInlined" severity="error" />
<issue id="RestrictedApi" severity="error">
<ignore path="*/org/thoughtcrime/securesms/mediasend/camerax/VideoCapture.java" />
<ignore path="*/org/thoughtcrime/securesms/mediasend/camerax/CameraXModule.java" />
<ignore path="*/org/thoughtcrime/securesms/conversation/*.java" />
<ignore path="*/org/thoughtcrime/securesms/lock/v2/CreateKbsPinViewModel.java" />
<ignore path="*/org/thoughtcrime/securesms/jobs/StickerPackDownloadJob.java" />
</issue>
</lint>

View File

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

View File

@@ -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,14 +15,17 @@ 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 java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
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}
@@ -29,13 +33,31 @@ import java.util.Map;
*/
public class FlipperSqlCipherAdapter extends DatabaseDriver<FlipperSqlCipherAdapter.Descriptor> {
private static final String TAG = Log.tag(FlipperSqlCipherAdapter.class);
public FlipperSqlCipherAdapter(Context context) {
super(context);
}
@Override
public List<Descriptor> getDatabases() {
return Collections.singletonList(new Descriptor(DatabaseFactory.getRawDatabase(getContext())));
try {
Field databaseHelperField = DatabaseFactory.class.getDeclaredField("databaseHelper");
databaseHelperField.setAccessible(true);
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);
}
return Collections.emptyList();
}
@Override
@@ -223,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;
}
@@ -235,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,13 +115,22 @@
<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:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|fontScale"
android:supportsPictureInPicture="true"
android:windowSoftInputMode="stateAlwaysHidden"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:taskAffinity=".calling"
android:launchMode="singleTask"/>
<activity android:name=".messagerequests.CalleeMustAcceptMessageRequestActivity"
android:theme="@style/TextSecure.DarkNoActionBar"
android:screenOrientation="portrait"
android:noHistory="true"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".InviteActivity"
android:theme="@style/Signal.Light.NoActionBar.Invite"
android:windowSoftInputMode="stateHidden"
@@ -127,7 +138,7 @@
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"
@@ -148,12 +159,15 @@
<activity android:name=".preferences.MmsPreferencesActivity"
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/TextSecure.LightNoActionBar"
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">
<intent-filter>
@@ -182,7 +196,7 @@
</activity>
<activity android:name=".stickers.StickerPackPreviewActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:launchMode="singleTask"
android:noHistory="true"
android:windowSoftInputMode="stateHidden"
@@ -221,6 +235,28 @@
</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" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https"
android:host="signal.group"/>
</intent-filter>
</activity>
<activity android:name=".conversation.ConversationActivity"
android:windowSoftInputMode="stateUnchanged"
android:launchMode="singleTask"
@@ -231,6 +267,11 @@
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"
@@ -241,15 +282,22 @@
android:theme="@style/TextSecure.LightTheme.Popup"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
<activity android:name=".MessageDetailsActivity"
android:label="@string/AndroidManifest__message_details"
<activity android:name=".messagedetails.MessageDetailsActivity"
android:windowSoftInputMode="stateHidden"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".GroupCreateActivity"
android:windowSoftInputMode="stateVisible"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".groups.ui.invitesandrequests.ManagePendingAndRequestingMembersActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"
android:theme="@style/Theme.Signal.DayNight.NoActionBar" />
<activity android:name=".groups.ui.managegroup.ManageGroupActivity"
android:windowSoftInputMode="stateAlwaysHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".recipients.ui.managerecipient.ManageRecipientActivity"
android:windowSoftInputMode="stateAlwaysHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".DatabaseMigrationActivity"
android:theme="@style/NoAnimation.Theme.AppCompat.Light.DarkActionBar"
@@ -264,7 +312,7 @@
<activity android:name=".PassphraseCreateActivity"
android:label="@string/AndroidManifest__create_passphrase"
android:windowSoftInputMode="stateUnchanged"
android:theme="@style/TextSecure.LightNoActionBar"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
@@ -274,7 +322,7 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".NewConversationActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:windowSoftInputMode="stateAlwaysVisible"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
@@ -284,7 +332,7 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".giph.ui.GiphyActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
@@ -309,6 +357,16 @@
</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"
@@ -333,18 +391,22 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".logsubmit.SubmitDebugLogActivity"
android:label="@string/AndroidManifest__log_submit"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".MediaPreviewActivity"
<activity android:name=".MediaPreviewActivity"
android:label="@string/AndroidManifest__media_preview"
android:windowSoftInputMode="stateHidden"
android:launchMode="singleTask"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".AvatarPreviewActivity"
android:label="@string/AndroidManifest__media_preview"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".mediaoverview.MediaOverviewActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
@@ -395,15 +457,11 @@
</activity>
<activity android:name=".RecipientPreferenceActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".mediasend.AvatarSelectionActivity"
android:theme="@style/TextSecure.FullScreenMedia"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".BlockedContactsActivity"
<activity android:name=".blocked.BlockedUsersActivity"
android:theme="@style/TextSecure.LightTheme"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
@@ -412,9 +470,12 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".profiles.edit.EditProfileActivity"
android:theme="@style/TextSecure.LightRegistrationTheme"
android:windowSoftInputMode="stateVisible"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
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"
@@ -426,17 +487,14 @@
android:windowSoftInputMode="adjustResize"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ClearProfileAvatarActivity"
android:theme="@style/Theme.AppCompat.Dialog.Alert"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:icon="@drawable/clear_profile_avatar"
android:label="@string/AndroidManifest_remove_photo">
<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"/>
<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=".contacts.TurnOffContactJoinedNotificationsActivity"
android:theme="@style/Theme.AppCompat.Dialog.Alert" />
<activity android:name=".messagerequests.MessageRequestMegaphoneActivity"
android:theme="@style/TextSecure.LightRegistrationTheme"
@@ -448,33 +506,77 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".contactshare.ContactNameEditActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".contactshare.SharedContactDetailsActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".ShortcutLauncherActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:exported="true"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity
android:name=".maps.PlacePickerActivity"
android:label="@string/PlacePickerActivity_title"
android:theme="@style/TextSecure.LightNoActionBar"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".MainActivity"
android:theme="@style/TextSecure.LightNoActionBar"
android:launchMode="singleTask"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
<service android:enabled="true" android:name="org.thoughtcrime.securesms.service.WebRtcCallService"/>
<activity android:name=".pin.PinRestoreActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
<activity android:name=".groups.ui.creategroup.CreateGroupActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar" />
<activity android:name=".groups.ui.addtogroup.AddToGroupsActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar" />
<activity android:name=".groups.ui.addmembers.AddMembersActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar" />
<activity android:name=".groups.ui.creategroup.details.AddGroupDetailsActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar" />
<activity android:name=".groups.ui.chooseadmin.ChooseNewAdminActivity"
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" />
<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=".service.IncomingMessageObserver$ForegroundService"/>
<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"
@@ -513,7 +615,9 @@
<service android:name=".service.GenericForegroundService"/>
<service android:name=".gcm.FcmService">
<service android:name=".gcm.FcmFetchService" />
<service android:name=".gcm.FcmReceiveService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
@@ -586,18 +690,25 @@
<receiver android:name=".revealable.ViewOnceMessageManager$ViewOnceAlarm" />
<receiver android:name=".service.TrimThreadsByDateManager$TrimThreadsByDateAlarm" />
<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">
@@ -606,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">
@@ -668,11 +779,7 @@
</intent-filter>
</receiver>
<receiver android:name=".notifications.MessageNotifier$ReminderReceiver">
<intent-filter>
<action android:name="org.thoughtcrime.securesms.MessageNotifier.REMINDER_ACTION"/>
</intent-filter>
</receiver>
<receiver android:name=".notifications.MessageNotifier$ReminderReceiver"/>
<receiver android:name=".notifications.DeleteNotificationReceiver">
<intent-filter>
@@ -719,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;
@@ -28,7 +28,6 @@ import android.os.Looper;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
@@ -46,42 +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 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,13 +14,12 @@
* limitations under the License.
*/
package org.thoughtcrime.securesms.mediasend.camerax;
package androidx.camera.view;
import android.Manifest.permission;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.util.Log;
import android.util.Rational;
import android.util.Size;
@@ -28,18 +27,20 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RequiresPermission;
import androidx.camera.core.AspectRatio;
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;
@@ -53,10 +54,10 @@ import androidx.lifecycle.OnLifecycleEvent;
import com.google.common.util.concurrent.ListenableFuture;
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;
@@ -69,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;
@@ -83,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
@@ -111,7 +111,6 @@ final class CameraXModule {
public void onDestroy(LifecycleOwner owner) {
if (owner == mCurrentLifecycle) {
clearCurrentLifecycle();
mPreview.setSurfaceProvider(null);
}
}
};
@@ -124,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>() {
@@ -150,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;
@@ -174,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
@@ -189,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
@@ -217,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
@@ -233,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;
}
@@ -246,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
@@ -263,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);
@@ -302,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.");
}
@@ -313,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.");
}
@@ -333,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
@@ -350,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;
@@ -389,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
@@ -455,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");
}
}
@@ -487,6 +495,10 @@ final class CameraXModule {
}
}
boolean isBoundToLifecycle() {
return mCamera != null;
}
int getRelativeCameraOrientation(boolean compensateForMirroring) {
int rotationDegrees = 0;
if (mCamera != null) {
@@ -521,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;
@@ -533,7 +550,7 @@ final class CameraXModule {
mImageCapture.setTargetRotation(getDisplaySurfaceRotation());
}
if (mVideoCapture != null && MediaConstraints.isVideoTranscodeAvailable()) {
if (mVideoCapture != null) {
mVideoCapture.setTargetRotation(getDisplaySurfaceRotation());
}
}
@@ -568,7 +585,7 @@ final class CameraXModule {
return false;
}
CameraInternal camera = mImageCapture.getBoundCamera();
CameraInternal camera = mImageCapture.getCamera();
if (camera == null) {
return false;
@@ -615,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() {
@@ -631,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
@@ -648,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

@@ -0,0 +1,58 @@
package org.signal.glide;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public final class Log {
private Log() {}
public static void v(@NonNull String tag, @NonNull String message) {
SignalGlideCodecs.getLogProvider().v(tag, message);
}
public static void d(@NonNull String tag, @NonNull String message) {
SignalGlideCodecs.getLogProvider().d(tag, message);
}
public static void i(@NonNull String tag, @NonNull String message) {
SignalGlideCodecs.getLogProvider().i(tag, message);
}
public static void w(@NonNull String tag, @NonNull String message) {
SignalGlideCodecs.getLogProvider().w(tag, message);
}
public static void e(@NonNull String tag, @NonNull String message) {
e(tag, message, null);
}
public static void e(@NonNull String tag, @NonNull String message, @Nullable Throwable throwable) {
SignalGlideCodecs.getLogProvider().e(tag, message, throwable);
}
public interface Provider {
void v(@NonNull String tag, @NonNull String message);
void d(@NonNull String tag, @NonNull String message);
void i(@NonNull String tag, @NonNull String message);
void w(@NonNull String tag, @NonNull String message);
void e(@NonNull String tag, @NonNull String message, @Nullable Throwable throwable);
Provider EMPTY = new Provider() {
@Override
public void v(@NonNull String tag, @NonNull String message) { }
@Override
public void d(@NonNull String tag, @NonNull String message) { }
@Override
public void i(@NonNull String tag, @NonNull String message) { }
@Override
public void w(@NonNull String tag, @NonNull String message) { }
@Override
public void e(@NonNull String tag, @NonNull String message, @NonNull Throwable throwable) { }
};
}
}

View File

@@ -0,0 +1,18 @@
package org.signal.glide;
import androidx.annotation.NonNull;
public final class SignalGlideCodecs {
private static Log.Provider logProvider = Log.Provider.EMPTY;
private SignalGlideCodecs() {}
public static void setLogProvider(@NonNull Log.Provider provider) {
logProvider = provider;
}
public static @NonNull Log.Provider getLogProvider() {
return logProvider;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.apng;
import android.content.Context;
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;
import org.signal.glide.common.loader.Loader;
import org.signal.glide.common.loader.ResourceStreamLoader;
/**
* @Description: APNGDrawable
* @Author: pengfei.zhou
* @CreateDate: 2019/3/27
*/
public class APNGDrawable extends FrameAnimationDrawable<APNGDecoder> {
public APNGDrawable(Loader provider) {
super(provider);
}
public APNGDrawable(APNGDecoder decoder) {
super(decoder);
}
@Override
protected APNGDecoder createFrameSeqDecoder(Loader streamLoader, FrameSeqDecoder.RenderListener listener) {
return new APNGDecoder(streamLoader, listener);
}
public static APNGDrawable fromAsset(Context context, String assetPath) {
AssetStreamLoader assetStreamLoader = new AssetStreamLoader(context, assetPath);
return new APNGDrawable(assetStreamLoader);
}
public static APNGDrawable fromFile(String filePath) {
FileLoader fileLoader = new FileLoader(filePath);
return new APNGDrawable(fileLoader);
}
public static APNGDrawable fromResource(Context context, int resId) {
ResourceStreamLoader resourceStreamLoader = new ResourceStreamLoader(context, resId);
return new APNGDrawable(resourceStreamLoader);
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.apng.decode;
import org.signal.glide.apng.io.APNGReader;
import java.io.IOException;
/**
* @Description: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/APNG#.27acTL.27:_The_Animation_Control_Chunk
* @Author: pengfei.zhou
* @CreateDate: 2019/3/27
*/
class ACTLChunk extends Chunk {
static final int ID = fourCCToInt("acTL");
int num_frames;
int num_plays;
@Override
void innerParse(APNGReader apngReader) throws IOException {
num_frames = apngReader.readInt();
num_plays = apngReader.readInt();
}
}

View File

@@ -0,0 +1,211 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.apng.decode;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import org.signal.glide.Log;
import org.signal.glide.apng.io.APNGReader;
import org.signal.glide.apng.io.APNGWriter;
import org.signal.glide.common.decode.Frame;
import org.signal.glide.common.decode.FrameSeqDecoder;
import org.signal.glide.common.io.Reader;
import org.signal.glide.common.loader.Loader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: APNG4Android
* @Author: pengfei.zhou
* @CreateDate: 2019-05-13
*/
public class APNGDecoder extends FrameSeqDecoder<APNGReader, APNGWriter> {
private static final String TAG = APNGDecoder.class.getSimpleName();
private APNGWriter apngWriter;
private int mLoopCount;
private final Paint paint = new Paint();
private class SnapShot {
byte dispose_op;
Rect dstRect = new Rect();
ByteBuffer byteBuffer;
}
private SnapShot snapShot = new SnapShot();
/**
* @param loader webp的reader
* @param renderListener 渲染的回调
*/
public APNGDecoder(Loader loader, FrameSeqDecoder.RenderListener renderListener) {
super(loader, renderListener);
paint.setAntiAlias(true);
}
@Override
protected APNGWriter getWriter() {
if (apngWriter == null) {
apngWriter = new APNGWriter();
}
return apngWriter;
}
@Override
protected APNGReader getReader(Reader reader) {
return new APNGReader(reader);
}
@Override
protected int getLoopCount() {
return mLoopCount;
}
@Override
protected void release() {
snapShot.byteBuffer = null;
apngWriter = null;
}
@Override
protected Rect read(APNGReader reader) throws IOException {
List<Chunk> chunks = APNGParser.parse(reader);
List<Chunk> otherChunks = new ArrayList<>();
boolean actl = false;
APNGFrame lastFrame = null;
byte[] ihdrData = new byte[0];
int canvasWidth = 0, canvasHeight = 0;
for (Chunk chunk : chunks) {
if (chunk instanceof ACTLChunk) {
mLoopCount = ((ACTLChunk) chunk).num_plays;
actl = true;
} else if (chunk instanceof FCTLChunk) {
APNGFrame frame = new APNGFrame(reader, (FCTLChunk) chunk);
frame.prefixChunks = otherChunks;
frame.ihdrData = ihdrData;
frames.add(frame);
lastFrame = frame;
} else if (chunk instanceof FDATChunk) {
if (lastFrame != null) {
lastFrame.imageChunks.add(chunk);
}
} else if (chunk instanceof IDATChunk) {
if (!actl) {
//如果为非APNG图片则只解码PNG
Frame frame = new StillFrame(reader);
frame.frameWidth = canvasWidth;
frame.frameHeight = canvasHeight;
frames.add(frame);
mLoopCount = 1;
break;
}
if (lastFrame != null) {
lastFrame.imageChunks.add(chunk);
}
} else if (chunk instanceof IHDRChunk) {
canvasWidth = ((IHDRChunk) chunk).width;
canvasHeight = ((IHDRChunk) chunk).height;
ihdrData = ((IHDRChunk) chunk).data;
} else if (!(chunk instanceof IENDChunk)) {
otherChunks.add(chunk);
}
}
frameBuffer = ByteBuffer.allocate((canvasWidth * canvasHeight / (sampleSize * sampleSize) + 1) * 4);
snapShot.byteBuffer = ByteBuffer.allocate((canvasWidth * canvasHeight / (sampleSize * sampleSize) + 1) * 4);
return new Rect(0, 0, canvasWidth, canvasHeight);
}
@Override
protected void renderFrame(Frame frame) {
if (frame == null || fullRect == null) {
return;
}
try {
Bitmap bitmap = obtainBitmap(fullRect.width() / sampleSize, fullRect.height() / sampleSize);
Canvas canvas = cachedCanvas.get(bitmap);
if (canvas == null) {
canvas = new Canvas(bitmap);
cachedCanvas.put(bitmap, canvas);
}
if (frame instanceof APNGFrame) {
// 从缓存中恢复当前帧
frameBuffer.rewind();
bitmap.copyPixelsFromBuffer(frameBuffer);
// 开始绘制前,处理快照中的设定
if (this.frameIndex == 0) {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
} else {
canvas.save();
canvas.clipRect(snapShot.dstRect);
switch (snapShot.dispose_op) {
// 从快照中恢复上一帧之前的显示内容
case FCTLChunk.APNG_DISPOSE_OP_PREVIOUS:
snapShot.byteBuffer.rewind();
bitmap.copyPixelsFromBuffer(snapShot.byteBuffer);
break;
// 清空上一帧所画区域
case FCTLChunk.APNG_DISPOSE_OP_BACKGROUND:
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
break;
// 什么都不做
case FCTLChunk.APNG_DISPOSE_OP_NON:
default:
break;
}
canvas.restore();
}
// 然后根据dispose设定传递到快照信息中
if (((APNGFrame) frame).dispose_op == FCTLChunk.APNG_DISPOSE_OP_PREVIOUS) {
if (snapShot.dispose_op != FCTLChunk.APNG_DISPOSE_OP_PREVIOUS) {
snapShot.byteBuffer.rewind();
bitmap.copyPixelsToBuffer(snapShot.byteBuffer);
}
}
snapShot.dispose_op = ((APNGFrame) frame).dispose_op;
canvas.save();
if (((APNGFrame) frame).blend_op == FCTLChunk.APNG_BLEND_OP_SOURCE) {
canvas.clipRect(
frame.frameX / sampleSize,
frame.frameY / sampleSize,
(frame.frameX + frame.frameWidth) / sampleSize,
(frame.frameY + frame.frameHeight) / sampleSize);
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
}
snapShot.dstRect.set(frame.frameX / sampleSize,
frame.frameY / sampleSize,
(frame.frameX + frame.frameWidth) / sampleSize,
(frame.frameY + frame.frameHeight) / sampleSize);
canvas.restore();
}
//开始真正绘制当前帧的内容
Bitmap inBitmap = obtainBitmap(frame.frameWidth, frame.frameHeight);
recycleBitmap(frame.draw(canvas, paint, sampleSize, inBitmap, getWriter()));
recycleBitmap(inBitmap);
frameBuffer.rewind();
bitmap.copyPixelsToBuffer(frameBuffer);
recycleBitmap(bitmap);
} catch (Throwable t) {
Log.e(TAG, "Failed to render!", t);
}
}
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.apng.decode;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import org.signal.glide.apng.io.APNGReader;
import org.signal.glide.apng.io.APNGWriter;
import org.signal.glide.common.decode.Frame;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.CRC32;
/**
* @Description: APNG4Android
* @Author: pengfei.zhou
* @CreateDate: 2019-05-13
*/
public class APNGFrame extends Frame<APNGReader, APNGWriter> {
public final byte blend_op;
public final byte dispose_op;
byte[] ihdrData;
List<Chunk> imageChunks = new ArrayList<>();
List<Chunk> prefixChunks = new ArrayList<>();
private static final byte[] sPNGSignatures = {(byte) 137, 80, 78, 71, 13, 10, 26, 10};
private static final byte[] sPNGEndChunk = {0, 0, 0, 0, 0x49, 0x45, 0x4E, 0x44, (byte) 0xAE, 0x42, 0x60, (byte) 0x82};
private static ThreadLocal<CRC32> sCRC32 = new ThreadLocal<>();
private CRC32 getCRC32() {
CRC32 crc32 = sCRC32.get();
if (crc32 == null) {
crc32 = new CRC32();
sCRC32.set(crc32);
}
return crc32;
}
public APNGFrame(APNGReader reader, FCTLChunk fctlChunk) {
super(reader);
blend_op = fctlChunk.blend_op;
dispose_op = fctlChunk.dispose_op;
frameDuration = fctlChunk.delay_num * 1000 / (fctlChunk.delay_den == 0 ? 100 : fctlChunk.delay_den);
frameWidth = fctlChunk.width;
frameHeight = fctlChunk.height;
frameX = fctlChunk.x_offset;
frameY = fctlChunk.y_offset;
}
private int encode(APNGWriter apngWriter) throws IOException {
int fileSize = 8 + 13 + 12;
//prefixChunks
for (Chunk chunk : prefixChunks) {
fileSize += chunk.length + 12;
}
//imageChunks
for (Chunk chunk : imageChunks) {
if (chunk instanceof IDATChunk) {
fileSize += chunk.length + 12;
} else if (chunk instanceof FDATChunk) {
fileSize += chunk.length + 8;
}
}
fileSize += sPNGEndChunk.length;
apngWriter.reset(fileSize);
apngWriter.putBytes(sPNGSignatures);
//IHDR Chunk
apngWriter.writeInt(13);
int start = apngWriter.position();
apngWriter.writeFourCC(IHDRChunk.ID);
apngWriter.writeInt(frameWidth);
apngWriter.writeInt(frameHeight);
apngWriter.putBytes(ihdrData);
CRC32 crc32 = getCRC32();
crc32.reset();
crc32.update(apngWriter.toByteArray(), start, 17);
apngWriter.writeInt((int) crc32.getValue());
//prefixChunks
for (Chunk chunk : prefixChunks) {
if (chunk instanceof IENDChunk) {
continue;
}
reader.reset();
reader.skip(chunk.offset);
reader.read(apngWriter.toByteArray(), apngWriter.position(), chunk.length + 12);
apngWriter.skip(chunk.length + 12);
}
//imageChunks
for (Chunk chunk : imageChunks) {
if (chunk instanceof IDATChunk) {
reader.reset();
reader.skip(chunk.offset);
reader.read(apngWriter.toByteArray(), apngWriter.position(), chunk.length + 12);
apngWriter.skip(chunk.length + 12);
} else if (chunk instanceof FDATChunk) {
apngWriter.writeInt(chunk.length - 4);
start = apngWriter.position();
apngWriter.writeFourCC(IDATChunk.ID);
reader.reset();
// skip to fdat data position
reader.skip(chunk.offset + 4 + 4 + 4);
reader.read(apngWriter.toByteArray(), apngWriter.position(), chunk.length - 4);
apngWriter.skip(chunk.length - 4);
crc32.reset();
crc32.update(apngWriter.toByteArray(), start, chunk.length);
apngWriter.writeInt((int) crc32.getValue());
}
}
//endChunk
apngWriter.putBytes(sPNGEndChunk);
return fileSize;
}
@Override
public Bitmap draw(Canvas canvas, Paint paint, int sampleSize, Bitmap reusedBitmap, APNGWriter writer) {
try {
int length = encode(writer);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
options.inMutable = true;
options.inBitmap = reusedBitmap;
byte[] bytes = writer.toByteArray();
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, length, options);
assert bitmap != null;
canvas.drawBitmap(bitmap, (float) frameX / sampleSize, (float) frameY / sampleSize, paint);
return bitmap;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.apng.decode;
import android.content.Context;
import org.signal.glide.apng.io.APNGReader;
import org.signal.glide.common.io.Reader;
import org.signal.glide.common.io.StreamReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* @link {https://www.w3.org/TR/PNG/#5PNG-file-signature}
* @Author: pengfei.zhou
* @CreateDate: 2019-05-13
*/
public class APNGParser {
static class FormatException extends IOException {
FormatException() {
super("APNG Format error");
}
}
public static boolean isAPNG(String filePath) {
InputStream inputStream = null;
try {
inputStream = new FileInputStream(filePath);
return isAPNG(new StreamReader(inputStream));
} catch (Exception e) {
return false;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static boolean isAPNG(Context context, String assetPath) {
InputStream inputStream = null;
try {
inputStream = context.getAssets().open(assetPath);
return isAPNG(new StreamReader(inputStream));
} catch (Exception e) {
return false;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static boolean isAPNG(Context context, int resId) {
InputStream inputStream = null;
try {
inputStream = context.getResources().openRawResource(resId);
return isAPNG(new StreamReader(inputStream));
} catch (Exception e) {
return false;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static boolean isAPNG(Reader in) {
APNGReader reader = (in instanceof APNGReader) ? (APNGReader) in : new APNGReader(in);
try {
if (!reader.matchFourCC("\u0089PNG") || !reader.matchFourCC("\r\n\u001a\n")) {
throw new FormatException();
}
while (reader.available() > 0) {
Chunk chunk = parseChunk(reader);
if (chunk instanceof ACTLChunk) {
return true;
}
}
} catch (IOException e) {
return false;
}
return false;
}
public static List<Chunk> parse(APNGReader reader) throws IOException {
if (!reader.matchFourCC("\u0089PNG") || !reader.matchFourCC("\r\n\u001a\n")) {
throw new FormatException();
}
List<Chunk> chunks = new ArrayList<>();
while (reader.available() > 0) {
chunks.add(parseChunk(reader));
}
return chunks;
}
private static Chunk parseChunk(APNGReader reader) throws IOException {
int offset = reader.position();
int size = reader.readInt();
int fourCC = reader.readFourCC();
Chunk chunk;
if (fourCC == ACTLChunk.ID) {
chunk = new ACTLChunk();
} else if (fourCC == FCTLChunk.ID) {
chunk = new FCTLChunk();
} else if (fourCC == FDATChunk.ID) {
chunk = new FDATChunk();
} else if (fourCC == IDATChunk.ID) {
chunk = new IDATChunk();
} else if (fourCC == IENDChunk.ID) {
chunk = new IENDChunk();
} else if (fourCC == IHDRChunk.ID) {
chunk = new IHDRChunk();
} else {
chunk = new Chunk();
}
chunk.offset = offset;
chunk.fourcc = fourCC;
chunk.length = size;
chunk.parse(reader);
chunk.crc = reader.readInt();
return chunk;
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.apng.decode;
import android.text.TextUtils;
import org.signal.glide.apng.io.APNGReader;
import java.io.IOException;
/**
* @Description: Length (长度) 4字节 指定数据块中数据域的长度,其长度不超过(2311)字节
* Chunk Type Code (数据块类型码) 4字节 数据块类型码由ASCII字母(A-Z和a-z)组成
* Chunk Data (数据块数据) 可变长度 存储按照Chunk Type Code指定的数据
* CRC (循环冗余检测) 4字节 存储用来检测是否有错误的循环冗余码
* @Link https://www.w3.org/TR/PNG
* @Author: pengfei.zhou
* @CreateDate: 2019/3/27
*/
class Chunk {
int length;
int fourcc;
int crc;
int offset;
static int fourCCToInt(String fourCC) {
if (TextUtils.isEmpty(fourCC) || fourCC.length() != 4) {
return 0xbadeffff;
}
return (fourCC.charAt(0) & 0xff)
| (fourCC.charAt(1) & 0xff) << 8
| (fourCC.charAt(2) & 0xff) << 16
| (fourCC.charAt(3) & 0xff) << 24
;
}
void parse(APNGReader reader) throws IOException {
int available = reader.available();
innerParse(reader);
int offset = available - reader.available();
if (offset > length) {
throw new IOException("Out of chunk area");
} else if (offset < length) {
reader.skip(length - offset);
}
}
void innerParse(APNGReader reader) throws IOException {
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.apng.decode;
import org.signal.glide.apng.io.APNGReader;
import java.io.IOException;
/**
* @Author: pengfei.zhou
* @CreateDate: 2019/3/27
* @see {link=https://developer.mozilla.org/en-US/docs/Mozilla/Tech/APNG#.27fcTL.27:_The_Frame_Control_Chunk}
*/
class FCTLChunk extends Chunk {
static final int ID = fourCCToInt("fcTL");
int sequence_number;
/**
* x_offset >= 0
* y_offset >= 0
* width > 0
* height > 0
* x_offset + width <= 'IHDR' width
* y_offset + height <= 'IHDR' height
*/
/**
* Width of the following frame.
*/
int width;
/**
* Height of the following frame.
*/
int height;
/**
* X position at which to render the following frame.
*/
int x_offset;
/**
* Y position at which to render the following frame.
*/
int y_offset;
/**
* The delay_num and delay_den parameters together specify a fraction indicating the time to
* display the current frame, in seconds. If the denominator is 0, it is to be treated as if it
* were 100 (that is, delay_num then specifies 1/100ths of a second).
* If the the value of the numerator is 0 the decoder should render the next frame as quickly as
* possible, though viewers may impose a reasonable lower bound.
* <p>
* Frame timings should be independent of the time required for decoding and display of each frame,
* so that animations will run at the same rate regardless of the performance of the decoder implementation.
*/
/**
* Frame delay fraction numerator.
*/
short delay_num;
/**
* Frame delay fraction denominator.
*/
short delay_den;
/**
* Type of frame area disposal to be done after rendering this frame.
* dispose_op specifies how the output buffer should be changed at the end of the delay (before rendering the next frame).
* If the first 'fcTL' chunk uses a dispose_op of APNG_DISPOSE_OP_PREVIOUS it should be treated as APNG_DISPOSE_OP_BACKGROUND.
*/
byte dispose_op;
/**
* Type of frame area rendering for this frame.
*/
byte blend_op;
/**
* No disposal is done on this frame before rendering the next; the contents of the output buffer are left as is.
*/
static final int APNG_DISPOSE_OP_NON = 0;
/**
* The frame's region of the output buffer is to be cleared to fully transparent black before rendering the next frame.
*/
static final int APNG_DISPOSE_OP_BACKGROUND = 1;
/**
* The frame's region of the output buffer is to be reverted to the previous contents before rendering the next frame.
*/
static final int APNG_DISPOSE_OP_PREVIOUS = 2;
/**
* blend_op<code> specifies whether the frame is to be alpha blended into the current output buffer content,
* or whether it should completely replace its region in the output buffer.
*/
/**
* All color components of the frame, including alpha, overwrite the current contents of the frame's output buffer region.
*/
static final int APNG_BLEND_OP_SOURCE = 0;
/**
* The frame should be composited onto the output buffer based on its alpha,
* using a simple OVER operation as described in the Alpha Channel Processing section of the Extensions
* to the PNG Specification, Version 1.2.0. Note that the second variation of the sample code is applicable.
*/
static final int APNG_BLEND_OP_OVER = 1;
@Override
void innerParse(APNGReader reader) throws IOException {
sequence_number = reader.readInt();
width = reader.readInt();
height = reader.readInt();
x_offset = reader.readInt();
y_offset = reader.readInt();
delay_num = reader.readShort();
delay_den = reader.readShort();
dispose_op = reader.peek();
blend_op = reader.peek();
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.apng.decode;
import org.signal.glide.apng.io.APNGReader;
import java.io.IOException;
/**
* @Description: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/APNG#.27fdAT.27:_The_Frame_Data_Chunk
* @Author: pengfei.zhou
* @CreateDate: 2019/3/27
*/
class FDATChunk extends Chunk {
static final int ID = fourCCToInt("fdAT");
int sequence_number;
@Override
void innerParse(APNGReader reader) throws IOException {
sequence_number = reader.readInt();
}
}

View File

@@ -0,0 +1,15 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.apng.decode;
/**
* @Description: 作用描述
* @Author: pengfei.zhou
* @CreateDate: 2019/3/27
*/
class IDATChunk extends Chunk {
static final int ID = fourCCToInt("IDAT");
}

View File

@@ -0,0 +1,15 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.apng.decode;
/**
* @Description: 作用描述
* @Author: pengfei.zhou
* @CreateDate: 2019/3/27
*/
class IENDChunk extends Chunk {
static final int ID = Chunk.fourCCToInt("IEND");
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.apng.decode;
import org.signal.glide.apng.io.APNGReader;
import java.io.IOException;
/**
* The IHDR chunk shall be the first chunk in the PNG datastream. It contains:
* <p>
* Width 4 bytes
* Height 4 bytes
* Bit depth 1 byte
* Colour type 1 byte
* Compression method 1 byte
* Filter method 1 byte
* Interlace method 1 byte
*
* @Author: pengfei.zhou
* @CreateDate: 2019/3/27
*/
class IHDRChunk extends Chunk {
static final int ID = fourCCToInt("IHDR");
/**
* 图像宽度,以像素为单位
*/
int width;
/**
* 图像高度,以像素为单位
*/
int height;
byte[] data = new byte[5];
@Override
void innerParse(APNGReader reader) throws IOException {
width = reader.readInt();
height = reader.readInt();
reader.read(data, 0, data.length);
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.apng.decode;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import org.signal.glide.apng.io.APNGReader;
import org.signal.glide.apng.io.APNGWriter;
import org.signal.glide.common.decode.Frame;
import java.io.IOException;
/**
* @Description: APNG4Android
* @Author: pengfei.zhou
* @CreateDate: 2019-05-13
*/
public class StillFrame extends Frame<APNGReader, APNGWriter> {
public StillFrame(APNGReader reader) {
super(reader);
}
@Override
public Bitmap draw(Canvas canvas, Paint paint, int sampleSize, Bitmap reusedBitmap, APNGWriter writer) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
options.inMutable = true;
options.inBitmap = reusedBitmap;
Bitmap bitmap = null;
try {
reader.reset();
bitmap = BitmapFactory.decodeStream(reader.toInputStream(), null, options);
assert bitmap != null;
paint.setXfermode(null);
canvas.drawBitmap(bitmap, 0, 0, paint);
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.apng.io;
import android.text.TextUtils;
import org.signal.glide.common.io.FilterReader;
import org.signal.glide.common.io.Reader;
import java.io.IOException;
/**
* @Description: APNGReader
* @Author: pengfei.zhou
* @CreateDate: 2019-05-13
*/
public class APNGReader extends FilterReader {
private static ThreadLocal<byte[]> __intBytes = new ThreadLocal<>();
protected static byte[] ensureBytes() {
byte[] bytes = __intBytes.get();
if (bytes == null) {
bytes = new byte[4];
__intBytes.set(bytes);
}
return bytes;
}
public APNGReader(Reader in) {
super(in);
}
public int readInt() throws IOException {
byte[] buf = ensureBytes();
read(buf, 0, 4);
return buf[3] & 0xFF |
(buf[2] & 0xFF) << 8 |
(buf[1] & 0xFF) << 16 |
(buf[0] & 0xFF) << 24;
}
public short readShort() throws IOException {
byte[] buf = ensureBytes();
read(buf, 0, 2);
return (short) (buf[1] & 0xFF |
(buf[0] & 0xFF) << 8);
}
/**
* @return read FourCC and match chars
*/
public boolean matchFourCC(String chars) throws IOException {
if (TextUtils.isEmpty(chars) || chars.length() != 4) {
return false;
}
int fourCC = readFourCC();
for (int i = 0; i < 4; i++) {
if (((fourCC >> (i * 8)) & 0xff) != chars.charAt(i)) {
return false;
}
}
return true;
}
public int readFourCC() throws IOException {
byte[] buf = ensureBytes();
read(buf, 0, 4);
return buf[0] & 0xff | (buf[1] & 0xff) << 8 | (buf[2] & 0xff) << 16 | (buf[3] & 0xff) << 24;
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.apng.io;
import org.signal.glide.common.io.ByteBufferWriter;
import java.nio.ByteOrder;
/**
* @Description: APNGWriter
* @Author: pengfei.zhou
* @CreateDate: 2019-05-13
*/
public class APNGWriter extends ByteBufferWriter {
public APNGWriter() {
super();
}
public void writeFourCC(int val) {
putByte((byte) (val & 0xff));
putByte((byte) ((val >> 8) & 0xff));
putByte((byte) ((val >> 16) & 0xff));
putByte((byte) ((val >> 24) & 0xff));
}
public void writeInt(int val) {
putByte((byte) ((val >> 24) & 0xff));
putByte((byte) ((val >> 16) & 0xff));
putByte((byte) ((val >> 8) & 0xff));
putByte((byte) (val & 0xff));
}
@Override
public void reset(int size) {
super.reset(size);
this.byteBuffer.order(ByteOrder.BIG_ENDIAN);
}
}

View File

@@ -0,0 +1,253 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.common;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.DrawFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import androidx.annotation.NonNull;
import androidx.vectordrawable.graphics.drawable.Animatable2Compat;
import org.signal.glide.Log;
import org.signal.glide.common.decode.FrameSeqDecoder;
import org.signal.glide.common.loader.Loader;
import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Set;
/**
* @Description: Frame animation drawable
* @Author: pengfei.zhou
* @CreateDate: 2019/3/27
*/
public abstract class FrameAnimationDrawable<Decoder extends FrameSeqDecoder> extends Drawable implements Animatable2Compat, FrameSeqDecoder.RenderListener {
private static final String TAG = FrameAnimationDrawable.class.getSimpleName();
private final Paint paint = new Paint();
private final Decoder frameSeqDecoder;
private DrawFilter drawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
private Matrix matrix = new Matrix();
private Set<AnimationCallback> animationCallbacks = new HashSet<>();
private Bitmap bitmap;
private static final int MSG_ANIMATION_START = 1;
private static final int MSG_ANIMATION_END = 2;
private Handler uiHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ANIMATION_START:
for (AnimationCallback animationCallback : animationCallbacks) {
animationCallback.onAnimationStart(FrameAnimationDrawable.this);
}
break;
case MSG_ANIMATION_END:
for (AnimationCallback animationCallback : animationCallbacks) {
animationCallback.onAnimationEnd(FrameAnimationDrawable.this);
}
break;
}
}
};
private Runnable invalidateRunnable = new Runnable() {
@Override
public void run() {
invalidateSelf();
}
};
private boolean autoPlay = true;
public FrameAnimationDrawable(Decoder frameSeqDecoder) {
paint.setAntiAlias(true);
this.frameSeqDecoder = frameSeqDecoder;
}
public FrameAnimationDrawable(Loader provider) {
paint.setAntiAlias(true);
this.frameSeqDecoder = createFrameSeqDecoder(provider, this);
}
public void setAutoPlay(boolean autoPlay) {
this.autoPlay = autoPlay;
}
protected abstract Decoder createFrameSeqDecoder(Loader streamLoader, FrameSeqDecoder.RenderListener listener);
/**
* @param loopLimit <=0为无限播放,>0为实际播放次数
*/
public void setLoopLimit(int loopLimit) {
frameSeqDecoder.setLoopLimit(loopLimit);
}
public void reset() {
frameSeqDecoder.reset();
}
public void pause() {
frameSeqDecoder.pause();
}
public void resume() {
frameSeqDecoder.resume();
}
public boolean isPaused() {
return frameSeqDecoder.isPaused();
}
@Override
public void start() {
if (autoPlay) {
frameSeqDecoder.start();
} else {
this.frameSeqDecoder.addRenderListener(this);
if (!this.frameSeqDecoder.isRunning()) {
this.frameSeqDecoder.start();
}
}
}
@Override
public void stop() {
if (autoPlay) {
frameSeqDecoder.stop();
} else {
this.frameSeqDecoder.removeRenderListener(this);
this.frameSeqDecoder.stopIfNeeded();
}
}
@Override
public boolean isRunning() {
return frameSeqDecoder.isRunning();
}
@Override
public void draw(Canvas canvas) {
if (bitmap == null || bitmap.isRecycled()) {
return;
}
canvas.setDrawFilter(drawFilter);
canvas.drawBitmap(bitmap, matrix, paint);
}
@Override
public void setBounds(int left, int top, int right, int bottom) {
super.setBounds(left, top, right, bottom);
boolean sampleSizeChanged = frameSeqDecoder.setDesiredSize(getBounds().width(), getBounds().height());
matrix.setScale(
1.0f * getBounds().width() * frameSeqDecoder.getSampleSize() / frameSeqDecoder.getBounds().width(),
1.0f * getBounds().height() * frameSeqDecoder.getSampleSize() / frameSeqDecoder.getBounds().height());
if (sampleSizeChanged)
this.bitmap = Bitmap.createBitmap(
frameSeqDecoder.getBounds().width() / frameSeqDecoder.getSampleSize(),
frameSeqDecoder.getBounds().height() / frameSeqDecoder.getSampleSize(),
Bitmap.Config.ARGB_8888);
}
@Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
paint.setColorFilter(colorFilter);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
public void onStart() {
Message.obtain(uiHandler, MSG_ANIMATION_START).sendToTarget();
}
@Override
public void onRender(ByteBuffer byteBuffer) {
if (!isRunning()) {
return;
}
if (this.bitmap == null || this.bitmap.isRecycled()) {
this.bitmap = Bitmap.createBitmap(
frameSeqDecoder.getBounds().width() / frameSeqDecoder.getSampleSize(),
frameSeqDecoder.getBounds().height() / frameSeqDecoder.getSampleSize(),
Bitmap.Config.ARGB_8888);
}
byteBuffer.rewind();
if (byteBuffer.remaining() < this.bitmap.getByteCount()) {
Log.e(TAG, "onRender:Buffer not large enough for pixels");
return;
}
this.bitmap.copyPixelsFromBuffer(byteBuffer);
uiHandler.post(invalidateRunnable);
}
@Override
public void onEnd() {
Message.obtain(uiHandler, MSG_ANIMATION_END).sendToTarget();
}
@Override
public boolean setVisible(boolean visible, boolean restart) {
if (this.autoPlay) {
if (visible) {
if (!isRunning()) {
start();
}
} else if (isRunning()) {
stop();
}
}
return super.setVisible(visible, restart);
}
@Override
public int getIntrinsicWidth() {
try {
return frameSeqDecoder.getBounds().width();
} catch (Exception exception) {
return 0;
}
}
@Override
public int getIntrinsicHeight() {
try {
return frameSeqDecoder.getBounds().height();
} catch (Exception exception) {
return 0;
}
}
@Override
public void registerAnimationCallback(@NonNull AnimationCallback animationCallback) {
this.animationCallbacks.add(animationCallback);
}
@Override
public boolean unregisterAnimationCallback(@NonNull AnimationCallback animationCallback) {
return this.animationCallbacks.remove(animationCallback);
}
@Override
public void clearAnimationCallbacks() {
this.animationCallbacks.clear();
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.common.decode;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import org.signal.glide.common.io.Reader;
import org.signal.glide.common.io.Writer;
/**
* @Description: One frame in an animation
* @Author: pengfei.zhou
* @CreateDate: 2019-05-13
*/
public abstract class Frame<R extends Reader, W extends Writer> {
protected final R reader;
public int frameWidth;
public int frameHeight;
public int frameX;
public int frameY;
public int frameDuration;
public Frame(R reader) {
this.reader = reader;
}
public abstract Bitmap draw(Canvas canvas, Paint paint, int sampleSize, Bitmap reusedBitmap, W writer);
}

View File

@@ -0,0 +1,539 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.common.decode;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import org.signal.glide.Log;
import org.signal.glide.common.executor.FrameDecoderExecutor;
import org.signal.glide.common.io.Reader;
import org.signal.glide.common.io.Writer;
import org.signal.glide.common.loader.Loader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
/**
* @Description: Abstract Frame Animation Decoder
* @Author: pengfei.zhou
* @CreateDate: 2019/3/27
*/
public abstract class FrameSeqDecoder<R extends Reader, W extends Writer> {
private static final String TAG = FrameSeqDecoder.class.getSimpleName();
private final int taskId;
private final Loader mLoader;
private final Handler workerHandler;
protected List<Frame> frames = new ArrayList<>();
protected int frameIndex = -1;
private int playCount;
private Integer loopLimit = null;
private Set<RenderListener> renderListeners = new HashSet<>();
private AtomicBoolean paused = new AtomicBoolean(true);
private static final Rect RECT_EMPTY = new Rect();
private Runnable renderTask = new Runnable() {
@Override
public void run() {
if (paused.get()) {
return;
}
if (canStep()) {
long start = System.currentTimeMillis();
long delay = step();
long cost = System.currentTimeMillis() - start;
workerHandler.postDelayed(this, Math.max(0, delay - cost));
for (RenderListener renderListener : renderListeners) {
renderListener.onRender(frameBuffer);
}
} else {
stop();
}
}
};
protected int sampleSize = 1;
private Set<Bitmap> cacheBitmaps = new HashSet<>();
protected Map<Bitmap, Canvas> cachedCanvas = new WeakHashMap<>();
protected ByteBuffer frameBuffer;
protected volatile Rect fullRect;
private W mWriter = getWriter();
private R mReader = null;
/**
* If played all the needed
*/
private boolean finished = false;
private enum State {
IDLE,
RUNNING,
INITIALIZING,
FINISHING,
}
private volatile State mState = State.IDLE;
public Loader getLoader() {
return mLoader;
}
protected abstract W getWriter();
protected abstract R getReader(Reader reader);
protected Bitmap obtainBitmap(int width, int height) {
Bitmap ret = null;
Iterator<Bitmap> iterator = cacheBitmaps.iterator();
while (iterator.hasNext()) {
int reuseSize = width * height * 4;
ret = iterator.next();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (ret != null && ret.getAllocationByteCount() >= reuseSize) {
iterator.remove();
if (ret.getWidth() != width || ret.getHeight() != height) {
ret.reconfigure(width, height, Bitmap.Config.ARGB_8888);
}
ret.eraseColor(0);
return ret;
}
} else {
if (ret != null && ret.getByteCount() >= reuseSize) {
if (ret.getWidth() == width && ret.getHeight() == height) {
iterator.remove();
ret.eraseColor(0);
}
return ret;
}
}
}
try {
Bitmap.Config config = Bitmap.Config.ARGB_8888;
ret = Bitmap.createBitmap(width, height, config);
} catch (OutOfMemoryError e) {
e.printStackTrace();
}
return ret;
}
protected void recycleBitmap(Bitmap bitmap) {
if (bitmap != null && !cacheBitmaps.contains(bitmap)) {
cacheBitmaps.add(bitmap);
}
}
/**
* 解码器的渲染回调
*/
public interface RenderListener {
/**
* 播放开始
*/
void onStart();
/**
* 帧播放
*/
void onRender(ByteBuffer byteBuffer);
/**
* 播放结束
*/
void onEnd();
}
/**
* @param loader webp的reader
* @param renderListener 渲染的回调
*/
public FrameSeqDecoder(Loader loader, @Nullable RenderListener renderListener) {
this.mLoader = loader;
if (renderListener != null) {
this.renderListeners.add(renderListener);
}
this.taskId = FrameDecoderExecutor.getInstance().generateTaskId();
this.workerHandler = new Handler(FrameDecoderExecutor.getInstance().getLooper(taskId));
}
public void addRenderListener(final RenderListener renderListener) {
this.workerHandler.post(new Runnable() {
@Override
public void run() {
renderListeners.add(renderListener);
}
});
}
public void removeRenderListener(final RenderListener renderListener) {
this.workerHandler.post(new Runnable() {
@Override
public void run() {
renderListeners.remove(renderListener);
}
});
}
public void stopIfNeeded() {
this.workerHandler.post(new Runnable() {
@Override
public void run() {
if (renderListeners.size() == 0) {
stop();
}
}
});
}
public Rect getBounds() {
if (fullRect == null) {
if (mState == State.FINISHING) {
Log.e(TAG, "In finishing,do not interrupt");
}
final Thread thread = Thread.currentThread();
workerHandler.post(new Runnable() {
@Override
public void run() {
try {
if (fullRect == null) {
if (mReader == null) {
mReader = getReader(mLoader.obtain());
} else {
mReader.reset();
}
initCanvasBounds(read(mReader));
}
} catch (Exception e) {
e.printStackTrace();
fullRect = RECT_EMPTY;
} finally {
LockSupport.unpark(thread);
}
}
});
LockSupport.park(thread);
}
return fullRect;
}
private void initCanvasBounds(Rect rect) {
fullRect = rect;
frameBuffer = ByteBuffer.allocate((rect.width() * rect.height() / (sampleSize * sampleSize) + 1) * 4);
if (mWriter == null) {
mWriter = getWriter();
}
}
private int getFrameCount() {
return this.frames.size();
}
/**
* @return Loop Count defined in file
*/
protected abstract int getLoopCount();
public void start() {
if (fullRect == RECT_EMPTY) {
return;
}
if (mState == State.RUNNING || mState == State.INITIALIZING) {
Log.i(TAG, debugInfo() + " Already started");
return;
}
if (mState == State.FINISHING) {
Log.e(TAG, debugInfo() + " Processing,wait for finish at " + mState);
}
mState = State.INITIALIZING;
if (Looper.myLooper() == workerHandler.getLooper()) {
innerStart();
} else {
workerHandler.post(new Runnable() {
@Override
public void run() {
innerStart();
}
});
}
}
@WorkerThread
private void innerStart() {
paused.compareAndSet(true, false);
final long start = System.currentTimeMillis();
try {
if (frames.size() == 0) {
try {
if (mReader == null) {
mReader = getReader(mLoader.obtain());
} else {
mReader.reset();
}
initCanvasBounds(read(mReader));
} catch (Throwable e) {
e.printStackTrace();
}
}
} finally {
Log.i(TAG, debugInfo() + " Set state to RUNNING,cost " + (System.currentTimeMillis() - start));
mState = State.RUNNING;
}
if (getNumPlays() == 0 || !finished) {
this.frameIndex = -1;
renderTask.run();
for (RenderListener renderListener : renderListeners) {
renderListener.onStart();
}
} else {
Log.i(TAG, debugInfo() + " No need to started");
}
}
@WorkerThread
private void innerStop() {
workerHandler.removeCallbacks(renderTask);
frames.clear();
for (Bitmap bitmap : cacheBitmaps) {
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
}
cacheBitmaps.clear();
if (frameBuffer != null) {
frameBuffer = null;
}
cachedCanvas.clear();
try {
if (mReader != null) {
mReader.close();
mReader = null;
}
if (mWriter != null) {
mWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
release();
mState = State.IDLE;
for (RenderListener renderListener : renderListeners) {
renderListener.onEnd();
}
}
public void stop() {
if (fullRect == RECT_EMPTY) {
return;
}
if (mState == State.FINISHING || mState == State.IDLE) {
Log.i(TAG, debugInfo() + "No need to stop");
return;
}
if (mState == State.INITIALIZING) {
Log.e(TAG, debugInfo() + "Processing,wait for finish at " + mState);
}
mState = State.FINISHING;
if (Looper.myLooper() == workerHandler.getLooper()) {
innerStop();
} else {
workerHandler.post(new Runnable() {
@Override
public void run() {
innerStop();
}
});
}
}
private String debugInfo() {
return "";
}
protected abstract void release();
public boolean isRunning() {
return mState == State.RUNNING || mState == State.INITIALIZING;
}
public boolean isPaused() {
return paused.get();
}
public void setLoopLimit(int limit) {
this.loopLimit = limit;
}
public void reset() {
this.playCount = 0;
this.frameIndex = -1;
this.finished = false;
}
public void pause() {
workerHandler.removeCallbacks(renderTask);
paused.compareAndSet(false, true);
}
public void resume() {
paused.compareAndSet(true, false);
workerHandler.removeCallbacks(renderTask);
workerHandler.post(renderTask);
}
public int getSampleSize() {
return sampleSize;
}
public boolean setDesiredSize(int width, int height) {
boolean sampleSizeChanged = false;
int sample = getDesiredSample(width, height);
if (sample != this.sampleSize) {
this.sampleSize = sample;
sampleSizeChanged = true;
final boolean tempRunning = isRunning();
workerHandler.removeCallbacks(renderTask);
workerHandler.post(new Runnable() {
@Override
public void run() {
innerStop();
try {
initCanvasBounds(read(getReader(mLoader.obtain())));
if (tempRunning) {
innerStart();
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
return sampleSizeChanged;
}
protected int getDesiredSample(int desiredWidth, int desiredHeight) {
if (desiredWidth == 0 || desiredHeight == 0) {
return 1;
}
int radio = Math.min(getBounds().width() / desiredWidth, getBounds().height() / desiredHeight);
int sample = 1;
while ((sample * 2) <= radio) {
sample *= 2;
}
return sample;
}
protected abstract Rect read(R reader) throws IOException;
private int getNumPlays() {
return this.loopLimit != null ? this.loopLimit : this.getLoopCount();
}
private boolean canStep() {
if (!isRunning()) {
return false;
}
if (frames.size() == 0) {
return false;
}
if (getNumPlays() <= 0) {
return true;
}
if (this.playCount < getNumPlays() - 1) {
return true;
} else if (this.playCount == getNumPlays() - 1 && this.frameIndex < this.getFrameCount() - 1) {
return true;
}
finished = true;
return false;
}
@WorkerThread
private long step() {
this.frameIndex++;
if (this.frameIndex >= this.getFrameCount()) {
this.frameIndex = 0;
this.playCount++;
}
Frame frame = getFrame(this.frameIndex);
if (frame == null) {
return 0;
}
renderFrame(frame);
return frame.frameDuration;
}
protected abstract void renderFrame(Frame frame);
private Frame getFrame(int index) {
if (index < 0 || index >= frames.size()) {
return null;
}
return frames.get(index);
}
/**
* Get Indexed frame
*
* @param index <0 means reverse from last index
*/
public Bitmap getFrameBitmap(int index) throws IOException {
if (mState != State.IDLE) {
Log.e(TAG, debugInfo() + ",stop first");
return null;
}
mState = State.RUNNING;
paused.compareAndSet(true, false);
if (frames.size() == 0) {
if (mReader == null) {
mReader = getReader(mLoader.obtain());
} else {
mReader.reset();
}
initCanvasBounds(read(mReader));
}
if (index < 0) {
index += this.frames.size();
}
if (index < 0) {
index = 0;
}
frameIndex = -1;
while (frameIndex < index) {
if (canStep()) {
step();
} else {
break;
}
}
frameBuffer.rewind();
Bitmap bitmap = Bitmap.createBitmap(getBounds().width() / getSampleSize(), getBounds().height() / getSampleSize(), Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(frameBuffer);
innerStop();
return bitmap;
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.common.executor;
import android.os.HandlerThread;
import android.os.Looper;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Description: com.github.penfeizhou.animation.executor
* @Author: pengfei.zhou
* @CreateDate: 2019-11-21
*/
public class FrameDecoderExecutor {
private static int sPoolNumber = 4;
private ArrayList<HandlerThread> mHandlerThreadGroup = new ArrayList<>();
private AtomicInteger counter = new AtomicInteger(0);
private FrameDecoderExecutor() {
}
static class Inner {
static final FrameDecoderExecutor sInstance = new FrameDecoderExecutor();
}
public void setPoolSize(int size) {
sPoolNumber = size;
}
public static FrameDecoderExecutor getInstance() {
return Inner.sInstance;
}
public Looper getLooper(int taskId) {
int idx = taskId % sPoolNumber;
if (idx >= mHandlerThreadGroup.size()) {
HandlerThread handlerThread = new HandlerThread("FrameDecoderExecutor-" + idx);
handlerThread.start();
mHandlerThreadGroup.add(handlerThread);
Looper looper = handlerThread.getLooper();
if (looper != null) {
return looper;
} else {
return Looper.getMainLooper();
}
} else {
if (mHandlerThreadGroup.get(idx) != null) {
Looper looper = mHandlerThreadGroup.get(idx).getLooper();
if (looper != null) {
return looper;
} else {
return Looper.getMainLooper();
}
} else {
return Looper.getMainLooper();
}
}
}
public int generateTaskId() {
return counter.getAndIncrement();
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.common.io;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
/**
* @Description: APNG4Android
* @Author: pengfei.zhou
* @CreateDate: 2019-05-14
*/
public class ByteBufferReader implements Reader {
private final ByteBuffer byteBuffer;
public ByteBufferReader(ByteBuffer byteBuffer) {
this.byteBuffer = byteBuffer;
byteBuffer.position(0);
}
@Override
public long skip(long total) throws IOException {
byteBuffer.position((int) (byteBuffer.position() + total));
return total;
}
@Override
public byte peek() throws IOException {
return byteBuffer.get();
}
@Override
public void reset() throws IOException {
byteBuffer.position(0);
}
@Override
public int position() {
return byteBuffer.position();
}
@Override
public int read(byte[] buffer, int start, int byteCount) throws IOException {
byteBuffer.get(buffer, start, byteCount);
return byteCount;
}
@Override
public int available() throws IOException {
return byteBuffer.limit() - byteBuffer.position();
}
@Override
public void close() throws IOException {
}
@Override
public InputStream toInputStream() throws IOException {
return new ByteArrayInputStream(byteBuffer.array());
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.common.io;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* @Description: ByteBufferWriter
* @Author: pengfei.zhou
* @CreateDate: 2019-05-12
*/
public class ByteBufferWriter implements Writer {
protected ByteBuffer byteBuffer;
public ByteBufferWriter() {
reset(10 * 1024);
}
@Override
public void putByte(byte b) {
byteBuffer.put(b);
}
@Override
public void putBytes(byte[] b) {
byteBuffer.put(b);
}
@Override
public int position() {
return byteBuffer.position();
}
@Override
public void skip(int length) {
byteBuffer.position(length + position());
}
@Override
public byte[] toByteArray() {
return byteBuffer.array();
}
@Override
public void close() {
}
@Override
public void reset(int size) {
if (byteBuffer == null || size > byteBuffer.capacity()) {
byteBuffer = ByteBuffer.allocate(size);
this.byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
}
byteBuffer.clear();
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.common.io;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/**
* @Description: FileReader
* @Author: pengfei.zhou
* @CreateDate: 2019-05-23
*/
public class FileReader extends FilterReader {
private final File mFile;
public FileReader(File file) throws IOException {
super(new StreamReader(new FileInputStream(file)));
mFile = file;
}
@Override
public void reset() throws IOException {
reader.close();
reader = new StreamReader(new FileInputStream(mFile));
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.common.io;
import java.io.IOException;
import java.io.InputStream;
/**
* @Description: FilterReader
* @Author: pengfei.zhou
* @CreateDate: 2019-05-23
*/
public class FilterReader implements Reader {
protected Reader reader;
public FilterReader(Reader in) {
this.reader = in;
}
@Override
public long skip(long total) throws IOException {
return reader.skip(total);
}
@Override
public byte peek() throws IOException {
return reader.peek();
}
@Override
public void reset() throws IOException {
reader.reset();
}
@Override
public int position() {
return reader.position();
}
@Override
public int read(byte[] buffer, int start, int byteCount) throws IOException {
return reader.read(buffer, start, byteCount);
}
@Override
public int available() throws IOException {
return reader.available();
}
@Override
public void close() throws IOException {
reader.close();
}
@Override
public InputStream toInputStream() throws IOException {
reset();
return reader.toInputStream();
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.common.io;
import java.io.IOException;
import java.io.InputStream;
/**
* @link {https://developers.google.com/speed/webp/docs/riff_container#terminology_basics}
* @Author: pengfei.zhou
* @CreateDate: 2019-05-11
*/
public interface Reader {
long skip(long total) throws IOException;
byte peek() throws IOException;
void reset() throws IOException;
int position();
int read(byte[] buffer, int start, int byteCount) throws IOException;
int available() throws IOException;
/**
* close io
*/
void close() throws IOException;
InputStream toInputStream() throws IOException;
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.common.io;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @Author: pengfei.zhou
* @CreateDate: 2019-05-11
*/
public class StreamReader extends FilterInputStream implements Reader {
private int position;
public StreamReader(InputStream in) {
super(in);
try {
in.reset();
} catch (IOException e) {
// e.printStackTrace();
}
}
@Override
public byte peek() throws IOException {
byte ret = (byte) read();
position++;
return ret;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int ret = super.read(b, off, len);
position += Math.max(0, ret);
return ret;
}
@Override
public synchronized void reset() throws IOException {
super.reset();
position = 0;
}
@Override
public long skip(long n) throws IOException {
long ret = super.skip(n);
position += ret;
return ret;
}
@Override
public int position() {
return position;
}
@Override
public InputStream toInputStream() throws IOException {
return this;
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.common.io;
import java.io.IOException;
/**
* @Description: APNG4Android
* @Author: pengfei.zhou
* @CreateDate: 2019-05-12
*/
public interface Writer {
void reset(int size);
void putByte(byte b);
void putBytes(byte[] b);
int position();
void skip(int length);
byte[] toByteArray();
void close() throws IOException;
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.common.loader;
import android.content.Context;
import java.io.IOException;
import java.io.InputStream;
/**
* @Description: 从Asset中读取流
* @Author: pengfei.zhou
* @CreateDate: 2019/3/28
*/
public class AssetStreamLoader extends StreamLoader {
private final Context mContext;
private final String mAssetName;
public AssetStreamLoader(Context context, String assetName) {
mContext = context.getApplicationContext();
mAssetName = assetName;
}
@Override
protected InputStream getInputStream() throws IOException {
return mContext.getAssets().open(mAssetName);
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.common.loader;
import org.signal.glide.common.io.ByteBufferReader;
import org.signal.glide.common.io.Reader;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* @Description: ByteBufferLoader
* @Author: pengfei.zhou
* @CreateDate: 2019-05-15
*/
public abstract class ByteBufferLoader implements Loader {
public abstract ByteBuffer getByteBuffer();
@Override
public Reader obtain() throws IOException {
return new ByteBufferReader(getByteBuffer());
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.common.loader;
import org.signal.glide.common.io.FileReader;
import org.signal.glide.common.io.Reader;
import java.io.File;
import java.io.IOException;
/**
* @Description: 从文件加载流
* @Author: pengfei.zhou
* @CreateDate: 2019/3/28
*/
public class FileLoader implements Loader {
private final File mFile;
private Reader mReader;
public FileLoader(String path) {
mFile = new File(path);
}
@Override
public synchronized Reader obtain() throws IOException {
return new FileReader(mFile);
}
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.common.loader;
import org.signal.glide.common.io.Reader;
import java.io.IOException;
/**
* @Description: Loader
* @Author: pengfei.zhou
* @CreateDate: 2019-05-14
*/
public interface Loader {
Reader obtain() throws IOException;
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.common.loader;
import android.content.Context;
import java.io.IOException;
import java.io.InputStream;
/**
* @Description: 从资源加载流
* @Author: pengfei.zhou
* @CreateDate: 2019/3/28
*/
public class ResourceStreamLoader extends StreamLoader {
private final Context mContext;
private final int mResId;
public ResourceStreamLoader(Context context, int resId) {
mContext = context.getApplicationContext();
mResId = resId;
}
@Override
protected InputStream getInputStream() throws IOException {
return mContext.getResources().openRawResource(mResId);
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2019 Zhou Pengfei
* SPDX-License-Identifier: Apache-2.0
*/
package org.signal.glide.common.loader;
import org.signal.glide.common.io.Reader;
import org.signal.glide.common.io.StreamReader;
import java.io.IOException;
import java.io.InputStream;
/**
* @Author: pengfei.zhou
* @CreateDate: 2019/3/28
*/
public abstract class StreamLoader implements Loader {
protected abstract InputStream getInputStream() throws IOException;
public final synchronized Reader obtain() throws IOException {
return new StreamReader(getInputStream());
}
}

View File

@@ -1,17 +1,22 @@
package org.thoughtcrime.securesms;
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.whispersystems.signalservice.api.account.AccountAttributes;
public final class AppCapabilities {
private AppCapabilities() {
}
private static final boolean UUID_CAPABLE = false;
private static final boolean GROUPS_V2_CAPABLE = false;
private static final boolean UUID_CAPABLE = false;
private static final boolean GV2_CAPABLE = true;
private static final boolean GV1_MIGRATION = true;
public static SignalServiceProfile.Capabilities getCapabilities() {
return new SignalServiceProfile.Capabilities(UUID_CAPABLE,
GROUPS_V2_CAPABLE);
/**
* @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 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,12 +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;
@@ -32,43 +33,53 @@ 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.logging.AndroidLogger;
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
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.MessageNotifier;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.registration.RegistrationUtil;
import org.thoughtcrime.securesms.revealable.ViewOnceMessageManager;
import org.thoughtcrime.securesms.ringrtc.RingRtcLogger;
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
import org.thoughtcrime.securesms.service.KeyCachingService;
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.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.VersionTracker;
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;
import org.webrtc.voiceengine.WebRtcAudioManager;
import org.webrtc.voiceengine.WebRtcAudioUtils;
@@ -91,12 +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 IncomingMessageObserver incomingMessageObserver;
private PersistentLogger persistentLogger;
private ExpiringMessageManager expiringMessageManager;
private ViewOnceMessageManager viewOnceMessageManager;
private PersistentLogger persistentLogger;
private volatile boolean isAppVisible;
@@ -106,51 +114,81 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate()");
initializeSecurityProvider();
initializeLogging();
initializeCrashHandling();
initializeAppDependencies();
initializeFirstEverAppLaunch();
initializeApplicationMigrations();
initializeMessageRetrieval();
initializeExpiringMessageManager();
initializeRevealableMessageManager();
initializeTypingStatusRepository();
initializeTypingStatusSender();
initializeGcmCheck();
initializeSignedPreKeyCheck();
initializePeriodicTasks();
initializeCircumvention();
initializeRingRtc();
initializePendingMessages();
initializeBlobProvider();
initializeCleanup();
Tracer.getInstance().start("Application#onCreate()");
AppStartup.getInstance().onApplicationCreate();
FeatureFlags.init();
NotificationChannels.create(this);
RefreshPreKeysJob.scheduleIfNecessary();
StorageSyncHelper.scheduleRoutineSync();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
long startTime = System.currentTimeMillis();
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();
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
@@ -158,11 +196,15 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
isAppVisible = false;
Log.i(TAG, "App is no longer visible.");
KeyCachingService.onAppBackgrounded(this);
MessageNotifier.setVisibleThread(-1);
ApplicationDependencies.getMessageNotifier().clearVisibleThread();
ApplicationDependencies.getFrameRateTracker().end();
ApplicationDependencies.getShakeToReport().disable();
}
public ExpiringMessageManager getExpiringMessageManager() {
if (expiringMessageManager == null) {
initializeExpiringMessageManager();
}
return expiringMessageManager;
}
@@ -170,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;
}
@@ -186,6 +220,13 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
return persistentLogger;
}
public void checkBuildExpiration() {
if (Util.getTimeUntilBuildExpiry() <= 0 && !SignalStore.misc().isClientDeprecated()) {
Log.w(TAG, "Build expired!");
SignalStore.misc().markClientDeprecated();
}
}
private void initializeSecurityProvider() {
try {
Class.forName("org.signal.aesgcmprovider.AesGcmCipher");
@@ -211,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());
}
@@ -227,7 +268,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
}
public void initializeMessageRetrieval() {
this.incomingMessageObserver = new IncomingMessageObserver(this);
ApplicationDependencies.getIncomingMessageObserver();
}
private void initializeAppDependencies() {
@@ -236,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);
}
}
@@ -270,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);
@@ -325,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() {
@@ -362,22 +390,50 @@ 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() {
SignalGlideCodecs.setLogProvider(new org.signal.glide.Log.Provider() {
@Override
public void v(@NonNull String tag, @NonNull String message) {
Log.v(tag, message);
}
@Override
public void d(@NonNull String tag, @NonNull String message) {
Log.d(tag, message);
}
@Override
public void i(@NonNull String tag, @NonNull String message) {
Log.i(tag, message);
}
@Override
public void w(@NonNull String tag, @NonNull String message) {
Log.w(tag, message);
}
@Override
public void e(@NonNull String tag, @NonNull String message, @Nullable Throwable throwable) {
Log.e(tag, message, throwable);
}
});
}
@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

@@ -17,34 +17,43 @@
*/
package org.thoughtcrime.securesms;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.PorterDuff;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.preference.Preference;
import org.thoughtcrime.securesms.help.HelpFragment;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.preferences.AdvancedPreferenceFragment;
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
import org.thoughtcrime.securesms.preferences.AppearancePreferenceFragment;
import org.thoughtcrime.securesms.preferences.BackupsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.ChatsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment;
import org.thoughtcrime.securesms.preferences.DataAndStoragePreferenceFragment;
import org.thoughtcrime.securesms.preferences.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.
@@ -53,13 +62,17 @@ import org.thoughtcrime.securesms.util.ThemeUtil;
*
*/
public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarActivity
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();
private static final String PREFERENCE_CATEGORY_PROFILE = "preference_category_profile";
private static final String PREFERENCE_CATEGORY_USERNAME = "preference_category_username";
private static final String PREFERENCE_CATEGORY_SMS_MMS = "preference_category_sms_mms";
private static final String PREFERENCE_CATEGORY_NOTIFICATIONS = "preference_category_notifications";
private static final String PREFERENCE_CATEGORY_APP_PROTECTION = "preference_category_app_protection";
@@ -69,10 +82,15 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
private static final String PREFERENCE_CATEGORY_DEVICES = "preference_category_devices";
private static final String PREFERENCE_CATEGORY_HELP = "preference_category_help";
private static final String PREFERENCE_CATEGORY_ADVANCED = "preference_category_advanced";
private static final String PREFERENCE_CATEGORY_DONATE = "preference_category_donate";
private static final String WAS_CONFIGURATION_UPDATED = "was_configuration_updated";
private final DynamicTheme dynamicTheme = new DynamicTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private boolean wasConfigurationUpdated = false;
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
@@ -86,11 +104,23 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
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();
@@ -112,20 +142,29 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
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);
@@ -134,6 +173,14 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
}
}
public void pushFragment(@NonNull Fragment fragment) {
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_from_end, R.anim.slide_to_start, R.anim.slide_from_start, R.anim.slide_to_end)
.replace(android.R.id.content, fragment)
.addToBackStack(null)
.commit();
}
public static class ApplicationPreferenceFragment extends CorrectedPreferenceFragment {
@Override
@@ -142,6 +189,8 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
this.findPreference(PREFERENCE_CATEGORY_PROFILE)
.setOnPreferenceClickListener(new ProfileClickListener());
this.findPreference(PREFERENCE_CATEGORY_USERNAME)
.setOnPreferenceClickListener(new UsernameClickListener());
this.findPreference(PREFERENCE_CATEGORY_SMS_MMS)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_SMS_MMS));
this.findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS)
@@ -159,7 +208,9 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
this.findPreference(PREFERENCE_CATEGORY_HELP)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_HELP));
this.findPreference(PREFERENCE_CATEGORY_ADVANCED)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_ADVANCED));
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_ADVANCED));
this.findPreference(PREFERENCE_CATEGORY_DONATE)
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_DONATE));
tintIcons();
}
@@ -168,12 +219,30 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
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
public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.preferences);
if (FeatureFlags.usernames()) {
UsernamePreference pref = (UsernamePreference) findPreference(PREFERENCE_CATEGORY_USERNAME);
pref.setVisible(shouldDisplayUsernameReminder());
pref.setOnLongClickListener(v -> {
new AlertDialog.Builder(requireContext())
.setMessage(R.string.ApplicationPreferencesActivity_hide_reminder)
.setPositiveButton(R.string.ApplicationPreferencesActivity_hide, (dialog, which) -> {
dialog.dismiss();
SignalStore.misc().hideUsernameReminder();
findPreference(PREFERENCE_CATEGORY_USERNAME).setVisible(false);
})
.setNegativeButton(android.R.string.cancel, ((dialog, which) -> dialog.dismiss()))
.setCancelable(true)
.show();
return true;
});
}
}
@Override
@@ -188,6 +257,11 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
private void setCategorySummaries() {
((ProfilePreference)this.findPreference(PREFERENCE_CATEGORY_PROFILE)).refresh();
if (FeatureFlags.usernames()) {
this.findPreference(PREFERENCE_CATEGORY_USERNAME)
.setVisible(shouldDisplayUsernameReminder());
}
this.findPreference(PREFERENCE_CATEGORY_SMS_MMS)
.setSummary(SmsMmsPreferenceFragment.getSummary(getActivity()));
this.findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS)
@@ -207,6 +281,10 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
}
}
private static boolean shouldDisplayUsernameReminder() {
return FeatureFlags.usernames() && !Recipient.self().getUsername().isPresent() && SignalStore.misc().shouldShowUsernameReminder();
}
private class CategoryClickListener implements Preference.OnPreferenceClickListener {
private String category;
@@ -235,7 +313,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
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);
@@ -247,6 +325,9 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
case PREFERENCE_CATEGORY_HELP:
fragment = new HelpFragment();
break;
case PREFERENCE_CATEGORY_DONATE:
CommunicationActions.openBrowserLink(requireContext(), getString(R.string.donate_url));
break;
default:
throw new AssertionError();
}
@@ -255,14 +336,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
Bundle args = new Bundle();
fragment.setArguments(args);
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.slide_from_end, R.anim.slide_to_start, R.anim.slide_from_start, R.anim.slide_to_end);
fragmentTransaction.replace(android.R.id.content, fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
((ApplicationPreferencesActivity) requireActivity()).pushFragment(fragment);
}
return true;
@@ -272,12 +346,15 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
private class ProfileClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
Intent intent = new Intent(preference.getContext(), EditProfileActivity.class);
intent.putExtra(EditProfileActivity.EXCLUDE_SYSTEM, true);
intent.putExtra(EditProfileActivity.DISPLAY_USERNAME, true);
intent.putExtra(EditProfileActivity.NEXT_BUTTON_TEXT, R.string.save);
requireActivity().startActivity(ManageProfileActivity.getIntent(requireActivity()));
return true;
}
}
requireActivity().startActivity(intent);
private class UsernameClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
requireActivity().startActivity(ManageProfileActivity.getIntentForUsernameEdit(preference.getContext()));
return true;
}
}

View File

@@ -0,0 +1,142 @@
package org.thoughtcrime.securesms;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.transition.TransitionInflater;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityOptionsCompat;
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.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.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.
*/
public final class AvatarPreviewActivity extends PassphraseRequiredActivity {
private static final String TAG = Log.tag(AvatarPreviewActivity.class);
private static final String RECIPIENT_ID_EXTRA = "recipient_id";
public static @NonNull Intent intentFromRecipientId(@NonNull Context context,
@NonNull RecipientId recipientId)
{
Intent intent = new Intent(context, AvatarPreviewActivity.class);
intent.putExtra(RECIPIENT_ID_EXTRA, recipientId.serialize());
return intent;
}
public static Bundle createTransitionBundle(@NonNull Activity activity, @NonNull View from) {
return ActivityOptionsCompat.makeSceneTransitionAnimation(activity, from, "avatar").toBundle();
}
@Override
protected void onCreate(Bundle savedInstanceState, boolean ready) {
super.onCreate(savedInstanceState, ready);
setTheme(R.style.TextSecure_MediaPreview);
setContentView(R.layout.contact_photo_preview_activity);
if (Build.VERSION.SDK_INT >= 21) {
postponeEnterTransition();
TransitionInflater inflater = TransitionInflater.from(this);
getWindow().setSharedElementEnterTransition(inflater.inflateTransition(R.transition.full_screen_avatar_image_enter_transition_set));
getWindow().setSharedElementReturnTransition(inflater.inflateTransition(R.transition.full_screen_avatar_image_return_transition_set));
}
Toolbar toolbar = findViewById(R.id.toolbar);
ImageView avatar = findViewById(R.id.avatar);
setSupportActionBar(toolbar);
requireSupportActionBar().setDisplayHomeAsUpEnabled(true);
Context context = getApplicationContext();
RecipientId recipientId = RecipientId.from(getIntent().getStringExtra(RECIPIENT_ID_EXTRA));
Recipient.live(recipientId).observe(this, recipient -> {
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();
GlideApp.with(this)
.asBitmap()
.load(contactPhoto)
.fallback(fallbackPhoto.asCallCard(this))
.error(fallbackPhoto.asCallCard(this))
.diskCacheStrategy(DiskCacheStrategy.ALL)
.addListener(new RequestListener<Bitmap>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Bitmap> target, boolean isFirstResource) {
Log.w(TAG, "Unable to load avatar, or avatar removed, closing");
finish();
return false;
}
@Override
public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, DataSource dataSource, boolean isFirstResource) {
return false;
}
})
.into(new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
avatar.setImageDrawable(RoundedBitmapDrawableFactory.create(resources, resource));
if (Build.VERSION.SDK_INT >= 21) {
startPostponedEnterTransition();
}
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
}
});
toolbar.setTitle(recipient.getDisplayName(context));
});
FullscreenHelper fullscreenHelper = new FullscreenHelper(this);
findViewById(android.R.id.content).setOnClickListener(v -> fullscreenHelper.toggleUiVisibility());
fullscreenHelper.configureToolbarSpacer(findViewById(R.id.toolbar_cutout_spacer));
fullscreenHelper.showAndHideWithSystemUI(getWindow(), findViewById(R.id.toolbar_layout));
}
@Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
}

View File

@@ -1,101 +0,0 @@
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.os.Bundle;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.app.ActivityOptionsCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageActivityHelper;
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;
import java.lang.reflect.Field;
public abstract class BaseActionBarActivity extends AppCompatActivity {
private static final String TAG = BaseActionBarActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
if (BaseActivity.isMenuWorkaroundRequired()) {
forceOverflowMenu();
}
super.onCreate(savedInstanceState);
}
@Override
protected void onResume() {
super.onResume();
initializeScreenshotSecurity();
DynamicLanguageActivityHelper.recreateIfNotInCorrectLanguage(this, TextSecurePreferences.getLanguage(this));
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return (keyCode == KeyEvent.KEYCODE_MENU && BaseActivity.isMenuWorkaroundRequired()) || super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && BaseActivity.isMenuWorkaroundRequired()) {
openOptionsMenu();
return true;
}
return super.onKeyUp(keyCode, event);
}
private void initializeScreenshotSecurity() {
if (TextSecurePreferences.isScreenSecurityEnabled(this)) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
}
}
/**
* Modified from: http://stackoverflow.com/a/13098824
*/
private void forceOverflowMenu() {
try {
ViewConfiguration config = ViewConfiguration.get(this);
Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
if(menuKeyField != null) {
menuKeyField.setAccessible(true);
menuKeyField.setBoolean(config, false);
}
} catch (IllegalAccessException e) {
Log.w(TAG, "Failed to force overflow menu.");
} catch (NoSuchFieldException e) {
Log.w(TAG, "Failed to force overflow menu.");
}
}
protected void startActivitySceneTransition(Intent intent, View sharedView, String transitionName) {
Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(this, sharedView, transitionName)
.toBundle();
ActivityCompat.startActivity(this, intent, bundle);
}
@TargetApi(VERSION_CODES.LOLLIPOP)
protected void setStatusBarColor(int color) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setStatusBarColor(color);
}
}
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(newBase, TextSecurePreferences.getLanguage(newBase)));
}
}

View File

@@ -1,46 +1,116 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
import android.view.KeyEvent;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
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;
public abstract class BaseActivity extends FragmentActivity {
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return (keyCode == KeyEvent.KEYCODE_MENU && isMenuWorkaroundRequired()) || super.onKeyDown(keyCode, event);
}
import java.util.Objects;
/**
* Base class for all activities. The vast majority of activities shouldn't extend this directly.
* Instead, they should extend {@link PassphraseRequiredActivity} so they're protected by
* screen lock.
*/
public abstract class BaseActivity extends AppCompatActivity {
private static final String TAG = Log.tag(BaseActivity.class);
@Override
public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && isMenuWorkaroundRequired()) {
openOptionsMenu();
return true;
}
return super.onKeyUp(keyCode, event);
}
public static boolean isMenuWorkaroundRequired() {
return VERSION.SDK_INT < VERSION_CODES.KITKAT &&
VERSION.SDK_INT > VERSION_CODES.GINGERBREAD_MR1 &&
("LGE".equalsIgnoreCase(Build.MANUFACTURER) || "E6710".equalsIgnoreCase(Build.DEVICE));
protected void onCreate(Bundle savedInstanceState) {
AppStartup.getInstance().onCriticalRenderEventStart();
logEvent("onCreate()");
super.onCreate(savedInstanceState);
AppStartup.getInstance().onCriticalRenderEventEnd();
}
@Override
protected void onResume() {
super.onResume();
DynamicLanguageActivityHelper.recreateIfNotInCorrectLanguage(this, TextSecurePreferences.getLanguage(this));
initializeScreenshotSecurity();
}
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(DynamicLanguageContextWrapper.updateContext(newBase, TextSecurePreferences.getLanguage(newBase)));
protected void onStart() {
logEvent("onStart()");
ApplicationDependencies.getShakeToReport().registerActivity(this);
super.onStart();
}
@Override
protected void onStop() {
logEvent("onStop()");
super.onStop();
}
@Override
protected void onDestroy() {
logEvent("onDestroy()");
super.onDestroy();
}
private void initializeScreenshotSecurity() {
if (TextSecurePreferences.isScreenSecurityEnabled(this)) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
}
}
protected void startActivitySceneTransition(Intent intent, View sharedView, String transitionName) {
Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(this, sharedView, transitionName)
.toBundle();
ActivityCompat.startActivity(this, intent, bundle);
}
@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
public void applyOverrideConfiguration(@NonNull Configuration overrideConfiguration) {
DynamicLanguageContextWrapper.prepareOverrideConfiguration(this, overrideConfiguration);
super.applyOverrideConfiguration(overrideConfiguration);
}
private void logEvent(@NonNull String event) {
Log.d(TAG, "[" + Log.tag(getClass()) + "] " + event);
}
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,13 +1,20 @@
package org.thoughtcrime.securesms;
import android.net.Uri;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.view.View;
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.database.model.ReactionRecord;
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;
@@ -20,17 +27,19 @@ import java.util.Locale;
import java.util.Set;
public interface BindableConversationItem extends Unbindable {
void bind(@NonNull MessageRecord messageRecord,
void bind(@NonNull LifecycleOwner lifecycleOwner,
@NonNull ConversationMessage messageRecord,
@NonNull Optional<MessageRecord> previousMessageRecord,
@NonNull Optional<MessageRecord> nextMessageRecord,
@NonNull GlideRequests glideRequests,
@NonNull Locale locale,
@NonNull Set<MessageRecord> batchSelected,
@NonNull Recipient recipients,
@Nullable String searchQuery,
boolean pulseHighlight);
@NonNull GlideRequests glideRequests,
@NonNull Locale locale,
@NonNull Set<ConversationMessage> batchSelected,
@NonNull Recipient recipients,
@Nullable String searchQuery,
boolean pulseMention,
boolean hasWallpaper);
MessageRecord getMessageRecord();
ConversationMessage getConversationMessage();
void setEventListener(@Nullable EventListener listener);
@@ -44,6 +53,21 @@ public interface BindableConversationItem extends Unbindable {
void onAddToContactsClicked(@NonNull Contact contact);
void onMessageSharedContactClicked(@NonNull List<Recipient> choices);
void onInviteSharedContactClicked(@NonNull List<Recipient> choices);
void onReactionClicked(long messageId, boolean isMms);
void 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

@@ -10,8 +10,11 @@ import java.util.Set;
public interface BindableConversationListItem extends Unbindable {
public void bind(@NonNull ThreadRecord thread,
@NonNull GlideRequests glideRequests, @NonNull Locale locale,
@NonNull Set<Long> typingThreads,
@NonNull Set<Long> selectedThreads, boolean batchMode);
void bind(@NonNull ThreadRecord thread,
@NonNull GlideRequests glideRequests, @NonNull Locale locale,
@NonNull Set<Long> typingThreads,
@NonNull Set<Long> selectedThreads, boolean batchMode);
void setBatchMode(boolean batchMode);
void updateTypingIndicator(@NonNull Set<Long> typingThreads);
}

View File

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

View File

@@ -1,149 +0,0 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.ListFragment;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.cursoradapter.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
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.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity {
private final DynamicTheme dynamicTheme = new DynamicTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@Override
public void onPreCreate() {
dynamicTheme.onCreate(this);
dynamicLanguage.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);
dynamicLanguage.onResume(this);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home: finish(); return true;
}
return false;
}
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(getActivity(), GlideApp.with(this), null));
getLoaderManager().initLoader(0, null, this);
}
@Override
public void onStart() {
super.onStart();
getLoaderManager().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();
Intent intent = new Intent(getActivity(), RecipientPreferenceActivity.class);
intent.putExtra(RecipientPreferenceActivity.RECIPIENT_ID, recipient.getId());
startActivity(intent);
}
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

@@ -0,0 +1,48 @@
package org.thoughtcrime.securesms;
import android.app.Activity;
import android.content.Intent;
import android.view.ContextThemeWrapper;
import androidx.appcompat.app.AlertDialog;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.util.DynamicTheme;
public final class ClearAvatarPromptActivity extends Activity {
private static final String ARG_TITLE = "arg_title";
public static Intent createForUserProfilePhoto() {
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(ApplicationDependencies.getApplication(), ClearAvatarPromptActivity.class);
intent.putExtra(ARG_TITLE, R.string.ClearProfileActivity_remove_group_photo);
return intent;
}
@Override
public void onResume() {
super.onResume();
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(message)
.setNegativeButton(android.R.string.cancel, (dialog, which) -> finish())
.setPositiveButton(R.string.ClearProfileActivity_remove, (dialog, which) -> {
Intent result = new Intent();
result.putExtra("delete", true);
setResult(Activity.RESULT_OK, result);
finish();
})
.setOnCancelListener(dialog -> finish())
.show();
}
}

View File

@@ -1,26 +0,0 @@
package org.thoughtcrime.securesms;
import android.app.Activity;
import android.content.Intent;
import androidx.appcompat.app.AlertDialog;
public class ClearProfileAvatarActivity extends Activity {
@Override
public void onResume() {
super.onResume();
new AlertDialog.Builder(this)
.setTitle(R.string.ClearProfileActivity_remove_profile_photo)
.setNegativeButton(android.R.string.cancel, (dialog, which) -> finish())
.setPositiveButton(R.string.ClearProfileActivity_remove, (dialog, which) -> {
Intent result = new Intent();
result.putExtra("delete", true);
setResult(Activity.RESULT_OK, result);
finish();
})
.show();
}
}

View File

@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.database.Cursor;
import android.os.AsyncTask;
import android.text.SpannableString;
import android.text.Spanned;
@@ -14,10 +13,8 @@ import androidx.appcompat.app.AlertDialog;
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.MessageDatabase;
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;
@@ -51,7 +48,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
super(context);
Recipient recipient = Recipient.resolved(mismatch.getRecipientId(context));
String name = recipient.toShortString(context);
String name = recipient.getDisplayName(context);
String introduction = context.getString(R.string.ConfirmIdentityDialog_your_safety_number_with_s_has_changed, name, name);
SpannableString spannableString = new SpannableString(introduction + " " +
context.getString(R.string.ConfirmIdentityDialog_you_may_wish_to_verify_your_safety_number_with_this_contact));
@@ -105,7 +102,6 @@ public class ConfirmIdentityDialog extends AlertDialog {
}
processMessageRecord(messageRecord);
processPendingMessageRecords(messageRecord.getThreadId(), mismatch);
return null;
}
@@ -115,29 +111,9 @@ public class ConfirmIdentityDialog extends AlertDialog {
else processIncomingMessageRecord(messageRecord);
}
private void processPendingMessageRecords(long threadId, IdentityKeyMismatch mismatch) {
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(getContext());
Cursor cursor = mmsSmsDatabase.getIdentityConflictMessagesForThread(threadId);
MmsSmsDatabase.Reader reader = mmsSmsDatabase.readerFor(cursor);
MessageRecord record;
try {
while ((record = reader.getNext()) != null) {
for (IdentityKeyMismatch recordMismatch : record.getIdentityKeyMismatches()) {
if (mismatch.equals(recordMismatch)) {
processMessageRecord(record);
}
}
}
} finally {
if (reader != null)
reader.close();
}
}
private void processOutgoingMessageRecord(MessageRecord messageRecord) {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext());
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(getContext());
MessageDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext());
MessageDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(getContext());
if (messageRecord.isMms()) {
mmsDatabase.removeMismatchedIdentity(messageRecord.getId(),
@@ -160,8 +136,8 @@ public class ConfirmIdentityDialog extends AlertDialog {
private void processIncomingMessageRecord(MessageRecord messageRecord) {
try {
PushDatabase pushDatabase = DatabaseFactory.getPushDatabase(getContext());
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext());
PushDatabase pushDatabase = DatabaseFactory.getPushDatabase(getContext());
MessageDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext());
smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
mismatch.getRecipientId(getContext()),
@@ -175,7 +151,9 @@ public class ConfirmIdentityDialog extends AlertDialog {
messageRecord.getDateSent(),
legacy ? Base64.decode(messageRecord.getBody()) : null,
!legacy ? Base64.decode(messageRecord.getBody()) : null,
0, null);
0,
0,
null);
long pushId = pushDatabase.insert(envelope);

View File

@@ -19,20 +19,18 @@ package org.thoughtcrime.securesms;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.thoughtcrime.securesms.logging.Log;
import 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.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
@@ -44,14 +42,16 @@ import java.lang.ref.WeakReference;
* @author Moxie Marlinspike
*
*/
public abstract class ContactSelectionActivity extends PassphraseRequiredActionBarActivity
public abstract class ContactSelectionActivity extends PassphraseRequiredActivity
implements SwipeRefreshLayout.OnRefreshListener,
ContactSelectionListFragment.OnContactSelectedListener
ContactSelectionListFragment.OnContactSelectedListener,
ContactSelectionListFragment.ScrollCallback
{
private static final String TAG = ContactSelectionActivity.class.getSimpleName();
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
public static final String EXTRA_LAYOUT_RES_ID = "layout_res_id";
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
protected ContactSelectionListFragment contactsFragment;
@@ -60,18 +60,17 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
}
@Override
protected void onCreate(Bundle icicle, boolean ready) {
if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) {
int displayMode = TextSecurePreferences.isSmsEnabled(this) ? DisplayMode.FLAG_ALL
: DisplayMode.FLAG_PUSH | DisplayMode.FLAG_ACTIVE_GROUPS | DisplayMode.FLAG_INACTIVE_GROUPS;
: DisplayMode.FLAG_PUSH | DisplayMode.FLAG_ACTIVE_GROUPS | DisplayMode.FLAG_INACTIVE_GROUPS | DisplayMode.FLAG_SELF;
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, displayMode);
}
setContentView(R.layout.contact_selection_activity);
setContentView(getIntent().getIntExtra(EXTRA_LAYOUT_RES_ID, R.layout.contact_selection_activity));
initializeToolbar();
initializeResources();
@@ -82,7 +81,6 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
public void onResume() {
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
}
protected ContactFilterToolbar getToolbar() {
@@ -90,10 +88,9 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
}
private void initializeToolbar() {
this.toolbar = ViewUtil.findById(this, R.id.toolbar);
this.toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
assert getSupportActionBar() != null;
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
getSupportActionBar().setDisplayShowTitleEnabled(false);
getSupportActionBar().setIcon(null);
@@ -102,7 +99,6 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
private void initializeResources() {
contactsFragment = (ContactSelectionListFragment) getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
contactsFragment.setOnContactSelectedListener(this);
contactsFragment.setOnRefreshListener(this);
}
@@ -116,11 +112,24 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
}
@Override
public void onContactSelected(Optional<RecipientId> recipientId, String number) {}
public boolean onBeforeContactSelected(Optional<RecipientId> recipientId, String number) {
return true;
}
@Override
public void onContactDeselected(Optional<RecipientId> recipientId, String number) {}
@Override
public void onBeginScroll() {
hideKeyboard();
}
private void hideKeyboard() {
ServiceUtil.getInputMethodManager(this)
.hideSoftInputFromWindow(toolbar.getWindowToken(), 0);
toolbar.clearFocus();
}
private static class RefreshDirectoryTask extends AsyncTask<Context, Void, Void> {
private final WeakReference<ContactSelectionActivity> activity;

View File

@@ -18,47 +18,64 @@ package org.thoughtcrime.securesms;
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;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.HorizontalScrollView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.fragment.app.FragmentActivity;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.transition.AutoTransition;
import androidx.transition.TransitionManager;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import com.google.android.material.chip.ChipGroup;
import com.pnikosis.materialishprogress.ProgressWheel;
import org.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;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.SelectedContact;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
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;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.UsernameUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.adapter.FixedViewsAdapter;
import org.thoughtcrime.securesms.util.adapter.RecyclerViewConcatenateAdapterStickyHeader;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
@@ -66,7 +83,7 @@ import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -76,39 +93,73 @@ import java.util.Set;
* @author Moxie Marlinspike
*
*/
public final class ContactSelectionListFragment extends Fragment
public final class ContactSelectionListFragment extends LoggingFragment
implements LoaderManager.LoaderCallbacks<Cursor>
{
@SuppressWarnings("unused")
private static final String TAG = Log.tag(ContactSelectionListFragment.class);
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";
private static final int CHIP_GROUP_EMPTY_CHILD_COUNT = 1;
private static final int CHIP_GROUP_REVEAL_DURATION_MS = 150;
private TextView emptyText;
private Set<SelectedContact> selectedContacts;
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;
public static final int NO_LIMIT = Integer.MAX_VALUE;
public static final String DISPLAY_MODE = "display_mode";
public static final String REFRESHABLE = "refreshable";
public static final String RECENTS = "recents";
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;
@Nullable private FixedViewsAdapter headerAdapter;
@Nullable private FixedViewsAdapter footerAdapter;
@Nullable private InviteCallback inviteCallback;
@Nullable private ListCallback listCallback;
@Nullable private ScrollCallback scrollCallback;
private GlideRequests glideRequests;
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) {
super.onAttach(context);
if (context instanceof InviteCallback) {
inviteCallback = (InviteCallback) context;
if (context instanceof ListCallback) {
listCallback = (ListCallback) context;
}
if (context instanceof ScrollCallback) {
scrollCallback = (ScrollCallback) context;
}
if (context instanceof OnContactSelectedListener) {
onContactSelectedListener = (OnContactSelectedListener) context;
}
if (context instanceof OnSelectionLimitReachedListener) {
onSelectionLimitReachedListener = (OnSelectionLimitReachedListener) context;
}
}
@@ -130,14 +181,16 @@ public final class ContactSelectionListFragment extends Fragment
if (!TextSecurePreferences.hasSuccessfullyRetrievedDirectory(getActivity())) {
handleContactPermissionGranted();
} else {
this.getLoaderManager().initLoader(0, null, this);
LoaderManager.getInstance(this).initLoader(0, null, this);
}
})
.onAnyDenied(() -> {
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
FragmentActivity activity = requireActivity();
if (getActivity().getIntent().getBooleanExtra(RECENTS, false)) {
getLoaderManager().initLoader(0, null, ContactSelectionListFragment.this);
activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
if (activity.getIntent().getBooleanExtra(RECENTS, false)) {
LoaderManager.getInstance(this).initLoader(0, null, ContactSelectionListFragment.this);
} else {
initializeNoContactsPermission();
}
@@ -149,64 +202,137 @@ public final class ContactSelectionListFragment extends Fragment
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.contact_selection_list_fragment, container, false);
emptyText = ViewUtil.findById(view, android.R.id.empty);
recyclerView = ViewUtil.findById(view, R.id.recycler_view);
swipeRefresh = ViewUtil.findById(view, R.id.swipe_refresh);
fastScroller = ViewUtil.findById(view, R.id.fast_scroller);
showContactsLayout = view.findViewById(R.id.show_contacts_container);
showContactsButton = view.findViewById(R.id.show_contacts_button);
showContactsDescription = view.findViewById(R.id.show_contacts_description);
showContactsProgress = view.findViewById(R.id.progress);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
emptyText = view.findViewById(android.R.id.empty);
recyclerView = view.findViewById(R.id.recycler_view);
swipeRefresh = view.findViewById(R.id.swipe_refresh);
fastScroller = view.findViewById(R.id.fast_scroller);
showContactsLayout = view.findViewById(R.id.show_contacts_container);
showContactsButton = view.findViewById(R.id.show_contacts_button);
showContactsDescription = view.findViewById(R.id.show_contacts_description);
showContactsProgress = view.findViewById(R.id.progress);
chipGroup = view.findViewById(R.id.chipGroup);
chipGroupScrollContainer = view.findViewById(R.id.chipGroupScrollContainer);
groupLimit = view.findViewById(R.id.group_limit);
constraintLayout = view.findViewById(R.id.container);
swipeRefresh.setEnabled(getActivity().getIntent().getBooleanExtra(REFRESHABLE, true));
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setItemAnimator(new DefaultItemAnimator() {
@Override
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) {
return 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;
}
currentSelection = getCurrentSelection();
updateGroupLimit(getChipCount());
return view;
}
private void updateGroupLimit(int chipCount) {
int members = currentSelection.size() + chipCount;
groupLimit.setText(getResources().getQuantityString(R.plurals.ContactSelectionListFragment_d_members, members, members));
groupLimit.setVisibility(isMulti && !hideCount ? View.VISIBLE : View.GONE);
groupLimit.setWarning(selectionWarningLimitExceeded());
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}
public @NonNull List<SelectedContact> getSelectedContacts() {
List<SelectedContact> selected = new LinkedList<>();
if (selectedContacts != null) {
selected.addAll(selectedContacts);
if (cursorRecyclerViewAdapter == null) {
return Collections.emptyList();
}
return selected;
return cursorRecyclerViewAdapter.getSelectedContacts();
}
private boolean isMulti() {
return getActivity().getIntent().getBooleanExtra(MULTI_SELECT, false);
public int getSelectedContactsCount() {
if (cursorRecyclerViewAdapter == null) {
return 0;
}
return cursorRecyclerViewAdapter.getSelectedContactsCount();
}
private Set<RecipientId> getCurrentSelection() {
List<RecipientId> currentSelection = requireActivity().getIntent().getParcelableArrayListExtra(CURRENT_SELECTION);
return currentSelection == null ? Collections.emptySet()
: Collections.unmodifiableSet(Stream.of(currentSelection).collect(Collectors.toSet()));
}
public boolean isMulti() {
return isMulti;
}
private void initializeCursor() {
glideRequests = GlideApp.with(this);
cursorRecyclerViewAdapter = new ContactSelectionListAdapter(requireContext(),
GlideApp.with(this),
glideRequests,
null,
new ListClickListener(),
isMulti());
selectedContacts = cursorRecyclerViewAdapter.getSelectedContacts();
isMulti,
currentSelection);
RecyclerViewConcatenateAdapterStickyHeader concatenateAdapter = new RecyclerViewConcatenateAdapterStickyHeader();
if (listCallback != null) {
headerAdapter = new FixedViewsAdapter(createNewGroupItem(listCallback));
headerAdapter.hide();
concatenateAdapter.addAdapter(headerAdapter);
}
concatenateAdapter.addAdapter(cursorRecyclerViewAdapter);
if (inviteCallback != null) {
footerAdapter = new FixedViewsAdapter(createInviteActionView(inviteCallback));
if (listCallback != null) {
footerAdapter = new FixedViewsAdapter(createInviteActionView(listCallback));
footerAdapter.hide();
concatenateAdapter.addAdapter(footerAdapter);
}
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) {
if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
if (scrollCallback != null) {
scrollCallback.onBeginScroll();
}
}
}
});
}
private View createInviteActionView(@NonNull InviteCallback inviteCallback) {
private View createInviteActionView(@NonNull ListCallback listCallback) {
View view = LayoutInflater.from(requireContext())
.inflate(R.layout.contact_selection_invite_action_item, (ViewGroup) requireView(), false);
view.setOnClickListener(v -> inviteCallback.onInvite());
view.setOnClickListener(v -> listCallback.onInvite());
return view;
}
private View createNewGroupItem(@NonNull ListCallback listCallback) {
View view = LayoutInflater.from(requireContext())
.inflate(R.layout.contact_selection_new_group_item, (ViewGroup) requireView(), false);
view.setOnClickListener(v -> listCallback.onNewGroup(false));
return view;
}
@@ -242,23 +368,28 @@ public final class ContactSelectionListFragment extends Fragment
swipeRefresh.setRefreshing(false);
}
public boolean hasQueryFilter() {
return !TextUtils.isEmpty(cursorFilter);
}
public void setRefreshing(boolean refreshing) {
swipeRefresh.setRefreshing(refreshing);
}
public void reset() {
selectedContacts.clear();
cursorRecyclerViewAdapter.clearSelectedContacts();
if (!isDetached() && !isRemoving() && getActivity() != null && !getActivity().isFinishing()) {
getLoaderManager().restartLoader(0, null, this);
LoaderManager.getInstance(this).restartLoader(0, null, this);
}
}
@Override
public @NonNull Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new ContactsCursorLoader(getActivity(),
getActivity().getIntent().getIntExtra(DISPLAY_MODE, DisplayMode.FLAG_ALL),
cursorFilter, getActivity().getIntent().getBooleanExtra(RECENTS, false));
FragmentActivity activity = requireActivity();
return new ContactsCursorLoader(activity,
activity.getIntent().getIntExtra(DISPLAY_MODE, DisplayMode.FLAG_ALL),
cursorFilter, activity.getIntent().getBooleanExtra(RECENTS, false));
}
@Override
@@ -272,6 +403,14 @@ public final class ContactSelectionListFragment extends Fragment
footerAdapter.show();
}
if (headerAdapter != null) {
if (TextUtils.isEmpty(cursorFilter)) {
headerAdapter.show();
} else {
headerAdapter.hide();
}
}
emptyText.setText(R.string.contact_selection_group_activity__no_contacts);
boolean useFastScroller = data != null && data.getCount() > 20;
recyclerView.setVerticalScrollBarEnabled(!useFastScroller);
@@ -323,8 +462,11 @@ public final class ContactSelectionListFragment extends Fragment
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();
@@ -336,7 +478,21 @@ public final class ContactSelectionListFragment extends Fragment
SelectedContact selectedContact = contact.isUsernameType() ? SelectedContact.forUsername(contact.getRecipientId().orNull(), contact.getNumber())
: SelectedContact.forPhone(contact.getRecipientId().orNull(), contact.getNumber());
if (!isMulti() || !selectedContacts.contains(selectedContact)) {
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 (selectionHardLimitReached()) {
if (onSelectionLimitReachedListener != null) {
onSelectionLimitReachedListener.onHardLimitReached(selectionLimit.getHardLimit());
} else {
GroupLimitDialog.showHardLimitMessage(requireContext());
}
return;
}
if (contact.isUsernameType()) {
AlertDialog loadingDialog = SimpleProgressDialog.show(requireContext());
@@ -346,31 +502,39 @@ public final class ContactSelectionListFragment extends Fragment
loadingDialog.dismiss();
if (uuid.isPresent()) {
Recipient recipient = Recipient.externalUsername(requireContext(), uuid.get(), contact.getNumber());
selectedContacts.add(SelectedContact.forUsername(recipient.getId(), contact.getNumber()));
contact.setChecked(true);
SelectedContact selected = SelectedContact.forUsername(recipient.getId(), contact.getNumber());
if (onContactSelectedListener != null) {
onContactSelectedListener.onContactSelected(Optional.of(recipient.getId()), null);
if (onContactSelectedListener.onBeforeContactSelected(Optional.of(recipient.getId()), null)) {
markContactSelected(selected);
cursorRecyclerViewAdapter.notifyItemRangeChanged(0, cursorRecyclerViewAdapter.getItemCount(), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
}
} else {
markContactSelected(selected);
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();
}
});
} else {
selectedContacts.add(selectedContact);
contact.setChecked(true);
if (onContactSelectedListener != null) {
onContactSelectedListener.onContactSelected(contact.getRecipientId(), contact.getNumber());
if (onContactSelectedListener.onBeforeContactSelected(contact.getRecipientId(), contact.getNumber())) {
markContactSelected(selectedContact);
cursorRecyclerViewAdapter.notifyItemRangeChanged(0, cursorRecyclerViewAdapter.getItemCount(), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
}
} else {
markContactSelected(selectedContact);
cursorRecyclerViewAdapter.notifyItemRangeChanged(0, cursorRecyclerViewAdapter.getItemCount(), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
}
}
} else {
selectedContacts.remove(selectedContact);
contact.setChecked(false);
markContactUnselected(selectedContact);
cursorRecyclerViewAdapter.notifyItemRangeChanged(0, cursorRecyclerViewAdapter.getItemCount(), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
if (onContactSelectedListener != null) {
onContactSelectedListener.onContactDeselected(contact.getRecipientId(), contact.getNumber());
@@ -379,20 +543,156 @@ public final class ContactSelectionListFragment extends Fragment
}
}
public void setOnContactSelectedListener(OnContactSelectedListener onContactSelectedListener) {
this.onContactSelectedListener = onContactSelectedListener;
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) {
addChipForSelectedContact(selectedContact);
}
}
private void markContactUnselected(@NonNull SelectedContact selectedContact) {
cursorRecyclerViewAdapter.removeFromSelectedContacts(selectedContact);
cursorRecyclerViewAdapter.notifyItemRangeChanged(0, cursorRecyclerViewAdapter.getItemCount(), ContactSelectionListAdapter.PAYLOAD_SELECTION_CHANGE);
removeChipForContact(selectedContact);
}
private void removeChipForContact(@NonNull SelectedContact contact) {
for (int i = chipGroup.getChildCount() - 1; i >= 0; i--) {
View v = chipGroup.getChildAt(i);
if (v instanceof ContactChip && contact.matches(((ContactChip) v).getContact())) {
chipGroup.removeView(v);
}
}
updateGroupLimit(getChipCount());
if (getChipCount() == 0) {
setChipGroupVisibility(ConstraintSet.GONE);
}
}
private void addChipForSelectedContact(@NonNull SelectedContact selectedContact) {
SimpleTask.run(getViewLifecycleOwner().getLifecycle(),
() -> Recipient.resolved(selectedContact.getOrCreateRecipientId(requireContext())),
resolved -> addChipForRecipient(resolved, selectedContact));
}
private void addChipForRecipient(@NonNull Recipient recipient, @NonNull SelectedContact selectedContact) {
final ContactChip chip = new ContactChip(requireContext());
if (getChipCount() == 0) {
setChipGroupVisibility(ConstraintSet.VISIBLE);
}
chip.setText(recipient.getShortDisplayName(requireContext()));
chip.setContact(selectedContact);
chip.setCloseIconVisible(true);
chip.setOnCloseIconClickListener(view -> {
markContactUnselected(selectedContact);
if (onContactSelectedListener != null) {
onContactSelectedListener.onContactDeselected(Optional.of(recipient.getId()), recipient.getE164().orNull());
}
});
chipGroup.getLayoutTransition().addTransitionListener(new LayoutTransition.TransitionListener() {
@Override
public void startTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType) {
}
@Override
public void endTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType) {
if (view == chip && transitionType == LayoutTransition.APPEARING) {
chipGroup.getLayoutTransition().removeTransitionListener(this);
registerChipRecipientObserver(chip, recipient.live());
chipGroup.post(ContactSelectionListFragment.this::smoothScrollChipsToEnd);
}
}
});
chip.setAvatar(glideRequests, recipient, () -> addChip(chip));
}
private void addChip(@NonNull ContactChip chip) {
chipGroup.addView(chip);
updateGroupLimit(getChipCount());
if (selectionWarningLimitReachedExactly()) {
if (onSelectionLimitReachedListener != null) {
onSelectionLimitReachedListener.onSuggestedLimitReached(selectionLimit.getRecommendedLimit());
} else {
GroupLimitDialog.showRecommendedLimitMessage(requireContext());
}
}
}
private int getChipCount() {
int count = chipGroup.getChildCount() - CHIP_GROUP_EMPTY_CHILD_COUNT;
if (count < 0) throw new AssertionError();
return count;
}
private void registerChipRecipientObserver(@NonNull ContactChip chip, @Nullable LiveRecipient recipient) {
if (recipient != null) {
recipient.observe(getViewLifecycleOwner(), resolved -> {
if (chip.isAttachedToWindow()) {
chip.setAvatar(glideRequests, resolved, null);
chip.setText(resolved.getShortDisplayName(chip.getContext()));
}
});
}
}
private void setChipGroupVisibility(int visibility) {
if (!requireActivity().getIntent().getBooleanExtra(DISPLAY_CHIPS, true)) {
return;
}
TransitionManager.beginDelayedTransition(constraintLayout, new AutoTransition().setDuration(CHIP_GROUP_REVEAL_DURATION_MS));
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(constraintLayout);
constraintSet.setVisibility(R.id.chipGroupScrollContainer, visibility);
constraintSet.applyTo(constraintLayout);
}
public void setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener onRefreshListener) {
this.swipeRefresh.setOnRefreshListener(onRefreshListener);
}
private void smoothScrollChipsToEnd() {
int x = chipGroupScrollContainer.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR ? chipGroup.getWidth() : 0;
chipGroupScrollContainer.smoothScrollTo(x, 0);
}
public interface OnContactSelectedListener {
void onContactSelected(Optional<RecipientId> recipientId, String number);
/** @return True if the contact is allowed to be selected, otherwise false. */
boolean onBeforeContactSelected(Optional<RecipientId> recipientId, String number);
void onContactDeselected(Optional<RecipientId> recipientId, String number);
}
public interface InviteCallback {
public interface OnSelectionLimitReachedListener {
void onSuggestedLimitReached(int limit);
void onHardLimitReached(int limit);
}
public interface ListCallback {
void onInvite();
void onNewGroup(boolean forceV1);
}
public interface ScrollCallback {
void onBeginScroll();
}
}

View File

@@ -1,101 +0,0 @@
package org.thoughtcrime.securesms;
import android.app.Activity;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.ListFragment;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import org.thoughtcrime.securesms.database.loaders.CountryListLoader;
import java.util.ArrayList;
import java.util.Map;
public class CountrySelectionFragment extends ListFragment implements LoaderManager.LoaderCallbacks<ArrayList<Map<String, String>>> {
private EditText countryFilter;
private CountrySelectedListener listener;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
return inflater.inflate(R.layout.country_selection_fragment, container, false);
}
@Override
public void onActivityCreated(Bundle bundle) {
super.onActivityCreated(bundle);
this.countryFilter = (EditText)getView().findViewById(R.id.country_search);
this.countryFilter.addTextChangedListener(new FilterWatcher());
getLoaderManager().initLoader(0, null, this).forceLoad();
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.listener = (CountrySelectedListener)activity;
}
@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
Map<String, String> item = (Map<String, String>)this.getListAdapter().getItem(position);
if (this.listener != null) {
this.listener.countrySelected(item.get("country_name"),
Integer.parseInt(item.get("country_code").substring(1)));
}
}
@Override
public @NonNull Loader<ArrayList<Map<String, String>>> onCreateLoader(int arg0, Bundle arg1) {
return new CountryListLoader(getActivity());
}
@Override
public void onLoadFinished(@NonNull Loader<ArrayList<Map<String, String>>> loader,
ArrayList<Map<String, String>> results)
{
String[] from = {"country_name", "country_code"};
int[] to = {R.id.country_name, R.id.country_code};
this.setListAdapter(new SimpleAdapter(getActivity(), results, R.layout.country_list_item, from, to));
if (this.countryFilter != null && this.countryFilter.getText().length() != 0) {
((SimpleAdapter)getListAdapter()).getFilter().filter(this.countryFilter.getText().toString());
}
}
@Override
public void onLoaderReset(@NonNull Loader<ArrayList<Map<String, String>>> arg0) {
this.setListAdapter(null);
}
public interface CountrySelectedListener {
public void countrySelected(String countryName, int countryCode);
}
private class FilterWatcher implements TextWatcher {
@Override
public void afterTextChanged(Editable s) {
if (getListAdapter() != null) {
((SimpleAdapter)getListAdapter()).getFilter().filter(s.toString());
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
}
}

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;
@@ -21,7 +22,7 @@ import org.thoughtcrime.securesms.database.SmsMigrator.ProgressDescription;
import org.thoughtcrime.securesms.service.ApplicationMigrationService;
import org.thoughtcrime.securesms.service.ApplicationMigrationService.ImportState;
public class DatabaseMigrationActivity extends PassphraseRequiredActionBarActivity {
public class DatabaseMigrationActivity extends PassphraseRequiredActivity {
private final ImportServiceConnection serviceConnection = new ImportServiceConnection();
private final ImportStateHandler importStateHandler = new ImportStateHandler();
@@ -150,7 +151,7 @@ public class DatabaseMigrationActivity extends PassphraseRequiredActionBarActivi
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 PassphraseRequiredActionBarActivi
}
private class ImportStateHandler extends Handler {
public ImportStateHandler() {
super(Looper.getMainLooper());
}
@Override
public void handleMessage(Message message) {
switch (message.what) {

View File

@@ -18,12 +18,11 @@ 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.push.AccountManagerFactory;
import org.thoughtcrime.securesms.qr.ScanListener;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.DynamicLanguage;
@@ -42,7 +41,7 @@ import org.whispersystems.signalservice.internal.push.DeviceLimitExceededExcepti
import java.io.IOException;
public class DeviceActivity extends PassphraseRequiredActionBarActivity
public class DeviceActivity extends PassphraseRequiredActivity
implements Button.OnClickListener, ScanListener, DeviceLinkFragment.LinkClickedListener
{

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 androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewAnimationUtils;
@@ -15,12 +13,14 @@ 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;
import org.thoughtcrime.securesms.util.ViewUtil;
public class DeviceAddFragment extends Fragment {
public class DeviceAddFragment extends LoggingFragment {
private ViewGroup container;
private LinearLayout overlay;
@@ -32,9 +32,9 @@ public class DeviceAddFragment extends Fragment {
@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;
@@ -53,7 +52,7 @@ public class DeviceListFragment extends ListFragment
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.locale = (Locale) getArguments().getSerializable(PassphraseRequiredActionBarActivity.LOCALE_EXTRA);
this.locale = (Locale) getArguments().getSerializable(PassphraseRequiredActivity.LOCALE_EXTRA);
}
@Override
@@ -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;
@@ -164,25 +163,29 @@ public class DeviceListFragment extends ListFragment
@SuppressLint("StaticFieldLeak")
private void handleDisconnectDevice(final long deviceId) {
new ProgressDialogAsyncTask<Void, Void, Void>(getActivity(),
R.string.DeviceListActivity_unlinking_device_no_ellipsis,
R.string.DeviceListActivity_unlinking_device)
new ProgressDialogAsyncTask<Void, Void, Boolean>(getActivity(),
R.string.DeviceListActivity_unlinking_device_no_ellipsis,
R.string.DeviceListActivity_unlinking_device)
{
@Override
protected Void doInBackground(Void... params) {
protected Boolean doInBackground(Void... params) {
try {
accountManager.removeDevice(deviceId);
return true;
} catch (IOException e) {
Log.w(TAG, e);
Toast.makeText(getActivity(), R.string.DeviceListActivity_network_failed, Toast.LENGTH_LONG).show();
return false;
}
return null;
}
@Override
protected void onPostExecute(Void result) {
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
getLoaderManager().restartLoader(0, null, DeviceListFragment.this);
if (result) {
getLoaderManager().restartLoader(0, null, DeviceListFragment.this);
} else {
Toast.makeText(getActivity(), R.string.DeviceListActivity_network_failed, Toast.LENGTH_LONG).show();
}
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}

View File

@@ -2,10 +2,11 @@ package org.thoughtcrime.securesms;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AlertDialog;
import android.view.Window;
public class DeviceProvisioningActivity extends PassphraseRequiredActionBarActivity {
import androidx.appcompat.app.AlertDialog;
public class DeviceProvisioningActivity extends PassphraseRequiredActivity {
@SuppressWarnings("unused")
private static final String TAG = DeviceProvisioningActivity.class.getSimpleName();
@@ -17,9 +18,6 @@ public class DeviceProvisioningActivity extends PassphraseRequiredActionBarActiv
@Override
protected void onCreate(Bundle bundle, boolean ready) {
assert getSupportActionBar() != null;
getSupportActionBar().hide();
AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle(getString(R.string.DeviceProvisioningActivity_link_a_signal_device))
.setMessage(getString(R.string.DeviceProvisioningActivity_it_looks_like_youre_trying_to_link_a_signal_device_using_a_3rd_party_scanner))
@@ -29,7 +27,7 @@ public class DeviceProvisioningActivity extends PassphraseRequiredActionBarActiv
startActivity(intent);
finish();
})
.setNegativeButton(R.string.DeviceProvisioningActivity_cancel, (dialog12, which) -> {
.setNegativeButton(android.R.string.cancel, (dialog12, which) -> {
dialog12.dismiss();
finish();
})

View File

@@ -1,14 +1,17 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import java.util.Arrays;
import cn.carbswang.android.numberpickerview.library.NumberPickerView;
public class ExpirationDialog extends AlertDialog {
@@ -36,7 +39,7 @@ public class ExpirationDialog extends AlertDialog {
builder.setView(view);
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> {
int selected = ((NumberPickerView)view.findViewById(R.id.expiration_number_picker)).getValue();
listener.onClick(context.getResources().getIntArray(R.array.expiration_times)[selected]);
listener.onClick(getExpirationTimes(context, currentExpiration)[selected]);
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
@@ -47,7 +50,7 @@ public class ExpirationDialog extends AlertDialog {
final View view = inflater.inflate(R.layout.expiration_dialog, null);
final NumberPickerView numberPickerView = view.findViewById(R.id.expiration_number_picker);
final TextView textView = view.findViewById(R.id.expiration_details);
final int[] expirationTimes = context.getResources().getIntArray(R.array.expiration_times);
final int[] expirationTimes = getExpirationTimes(context, currentExpiration);
final String[] expirationDisplayValues = new String[expirationTimes.length];
int selectedIndex = expirationTimes.length - 1;
@@ -80,6 +83,19 @@ public class ExpirationDialog extends AlertDialog {
return view;
}
private static int[] getExpirationTimes(Context context, int currentExpiration) {
int[] expirationTimes = context.getResources().getIntArray(R.array.expiration_times);
int location = Arrays.binarySearch(expirationTimes, currentExpiration);
if (location < 0) {
int[] temp = Arrays.copyOf(expirationTimes, expirationTimes.length + 1);
temp[temp.length - 1] = currentExpiration;
Arrays.sort(temp);
expirationTimes = temp;
}
return expirationTimes;
}
public interface OnClickListener {
public void onClick(int expirationTime);
}

View File

@@ -1,619 +0,0 @@
/*
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.transition.Transition;
import org.thoughtcrime.securesms.components.PushRecipientsPanel;
import org.thoughtcrime.securesms.components.PushRecipientsPanel.RecipientsPanelChangedListener;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
import org.thoughtcrime.securesms.conversation.ConversationActivity;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.groups.GroupManager.GroupActionResult;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mediasend.AvatarSelectionActivity;
import org.thoughtcrime.securesms.mediasend.AvatarSelectionBottomSheetDialogFragment;
import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter;
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter.OnRecipientDeletedListener;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* Activity to create and update groups
*
* @author Jake McGinty
*/
public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
implements OnRecipientDeletedListener,
RecipientsPanelChangedListener
{
private final static String TAG = GroupCreateActivity.class.getSimpleName();
public static final String GROUP_ID_EXTRA = "group_id";
public static final String GROUP_THREAD_EXTRA = "group_thread";
private final DynamicTheme dynamicTheme = new DynamicTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private static final short REQUEST_CODE_SELECT_AVATAR = 26165;
private static final int PICK_CONTACT = 1;
private EditText groupName;
private ListView lv;
private ImageView avatar;
private TextView creatingText;
private Bitmap avatarBmp;
@NonNull private Optional<GroupData> groupToUpdate = Optional.absent();
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
}
@Override
protected void onCreate(Bundle state, boolean ready) {
setContentView(R.layout.group_create_activity);
//noinspection ConstantConditions
initializeAppBar();
initializeResources();
initializeExistingGroup();
}
@Override
public void onResume() {
super.onResume();
dynamicTheme.onResume(this);
dynamicLanguage.onResume(this);
updateViewState();
}
private boolean isSignalGroup() {
return TextSecurePreferences.isPushRegistered(this) && !getAdapter().hasNonPushMembers();
}
private void disableSignalGroupViews(int reasonResId) {
View pushDisabled = findViewById(R.id.push_disabled);
pushDisabled.setVisibility(View.VISIBLE);
((TextView) findViewById(R.id.push_disabled_reason)).setText(reasonResId);
avatar.setEnabled(false);
groupName.setEnabled(false);
}
private void enableSignalGroupViews() {
findViewById(R.id.push_disabled).setVisibility(View.GONE);
avatar.setEnabled(true);
groupName.setEnabled(true);
}
@SuppressWarnings("ConstantConditions")
private void updateViewState() {
if (!TextSecurePreferences.isPushRegistered(this)) {
disableSignalGroupViews(R.string.GroupCreateActivity_youre_not_registered_for_signal);
getSupportActionBar().setTitle(R.string.GroupCreateActivity_actionbar_mms_title);
} else if (getAdapter().hasNonPushMembers()) {
disableSignalGroupViews(R.string.GroupCreateActivity_contacts_dont_support_push);
getSupportActionBar().setTitle(R.string.GroupCreateActivity_actionbar_mms_title);
} else {
enableSignalGroupViews();
getSupportActionBar().setTitle(groupToUpdate.isPresent()
? R.string.GroupCreateActivity_actionbar_edit_title
: R.string.GroupCreateActivity_actionbar_title);
}
}
private static boolean isActiveInDirectory(Recipient recipient) {
return recipient.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED;
}
private void addSelectedContacts(@NonNull Recipient... recipients) {
new AddMembersTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, recipients);
}
private void addSelectedContacts(@NonNull Collection<Recipient> recipients) {
addSelectedContacts(recipients.toArray(new Recipient[recipients.size()]));
}
private void initializeAppBar() {
Drawable upIcon = ContextCompat.getDrawable(this, R.drawable.ic_arrow_left_24);
getSupportActionBar().setHomeAsUpIndicator(upIcon);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
private void initializeResources() {
RecipientsEditor recipientsEditor = ViewUtil.findById(this, R.id.recipients_text);
PushRecipientsPanel recipientsPanel = ViewUtil.findById(this, R.id.recipients);
lv = ViewUtil.findById(this, R.id.selected_contacts_list);
avatar = ViewUtil.findById(this, R.id.avatar);
groupName = ViewUtil.findById(this, R.id.group_name);
creatingText = ViewUtil.findById(this, R.id.creating_group_text);
SelectedRecipientsAdapter adapter = new SelectedRecipientsAdapter(this);
adapter.setOnRecipientDeletedListener(this);
lv.setAdapter(adapter);
recipientsEditor.setHint(R.string.recipients_panel__add_members);
recipientsPanel.setPanelChangeListener(this);
findViewById(R.id.contacts_button).setOnClickListener(new AddRecipientButtonListener());
avatar.setImageDrawable(getDefaultGroupAvatar());
avatar.setOnClickListener(view -> AvatarSelectionBottomSheetDialogFragment.create(avatarBmp != null, false, REQUEST_CODE_SELECT_AVATAR).show(getSupportFragmentManager(), null));
}
private Drawable getDefaultGroupAvatar() {
return new ResourceContactPhoto(R.drawable.ic_group_outline_34, R.drawable.ic_group_outline_20).asDrawable(this, ContactColors.UNKNOWN_COLOR.toConversationColor(this));
}
private void initializeExistingGroup() {
final GroupId groupId = GroupId.parseNullable(getIntent().getStringExtra(GROUP_ID_EXTRA));
if (groupId != null) {
new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, groupId);
}
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuInflater inflater = this.getMenuInflater();
menu.clear();
inflater.inflate(R.menu.group_create, menu);
super.onPrepareOptionsMenu(menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
case R.id.menu_create_group:
if (groupToUpdate.isPresent()) handleGroupUpdate();
else handleGroupCreate();
return true;
}
return false;
}
@Override
public void onRecipientDeleted(Recipient recipient) {
getAdapter().remove(recipient);
updateViewState();
}
@Override
public void onRecipientsPanelUpdate(List<Recipient> recipients) {
if (recipients != null && !recipients.isEmpty()) addSelectedContacts(recipients);
}
private void handleGroupCreate() {
if (getAdapter().getCount() < 1) {
Log.i(TAG, getString(R.string.GroupCreateActivity_contacts_no_members));
Toast.makeText(getApplicationContext(), R.string.GroupCreateActivity_contacts_no_members, Toast.LENGTH_SHORT).show();
return;
}
if (isSignalGroup()) {
new CreateSignalGroupTask(this, avatarBmp, getGroupName(), getAdapter().getRecipients()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
new CreateMmsGroupTask(this, getAdapter().getRecipients()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
private void handleGroupUpdate() {
new UpdateSignalGroupTask(this, groupToUpdate.get().id, avatarBmp,
getGroupName(), getAdapter().getRecipients()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void handleOpenConversation(long threadId, Recipient recipient) {
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
intent.putExtra(ConversationActivity.RECIPIENT_EXTRA, recipient.getId());
startActivity(intent);
finish();
}
private SelectedRecipientsAdapter getAdapter() {
return (SelectedRecipientsAdapter)lv.getAdapter();
}
private @Nullable String getGroupName() {
return groupName.getText() != null ? groupName.getText().toString() : null;
}
@Override
public void onActivityResult(int reqCode, int resultCode, final Intent data) {
super.onActivityResult(reqCode, resultCode, data);
if (data == null || resultCode != Activity.RESULT_OK)
return;
switch (reqCode) {
case PICK_CONTACT:
List<RecipientId> selected = data.getParcelableArrayListExtra(PushContactSelectionActivity.KEY_SELECTED_RECIPIENTS);
for (RecipientId contact : selected) {
Recipient recipient = Recipient.resolved(contact);
addSelectedContacts(recipient);
}
break;
case REQUEST_CODE_SELECT_AVATAR:
if (data.getBooleanExtra("delete", false)) {
avatarBmp = null;
avatar.setImageDrawable(getDefaultGroupAvatar());
return;
}
final Media result = data.getParcelableExtra(AvatarSelectionActivity.EXTRA_MEDIA);
final DecryptableUri decryptableUri = new DecryptableUri(result.getUri());
GlideApp.with(this)
.asBitmap()
.load(decryptableUri)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.centerCrop()
.override(AvatarHelper.AVATAR_DIMENSIONS, AvatarHelper.AVATAR_DIMENSIONS)
.into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
setAvatar(decryptableUri, resource);
}
});
}
}
private class AddRecipientButtonListener implements View.OnClickListener {
@Override
public void onClick(View v) {
Intent intent = new Intent(GroupCreateActivity.this, PushContactSelectionActivity.class);
if (groupToUpdate.isPresent()) {
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_PUSH);
} else {
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_PUSH | DisplayMode.FLAG_SMS);
}
startActivityForResult(intent, PICK_CONTACT);
}
}
private static class CreateMmsGroupTask extends AsyncTask<Void,Void,GroupActionResult> {
private final GroupCreateActivity activity;
private final Set<Recipient> members;
public CreateMmsGroupTask(GroupCreateActivity activity, Set<Recipient> members) {
this.activity = activity;
this.members = members;
}
@Override
protected GroupActionResult doInBackground(Void... avoid) {
List<RecipientId> memberAddresses = new LinkedList<>();
for (Recipient recipient : members) {
memberAddresses.add(recipient.getId());
}
memberAddresses.add(Recipient.self().getId());
GroupId.Mms groupId = DatabaseFactory.getGroupDatabase(activity).getOrCreateMmsGroupForMembers(memberAddresses);
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(activity).getOrInsertFromGroupId(groupId);
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
long threadId = DatabaseFactory.getThreadDatabase(activity).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.DEFAULT);
return new GroupActionResult(groupRecipient, threadId);
}
@Override
protected void onPostExecute(GroupActionResult result) {
activity.handleOpenConversation(result.getThreadId(), result.getGroupRecipient());
}
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
}
private abstract static class SignalGroupTask extends AsyncTask<Void,Void,Optional<GroupActionResult>> {
protected GroupCreateActivity activity;
protected Bitmap avatar;
protected Set<Recipient> members;
protected String name;
public SignalGroupTask(GroupCreateActivity activity,
Bitmap avatar,
String name,
Set<Recipient> members)
{
this.activity = activity;
this.avatar = avatar;
this.name = name;
this.members = members;
}
@Override
protected void onPreExecute() {
activity.findViewById(R.id.group_details_layout).setVisibility(View.GONE);
activity.findViewById(R.id.creating_group_layout).setVisibility(View.VISIBLE);
activity.findViewById(R.id.menu_create_group).setVisibility(View.GONE);
final int titleResId = activity.groupToUpdate.isPresent()
? R.string.GroupCreateActivity_updating_group
: R.string.GroupCreateActivity_creating_group;
activity.creatingText.setText(activity.getString(titleResId, activity.getGroupName()));
}
@Override
protected void onPostExecute(Optional<GroupActionResult> groupActionResultOptional) {
if (activity.isFinishing()) return;
activity.findViewById(R.id.group_details_layout).setVisibility(View.VISIBLE);
activity.findViewById(R.id.creating_group_layout).setVisibility(View.GONE);
activity.findViewById(R.id.menu_create_group).setVisibility(View.VISIBLE);
}
}
private static class CreateSignalGroupTask extends SignalGroupTask {
public CreateSignalGroupTask(GroupCreateActivity activity, Bitmap avatar, String name, Set<Recipient> members) {
super(activity, avatar, name, members);
}
@Override
protected Optional<GroupActionResult> doInBackground(Void... aVoid) {
return Optional.of(GroupManager.createGroup(activity, members, avatar, name, false));
}
@Override
protected void onPostExecute(Optional<GroupActionResult> result) {
if (result.isPresent() && result.get().getThreadId() > -1) {
if (!activity.isFinishing()) {
activity.handleOpenConversation(result.get().getThreadId(), result.get().getGroupRecipient());
}
} else {
super.onPostExecute(result);
Toast.makeText(activity.getApplicationContext(),
R.string.GroupCreateActivity_contacts_invalid_number, Toast.LENGTH_LONG).show();
}
}
}
private static class UpdateSignalGroupTask extends SignalGroupTask {
private final GroupId groupId;
public UpdateSignalGroupTask(GroupCreateActivity activity, GroupId groupId,
Bitmap avatar, String name, Set<Recipient> members)
{
super(activity, avatar, name, members);
this.groupId = groupId;
}
@Override
protected Optional<GroupActionResult> doInBackground(Void... aVoid) {
try {
return Optional.of(GroupManager.updateGroup(activity, groupId, members, avatar, name));
} catch (InvalidNumberException e) {
return Optional.absent();
}
}
@Override
protected void onPostExecute(Optional<GroupActionResult> result) {
if (result.isPresent() && result.get().getThreadId() > -1) {
if (!activity.isFinishing()) {
Intent intent = activity.getIntent();
intent.putExtra(GROUP_THREAD_EXTRA, result.get().getThreadId());
intent.putExtra(GROUP_ID_EXTRA, result.get().getGroupRecipient().requireGroupId().toString());
activity.setResult(RESULT_OK, intent);
activity.finish();
}
} else {
super.onPostExecute(result);
Toast.makeText(activity.getApplicationContext(),
R.string.GroupCreateActivity_contacts_invalid_number, Toast.LENGTH_LONG).show();
}
}
}
private static class AddMembersTask extends AsyncTask<Recipient,Void,List<AddMembersTask.Result>> {
static class Result {
Optional<Recipient> recipient;
boolean isPush;
String reason;
public Result(@Nullable Recipient recipient, boolean isPush, @Nullable String reason) {
this.recipient = Optional.fromNullable(recipient);
this.isPush = isPush;
this.reason = reason;
}
}
private GroupCreateActivity activity;
private boolean failIfNotPush;
public AddMembersTask(@NonNull GroupCreateActivity activity) {
this.activity = activity;
this.failIfNotPush = activity.groupToUpdate.isPresent();
}
@Override
protected List<Result> doInBackground(Recipient... recipients) {
final List<Result> results = new LinkedList<>();
for (Recipient recipient : recipients) {
boolean isPush = isActiveInDirectory(recipient);
if (failIfNotPush && !isPush) {
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_cannot_add_non_push_to_existing_group,
recipient.toShortString(activity))));
} else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipient.getE164().or(""))) {
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_youre_already_in_the_group)));
} else {
results.add(new Result(recipient, isPush, null));
}
}
return results;
}
@Override
protected void onPostExecute(List<Result> results) {
if (activity.isFinishing()) return;
for (Result result : results) {
if (result.recipient.isPresent()) {
activity.getAdapter().add(result.recipient.get(), result.isPush);
} else {
Toast.makeText(activity, result.reason, Toast.LENGTH_SHORT).show();
}
}
activity.updateViewState();
}
}
private static class FillExistingGroupInfoAsyncTask extends ProgressDialogAsyncTask<GroupId, Void, Optional<GroupData>> {
private GroupCreateActivity activity;
public FillExistingGroupInfoAsyncTask(GroupCreateActivity activity) {
super(activity,
R.string.GroupCreateActivity_loading_group_details,
R.string.please_wait);
this.activity = activity;
}
@Override
protected Optional<GroupData> doInBackground(GroupId... groupIds) {
final GroupDatabase db = DatabaseFactory.getGroupDatabase(activity);
final List<Recipient> recipients = db.getGroupMembers(groupIds[0], GroupDatabase.MemberSet.FULL_MEMBERS_EXCLUDING_SELF);
final Optional<GroupRecord> group = db.getGroup(groupIds[0]);
final Set<Recipient> existingContacts = new HashSet<>(recipients.size());
existingContacts.addAll(recipients);
if (group.isPresent()) {
Bitmap avatar = null;
try {
avatar = BitmapFactory.decodeStream(AvatarHelper.getAvatar(getContext(), group.get().getRecipientId()));
} catch (IOException e) {
Log.w(TAG, "Failed to read avatar.");
}
return Optional.of(new GroupData(groupIds[0],
existingContacts,
avatar,
BitmapUtil.toByteArray(avatar),
group.get().getTitle()));
} else {
return Optional.absent();
}
}
@Override
protected void onPostExecute(Optional<GroupData> group) {
super.onPostExecute(group);
if (group.isPresent() && !activity.isFinishing()) {
activity.groupToUpdate = group;
activity.groupName.setText(group.get().name);
if (group.get().avatarBmp != null) {
activity.setAvatar(group.get().avatarBytes, group.get().avatarBmp);
}
SelectedRecipientsAdapter adapter = new SelectedRecipientsAdapter(activity, group.get().recipients);
adapter.setOnRecipientDeletedListener(activity);
activity.lv.setAdapter(adapter);
activity.updateViewState();
}
}
}
private <T> void setAvatar(T model, Bitmap bitmap) {
avatarBmp = bitmap;
GlideApp.with(this)
.load(model)
.circleCrop()
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(avatar);
}
private static class GroupData {
GroupId id;
Set<Recipient> recipients;
Bitmap avatarBmp;
byte[] avatarBytes;
String name;
GroupData(GroupId id, Set<Recipient> recipients, Bitmap avatarBmp, byte[] avatarBytes, String name) {
this.id = id;
this.recipients = recipients;
this.avatarBmp = avatarBmp;
this.avatarBytes = avatarBytes;
this.name = name;
}
}
}

View File

@@ -1,79 +1,57 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.Lifecycle;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LiveData;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.groups.LiveGroup;
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry;
import org.thoughtcrime.securesms.groups.ui.GroupMemberListView;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientExporter;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.thoughtcrime.securesms.recipients.ui.bottomsheet.RecipientBottomSheetDialogFragment;
import java.util.ArrayList;
import java.util.List;
public final class GroupMembersDialog {
private final Context context;
private final Recipient groupRecipient;
private final Lifecycle lifecycle;
private final FragmentActivity fragmentActivity;
private final Recipient groupRecipient;
public GroupMembersDialog(@NonNull Context context,
@NonNull Recipient groupRecipient,
@NonNull Lifecycle lifecycle)
public GroupMembersDialog(@NonNull FragmentActivity activity,
@NonNull Recipient groupRecipient)
{
this.context = context;
this.groupRecipient = groupRecipient;
this.lifecycle = lifecycle;
this.fragmentActivity = activity;
this.groupRecipient = groupRecipient;
}
public void display() {
SimpleTask.run(
lifecycle,
() -> DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupRecipient.requireGroupId(), GroupDatabase.MemberSet.FULL_MEMBERS_INCLUDING_SELF),
members -> {
AlertDialog dialog = new AlertDialog.Builder(context)
.setTitle(R.string.ConversationActivity_group_members)
.setIconAttribute(R.attr.group_members_dialog_icon)
.setCancelable(true)
.setView(R.layout.dialog_group_members)
.setPositiveButton(android.R.string.ok, null)
.show();
AlertDialog dialog = new AlertDialog.Builder(fragmentActivity)
.setTitle(R.string.ConversationActivity_group_members)
.setIcon(R.drawable.ic_group_24)
.setCancelable(true)
.setView(R.layout.dialog_group_members)
.setPositiveButton(android.R.string.ok, null)
.show();
GroupMemberListView memberListView = dialog.findViewById(R.id.list_members);
GroupMemberListView memberListView = dialog.findViewById(R.id.list_members);
ArrayList<GroupMemberEntry.FullMember> pendingMembers = new ArrayList<>(members.size());
for (Recipient member : members) {
GroupMemberEntry.FullMember entry = new GroupMemberEntry.FullMember(member);
LiveGroup liveGroup = new LiveGroup(groupRecipient.requireGroupId());
LiveData<List<GroupMemberEntry.FullMember>> fullMembers = liveGroup.getFullMembers();
entry.setOnClick(() -> contactClick(member));
//noinspection ConstantConditions
fullMembers.observe(fragmentActivity, memberListView::setMembers);
if (member.isLocalNumber()) {
pendingMembers.add(0, entry);
} else {
pendingMembers.add(entry);
}
}
dialog.setOnDismissListener(d -> fullMembers.removeObservers(fragmentActivity));
//noinspection ConstantConditions
memberListView.setMembers(pendingMembers);
}
);
memberListView.setRecipientClickListener(recipient -> {
dialog.dismiss();
contactClick(recipient);
});
}
private void contactClick(@NonNull Recipient recipient) {
if (recipient.getContactUri() != null) {
Intent intent = new Intent(context, RecipientPreferenceActivity.class);
intent.putExtra(RecipientPreferenceActivity.RECIPIENT_ID, recipient.getId());
context.startActivity(intent);
} else {
context.startActivity(RecipientExporter.export(recipient).asAddContactIntent());
}
RecipientBottomSheetDialogFragment.create(recipient.getId(), groupRecipient.requireGroupId())
.show(fragmentActivity.getSupportFragmentManager(), "BOTTOM");
}
}

View File

@@ -22,13 +22,12 @@ import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.SelectedContact;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.groups.SelectionLimits;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sms.MessageSender;
@@ -36,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;
@@ -44,7 +44,7 @@ import org.whispersystems.libsignal.util.guava.Optional;
import java.util.concurrent.ExecutionException;
public class InviteActivity extends PassphraseRequiredActionBarActivity implements ContactSelectionListFragment.OnContactSelectedListener {
public class InviteActivity extends PassphraseRequiredActivity implements ContactSelectionListFragment.OnContactSelectedListener {
private ContactSelectionListFragment contactsFragment;
private EditText inviteText;
@@ -64,7 +64,8 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
@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);
@@ -93,26 +94,32 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
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();
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) {
@@ -122,27 +129,28 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
}
@Override
public void onContactSelected(Optional<RecipientId> recipientId, String number) {
updateSmsButtonText();
public boolean onBeforeContactSelected(Optional<RecipientId> recipientId, String number) {
updateSmsButtonText(contactsFragment.getSelectedContacts().size() + 1);
return true;
}
@Override
public void onContactDeselected(Optional<RecipientId> recipientId, String number) {
updateSmsButtonText();
updateSmsButtonText(contactsFragment.getSelectedContacts().size());
}
private void sendSmsInvites() {
new SendSmsInvitesAsyncTask(this, inviteText.getText().toString())
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
contactsFragment.getSelectedContacts()
.toArray(new SelectedContact[contactsFragment.getSelectedContacts().size()]));
.toArray(new SelectedContact[0]));
}
private void updateSmsButtonText() {
private void updateSmsButtonText(int count) {
smsSendButton.setText(getResources().getQuantityString(R.plurals.InviteActivity_send_sms_to_friends,
contactsFragment.getSelectedContacts().size(),
contactsFragment.getSelectedContacts().size()));
smsSendButton.setEnabled(!contactsFragment.getSelectedContacts().isEmpty());
count,
count));
smsSendButton.setEnabled(count > 0);
}
@Override public void onBackPressed() {
@@ -156,17 +164,17 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
private void cancelSmsSelection() {
setPrimaryColorsToolbarNormal();
contactsFragment.reset();
updateSmsButtonText();
updateSmsButtonText(contactsFragment.getSelectedContacts().size());
ViewUtil.animateOut(smsSendFrame, slideOutAnimation, View.GONE);
}
private void setPrimaryColorsToolbarNormal() {
primaryToolbar.setBackgroundColor(0);
primaryToolbar.getNavigationIcon().setColorFilter(null);
primaryToolbar.setTitleTextColor(ThemeUtil.getThemedColor(this, R.attr.title_text_color_primary));
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);
}
@@ -176,11 +184,11 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
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

@@ -0,0 +1,45 @@
package org.thoughtcrime.securesms;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import org.signal.core.util.logging.Log;
/**
* Simply logs out lifecycle events.
*/
public abstract class LoggingFragment extends Fragment {
private static final String TAG = Log.tag(LoggingFragment.class);
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
logEvent("onCreate()");
super.onCreate(savedInstanceState);
}
@Override
public void onStart() {
logEvent("onStart()");
super.onStart();
}
@Override
public void onStop() {
logEvent("onStop()");
super.onStop();
}
@Override
public void onDestroy() {
logEvent("onDestroy()");
super.onDestroy();
}
private void logEvent(@NonNull String event) {
Log.d(TAG, "[" + Log.tag(getClass()) + "] " + event);
}
}

View File

@@ -1,23 +1,61 @@
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 PassphraseRequiredActionBarActivity {
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
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleGroupLinkInIntent(intent);
}
@Override
@@ -39,7 +77,22 @@ public class MainActivity extends PassphraseRequiredActionBarActivity {
}
}
@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;
}
private void handleGroupLinkInIntent(Intent intent) {
Uri data = intent.getData();
if (data != null) {
CommunicationActions.handlePotentialGroupLinkUrl(this, data.toString());
}
}
}

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