Compare commits

..

478 Commits

Author SHA1 Message Date
Cody Henthorne
c4bc2162f2 Bump version to 5.36.3 2022-04-26 11:55:37 -04:00
Cody Henthorne
bfd966217f Updated language translations. 2022-04-26 11:49:27 -04:00
Greyson Parrelli
797c02e893 Improve reliability of launching FCM foreground service.
We were getting weird errors around the service not calling
startForeground() that I couldn't reproduce. I figure it must be
something with how we only sometimes start the FcmFetchService in the
foreground. So I think the safest thing to do is to just use
GenericForegroundService.
2022-04-26 11:22:01 -04:00
Cody Henthorne
65372e547a Auto-decline invites to a group by a blocked user. 2022-04-25 13:41:50 -04:00
Alex Hart
2454b2e0db Fix crash when trying to access media controller after activity is destroyed and reference is nullified. 2022-04-25 10:29:30 -03:00
Alex Hart
0505a46603 Fix crash when item animation ends after we leave fragment. 2022-04-25 10:09:28 -03:00
Alex Hart
7f77cd6a22 Prevent crash when user quickly leaves the share fragment. 2022-04-25 10:04:21 -03:00
Alex Hart
efe7b3099f Bump version to 5.36.2 2022-04-22 16:50:21 -03:00
Alex Hart
26a831b49f Updated language translations. 2022-04-22 16:50:21 -03:00
Alex Hart
a3a5bb8177 Fix direct shares. 2022-04-22 16:50:21 -03:00
Alex Hart
4282f3eb6d Add explicit export to ShareActivity. 2022-04-22 16:50:21 -03:00
Greyson Parrelli
8a49db650a Do not show SMS contacts as shortcuts if we're not the SMS app. 2022-04-22 16:50:21 -03:00
Greyson Parrelli
fadd4ac61e Fix possible index out of bounds exception in ConversationAdapter.
If we're deferring to super.getItem(), then we should be guarding with
super.getItemCount().
2022-04-22 16:50:21 -03:00
Alex Hart
d0c14895d0 Fix crash when parent does not implement optional bottom sheet callback. 2022-04-22 16:50:21 -03:00
Greyson Parrelli
32ee18240b Fix crash that occurs if we don't have permission to add an account. 2022-04-22 14:32:05 -04:00
Cody Henthorne
cd10aa90cc Adjust tap area on forwarding fab. 2022-04-22 13:40:07 -04:00
Rashad Sookram
33d28c4359 Inset audio level indicator by nav bar height. 2022-04-22 12:36:05 -04:00
Greyson Parrelli
530403ec04 Updated emoji to version 14.0 2022-04-22 11:40:07 -04:00
Greyson Parrelli
f15072bc8d Fix other group update description bugs and add tests. 2022-04-22 08:32:07 -04:00
Ehren Kret
8c2db972cf Fix crash if recipient appears multiple times in group update description.
Without starting from start index, if the same recipient appears
multiple times in the recipient list, this function will crash.
2022-04-22 07:55:42 -04:00
Alex Hart
ff8f9ca81a Bump version to 5.36.1 2022-04-21 12:45:39 -03:00
Alex Hart
40991cc8e9 Updated language translations. 2022-04-21 12:45:12 -03:00
Cody Henthorne
2f551ee3f2 Do not auto-leave groups you have requested to join. 2022-04-21 11:30:13 -04:00
Alex Hart
f1ab0a05f1 Fix issue preventing stories shared element transition from starting. 2022-04-21 12:12:15 -03:00
Greyson Parrelli
fb919466de Enqueue a profile fetch to resolve identity key conflicts. 2022-04-20 19:04:24 -04:00
Greyson Parrelli
4a4cf08cd8 Do not run StorageForcePushJob if you're not registered. 2022-04-20 18:52:41 -04:00
Alex Hart
ed20c24326 Bump version to 5.36.0 2022-04-20 16:32:55 -03:00
Alex Hart
e01cbcec62 Updated language translations. 2022-04-20 16:32:55 -03:00
Rashad Sookram
04d6ccc30e Re-enable audio level indicators in calls. 2022-04-20 16:32:55 -03:00
Alex Hart
6860f96973 Add subscription retry on 402 and print out status when we think a sub is active. 2022-04-20 16:32:55 -03:00
Alex Hart
944c8530d8 Improve remote delete handling in group story threads. 2022-04-20 16:32:55 -03:00
Alex Hart
8b1552952c Stories: Pass recipients through via constructor. 2022-04-20 16:32:55 -03:00
Greyson Parrelli
dfcadde076 Ensure we enqueue a storage sync after a safety number change. 2022-04-20 16:32:55 -03:00
Cody Henthorne
55acd0f048 Auto-leave group if added by blocked user. 2022-04-20 16:32:55 -03:00
Alex Hart
820c016aad Allow quoted story to launch into viewer. 2022-04-20 16:32:55 -03:00
Alex Hart
d1d63d83dc Drop stories from inactive groups. 2022-04-20 16:32:55 -03:00
Alex Hart
7da5b2cdef Fix spinner group change description. 2022-04-20 16:32:55 -03:00
Alex Hart
442dde5c40 Keep caption when forwarding media with a body to a story. 2022-04-20 16:32:55 -03:00
Cody Henthorne
f038e81ff3 Fix long name issues in reaction and recipient bottom sheet.
Fixes #12113
2022-04-20 16:32:55 -03:00
Greyson Parrelli
32c4fcb065 Improve handling of empty profiles. 2022-04-20 16:32:55 -03:00
Alex Hart
e2703b459f Rework color selector and background. 2022-04-20 16:32:55 -03:00
Cody Henthorne
405d99fbe2 Allow keyboard switch when disabling pin reminders.
Fixes #9862
2022-04-20 16:32:55 -03:00
Evan Hahn
7b89687206 Update donation strings: "One-time", not "One Time". 2022-04-20 16:32:55 -03:00
Alex Hart
d74f1a386c Set story text placeholder alpha to 60 percent. 2022-04-20 16:32:55 -03:00
Alex Hart
b041ed1510 Ensure delivery receipts are sent for 1:1 story replies and reactions. 2022-04-20 16:32:55 -03:00
Alex Hart
3426556a51 Disable group private replies. 2022-04-20 16:32:54 -03:00
Greyson Parrelli
e2cb535f3f Make names in group update descriptions tappable. 2022-04-20 16:32:54 -03:00
Alex Hart
3b17a41415 Send actual quote author in story direct reply. 2022-04-20 16:32:54 -03:00
Alex Hart
631720f111 Ensure direct replies respect disappearing message timeout. 2022-04-20 16:32:54 -03:00
Cody Henthorne
ab031d3dad Add internal setting to clear keep longer logs. 2022-04-19 08:17:21 -04:00
Alex Hart
6101048f07 Update donor badge strings. 2022-04-18 16:38:06 -03:00
Alex Hart
115f7063d5 Add support and tracking of ChargeFailure in ActiveSubscription. 2022-04-18 16:37:12 -03:00
Alex Hart
159d67ec59 Fix crash for non-story replies. 2022-04-18 13:26:28 -03:00
Greyson Parrelli
e09ce4c820 Use foreground services to process notification when appropriate.
Right now, the only condition is once every 3 minutes on Android 12.

This is ok because Android 12 will allow us (once every 2 minutes or
so) to start a foreground service, and it won't show it for the first 10
seconds. So we can kind of do it without any visual penalty.
2022-04-18 11:27:32 -04:00
Alex Hart
8cfc013960 Fix issue where story viewer would get stuck. 2022-04-18 10:24:54 -03:00
Alex Hart
a436c46cb2 Remove label from remote deleted story reactions. 2022-04-18 09:43:59 -03:00
Alex Hart
893be51810 Allow 1:1 replies to increment thread unread counter. 2022-04-18 09:40:05 -03:00
Greyson Parrelli
97b5a49e36 Update default state for whether legacy passwords are disabled.
This was a feature that was removed from the app over 4.5 years ago.
The value should have been manually set to false when they set a
password, meaning that it should be safe to set the default to true.

Fixes #10367
2022-04-17 10:12:44 -04:00
Alex Hart
043f06e188 Prevent sending videos over 30s in length to a story. 2022-04-15 16:07:15 -04:00
Alex Hart
fa13b464f8 Fix elongated message bubbles when intentional newlines are present. 2022-04-15 16:07:15 -04:00
Alex Hart
bfaaf20fd9 Fix image editor outlining on Android 12+. 2022-04-15 16:07:15 -04:00
Peter Thatcher
2f97b80b9c Add internal setting for call bandwidth mode. 2022-04-15 16:07:15 -04:00
Alex Hart
eee9c967fa Fix overlapping text issue in review cards. 2022-04-15 16:07:15 -04:00
Alex Hart
515981c044 Add horizontal margins to donation receipt PNG. 2022-04-15 16:07:15 -04:00
Alex Hart
a06528e5e1 Always notifyIfReady for each boolean change. 2022-04-15 16:07:14 -04:00
Alex Hart
98194c854a Add blur and adjust layout for story error slate. 2022-04-15 16:07:14 -04:00
Alex Hart
2d60a88a75 Fix crossfade target aspect ratio. 2022-04-15 16:07:14 -04:00
Alex Hart
c3e7d6c74c Fix bug causing cancellation of dialog fragment. 2022-04-15 16:07:14 -04:00
Greyson Parrelli
8da66bc789 Fix corner case in story distribution list syncing. 2022-04-15 16:07:14 -04:00
Alex Hart
9ceb5b2e85 Fix view-off-main bug in Landing fragment. 2022-04-15 16:07:14 -04:00
Alex Hart
17b8e086c9 Fix crash when trying to view 1:1 conversation with reaction quote. 2022-04-15 16:07:14 -04:00
Alex Hart
9a097d113d Update share interstitial to use proper title. 2022-04-15 16:07:14 -04:00
Alex Hart
46ca1e16bb Fix crash when trying to add a link to a text post. 2022-04-15 16:07:14 -04:00
Alex Hart
d4d3124a90 Prevent flicker of user avatar in MyStories when moving between tabs. 2022-04-15 16:07:14 -04:00
Greyson Parrelli
35a9fddbb2 Add basic support for receiving messages at your PNI.
We haven't implemented merging yet, so this is still very basic, but it
"works".
2022-04-15 16:07:14 -04:00
Alex Hart
41e417ff0b Add proper interpolator and duration to chrome show/hide. 2022-04-15 16:07:14 -04:00
Alex Hart
f6614c1174 Set story viewer background exit fade duration to 100ms. 2022-04-15 16:07:14 -04:00
Alex Hart
9136bcf5e8 Add radius animator to cross fade when launching story viewer. 2022-04-15 16:07:14 -04:00
Greyson Parrelli
7c156d10d6 Keep active table selected in Browse page in Spinner. 2022-04-15 16:07:14 -04:00
Alex Hart
3372d942ec Swap out outlinethumbnailview for shapeableimageviews in mystories. 2022-04-15 16:07:14 -04:00
Greyson Parrelli
7fc9876b1e Start transaction earlier in backup restore.
Fixes #12159
2022-04-15 16:07:14 -04:00
Alex Hart
cff62e9528 Add 12dp margin to top of stories context menu. 2022-04-15 16:07:14 -04:00
Alex Hart
24f59b0a17 Hide bottom nav when viewing archived conversations. 2022-04-15 16:07:14 -04:00
Alex Hart
0a07800eba Fix caption sending for outgoing image and video stories. 2022-04-15 16:07:14 -04:00
Alex Hart
c863e9ed4d Fix crash when trying to create a text story. 2022-04-15 16:07:14 -04:00
Alex Hart
523537cf05 Enable sharing to stories and refactor share activity. 2022-04-15 16:07:14 -04:00
Greyson Parrelli
fd4543ffe0 Improve speed of many SMS/MMS queries by removing unnecessary attachment join. 2022-04-15 16:07:14 -04:00
Greyson Parrelli
83b0309f23 Fix bug in Spinner where some query history items didn't work. 2022-04-15 16:07:14 -04:00
Greyson Parrelli
5cabe5ecfa Default the query history to hidden in Spinner. 2022-04-15 16:07:14 -04:00
Greyson Parrelli
fae3004512 Fix issue where you could have multiple context menus.
Fixes #12149
2022-04-15 16:07:14 -04:00
Alex Hart
e143c47c25 Fix crash and icon change issue with shared element transition. 2022-04-15 16:07:14 -04:00
Greyson Parrelli
27c3fca324 Change ContactRecordProcess to merge identity key/state as a group. 2022-04-15 16:07:14 -04:00
Alex Hart
26d637cafc Add explicit themes to fabs. 2022-04-15 16:07:14 -04:00
Alex Hart
03e8fe9f27 Migrate all internal shares to MultiselectForwardFragment. 2022-04-15 16:07:14 -04:00
Artem Varaksa
23939aeee3 Removed unused string and the variable that was used in it.
Close #12146
2022-04-15 16:07:14 -04:00
clauz9
d7b793ce4c Fade out fab buttons and megaphone when entering action mode or search.
Closes #12112
2022-04-15 16:07:14 -04:00
Greyson Parrelli
d3096c56cb Basic client usage of CDSHv2.
This provides a basic (read: useful-for-development-yet-broken) client
usage of CDSHv2.
2022-04-15 16:07:14 -04:00
clauz9
b0e7b49056 Remove redundant exit animation.
Fixes #12119
2022-04-15 16:07:14 -04:00
Cody Henthorne
2f0f26c328 Add story send multi-send, error, and improved SNC states. 2022-04-15 16:07:14 -04:00
Alex Hart
7f2f5a182f Add shared element transition for camera fab. 2022-04-15 16:07:14 -04:00
Alex Hart
33b88796e8 Simplify layout and do not display until data is loaded to prevent flashing. 2022-04-15 16:07:14 -04:00
Greyson Parrelli
31e4db2186 Bump version to 5.35.3 2022-04-15 16:06:11 -04:00
Greyson Parrelli
76ad7866ec Updated language translations. 2022-04-15 16:05:41 -04:00
Rashad Sookram
e9804eccbb Disable audio level indicator in calls. 2022-04-15 15:45:50 -04:00
Rashad Sookram
d62d0efb1d Fix screen pulsing when your video is disabled. 2022-04-15 11:44:49 -04:00
Greyson Parrelli
3ec9cd1244 Bump version to 5.35.2 2022-04-13 11:56:41 -04:00
Greyson Parrelli
bd5f48f193 Updated language translations. 2022-04-13 11:56:22 -04:00
Alex Hart
98fc3e5b0b Log warning instead of throwing NPE for voice note controller. 2022-04-13 12:37:24 -03:00
Greyson Parrelli
d06c633dc4 Fix full text search filter.
Closes #12158
2022-04-13 10:58:16 -04:00
Greyson Parrelli
d401386e2d Bump version to 5.35.1 2022-04-11 20:44:10 -04:00
Greyson Parrelli
8f0f9e64b9 Updated language translations. 2022-04-11 20:43:47 -04:00
Greyson Parrelli
bd5ac85ac0 Fix old DB migration.
A cautionary tale that serves as a reminder to never call external code
during a migration...

Fixes #12147
2022-04-11 20:38:32 -04:00
Greyson Parrelli
417070e957 Prevent possible deadlock in identity cache. 2022-04-11 12:30:18 -04:00
Greyson Parrelli
a92638e897 Fix possible threading issue in RetrieveProfileJob. 2022-04-11 12:00:01 -04:00
Rashad Sookram
08abe890ff Adjust audio levels animation. 2022-04-11 11:56:46 -04:00
Cody Henthorne
66b6420f21 Revert "Fix overlapping text with voice notes."
This reverts commit dabd131222.
2022-04-11 11:54:29 -04:00
Rashad Sookram
b21bd5a01e Ensure PiP view is animated to its final position. 2022-04-08 17:34:50 -04:00
Cody Henthorne
d11e8ec04b Bump version to 5.35.0 2022-04-08 12:29:29 -04:00
Cody Henthorne
3e5fe0f1cb Updated language translations. 2022-04-08 12:22:37 -04:00
Alex Hart
b65d62e065 Update prioritization of donation error bottom sheets. 2022-04-08 12:19:27 -04:00
Alex Hart
fc55be0916 Stop voice note on video playback. 2022-04-08 12:19:27 -04:00
Alex Hart
a87aa0fbe2 Don't keep around shortcuts for archived chats. 2022-04-08 12:19:27 -04:00
Alex Hart
a44a105cbc Add ability to copy text slides in full. 2022-04-08 12:19:27 -04:00
Alex Hart
c4817ac017 Allow generic links to be sent as stories. 2022-04-08 12:19:27 -04:00
Cody Henthorne
65835606cc Fix reply UX on reply disabled 1:1 stories. 2022-04-08 12:19:27 -04:00
Cody Henthorne
ff26922afb Tweak private story reaction UI. 2022-04-08 12:19:27 -04:00
Alex Hart
a894ba7a51 Implement cross-fade for story thumb shared element animation. 2022-04-08 12:19:26 -04:00
Cody Henthorne
cb63fe600c Fix react with any closing for story direct replies. 2022-04-08 12:19:26 -04:00
Jim Gustafson
2d6146351d Update to RingRTC v2.20.1 2022-04-08 12:19:26 -04:00
Alex Hart
e5953b25e1 Disallow fling gesture when we are translating for on back. 2022-04-08 12:19:26 -04:00
Alex Hart
6354cb194c Update ordering query to display content in expected order. 2022-04-08 12:19:26 -04:00
Cody Henthorne
8d6beb92cb Reply sheet polish. 2022-04-08 12:19:26 -04:00
Cody Henthorne
bb5edccf34 Update view count in My Story view. 2022-04-08 12:19:26 -04:00
Cody Henthorne
6d86b25acd Improve story contact search. 2022-04-08 12:19:26 -04:00
Alex Hart
04677d21bb Push repository calls to background. 2022-04-08 12:19:26 -04:00
Alex Hart
20022b88fc Fix issue where if no stories exist we would never display. 2022-04-08 12:19:26 -04:00
Cody Henthorne
3088d7f182 Adjust quote view colors. 2022-04-08 12:19:26 -04:00
Alex Hart
ce8dafd33d Start align text when displaying in smallest size otherwise center. 2022-04-08 12:19:26 -04:00
Alex Hart
6054285ddb Add description to story link previews. 2022-04-08 12:19:26 -04:00
Alex Hart
dabea5169b Fix opening long messages. 2022-04-08 12:19:26 -04:00
Alex Hart
7fb5ceeda4 Allow hidden story viewing. 2022-04-06 14:37:25 -03:00
Cody Henthorne
dc6fd8be7f Fix story reply crash and tweak UI. 2022-04-06 13:17:33 -04:00
Alex Hart
c271b9c2de Prevent multiple clicks when accessing the viewer. 2022-04-06 12:38:43 -04:00
Alex Hart
6fb6092a6b Implement a cache for faster typeface resolution. 2022-04-06 12:38:43 -04:00
Alex Hart
46bb64ad24 Display reply icon if you responded 1:1 to the displayed story. 2022-04-06 12:38:43 -04:00
Alex Hart
bcd16ce296 Lock orientation when creating a text post. 2022-04-06 12:38:43 -04:00
Alex Hart
07ec14d5c4 Fix issue where reaction animation would only play first time. 2022-04-06 12:38:43 -04:00
Alex Hart
d716416d1d Prevent caption from swallowing taps if no overlay needed. 2022-04-06 12:38:43 -04:00
Alex Hart
343871ed8b Use unread thread count for bottom bar. 2022-04-06 12:38:43 -04:00
Alex Hart
aa60247e42 Add rounded corners back to secondary story. 2022-04-06 12:38:43 -04:00
Alex Hart
283e3e99a5 Adjust badge positioning on stories landing items. 2022-04-06 12:38:43 -04:00
Alex Hart
ca79bdb16b Preserve tab state between configuration changes. 2022-04-06 12:38:43 -04:00
Alex Hart
a75d2cfa34 Adjust viewer ordering to match landing page. 2022-04-06 12:38:43 -04:00
Cody Henthorne
1746f37276 Use smarter scrolling for group story replies. 2022-04-06 12:38:43 -04:00
Rashad Sookram
73f32868a2 Display audio levels in 1:1 calls. 2022-04-06 12:38:43 -04:00
Cody Henthorne
dabd131222 Fix overlapping text with voice notes. 2022-04-06 12:38:43 -04:00
Greyson Parrelli
612c6db6db Rename some CDS-related classes. 2022-04-06 12:38:43 -04:00
Cody Henthorne
c56ef33833 Fix resend after safety number change in groups or distribution lists. 2022-04-06 12:38:43 -04:00
Alex Hart
2253e25ae1 Consolidate toolbar_basic usage to single location. 2022-04-06 12:38:43 -04:00
Alex Hart
adb24d480a Remove access modifier from ChatColors constructor. 2022-04-06 12:38:43 -04:00
Cody Henthorne
9fb1dcf28f Fix overlapping text with remote delete. 2022-04-06 12:38:43 -04:00
Cody Henthorne
bba36a5724 Keep gif search open when viewing a result. 2022-04-06 12:38:43 -04:00
Alex Hart
fa515be258 Hide tab bar during multiselect. 2022-04-06 12:38:43 -04:00
Cody Henthorne
be241524db Fix font networking main thread crash. 2022-04-06 12:38:43 -04:00
Cody Henthorne
bb66c3fa68 Fix story views not using entire bottom sheet space. 2022-04-06 12:38:43 -04:00
Greyson Parrelli
a32d5bef20 Refactor more ContactDiscovery code. 2022-04-06 12:38:43 -04:00
Greyson Parrelli
d409278dd5 Do not allow emoji in image editing if device doesn't support it. 2022-04-06 12:37:43 -04:00
Alex Hart
3328e43a40 Add initial shared element transition between conversation list and stories. 2022-04-06 12:37:43 -04:00
Alex Hart
678e832058 Update stories camera fab coloring. 2022-04-06 12:37:43 -04:00
Alex Hart
2c341f450f Add finalized private story icons. 2022-04-06 12:37:43 -04:00
Alex Hart
5854074d4a Update my stories item and landing page empty notice. 2022-04-06 12:37:43 -04:00
Alex Hart
dbe186248d Rework sizing on landing page. 2022-04-06 12:37:43 -04:00
Alex Hart
0504161b04 Change stories camera fab icon to outline. 2022-04-06 12:37:43 -04:00
Alex Hart
9748f1cff8 Add padding to context menus in story landing and my story. 2022-04-06 12:37:43 -04:00
Alex Hart
6a061ed52c Remove stroke from story thumbnail. 2022-04-06 12:37:43 -04:00
Alex Hart
102d58502a Fix bad layout of group story text replies. 2022-04-06 12:37:43 -04:00
Alex Hart
477698f917 Use media forward sheet when forwarding in Media preview. 2022-04-06 12:37:43 -04:00
Alex Hart
d865b5d7b5 Color the send button properly for insecure chats. 2022-04-06 12:37:43 -04:00
Alex Hart
eeaf6df925 Change wording of receipts button. 2022-04-06 12:37:43 -04:00
Alex Hart
95abca4e03 Dismiss after pressing don't show this again. 2022-04-06 12:37:43 -04:00
Alex Hart
c5906b6f3a Fix background color on forward sheet bottom bar. 2022-04-06 12:37:43 -04:00
Alex Hart
91c581b475 Do not process story records if capability doesn't support it. 2022-04-06 12:37:43 -04:00
Fumiaki Yoshimatsu
47760867d5 Adds additional padding to the bottom of the line so the following line wouldn't overlap the previous line.
Fixes a bug [reported by Salt505 in the beta forum](https://community.signalusers.org/t/beta-feedback-for-the-upcoming-android-5-26-release/38629/163).
2022-04-06 12:37:43 -04:00
Alex Hart
5612a5d9e4 Fix issue where all forwarded MMS media would try to send as a secure message. 2022-04-06 12:37:43 -04:00
Alex Hart
4c462bd75a Enforce L1 media restrictions on link preview thumbnails.
Co-Authored-By: Alexandre Erwin Ittner <110642+ittner@users.noreply.github.com>
2022-04-06 12:37:43 -04:00
Cesar Valiente
092b30f64f Utilize isSeparating for better foldable device support. 2022-04-06 12:37:43 -04:00
Greyson Parrelli
b34ca8ca2f Improve handling of unknown IDs in storage service. 2022-04-06 12:37:43 -04:00
clauz9
e2c54eef77 Filter out some Base64 encoded status messages from search. 2022-04-06 12:37:43 -04:00
Fumiaki Yoshimatsu
2a2c27edef Remove unnecessary marginEnd attribute. 2022-04-06 12:37:43 -04:00
Rashad Sookram
ec92d5ddb7 Display audio levels for each participant in group calls. 2022-04-06 12:37:43 -04:00
Alex Hart
a9f208153c Fix artifacting corners on landing page. 2022-04-06 12:37:43 -04:00
Alex Hart
d9ffd67f36 Update My Stories logic when user has not sent to a distribution list. 2022-04-06 12:37:43 -04:00
Alex Hart
19861ef0d1 Implement specification testing for StoryViewerViewModel. 2022-04-06 12:37:43 -04:00
Alex Hart
469879c211 Implement proper story viewer ordering. 2022-04-06 12:37:43 -04:00
clado
157198fd17 Add content descriptions for incoming call buttons.
Fixes #11995
2022-04-06 12:37:43 -04:00
Greyson Parrelli
0d61b8db38 Fixed threading issues with Spinner recent queries. 2022-04-06 12:37:43 -04:00
Greyson Parrelli
593334456a Add a local query history to Spinner. 2022-04-06 12:37:43 -04:00
Greyson Parrelli
98b9cc23e4 Add extension functions to improve writability of database queries. 2022-04-06 12:37:43 -04:00
Alex Hart
3e42c044b8 Add RxStore and StoryViewerPage forward navigation. 2022-04-06 12:37:43 -04:00
Cody Henthorne
11c3ea769e Fix emoji keyboard bugs and group story replies. 2022-04-06 12:37:43 -04:00
Sgn-32
b8bb2e234b Elimination of country code 0 in Delete account 2022-04-06 12:37:43 -04:00
Cody Henthorne
87b00bb156 Fix various story reply bottom sheet issues. 2022-04-06 12:37:43 -04:00
Alex Hart
3da2fc4d9b Clear storage keys for deleted distribution lists. 2022-04-06 12:37:43 -04:00
Cody Henthorne
972ab9b368 Process incomming story views even if read receipts are disabled. 2022-04-06 12:37:43 -04:00
Alex Hart
c359b0134a Implement StoryDistributionListRecord and processing. 2022-04-06 12:37:43 -04:00
Alex Hart
2cd7462573 Add long-press action to mystories items for helpful debugging info. 2022-04-06 12:37:43 -04:00
Alex Hart
2a7d515932 Add handler to My Stories row to open my stories
If there are sent group stories.
2022-04-06 12:37:43 -04:00
Alex Hart
1bb04035ab Update playback to match specifications. 2022-04-06 12:37:43 -04:00
Alex Hart
267efb0763 Start viewer when clicking on story ring. 2022-04-06 12:37:43 -04:00
Alex Hart
0ef215dfc5 Size story bottomsheets to 60 percent height. 2022-04-06 12:37:43 -04:00
Cody Henthorne
50bea8140f Fix story reply mention fragment from taking over screen. 2022-04-06 12:37:43 -04:00
Chris Eager
086e3ed4ec Update message reporting to use sender ACI instead of E164.
Co-authored-by: Greyson Parrelli <greyson@signal.org>
2022-04-06 12:37:43 -04:00
Cody Henthorne
b02539684a Fix unresolved authors of story replies. 2022-04-06 12:37:43 -04:00
Alex Hart
b9747607ad Add blur to background for non 9:16 story content. 2022-04-06 12:37:43 -04:00
Cody Henthorne
284a6ae667 Add defaults for script/text font pairings and guessing of script based on body contents.
Co-authored-by: Alex Hart <alex@signal.org>
2022-04-06 12:37:43 -04:00
Alex Hart
116e711f1a Add logging for when we do nothing during a keep-alive job. 2022-04-06 12:36:32 -04:00
Cody Henthorne
7aeb641036 Include mentions on incoming story replies. 2022-04-06 12:36:32 -04:00
Fumiaki Yoshimatsu
e4f69c0b6f Add padding to the toolbar in media preview activities.
Fixes #10298
2022-04-06 12:36:32 -04:00
Sgn-32
48d7228ae7 Replace use of AlertDialog.Builder with MaterialAlertDialogBuilder. 2022-04-06 12:36:32 -04:00
Alex Hart
ae28df901f Hide view once for story first sending. 2022-04-06 12:36:32 -04:00
Alex Hart
f17f45f277 Fix bad check for story context. 2022-04-06 12:36:32 -04:00
Alex Hart
2b15fc2966 Do not drop group stories if not profile sharing with sender. 2022-04-06 12:36:32 -04:00
Alex Hart
76a9342afa Safety number check and profile refresh for group sends. 2022-04-06 12:36:32 -04:00
Greyson Parrelli
14849d6e45 Fix case-insensitive queries for groups with non-ASCII characters.
Fixes #11464
2022-04-06 12:36:32 -04:00
Greyson Parrelli
77ea2deada Move more util classes to core-util. 2022-04-06 12:36:32 -04:00
Greyson Parrelli
390b7ff834 Convert SqlUtil to Kotlin. 2022-04-06 12:36:32 -04:00
Greyson Parrelli
0e4187b062 Use existing contact type for our linked entry. Add test to sample app.
Fixes #9431
Closes #9434

Co-authored-by: swatts <github@stargw.net>
2022-04-06 12:36:32 -04:00
Cody Henthorne
4098f77e08 Bump version to 5.34.10 2022-04-06 12:18:57 -04:00
Cody Henthorne
a60eed35fe Updated language translations. 2022-04-06 12:18:10 -04:00
Cody Henthorne
904215fe38 Fix font networking main thread crash. 2022-04-06 12:04:55 -04:00
Greyson Parrelli
6376642d38 Pre-resolve recipient needed for group messages in the conversation list. 2022-04-06 11:13:56 -04:00
Cody Henthorne
6b36a446f0 Bump version to 5.34.9 2022-04-05 10:12:35 -04:00
Cody Henthorne
95ee7f5c00 Updated language translations. 2022-04-05 10:08:22 -04:00
Greyson Parrelli
b109effc94 Prevent possibility of recursively enqueuing early message jobs. 2022-04-04 19:26:08 -04:00
Alex Hart
44efda8318 Bump version to 5.34.8 2022-03-31 17:09:07 -03:00
Alex Hart
e6e1b6d746 Updated language translations. 2022-03-31 17:08:31 -03:00
Greyson Parrelli
e303cbcc22 Fix some toolbar theming issues. 2022-03-31 14:06:37 -04:00
Greyson Parrelli
c67aed5b65 Keep some crucial local logs (like crashes) for longer. 2022-03-30 16:32:45 -04:00
Greyson Parrelli
0278882c30 Prevent possible crash while reading contacts cursor. 2022-03-30 16:17:15 -04:00
Alex Hart
42ccd638bd Bump version to 5.34.7 2022-03-30 12:19:36 -03:00
Alex Hart
e11577bd23 Updated language translations. 2022-03-30 12:15:24 -03:00
Greyson Parrelli
3c0b87bbca Fix possible backup crash due to foreign key constraint. 2022-03-30 09:56:26 -04:00
Christian Clauss
84a61b01ca Fix syntax error in apntool.py
Old-style exceptions are syntax errors in Python 3 while new-style exceptions work as expected on both Python 2 and Python 3.

Closes #10622
2022-03-30 09:39:34 -04:00
Greyson Parrelli
c149e008fd Update apostrophes in code comments. 2022-03-30 09:37:12 -04:00
Greyson Parrelli
c7a345eb0b Ignore inbound SMS/MMS from yourself. 2022-03-29 18:39:46 -04:00
Sgn-32
348b6e9742 Enable hyphenation on signal bottom action bar item title.
Closes #11896
2022-03-29 18:21:25 -04:00
Greyson Parrelli
003b3e02e4 Add ability to view your own profile photo fullscreen.
From the profile management screen.

Fixes #11033
2022-03-29 18:21:16 -04:00
Fumiaki Yoshimatsu
5b668d7931 Refresh the storage managment title on long-click.
Fixes #9698
Closes #10021
2022-03-29 18:21:08 -04:00
Greyson Parrelli
87748fa80c Use an inset ripple for contact list items.
Closes #10786

Co-authored-by: Thore Goebel <hello@thore.io>
2022-03-29 18:20:58 -04:00
Greyson Parrelli
ad0482fb5b Bump version to 5.34.6 2022-03-29 13:58:01 -04:00
Greyson Parrelli
12ceb1cb32 Updated language translations. 2022-03-29 13:57:18 -04:00
Greyson Parrelli
9ec2c5da52 Show some buttons in media preview toolbar if there's room.
Fixes #11661
2022-03-29 10:43:04 -04:00
Greyson Parrelli
f742d34588 Remove unnecessary package exclusions.
Fixes #9540
2022-03-29 10:27:24 -04:00
Cody Henthorne
4d8e058d33 Allow hyphenation of enter sms code buttons. 2022-03-29 10:13:32 -04:00
Cody Henthorne
77ba6e0f7b Enable telecom integration for internal users. 2022-03-29 10:08:04 -04:00
Fumiaki Yoshimatsu
76a0e5c851 Allow search view to be full width of the screen in landscape.
Fixes #10299
Closes #10321
2022-03-29 10:05:45 -04:00
Greyson Parrelli
f3096cc24c Use more direct language in PIN reminder toast.
Fixes #10207
2022-03-29 09:37:20 -04:00
Greyson Parrelli
49957e1d95 Prevent changing disappearing message timer for blocked users.
Fixes #10973
2022-03-29 09:32:46 -04:00
Cody Henthorne
2f5cb5f090 Add story distribution list deduplication handling. 2022-03-28 19:43:42 -04:00
Ehren Kret
ba394e1021 Use notifyDataSetChanged for potentially structural modification.
This has the potential to change the structure of the result set
displayed in the recycler view. Thus the functions that tell it the
structure changed need to be called. For an immediate fix, changing
this back to notifyDataSetChanged seems to resolve the crash.
2022-03-28 19:22:02 -04:00
Greyson Parrelli
7611c64493 Do not bidi-isolate all-ASCII strings.
Fixes #11630
2022-03-28 19:07:21 -04:00
Greyson Parrelli
231248d20a Update android test orchestrator to 1.4.1 2022-03-28 19:07:21 -04:00
Alex Hart
a3a79fc58d Adjust text story button protections. 2022-03-28 19:07:21 -04:00
Alex Hart
6476e585c4 Fix cast in story landing fragment. 2022-03-28 19:07:21 -04:00
Greyson Parrelli
fd2961710d Fix androidTests. 2022-03-28 19:07:21 -04:00
Greyson Parrelli
7f4ab67f98 Fix timing issue with receipt updates. 2022-03-28 19:07:21 -04:00
Greyson Parrelli
b9ce38b85b Don't allow KEEP_LONGER logs to get too large. 2022-03-28 19:07:21 -04:00
Greyson Parrelli
50d5658add Fix possible NPE when backing out of conversation. 2022-03-28 19:07:21 -04:00
Greyson Parrelli
72777bc6cd Disallow some unicode sequences in link previews. 2022-03-28 19:07:21 -04:00
Rashad Sookram
f2046c3c05 Vibrate when entering call reconnecting state. 2022-03-28 19:07:21 -04:00
Sgn-32
745dfc3fbb Fix translation issue with media preview.
Fixes #12072
Closes #12092
2022-03-28 19:07:21 -04:00
Greyson Parrelli
cb77165b53 Fix issue where search results could flicker.
Shoutout to @clauz9 for the help in researching this bug!
2022-03-28 19:07:21 -04:00
Alex Hart
bde4700e87 Hide new user entry in story recipient selection. 2022-03-28 19:07:21 -04:00
Alex Hart
e58cea9a26 Fix bad use of toString in StripeApi. 2022-03-28 19:07:21 -04:00
Alex Hart
556dc0d1ec Show story rings for self and if you sent a story to a group. 2022-03-28 19:07:21 -04:00
Alex Hart
8c1ddcf1c0 Fix issue where thumb resource wasn't set to null after clear. 2022-03-28 19:07:21 -04:00
Alex Hart
2549c1f97d Add placeholder for text post thumbs. 2022-03-28 19:07:21 -04:00
Alex Hart
5faa497821 Get receipt credential presentation BEFORE recording receipt so that the retry does not add another receipt. 2022-03-28 19:07:21 -04:00
Alex Hart
d7a7e72c3a Fix flashing when entering text story. 2022-03-28 19:07:21 -04:00
Alex Hart
af1701e6fa Add HTTPS scheme when user enters a web address. 2022-03-28 19:07:21 -04:00
Rashad Sookram
32d1cc7d54 Recreate MainActivity on language change. 2022-03-28 19:07:20 -04:00
Alex Hart
783a615c07 Adjust text story creation layout size. 2022-03-28 19:07:20 -04:00
Alex Hart
65bfee6eba Display total unread messages including mark as unread instead of unread thread count. 2022-03-28 19:07:20 -04:00
Greyson Parrelli
8d4419705b Update to libsignal-client 0.15.0 2022-03-28 19:07:20 -04:00
Alex Hart
6c3baf229c Fix broadcast send when user sends to both story and non-story. 2022-03-28 19:07:20 -04:00
Rashad Sookram
6e9a6283fc Fix layout for long story replies. 2022-03-28 19:07:20 -04:00
Alex Hart
5b3899237b Trampoline call to generate preview if view is not laid out. 2022-03-28 19:07:20 -04:00
Greyson Parrelli
dddf830e47 Move system contact interactions into their own module. 2022-03-28 19:07:20 -04:00
Alex Hart
fd930d0b1d Conversation tab bar animations. 2022-03-28 19:07:20 -04:00
Greyson Parrelli
2b5d65ae04 Revert "Update to libsignal-client 0.15.0"
This reverts commit 3d5f04eba757563dd92366d994a96cf323b8d540.
2022-03-28 19:07:20 -04:00
clauz9
2ebaa04c2f Remove leftover code in SearchUtil.
Closes #12090
2022-03-28 19:07:20 -04:00
Jordan Rose
1e316ea19f Update to libsignal-client 0.15.0 2022-03-28 19:07:20 -04:00
Cody Henthorne
ac9257ec1c Revert "Track inconsistencies between new and old network availability for internal users."
This reverts commit 007975e7da.
2022-03-28 19:07:20 -04:00
Alex Hart
9b83c5e283 Ensure we do not send captions for non-story messages. 2022-03-28 19:07:20 -04:00
Alex Hart
a7a4972013 Check if there is an attachment available before trying to send it to a story. 2022-03-28 19:07:20 -04:00
Alex Hart
f6f4e6fde7 Add animations to camera toggle. 2022-03-28 19:07:20 -04:00
Alex Hart
e9160c2449 Suppress multiple clicks on tap to add. 2022-03-28 19:07:20 -04:00
Alex Hart
b0b1029d0f Add transition fixes and improvements. 2022-03-28 19:07:20 -04:00
Rashad Sookram
72b3a0555d Improve transition back to creation fragment. 2022-03-28 19:07:20 -04:00
Greyson Parrelli
135fde68c1 Migrate some cursor utils to core-util. 2022-03-28 19:07:20 -04:00
Alex Hart
954e45ed97 Fix capitalization retention for text stories. 2022-03-28 19:07:20 -04:00
Alex Hart
2b3f16d3ad Material3 Bottom Bar styling and fab for Stories only. 2022-03-28 19:07:20 -04:00
Alex Hart
6820b84921 Story Viewer shared element transition. 2022-03-28 19:07:20 -04:00
Cody Henthorne
6a5f5f4ffa Show verified badge on Note to Self. 2022-03-28 19:07:20 -04:00
Rashad Sookram
19381342b3 Update UI for replies to unavailable stories. 2022-03-28 19:07:20 -04:00
Greyson Parrelli
c2627dda8d Migrate contact interactions to SystemContactsRepository. 2022-03-28 19:07:20 -04:00
Alex Hart
db309b7930 Implement slide to close in Story viewer. 2022-03-28 19:07:20 -04:00
Cody Henthorne
403958fed3 Handle 1:1 call reconnecting events. 2022-03-28 19:07:20 -04:00
Cody Henthorne
3af53f2089 Allow ringrtc to decided how to respond to send message failure. 2022-03-28 19:07:20 -04:00
Cody Henthorne
e472760d92 Fix MessageCountsViewModel memory leak. 2022-03-28 19:07:20 -04:00
Alex Hart
b04acd8ae0 Add caption to outgoing stories. 2022-03-28 19:07:20 -04:00
Cody Henthorne
6890973ce8 Enforce limit for total number of blocked requests. 2022-03-28 19:07:20 -04:00
Alex Hart
b3d9a85fa2 Fix button alignment. 2022-03-28 19:07:20 -04:00
Greyson Parrelli
9f027ed584 Ensure unidentified access is correct when fetching own profile. 2022-03-28 19:07:20 -04:00
Greyson Parrelli
8fb598e60a Restart app after refreshing remote config via internal settings. 2022-03-28 19:07:20 -04:00
Greyson Parrelli
2edaba39a0 Fix support for PniIdentity sync message. 2022-03-28 19:07:20 -04:00
clauz9
b0be7effe8 Use notifyItemRangeChange in ConversationListFragment onResume().
Closes #12085
2022-03-28 19:07:20 -04:00
Greyson Parrelli
142979ce93 Add job to clean up early message receipts. 2022-03-28 19:07:20 -04:00
Rashad Sookram
093dd7c62c Update Material Design Components to 1.5.0. 2022-03-28 19:07:20 -04:00
Cody Henthorne
4acafc3d77 Fix translation issue with media preview.
Fixes #12072
2022-03-28 19:07:20 -04:00
Alex Hart
65bf0aad79 Fixes text story preview sizing on pixel 2. 2022-03-28 19:07:20 -04:00
Alex Hart
ef6e846512 Text Camera Switch view positioning fixes. 2022-03-28 19:07:20 -04:00
Alex Hart
782464f664 Make messaging in keepalive more explicit. 2022-03-28 19:07:20 -04:00
Jim Gustafson
c7352f62e5 Update to RingRTC v2.20.0 2022-03-28 19:07:20 -04:00
Rashad Sookram
90dd6b7cb3 Compile against API level 31. 2022-03-28 19:07:20 -04:00
Rashad Sookram
ddfb4bf0a5 Fix context menu animation crash on Kitkat. 2022-03-28 19:07:20 -04:00
Alex Hart
cdef21d6c0 Add animation when you directly react to a story. 2022-03-28 19:07:20 -04:00
Alex Hart
c0f843061e Donations logs. 2022-03-28 19:07:19 -04:00
Alex Hart
5774771ea6 Reverse direction of swipe to reply. 2022-03-28 19:07:19 -04:00
Rashad Sookram
ab8d5474e0 Handle keyboard resize when creating a text story. 2022-03-28 19:07:19 -04:00
Alex Hart
6497ec8098 Adjust sizes for text post text size. 2022-03-28 19:07:19 -04:00
Greyson Parrelli
83c3b16b92 Add ContactDiscovery abstraction for doing CDS refreshes. 2022-03-28 19:07:19 -04:00
Alex Hart
3c2bd032ba Fix NPE when secondary story does not have media and is not a text story. 2022-03-28 19:07:19 -04:00
Alex Hart
f798866619 Maintain app bar layout when switching tabs. 2022-03-28 19:07:19 -04:00
Greyson Parrelli
ffad2c7386 Bump version to 5.34.5 2022-03-28 12:53:12 -04:00
Greyson Parrelli
7252e54593 Updated language translations. 2022-03-28 12:53:12 -04:00
Greyson Parrelli
cfab4dc658 Update stale-bot config. 2022-03-28 12:53:11 -04:00
Cody Henthorne
7f5a8ce6bb Disable telecom integration. 2022-03-28 12:34:50 -04:00
Greyson Parrelli
d02a597451 Bump version to 5.34.4 2022-03-23 11:54:11 -04:00
Greyson Parrelli
8d92a1f195 Updated language translations. 2022-03-23 11:53:53 -04:00
Greyson Parrelli
74e630aacb Fix possible crash and remove old fastRecord system. 2022-03-23 11:44:37 -04:00
Greyson Parrelli
65a12767f9 Bump version to 5.34.3 2022-03-21 18:46:52 -04:00
Greyson Parrelli
ab9d813636 Updated language translations. 2022-03-21 18:46:07 -04:00
Cody Henthorne
007975e7da Track inconsistencies between new and old network availability for internal users. 2022-03-21 17:21:42 -04:00
Greyson Parrelli
86ca1ebda0 Add basic handling for ProofRequiredExceptions on other message types. 2022-03-21 16:36:07 -04:00
Cody Henthorne
57fb3e6377 Fix crash when accidently triggering a second voice note record. 2022-03-21 13:36:03 -04:00
Cody Henthorne
7e6fcb80a3 Revert all new network detection API usage and refactorings. 2022-03-21 12:21:46 -04:00
Cody Henthorne
5e46e1e3d9 Bump version to 5.34.2 2022-03-18 15:48:26 -04:00
Cody Henthorne
21e370de9b Updated language translations. 2022-03-18 15:45:33 -04:00
Cody Henthorne
77ef877c59 Show block request if request follows a collapsed event. 2022-03-18 15:26:38 -04:00
Greyson Parrelli
77caedb3bb Avoid recipient resolves in Recipient#getGroupName(). 2022-03-18 14:49:06 -04:00
Cody Henthorne
3e77975c17 Revert "Fix soft keyboard popping up when the text was selected when the other keyboard was open."
This reverts commit 6d41d1f6d2.
2022-03-18 14:09:52 -04:00
Alex Hart
75e15c81e1 Ensure messages are marked read when entering a conversation with an active typing indicator. 2022-03-18 13:15:37 -03:00
Cody Henthorne
782a1ce301 Fix NPE in conversation update group info. 2022-03-18 12:00:04 -04:00
Cody Henthorne
be21b9e163 Bump version to 5.34.1 2022-03-18 11:14:35 -04:00
Cody Henthorne
284140871e Updated language translations. 2022-03-18 11:06:32 -04:00
Cody Henthorne
e6ac40a07c Fix various corner case block/reject join request bugs. 2022-03-18 11:03:38 -04:00
Rashad Sookram
b8e98350c1 Add back aapt2 checksums for osx/windows. 2022-03-18 11:03:38 -04:00
Cody Henthorne
445ff263c6 Fix receiving group invite bug. 2022-03-18 11:03:38 -04:00
Alex Hart
e10c40d2b8 Fix infinite loop when dealing with large video files. 2022-03-18 11:03:38 -04:00
Alex Hart
a41a2b3e64 Fix wonky bottom sheet animations. 2022-03-18 11:03:38 -04:00
Alex Hart
e603391c35 Fix several theming issues for Contact Selection. 2022-03-18 10:32:03 -03:00
Cody Henthorne
7e0cd99f48 Fix block request button not showing up always. 2022-03-17 19:51:22 -04:00
Cody Henthorne
daedb8261d Fix empty group update bug. 2022-03-17 19:28:50 -04:00
Cody Henthorne
2c8744a319 Bump version to 5.34.0 2022-03-17 16:18:31 -04:00
Cody Henthorne
a7c441225b Updated language translations. 2022-03-17 16:06:22 -04:00
Alex Hart
e3c491860a Allow forwarding of Text Stories. 2022-03-17 16:02:43 -04:00
Greyson Parrelli
43ad0b2294 Fix issue where group read was happening on main thread. 2022-03-17 16:02:43 -04:00
Alex Hart
bf897d10d2 Add avatar to my story row and wire in badges. 2022-03-17 16:02:43 -04:00
Cody Henthorne
0b1a93d3e6 Disable Telecom integration solely for OnePlus devices. 2022-03-17 16:02:43 -04:00
Alex Hart
7edef20f4f Guard first time on add to my story. 2022-03-17 16:02:43 -04:00
Greyson Parrelli
945c308cf5 Update group update messages faster. 2022-03-17 16:02:43 -04:00
Cody Henthorne
f91494f813 Remove newer network detection APIs. 2022-03-17 16:02:43 -04:00
Cody Henthorne
9d28caac00 Fix bug preventing adding and inviting by phone number. 2022-03-17 16:02:43 -04:00
Alex Hart
798f3a7b0e Only display tilted image for My Story. 2022-03-17 16:02:43 -04:00
Alex Hart
768e170ed4 Fix hidden story behaviour. 2022-03-17 16:02:43 -04:00
Greyson Parrelli
a0ebb891de Resolve multiple times when generating static IPs.
An attempt to make the list somewhat more stable.
2022-03-17 16:02:43 -04:00
Greyson Parrelli
570b39f82e Debounce menu invalidations in conversation. 2022-03-17 16:02:43 -04:00
Alex Hart
dc50899fe0 Allow addition of text to text slide by tapping anywhere. 2022-03-17 16:02:43 -04:00
Alex Hart
0f889e0259 Add progress indicator to story text post link loader. 2022-03-17 16:02:43 -04:00
Greyson Parrelli
cb906edd11 More accurate timings of conversation-open component. 2022-03-17 16:02:43 -04:00
Greyson Parrelli
604f6709db Fix bug where wallpaper didn't update after changing. 2022-03-17 16:02:43 -04:00
Greyson Parrelli
0359f27cd9 Fix true update queries for blobs. 2022-03-17 16:02:43 -04:00
Greyson Parrelli
0ca438ed25 Update MSL appending to create a new entry if the original one is gone. 2022-03-17 16:02:43 -04:00
Rashad Sookram
6b6e9e92e8 Fix remote delete for private stories. 2022-03-17 16:02:43 -04:00
Greyson Parrelli
b5e0991f5e Log early delivery receipts. 2022-03-17 16:02:43 -04:00
Alex Hart
f06f0e7ae0 Pop open keyboard when we enter the link entry fragment. 2022-03-17 16:02:43 -04:00
Alex Hart
0fcbb5ffda Send user to My Stories from failure notification. 2022-03-17 16:02:42 -04:00
Alex Hart
b1f7dbefd8 Drop stories from users we would normally show a message request for. 2022-03-17 16:02:42 -04:00
Alex Hart
8fc2d5be37 Add 32dp space to bottom of choose story type bottom sheet. 2022-03-17 16:02:42 -04:00
Alex Hart
40020728de Ensure proper text size is used when displaying and editing text stories. 2022-03-17 16:02:42 -04:00
Greyson Parrelli
4abb169568 Do not suggest SMS during onboarding. 2022-03-17 16:02:42 -04:00
Alex Hart
da1ac5358f Add initial support for rendering link previews in text story previews. 2022-03-17 16:02:42 -04:00
Greyson Parrelli
d504bd593a Improve wallpaper load speed. 2022-03-17 16:02:42 -04:00
Alex Hart
63e48efdfe Allow user to launch directly to a specific story, fix story chronology. 2022-03-17 16:02:42 -04:00
Greyson Parrelli
8bb27b60fa Revert "Fix data race preventing some story sends."
This reverts commit cde70269817464880ddb8e2c67e59ca8b571073b.
2022-03-17 16:02:42 -04:00
Alex Hart
437c1e2f21 Implement UI and backend for sending story reactions.
Co-authored-by: Rashad Sookram <rashad@signal.org>
2022-03-17 16:02:42 -04:00
Alex Hart
7f4a12c179 Fix issue where stories with links would fail to send. 2022-03-17 16:02:42 -04:00
Alex Hart
19d3bbc70a Order recipients in viewer by story sent date. 2022-03-17 16:02:42 -04:00
Alex Hart
559561bf72 Add support for message resends. 2022-03-17 16:02:42 -04:00
Rashad Sookram
c8c0589ac4 Hide spell check errors in post preview. 2022-03-17 16:02:42 -04:00
Greyson Parrelli
666218773c Improve conversation open speed.
Co-authored-by: Cody Henthorne <cody@signal.org>
2022-03-17 16:02:42 -04:00
Cody Henthorne
d3049a3433 Add block request action button to collapsed join request events. 2022-03-17 12:12:56 -04:00
Greyson Parrelli
130d5a8945 Add index to improve speed of MMS count. 2022-03-17 12:12:56 -04:00
Greyson Parrelli
172751cd42 Iterate over a snapshot of transaction listeners. 2022-03-17 12:12:56 -04:00
Rashad Sookram
3ad7c96a3c Fix ellipsis appearing in the middle of a message. 2022-03-17 12:12:56 -04:00
Fumiaki Yoshimatsu
6d41d1f6d2 Fix soft keyboard popping up when the text was selected when the other keyboard was open.
Fixes #11780
2022-03-17 12:12:56 -04:00
Rashad Sookram
cb74833dc2 Fix scheduling of ExpireStoriesAlarm. 2022-03-17 12:12:56 -04:00
Alex Hart
8c7b6293fb Fix data race preventing some story sends. 2022-03-17 12:12:56 -04:00
Cody Henthorne
9d1f46da9f Collapse multiple join request/cancels when from a single person. 2022-03-17 12:12:56 -04:00
Greyson Parrelli
216059b659 Fix layout for long text in SMS verification buttons.
Fixes #12037
2022-03-17 12:12:56 -04:00
Greyson Parrelli
18392ed0a4 Render date dividers based on sent time.
The time we use to render date headers needs to match the time we use to
render timestamps in the footer. We should be using sent time in both
cases.

Fixes #11589
2022-03-17 12:12:56 -04:00
Rashad Sookram
63a4d20ea9 Keep 1:1 replies after expiry and fix queries. 2022-03-17 12:12:56 -04:00
Greyson Parrelli
057231b9c3 Update libsignal-client to 0.14.0 2022-03-17 12:12:56 -04:00
clauz9
749bbf428d Make sure isSearchRequest is true when searchViewItem is expanded.
fixes signalapp#12054
2022-03-17 12:12:56 -04:00
Alex Hart
b0458f10a3 Ensure identity records are good before trying to send media. 2022-03-17 12:12:56 -04:00
Greyson Parrelli
5b91c927b6 Refresh our own profile before rotating our profile key. 2022-03-17 12:12:56 -04:00
Greyson Parrelli
b45740884b Only upload your avatar if it's being changed.
New server param means we don't have to upload the avatar if we want to
keep it the same.
2022-03-17 12:12:56 -04:00
Alex Hart
87ad4be117 Fix issue where user could not select a group story. 2022-03-17 12:12:56 -04:00
Greyson Parrelli
78de70881f Fix responsiveness of profile photo edit UI.
There were various issues around the profile photo updating correctly in
the edit view. We want to make sure that what the user sees there is
what other people are seeing.

So I made some changes to make sure that when you remove your profile
photo the UI updates right away, as well as fixed most flickering
issues.
2022-03-17 12:12:56 -04:00
Greyson Parrelli
e7a370a549 Fix paging issue where DataStatus was not updated on insert. 2022-03-17 12:12:56 -04:00
Alex Hart
54eb579558 Allow external shares to a story. 2022-03-17 12:12:56 -04:00
Alex Hart
732b67d8cb Allow injectable typefaces in image text editor.
Co-authored-by: Rashad Sookram <rashad@signal.org>
2022-03-17 12:12:56 -04:00
Cody Henthorne
eed45b57a1 Prevent rejected/kicked group members from joining again via group link. 2022-03-17 12:12:56 -04:00
Greyson Parrelli
3503c60fd1 Add routine check to ensure GV2 profiles are up-to-date. 2022-03-17 12:12:56 -04:00
Cody Henthorne
c17ba30cfc Show different messages based on join group link error header. 2022-03-17 12:12:56 -04:00
Rashad Sookram
5167c7235d Don't animate to replies tab during open. 2022-03-17 12:12:56 -04:00
Greyson Parrelli
803f94012a Handle profile key changes consistently. 2022-03-17 12:12:56 -04:00
Alex Hart
9281bcdd7d Only display stories if you entered through stories. 2022-03-17 12:12:56 -04:00
Alex Hart
4dca554967 Add better text reflow as font changes. 2022-03-17 12:12:55 -04:00
Alex Hart
7c45fb6c17 Fix issue where names with emoji would not display. 2022-03-17 12:12:55 -04:00
Alex Hart
8aa283488f Clear search query in Story recipient selection after a selection is made. 2022-03-17 12:12:55 -04:00
Alex Hart
604c65c7fb Add finalized story icon assets. 2022-03-17 12:12:55 -04:00
Alex Hart
711148423d Excise PowerMock and reenable like a bunch of ignored tests.
Co-authored-by: Rashad Sookram <rashad@signal.org>
2022-03-17 12:12:55 -04:00
Alex Hart
1f82ceecc6 Story Status for landing page and my stories. 2022-03-17 12:12:55 -04:00
Rashad Sookram
1ac8701ada Update Gradle to 7.4.1. 2022-03-17 12:12:55 -04:00
Alex Hart
d61e33fdf3 Fix story display size logic. 2022-03-17 12:12:55 -04:00
Greyson Parrelli
e552b5160f Implement CdshV2Service. 2022-03-17 12:12:55 -04:00
Greyson Parrelli
7e063e8ad8 Refactor CDSH to allow for code reuse. 2022-03-17 12:12:55 -04:00
Greyson Parrelli
88a34936cd Add more device info at the top of Spinner. 2022-03-17 12:12:55 -04:00
Greyson Parrelli
c1181478dd Remove GV2 capability check. 2022-03-17 12:12:55 -04:00
Greyson Parrelli
d13d8628b5 Small UI tweaks.
- Update distance below avatar in conversation settings.
- Slightly increase bubble corner radius.
2022-03-17 12:12:55 -04:00
Alex Hart
6048208c8c Fix crash with incorrectly tagged story. 2022-03-17 12:12:55 -04:00
Alex Hart
78214fb39b Update click boundaries in story viewer. 2022-03-17 12:12:55 -04:00
Alex Hart
ff8d7fa6c2 Add send/recv/render support for text stories. 2022-03-17 12:12:55 -04:00
Greyson Parrelli
3a2e8b9b19 Add internal button to force an emoji search index download. 2022-03-17 12:12:55 -04:00
Cody Henthorne
bca4289c96 Updated language translations. 2022-03-17 12:12:55 -04:00
Cody Henthorne
3fbd9baf0c Disable telecom integration. 2022-03-17 12:11:51 -04:00
Cody Henthorne
e12c96f4b2 Bump version to 5.33.6 2022-03-17 12:09:16 -04:00
Cody Henthorne
eec26aa481 Updated language translations. 2022-03-17 12:07:17 -04:00
Cody Henthorne
865aeda6f2 Disable telecom integration. 2022-03-17 12:04:33 -04:00
Cody Henthorne
2c4ebedda4 Bump version to 5.33.5 2022-03-16 13:57:08 -04:00
Cody Henthorne
042bc8d79a Updated language translations. 2022-03-16 13:56:52 -04:00
Cody Henthorne
4c7bd80f72 Fix early ringing state on slow connections. 2022-03-16 13:37:28 -04:00
Cody Henthorne
3a8591fdfb Bump version to 5.33.4 2022-03-16 10:22:25 -04:00
Cody Henthorne
629aaa2093 Updated language translations. 2022-03-16 10:18:41 -04:00
Cody Henthorne
5b5b118b7a Fix disconnect sound on call termination. 2022-03-16 10:08:06 -04:00
Greyson Parrelli
c7016aa462 Fallback to legacy network detection. 2022-03-15 11:28:31 -04:00
Cody Henthorne
cf857e109a Fix mention rendering bug. 2022-03-15 10:32:59 -04:00
Alex Hart
1c79840684 Bump version to 5.33.3 2022-03-11 16:10:06 -04:00
Alex Hart
4ba7de9519 Updated language translations. 2022-03-11 16:09:14 -04:00
Cody Henthorne
2eb8df347e Fix mention rendering regression. 2022-03-11 14:18:42 -05:00
Rashad Sookram
9056371c41 Fix quote preview being cut off.
When determining the height to force for the animation, the text was
being measured assuming it had infinite width, which made
it seem like it could fit on one line.
2022-03-11 11:38:56 -05:00
Greyson Parrelli
1f57e1f366 Add more logging around network changes. 2022-03-11 10:35:40 -05:00
Alex Hart
aeb568bcf4 Fix crash when recreating conversation react with any emoji fragment. 2022-03-11 09:44:47 -04:00
Cody Henthorne
b7afe4411e Fix NPE in telecom integration. 2022-03-10 16:17:13 -05:00
Alex Hart
cba784b8ec Bump version to 5.33.2 2022-03-10 16:53:01 -04:00
Alex Hart
3aba15e88d Updated language translations. 2022-03-10 16:52:01 -04:00
Cody Henthorne
fa384e93dc Fix crash importing backups. 2022-03-10 15:46:45 -05:00
Alex Hart
1f3e04da29 Fix text size in generated text drawables. 2022-03-10 12:32:42 -04:00
Greyson Parrelli
a484d48377 Update network connectivity observer to be more optimistic. 2022-03-10 11:21:21 -05:00
Greyson Parrelli
15f51ea26e Fix crash if synced pinned contact is malformed. 2022-03-10 11:14:55 -05:00
Greyson Parrelli
80bfa103ab Fix narrow race around generation of some ACI keys. 2022-03-10 11:11:14 -05:00
Cody Henthorne
66f93e0d32 Do not drop 1:1 messages with mentions due to iOS and desktop regression.
iOS and Desktop both regressed in multi-forwarding by including mentions
in 1:1 forwards instead of replacing them with plain text. Android by
default drops these as invalid messages. Since there are clients in the
wild that do this now, we have to stop dropping them and try to resolve
them per normal mechanisms.
2022-03-09 12:09:46 -05:00
Rashad Sookram
366780f6cb Revert "Avoid querying conversation size twice."
This reverts commit fe088c39c7.
2022-03-09 11:55:52 -05:00
Alex Hart
fb4c1fc268 Bump version to 5.33.1 2022-03-08 16:11:46 -04:00
Alex Hart
e3bb7ccbd3 Updated language translations. 2022-03-08 16:11:09 -04:00
Alex Hart
e3fb8a2137 Only update tab if it has actually changed. 2022-03-08 12:01:21 -04:00
1512 changed files with 48495 additions and 17853 deletions

23
.github/stale.yml vendored
View File

@@ -0,0 +1,23 @@
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 60
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: 7
issues:
exemptLabels:
- acknowledged
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale Issue or Pull Request.
closeComment: >
This issue has been closed due to inactivity.
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 1

View File

@@ -96,7 +96,7 @@ try:
print("\nTo include this in the distribution, copy it to the project's assets/databases/ directory.")
print("If you support API 10 or lower, you must use the gzipped version to avoid corruption.")
except sqlite3.Error, e:
except sqlite3.Error as e:
if connection:
connection.rollback()
print("Error: %s" % e.args[0])

View File

@@ -63,8 +63,8 @@ ktlint {
version = "0.43.2"
}
def canonicalVersionCode = 1019
def canonicalVersionName = "5.33.0"
def canonicalVersionCode = 1044
def canonicalVersionName = "5.36.3"
def postFixSize = 100
def abiPostFix = ['universal' : 0,
@@ -455,8 +455,9 @@ dependencies {
implementation project(':device-transfer')
implementation project(':image-editor')
implementation project(':donations')
implementation project(':contacts')
implementation libs.signal.client.android
implementation libs.libsignal.android
implementation libs.google.protobuf.javalite
implementation(libs.mobilecoin) {
@@ -524,10 +525,7 @@ dependencies {
testImplementation testLibs.junit.junit
testImplementation testLibs.assertj.core
testImplementation testLibs.mockito.core
testImplementation testLibs.powermock.api.mockito
testImplementation testLibs.powermock.module.junit4.core
testImplementation testLibs.powermock.module.junit4.rule
testImplementation testLibs.powermock.classloading.xstream
testImplementation testLibs.mockito.kotlin
testImplementation testLibs.androidx.test.core
testImplementation (testLibs.robolectric.robolectric) {
@@ -554,7 +552,7 @@ dependencies {
implementation libs.rxjava3.rxkotlin
implementation libs.rxdogtag
androidTestUtil 'androidx.test:orchestrator:1.4.0'
androidTestUtil 'androidx.test:orchestrator:1.4.1'
}
def getLastCommitTimestamp() {

View File

@@ -2,6 +2,7 @@
-dontobfuscate
-keepattributes SourceFile,LineNumberTable
-keep class org.whispersystems.** { *; }
-keep class org.signal.libsignal.protocol.** { *; }
-keep class org.thoughtcrime.securesms.** { *; }
-keepclassmembers class ** {
public void onEvent*(**);

View File

@@ -8,7 +8,6 @@ import org.thoughtcrime.securesms.database.model.DistributionListRecord
import org.thoughtcrime.securesms.database.model.StoryType
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.push.ACI
import java.lang.IllegalStateException
import java.util.UUID
class DistributionListDatabaseTest {

View File

@@ -0,0 +1,194 @@
package org.thoughtcrime.securesms.database
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.database.model.StoryType
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.mms.IncomingMediaMessage
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.PNI
import org.whispersystems.signalservice.api.push.ServiceId
import java.util.UUID
@Suppress("ClassName")
@RunWith(AndroidJUnit4::class)
class MmsDatabaseTest_stories {
private lateinit var mms: MmsDatabase
private val localAci = ACI.from(UUID.randomUUID())
private val localPni = PNI.from(UUID.randomUUID())
private lateinit var myStory: Recipient
private lateinit var recipients: List<RecipientId>
@Before
fun setUp() {
mms = SignalDatabase.mms
mms.deleteAllThreads()
SignalStore.account().setAci(localAci)
SignalStore.account().setPni(localPni)
myStory = Recipient.resolved(SignalDatabase.recipients.getOrInsertFromDistributionListId(DistributionListId.MY_STORY))
recipients = (0 until 5).map { SignalDatabase.recipients.getOrInsertFromServiceId(ServiceId.from(UUID.randomUUID())) }
}
@Test
fun givenNoStories_whenIGetOrderedStoryRecipientsAndIds_thenIExpectAnEmptyList() {
// WHEN
val result = mms.orderedStoryRecipientsAndIds
// THEN
assertEquals(0, result.size)
}
@Test
fun givenOneOutgoingAndOneIncomingStory_whenIGetOrderedStoryRecipientsAndIds_thenIExpectIncomingThenOutgoing() {
// GIVEN
val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(myStory)
val sender = recipients[0]
MmsHelper.insert(
recipient = myStory,
sentTimeMillis = 1,
storyType = StoryType.STORY_WITH_REPLIES,
threadId = threadId
)
MmsHelper.insert(
IncomingMediaMessage(
from = sender,
sentTimeMillis = 2,
serverTimeMillis = 2,
receivedTimeMillis = 2,
storyType = StoryType.STORY_WITH_REPLIES
),
-1L
)
// WHEN
val result = mms.orderedStoryRecipientsAndIds
// THEN
assertEquals(listOf(sender.toLong(), myStory.id.toLong()), result.map { it.recipientId.toLong() })
}
@Test
fun givenAStory_whenISetIncomingStoryMessageViewed_thenIExpectASetReceiptTimestamp() {
// GIVEN
val sender = recipients[0]
val messageId = MmsHelper.insert(
IncomingMediaMessage(
from = sender,
sentTimeMillis = 2,
serverTimeMillis = 2,
receivedTimeMillis = 2,
storyType = StoryType.STORY_WITH_REPLIES
),
-1L
).get().messageId
val messageBeforeMark = SignalDatabase.mms.getMessageRecord(messageId)
assertFalse(messageBeforeMark.incomingStoryViewedAtTimestamp > 0)
// WHEN
SignalDatabase.mms.setIncomingMessageViewed(messageId)
// THEN
val messageAfterMark = SignalDatabase.mms.getMessageRecord(messageId)
assertTrue(messageAfterMark.incomingStoryViewedAtTimestamp > 0)
}
@Test
fun given5ViewedStories_whenIGetOrderedStoryRecipientsAndIds_thenIExpectLatestViewedFirst() {
// GIVEN
val messageIds = recipients.take(5).map {
MmsHelper.insert(
IncomingMediaMessage(
from = it,
sentTimeMillis = 2,
serverTimeMillis = 2,
receivedTimeMillis = 2,
storyType = StoryType.STORY_WITH_REPLIES,
),
-1L
).get().messageId
}
val randomizedOrderedIds = messageIds.shuffled()
randomizedOrderedIds.forEach {
SignalDatabase.mms.setIncomingMessageViewed(it)
Thread.sleep(5)
}
// WHEN
val result = SignalDatabase.mms.orderedStoryRecipientsAndIds
val resultOrderedIds = result.map { it.messageId }
// THEN
assertEquals(randomizedOrderedIds.reversed(), resultOrderedIds)
}
@Test
fun given15Stories_whenIGetOrderedStoryRecipientsAndIds_thenIExpectUnviewedThenInterspersedViewedAndSelfSendsAllDescending() {
val myStoryThread = SignalDatabase.threads.getOrCreateThreadIdFor(myStory)
val unviewedIds: List<Long> = (0 until 5).map {
Thread.sleep(5)
MmsHelper.insert(
IncomingMediaMessage(
from = recipients[it],
sentTimeMillis = System.currentTimeMillis(),
serverTimeMillis = 2,
receivedTimeMillis = 2,
storyType = StoryType.STORY_WITH_REPLIES,
),
-1L
).get().messageId
}
val viewedIds: List<Long> = (0 until 5).map {
Thread.sleep(5)
MmsHelper.insert(
IncomingMediaMessage(
from = recipients[it],
sentTimeMillis = System.currentTimeMillis(),
serverTimeMillis = 2,
receivedTimeMillis = 2,
storyType = StoryType.STORY_WITH_REPLIES,
),
-1L
).get().messageId
}
val interspersedIds: List<Long> = (0 until 10).map {
Thread.sleep(5)
if (it % 2 == 0) {
SignalDatabase.mms.setIncomingMessageViewed(viewedIds[it / 2])
viewedIds[it / 2]
} else {
MmsHelper.insert(
recipient = myStory,
sentTimeMillis = System.currentTimeMillis(),
storyType = StoryType.STORY_WITH_REPLIES,
threadId = myStoryThread
)
}
}
val result = SignalDatabase.mms.orderedStoryRecipientsAndIds
val resultOrderedIds = result.map { it.messageId }
assertEquals(unviewedIds.reversed() + interspersedIds.reversed(), resultOrderedIds)
}
}

View File

@@ -0,0 +1,64 @@
package org.thoughtcrime.securesms.database
import org.thoughtcrime.securesms.database.model.StoryType
import org.thoughtcrime.securesms.mms.IncomingMediaMessage
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage
import org.thoughtcrime.securesms.recipients.Recipient
import java.util.Optional
/**
* Helper methods for inserting an MMS message into the MMS table.
*/
object MmsHelper {
fun insert(
recipient: Recipient = Recipient.UNKNOWN,
body: String = "body",
sentTimeMillis: Long = System.currentTimeMillis(),
subscriptionId: Int = -1,
expiresIn: Long = 0,
viewOnce: Boolean = false,
distributionType: Int = ThreadDatabase.DistributionTypes.DEFAULT,
threadId: Long = 1,
storyType: StoryType = StoryType.NONE
): Long {
val message = OutgoingMediaMessage(
recipient,
body,
emptyList(),
sentTimeMillis,
subscriptionId,
expiresIn,
viewOnce,
distributionType,
storyType,
null,
false,
null,
emptyList(),
emptyList(),
emptyList(),
emptySet(),
emptySet()
)
return insert(
message = message,
threadId = threadId,
)
}
fun insert(
message: OutgoingMediaMessage,
threadId: Long
): Long {
return SignalDatabase.mms.insertMessageOutbox(message, threadId, false, GroupReceiptDatabase.STATUS_UNKNOWN, null)
}
fun insert(
message: IncomingMediaMessage,
threadId: Long
): Optional<MessageDatabase.InsertResult> {
return SignalDatabase.mms.insertSecureDecryptedMessageInbox(message, threadId)
}
}

View File

@@ -18,10 +18,9 @@ import org.thoughtcrime.securesms.keyvalue.MockKeyValuePersistentStorage
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.libsignal.util.guava.Optional
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.PNI
import java.lang.IllegalArgumentException
import java.util.Optional
import java.util.UUID
@RunWith(AndroidJUnit4::class)
@@ -523,7 +522,7 @@ class RecipientDatabaseTest {
}
private fun ensureDbEmpty() {
SignalDatabase.rawDatabase.rawQuery("SELECT COUNT(*) FROM ${RecipientDatabase.TABLE_NAME}", null).use { cursor ->
SignalDatabase.rawDatabase.rawQuery("SELECT COUNT(*) FROM ${RecipientDatabase.TABLE_NAME} WHERE ${RecipientDatabase.DISTRIBUTION_LIST_ID} IS NULL ", null).use { cursor ->
assertTrue(cursor.moveToFirst())
assertEquals(0, cursor.getLong(0))
}

View File

@@ -9,13 +9,16 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.signal.core.util.CursorUtil
import org.signal.libsignal.protocol.IdentityKey
import org.signal.libsignal.protocol.SignalProtocolAddress
import org.signal.libsignal.protocol.state.SessionRecord
import org.signal.libsignal.zkgroup.groups.GroupMasterKey
import org.signal.storageservice.protos.groups.local.DecryptedGroup
import org.signal.storageservice.protos.groups.local.DecryptedMember
import org.signal.zkgroup.groups.GroupMasterKey
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.database.model.DistributionListRecord
@@ -30,14 +33,10 @@ import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.sms.IncomingTextMessage
import org.thoughtcrime.securesms.util.CursorUtil
import org.whispersystems.libsignal.IdentityKey
import org.whispersystems.libsignal.SignalProtocolAddress
import org.whispersystems.libsignal.state.SessionRecord
import org.whispersystems.libsignal.util.guava.Optional
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.PNI
import org.whispersystems.signalservice.api.util.UuidUtil
import java.util.Optional
import java.util.UUID
@RunWith(AndroidJUnit4::class)
@@ -76,8 +75,6 @@ class RecipientDatabaseTest_merges {
SignalStore.account().setAci(localAci)
SignalStore.account().setPni(localPni)
ensureDbEmpty()
}
/** High trust lets you merge two different users into one. You should prefer the ACI user. Not shown: merging threads, dropping e164 sessions, etc. */
@@ -217,19 +214,12 @@ class RecipientDatabaseTest_merges {
private val context: Application
get() = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as Application
private fun ensureDbEmpty() {
SignalDatabase.rawDatabase.rawQuery("SELECT COUNT(*) FROM ${RecipientDatabase.TABLE_NAME}", null).use { cursor ->
assertTrue(cursor.moveToFirst())
assertEquals(0, cursor.getLong(0))
}
}
private fun smsMessage(sender: RecipientId, time: Long = 0, body: String = "", groupId: Optional<GroupId> = Optional.absent()): IncomingTextMessage {
private fun smsMessage(sender: RecipientId, time: Long = 0, body: String = "", groupId: Optional<GroupId> = Optional.empty()): IncomingTextMessage {
return IncomingTextMessage(sender, 1, time, time, time, body, groupId, 0, true, null)
}
private fun mmsMessage(sender: RecipientId, time: Long = 0, body: String = "", groupId: Optional<GroupId> = Optional.absent()): IncomingMediaMessage {
return IncomingMediaMessage(sender, groupId, body, time, time, time, emptyList(), 0, 0, false, false, true, Optional.absent())
private fun mmsMessage(sender: RecipientId, time: Long = 0, body: String = "", groupId: Optional<GroupId> = Optional.empty()): IncomingMediaMessage {
return IncomingMediaMessage(sender, groupId, body, time, time, time, emptyList(), 0, 0, false, false, true, Optional.empty())
}
private fun identityKey(value: Byte): IdentityKey {

View File

@@ -0,0 +1,292 @@
package org.thoughtcrime.securesms.database
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.`is`
import org.hamcrest.Matchers.notNullValue
import org.hamcrest.Matchers.nullValue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.signal.core.util.Hex
import org.signal.libsignal.zkgroup.groups.GroupMasterKey
import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context
import org.thoughtcrime.securesms.database.model.databaseprotos.addMember
import org.thoughtcrime.securesms.database.model.databaseprotos.addRequestingMember
import org.thoughtcrime.securesms.database.model.databaseprotos.deleteRequestingMember
import org.thoughtcrime.securesms.database.model.databaseprotos.groupChange
import org.thoughtcrime.securesms.database.model.databaseprotos.groupContext
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.sms.IncomingGroupUpdateMessage
import org.thoughtcrime.securesms.sms.IncomingTextMessage
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.PNI
import org.whispersystems.signalservice.api.push.ServiceId
import java.util.Optional
import java.util.UUID
@Suppress("ClassName", "TestFunctionName")
@RunWith(AndroidJUnit4::class)
class SmsDatabaseTest_collapseJoinRequestEventsIfPossible {
private lateinit var recipients: RecipientDatabase
private lateinit var sms: SmsDatabase
private val localAci = ACI.from(UUID.randomUUID())
private val localPni = PNI.from(UUID.randomUUID())
private var wallClock: Long = 1000
private lateinit var alice: RecipientId
private lateinit var bob: RecipientId
@Before
fun setUp() {
recipients = SignalDatabase.recipients
sms = SignalDatabase.sms
SignalStore.account().setAci(localAci)
SignalStore.account().setPni(localPni)
alice = recipients.getOrInsertFromServiceId(aliceServiceId)
bob = recipients.getOrInsertFromServiceId(bobServiceId)
}
/**
* Do nothing if no previous messages.
*/
@Test
fun noPreviousMessage() {
val result = sms.collapseJoinRequestEventsIfPossible(
1,
groupUpdateMessage(
sender = alice,
groupContext = groupContext(masterKey = masterKey) {
change = groupChange(editor = aliceServiceId) {
deleteRequestingMember(aliceServiceId)
}
}
)
)
assertThat("result is null when not collapsing", result.orElse(null), nullValue())
}
/**
* Do nothing if previous message is text.
*/
@Test
fun previousTextMesssage() {
val threadId = sms.insertMessageInbox(smsMessage(sender = alice, body = "What up")).get().threadId
val result = sms.collapseJoinRequestEventsIfPossible(
threadId,
groupUpdateMessage(
sender = alice,
groupContext = groupContext(masterKey = masterKey) {
change = groupChange(editor = aliceServiceId) {
deleteRequestingMember(aliceServiceId)
}
}
)
)
assertThat("result is null when not collapsing", result.orElse(null), nullValue())
}
/**
* Do nothing if previous is unrelated group change.
*/
@Test
fun previousUnrelatedGroupChange() {
val threadId = sms.insertMessageInbox(
groupUpdateMessage(
sender = alice,
groupContext = groupContext(masterKey = masterKey) {
change = groupChange(editor = aliceServiceId) {
addMember(bobServiceId)
}
}
)
).get().threadId
val result = sms.collapseJoinRequestEventsIfPossible(
threadId,
groupUpdateMessage(
sender = alice,
groupContext = groupContext(masterKey = masterKey) {
change = groupChange(editor = aliceServiceId) {
deleteRequestingMember(aliceServiceId)
}
}
)
)
assertThat("result is null when not collapsing", result.orElse(null), nullValue())
}
/**
* Do nothing if previous join request is from a different recipient.
*/
@Test
fun previousJoinRequestFromADifferentRecipient() {
val threadId = sms.insertMessageInbox(
groupUpdateMessage(
sender = bob,
groupContext = groupContext(masterKey = masterKey) {
change = groupChange(editor = bobServiceId) {
deleteRequestingMember(bobServiceId)
}
}
)
).get().threadId
val result = sms.collapseJoinRequestEventsIfPossible(
threadId,
groupUpdateMessage(
sender = alice,
groupContext = groupContext(masterKey = masterKey) {
change = groupChange(editor = aliceServiceId) {
deleteRequestingMember(aliceServiceId)
}
}
)
)
assertThat("result is null when not collapsing", result.orElse(null), nullValue())
}
/**
* Collapse if previous is join request from same.
*/
@Test
fun previousJoinRequestCollapse() {
val latestMessage: MessageDatabase.InsertResult = sms.insertMessageInbox(
groupUpdateMessage(
sender = alice,
groupContext = groupContext(masterKey = masterKey) {
change = groupChange(editor = aliceServiceId) {
addRequestingMember(aliceServiceId)
}
}
)
).get()
val result = sms.collapseJoinRequestEventsIfPossible(
latestMessage.threadId,
groupUpdateMessage(
sender = alice,
groupContext = groupContext(masterKey = masterKey) {
change = groupChange(editor = aliceServiceId) {
deleteRequestingMember(aliceServiceId)
}
}
)
)
assertThat("result is not null when collapsing", result.orElse(null), notNullValue())
assertThat("result message id should be same as latest message", result.get().messageId, `is`(latestMessage.messageId))
}
/**
* Collapse if previous is join request from same, and leave second previous alone if text.
*/
@Test
fun previousJoinThenTextCollapse() {
val secondLatestMessage = sms.insertMessageInbox(smsMessage(sender = alice, body = "What up")).get()
val latestMessage: MessageDatabase.InsertResult = sms.insertMessageInbox(
groupUpdateMessage(
sender = alice,
groupContext = groupContext(masterKey = masterKey) {
change = groupChange(editor = aliceServiceId) {
addRequestingMember(aliceServiceId)
}
}
)
).get()
assert(secondLatestMessage.threadId == latestMessage.threadId)
val result = sms.collapseJoinRequestEventsIfPossible(
latestMessage.threadId,
groupUpdateMessage(
sender = alice,
groupContext = groupContext(masterKey = masterKey) {
change = groupChange(editor = aliceServiceId) {
deleteRequestingMember(aliceServiceId)
}
}
)
)
assertThat("result is not null when collapsing", result.orElse(null), notNullValue())
assertThat("result message id should be same as latest message", result.get().messageId, `is`(latestMessage.messageId))
}
/**
* Collapse "twice" is previous is a join request and second previous is already collapsed join/delete from the same recipient.
*/
@Test
fun previousCollapseAndJoinRequestDoubleCollapse() {
val secondLatestMessage: MessageDatabase.InsertResult = sms.insertMessageInbox(
groupUpdateMessage(
sender = alice,
groupContext = groupContext(masterKey = masterKey) {
change = groupChange(editor = aliceServiceId) {
addRequestingMember(aliceServiceId)
deleteRequestingMember(aliceServiceId)
}
}
)
).get()
val latestMessage: MessageDatabase.InsertResult = sms.insertMessageInbox(
groupUpdateMessage(
sender = alice,
groupContext = groupContext(masterKey = masterKey) {
change = groupChange(editor = aliceServiceId) {
addRequestingMember(aliceServiceId)
}
}
)
).get()
assert(secondLatestMessage.threadId == latestMessage.threadId)
val result = sms.collapseJoinRequestEventsIfPossible(
latestMessage.threadId,
groupUpdateMessage(
sender = alice,
groupContext = groupContext(masterKey = masterKey) {
change = groupChange(editor = aliceServiceId) {
deleteRequestingMember(aliceServiceId)
}
}
)
)
assertThat("result is not null when collapsing", result.orElse(null), notNullValue())
assertThat("result message id should be same as second latest message", result.get().messageId, `is`(secondLatestMessage.messageId))
assertThat("latest message should be deleted", sms.getMessageRecordOrNull(latestMessage.messageId), nullValue())
}
private fun smsMessage(sender: RecipientId, body: String? = ""): IncomingTextMessage {
wallClock++
return IncomingTextMessage(sender, 1, wallClock, wallClock, wallClock, body, Optional.of(groupId), 0, true, null)
}
private fun groupUpdateMessage(sender: RecipientId, groupContext: DecryptedGroupV2Context): IncomingGroupUpdateMessage {
return IncomingGroupUpdateMessage(smsMessage(sender, null), groupContext)
}
companion object {
private val aliceServiceId: ServiceId = ACI.from(UUID.fromString("3436efbe-5a76-47fa-a98a-7e72c948a82e"))
private val bobServiceId: ServiceId = ACI.from(UUID.fromString("8de7f691-0b60-4a68-9cd9-ed2f8453f9ed"))
private val masterKey = GroupMasterKey(Hex.fromStringCondensed("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"))
private val groupId = GroupId.v2(masterKey)
}
}

View File

@@ -0,0 +1,192 @@
package org.thoughtcrime.securesms.database
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.containsInAnyOrder
import org.hamcrest.Matchers.hasSize
import org.hamcrest.Matchers.`is`
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.thoughtcrime.securesms.database.model.StoryType
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.push.ServiceId
import java.util.UUID
@RunWith(AndroidJUnit4::class)
class StorySendsDatabaseTest {
private lateinit var recipients1to10: List<RecipientId>
private lateinit var recipients11to20: List<RecipientId>
private lateinit var recipients6to15: List<RecipientId>
private lateinit var recipients6to10: List<RecipientId>
private var messageId1: Long = 0
private var messageId2: Long = 0
private var messageId3: Long = 0
private lateinit var storySends: StorySendsDatabase
@Before
fun setup() {
storySends = SignalDatabase.storySends
messageId1 = MmsHelper.insert(storyType = StoryType.STORY_WITHOUT_REPLIES)
messageId2 = MmsHelper.insert(storyType = StoryType.STORY_WITH_REPLIES)
messageId3 = MmsHelper.insert(storyType = StoryType.STORY_WITHOUT_REPLIES)
recipients1to10 = makeRecipients(10)
recipients11to20 = makeRecipients(10)
recipients6to15 = recipients1to10.takeLast(5) + recipients11to20.take(5)
recipients6to10 = recipients1to10.takeLast(5)
}
@Test
fun getRecipientsToSendTo_noOverlap() {
storySends.insert(messageId1, recipients1to10, 100, false)
storySends.insert(messageId2, recipients11to20, 200, true)
storySends.insert(messageId3, recipients1to10, 300, false)
val recipientIdsForMessage1 = storySends.getRecipientsToSendTo(messageId1, 100, false)
val recipientIdsForMessage2 = storySends.getRecipientsToSendTo(messageId2, 200, true)
assertThat(recipientIdsForMessage1, hasSize(10))
assertThat(recipientIdsForMessage1, containsInAnyOrder(*recipients1to10.toTypedArray()))
assertThat(recipientIdsForMessage2, hasSize(10))
assertThat(recipientIdsForMessage2, containsInAnyOrder(*recipients11to20.toTypedArray()))
}
@Test
fun getRecipientsToSendTo_overlap() {
storySends.insert(messageId1, recipients1to10, 100, false)
storySends.insert(messageId2, recipients6to15, 100, true)
val recipientIdsForMessage1 = storySends.getRecipientsToSendTo(messageId1, 100, false)
val recipientIdsForMessage2 = storySends.getRecipientsToSendTo(messageId2, 100, true)
assertThat(recipientIdsForMessage1, hasSize(5))
assertThat(recipientIdsForMessage1, containsInAnyOrder(*recipients1to10.take(5).toTypedArray()))
assertThat(recipientIdsForMessage2, hasSize(10))
assertThat(recipientIdsForMessage2, containsInAnyOrder(*recipients6to15.toTypedArray()))
}
@Test
fun getRecipientsToSendTo_overlapAll() {
val recipient1 = recipients1to10.first()
val recipient2 = recipients11to20.first()
storySends.insert(messageId1, listOf(recipient1, recipient2), 100, false)
storySends.insert(messageId2, listOf(recipient1), 100, true)
storySends.insert(messageId3, listOf(recipient2), 100, true)
val recipientIdsForMessage1 = storySends.getRecipientsToSendTo(messageId1, 100, false)
val recipientIdsForMessage2 = storySends.getRecipientsToSendTo(messageId2, 100, true)
val recipientIdsForMessage3 = storySends.getRecipientsToSendTo(messageId3, 100, true)
assertThat(recipientIdsForMessage1, hasSize(0))
assertThat(recipientIdsForMessage2, hasSize(1))
assertThat(recipientIdsForMessage2, containsInAnyOrder(recipient1))
assertThat(recipientIdsForMessage3, hasSize(1))
assertThat(recipientIdsForMessage3, containsInAnyOrder(recipient2))
}
@Test
fun getRecipientsToSendTo_overlapWithEarlierMessage() {
storySends.insert(messageId1, recipients6to15, 100, true)
storySends.insert(messageId2, recipients1to10, 100, false)
val recipientIdsForMessage1 = storySends.getRecipientsToSendTo(messageId1, 100, true)
val recipientIdsForMessage2 = storySends.getRecipientsToSendTo(messageId2, 100, false)
assertThat(recipientIdsForMessage1, hasSize(10))
assertThat(recipientIdsForMessage1, containsInAnyOrder(*recipients6to15.toTypedArray()))
assertThat(recipientIdsForMessage2, hasSize(5))
assertThat(recipientIdsForMessage2, containsInAnyOrder(*recipients1to10.take(5).toTypedArray()))
}
@Test
fun getRemoteDeleteRecipients_noOverlap() {
storySends.insert(messageId1, recipients1to10, 100, false)
storySends.insert(messageId2, recipients11to20, 200, true)
storySends.insert(messageId3, recipients1to10, 300, false)
val recipientIdsForMessage1 = storySends.getRemoteDeleteRecipients(messageId1, 100)
val recipientIdsForMessage2 = storySends.getRemoteDeleteRecipients(messageId2, 200)
assertThat(recipientIdsForMessage1, hasSize(10))
assertThat(recipientIdsForMessage1, containsInAnyOrder(*recipients1to10.toTypedArray()))
assertThat(recipientIdsForMessage2, hasSize(10))
assertThat(recipientIdsForMessage2, containsInAnyOrder(*recipients11to20.toTypedArray()))
}
@Test
fun getRemoteDeleteRecipients_overlapNoPreviousDeletes() {
storySends.insert(messageId1, recipients1to10, 200, false)
storySends.insert(messageId2, recipients6to15, 200, true)
val recipientIdsForMessage1 = storySends.getRemoteDeleteRecipients(messageId1, 200)
val recipientIdsForMessage2 = storySends.getRemoteDeleteRecipients(messageId2, 200)
assertThat(recipientIdsForMessage1, hasSize(5))
assertThat(recipientIdsForMessage1, containsInAnyOrder(*recipients1to10.take(5).toTypedArray()))
assertThat(recipientIdsForMessage2, hasSize(5))
assertThat(recipientIdsForMessage2, containsInAnyOrder(*recipients6to15.takeLast(5).toTypedArray()))
}
@Test
fun getRemoteDeleteRecipients_overlapWithPreviousDeletes() {
storySends.insert(messageId1, recipients1to10, 200, false)
SignalDatabase.mms.markAsRemoteDelete(messageId1)
storySends.insert(messageId2, recipients6to15, 200, true)
val recipientIdsForMessage2 = storySends.getRemoteDeleteRecipients(messageId2, 200)
assertThat(recipientIdsForMessage2, hasSize(10))
assertThat(recipientIdsForMessage2, containsInAnyOrder(*recipients6to15.toTypedArray()))
}
@Test
fun canReply_storyWithReplies() {
storySends.insert(messageId2, recipients1to10, 200, true)
val canReply = storySends.canReply(recipients1to10[0], 200)
assertThat(canReply, `is`(true))
}
@Test
fun canReply_storyWithoutReplies() {
storySends.insert(messageId1, recipients1to10, 200, false)
val canReply = storySends.canReply(recipients1to10[0], 200)
assertThat(canReply, `is`(false))
}
@Test
fun canReply_storyWithAndWithoutRepliesOverlap() {
storySends.insert(messageId1, recipients1to10, 200, false)
storySends.insert(messageId2, recipients6to10, 200, true)
val message1OnlyRecipientCanReply = storySends.canReply(recipients1to10[0], 200)
val message2RecipientCanReply = storySends.canReply(recipients6to10[0], 200)
assertThat(message1OnlyRecipientCanReply, `is`(false))
assertThat(message2RecipientCanReply, `is`(true))
}
private fun makeRecipients(count: Int): List<RecipientId> {
return (1..count).map {
SignalDatabase.recipients.getOrInsertFromServiceId(ServiceId.from(UUID.randomUUID()))
}
}
}

View File

@@ -4,7 +4,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.thoughtcrime.securesms.util.Hex;
import org.signal.core.util.Hex;
import org.whispersystems.signalservice.api.kbs.HashedPin;
import org.whispersystems.signalservice.api.kbs.KbsData;
import org.whispersystems.signalservice.api.kbs.MasterKey;

View File

@@ -179,11 +179,12 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:windowSoftInputMode="adjustResize" />
<activity android:name=".sharing.ShareActivity"
<activity android:name=".sharing.v2.ShareActivity"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:excludeFromRecents="true"
android:taskAffinity=""
android:windowSoftInputMode="stateHidden"
android:exported="true"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
<intent-filter>
<action android:name="android.intent.action.SEND" />
@@ -403,7 +404,7 @@
<activity
android:name=".stories.viewer.StoryViewerActivity"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:theme="@style/TextSecure.DarkNoActionBar"
android:theme="@style/TextSecure.DarkNoActionBar.StoryViewer"
android:windowSoftInputMode="stateAlwaysHidden|adjustNothing" />
<activity android:name=".components.settings.app.changenumber.ChangeNumberLockActivity"
@@ -757,6 +758,8 @@
<receiver android:name=".service.ExpirationListener" />
<receiver android:name=".service.ExpiringStoriesManager$ExpireStoriesAlarm" />
<receiver android:name=".revealable.ViewOnceMessageManager$ViewOnceAlarm" />
<receiver android:name=".service.PendingRetryReceiptManager$PendingRetryReceiptAlarm" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 233 KiB

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 KiB

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 91 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,14 @@
package com.google.android.material.bottomsheet
import android.view.View
import android.widget.FrameLayout
import java.lang.ref.WeakReference
/**
* Manually adjust the nested scrolling child for a given [BottomSheetBehavior].
*/
object BottomSheetBehaviorHack {
fun setNestedScrollingChild(behavior: BottomSheetBehavior<FrameLayout>, view: View) {
behavior.nestedScrollingChildRef = WeakReference(view)
}
}

View File

@@ -35,6 +35,7 @@ import org.signal.core.util.logging.AndroidLogger;
import org.signal.core.util.logging.Log;
import org.signal.core.util.tracing.Tracer;
import org.signal.glide.SignalGlideCodecs;
import org.signal.libsignal.protocol.logging.SignalProtocolLoggerProvider;
import org.signal.ringrtc.CallManager;
import org.thoughtcrime.securesms.avatar.AvatarPickerStorage;
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider;
@@ -53,6 +54,7 @@ import org.thoughtcrime.securesms.jobs.DownloadLatestEmojiDataJob;
import org.thoughtcrime.securesms.jobs.EmojiSearchIndexDownloadJob;
import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
import org.thoughtcrime.securesms.jobs.FontDownloaderJob;
import org.thoughtcrime.securesms.jobs.GroupV2UpdateSelfProfileKeyJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.jobs.ProfileUploadJob;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
@@ -91,7 +93,6 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.VersionTracker;
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;
import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider;
import java.net.SocketException;
import java.net.SocketTimeoutException;
@@ -201,6 +202,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
.addPostRender(() -> AndroidTelecomUtil.registerPhoneAccount())
.addPostRender(() -> ApplicationDependencies.getJobManager().add(new FontDownloaderJob()))
.addPostRender(CheckServiceReachabilityJob::enqueueIfNecessary)
.addPostRender(GroupV2UpdateSelfProfileKeyJob::enqueueForGroupsIfNecessary)
.execute();
Log.d(TAG, "onCreate() took " + (System.currentTimeMillis() - startTime) + " ms");

View File

@@ -14,6 +14,7 @@ import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityOptionsCompat;
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
@@ -58,6 +59,12 @@ public final class AvatarPreviewActivity extends PassphraseRequiredActivity {
return ActivityOptionsCompat.makeSceneTransitionAnimation(activity, from, "avatar").toBundle();
}
@Override
protected void attachBaseContext(@NonNull Context newBase) {
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);
super.attachBaseContext(newBase);
}
@Override
protected void onCreate(Bundle savedInstanceState, boolean ready) {
super.onCreate(savedInstanceState, ready);
@@ -132,7 +139,7 @@ public final class AvatarPreviewActivity extends PassphraseRequiredActivity {
findViewById(android.R.id.content).setOnClickListener(v -> fullscreenHelper.toggleUiVisibility());
fullscreenHelper.configureToolbarSpacer(findViewById(R.id.toolbar_cutout_spacer));
fullscreenHelper.configureToolbarLayout(findViewById(R.id.toolbar_cutout_spacer), toolbar);
fullscreenHelper.showAndHideWithSystemUI(getWindow(), findViewById(R.id.toolbar_layout));
}

View File

@@ -26,10 +26,10 @@ import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.stickers.StickerLocator;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
public interface BindableConversationItem extends Unbindable, GiphyMp4Playable, Colorizable, Multiselectable {
@@ -60,6 +60,10 @@ public interface BindableConversationItem extends Unbindable, GiphyMp4Playable,
// Intentionally Blank.
}
default void updateSelectedState() {
// Intentionall Blank.
}
interface EventListener {
void onQuoteClicked(MmsMessageRecord messageRecord);
void onLinkPreviewClicked(@NonNull LinkPreview linkPreview);
@@ -94,6 +98,8 @@ public interface BindableConversationItem extends Unbindable, GiphyMp4Playable,
void onChangeNumberUpdateContact(@NonNull Recipient recipient);
void onCallToAction(@NonNull String action);
void onDonateClicked();
void onBlockJoinRequest(@NonNull Recipient recipient);
void onRecipientNameClicked(@NonNull RecipientId target);
/** @return true if handled, false if you want to let the normal url handling continue */
boolean onUrlClicked(@NonNull String url);

View File

@@ -2,7 +2,6 @@ package org.thoughtcrime.securesms;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.conversationlist.model.Conversation;
import org.thoughtcrime.securesms.conversationlist.model.ConversationSet;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.mms.GlideRequests;

View File

@@ -13,7 +13,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.signal.core.util.concurrent.SimpleTask;
/**
* This should be used whenever we want to prompt the user to block/unblock a recipient.

View File

@@ -27,16 +27,16 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.components.ContactFilterView;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Optional;
import java.util.function.Consumer;
/**
@@ -153,7 +153,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActivit
@Override
protected Void doInBackground(Context... params) {
try {
DirectoryHelper.refreshDirectory(params[0], true);
ContactDiscovery.refreshAll(params[0], true);
} catch (IOException e) {
Log.w(TAG, e);
}

View File

@@ -70,7 +70,7 @@ import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.HeaderAction;
import org.thoughtcrime.securesms.contacts.LetterHeaderDecoration;
import org.thoughtcrime.securesms.contacts.SelectedContact;
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery;
import org.thoughtcrime.securesms.groups.SelectionLimits;
import org.thoughtcrime.securesms.groups.ui.GroupLimitDialog;
import org.thoughtcrime.securesms.mms.GlideApp;
@@ -85,13 +85,13 @@ 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;
import org.signal.core.util.concurrent.SimpleTask;
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
@@ -576,7 +576,7 @@ public final class ContactSelectionListFragment extends LoggingFragment
@Override
protected Boolean doInBackground(Void... voids) {
try {
DirectoryHelper.refreshDirectory(context, false);
ContactDiscovery.refreshAll(context, false);
return true;
} catch (IOException e) {
Log.w(TAG, e);
@@ -637,8 +637,8 @@ public final class ContactSelectionListFragment extends LoggingFragment
private class ListClickListener implements ContactSelectionListAdapter.ItemClickListener {
@Override
public void onItemClick(ContactSelectionListItem contact) {
SelectedContact selectedContact = contact.isUsernameType() ? SelectedContact.forUsername(contact.getRecipientId().orNull(), contact.getNumber())
: SelectedContact.forPhone(contact.getRecipientId().orNull(), contact.getNumber());
SelectedContact selectedContact = contact.isUsernameType() ? SelectedContact.forUsername(contact.getRecipientId().orElse(null), contact.getNumber())
: SelectedContact.forPhone(contact.getRecipientId().orElse(null), contact.getNumber());
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();
@@ -774,7 +774,7 @@ public final class ContactSelectionListFragment extends LoggingFragment
markContactUnselected(selectedContact);
if (onContactSelectedListener != null) {
onContactSelectedListener.onContactDeselected(Optional.of(recipient.getId()), recipient.getE164().orNull());
onContactSelectedListener.onContactDeselected(Optional.of(recipient.getId()), recipient.getE164().orElse(null));
}
});

View File

@@ -17,12 +17,14 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.signal.zkgroup.profiles.ProfileKey;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.signal.libsignal.protocol.IdentityKeyPair;
import org.signal.libsignal.protocol.InvalidKeyException;
import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECPublicKey;
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
@@ -34,11 +36,6 @@ import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.ecc.Curve;
import org.whispersystems.libsignal.ecc.ECPublicKey;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.push.exceptions.NotFoundException;
import org.whispersystems.signalservice.internal.push.DeviceLimitExceededException;

View File

@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;

View File

@@ -38,8 +38,8 @@ import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture.Listener;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.thoughtcrime.securesms.util.text.AfterTextChanged;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
@@ -251,7 +251,7 @@ public class InviteActivity extends PassphraseRequiredActivity implements Contac
for (SelectedContact contact : contacts) {
RecipientId recipientId = contact.getOrCreateRecipientId(context);
Recipient recipient = Recipient.resolved(recipientId);
int subscriptionId = recipient.getDefaultSubscriptionId().or(-1);
int subscriptionId = recipient.getDefaultSubscriptionId().orElse(-1);
MessageSender.send(context, new OutgoingTextMessage(recipient, message, subscriptionId), -1L, true, null, null);

View File

@@ -10,7 +10,6 @@ import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.Transformations;
import androidx.lifecycle.ViewModelProvider;
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController;
@@ -19,14 +18,12 @@ import org.thoughtcrime.securesms.devicetransfer.olddevice.OldDeviceTransferLock
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.stories.Stories;
import org.thoughtcrime.securesms.stories.tabs.ConversationListTabRepository;
import org.thoughtcrime.securesms.stories.tabs.ConversationListTabsState;
import org.thoughtcrime.securesms.stories.tabs.ConversationListTabsViewModel;
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;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.WindowUtil;
public class MainActivity extends PassphraseRequiredActivity implements VoiceNoteMediaControllerOwner {
@@ -61,8 +58,6 @@ public class MainActivity extends PassphraseRequiredActivity implements VoiceNot
ConversationListTabRepository repository = new ConversationListTabRepository();
ConversationListTabsViewModel.Factory factory = new ConversationListTabsViewModel.Factory(repository);
navigator.onCreate(savedInstanceState);
handleGroupLinkInIntent(getIntent());
handleProxyInIntent(getIntent());
handleSignalMeIntent(getIntent());
@@ -70,18 +65,6 @@ public class MainActivity extends PassphraseRequiredActivity implements VoiceNot
CachedInflater.from(this).clear();
conversationListTabsViewModel = new ViewModelProvider(this, factory).get(ConversationListTabsViewModel.class);
Transformations.map(conversationListTabsViewModel.getState(), ConversationListTabsState::getTab)
.observe(this, tab -> {
switch (tab) {
case CHATS:
getSupportFragmentManager().popBackStack();
break;
case STORIES:
navigator.goToStories();
break;
}
});
updateTabVisibility();
}
@@ -135,11 +118,11 @@ public class MainActivity extends PassphraseRequiredActivity implements VoiceNot
private void updateTabVisibility() {
if (Stories.isFeatureEnabled()) {
findViewById(R.id.conversation_list_tabs).setVisibility(View.VISIBLE);
WindowUtil.setNavigationBarColor(getWindow(), ContextCompat.getColor(this, R.color.signal_background_secondary));
WindowUtil.setNavigationBarColor(getWindow(), ContextCompat.getColor(this, R.color.signal_colorSecondaryContainer));
} else {
findViewById(R.id.conversation_list_tabs).setVisibility(View.GONE);
WindowUtil.setNavigationBarColor(getWindow(), ContextCompat.getColor(this, R.color.signal_background_primary));
navigator.goToChats();
conversationListTabsViewModel.onChatsSelected();
}
}

View File

@@ -2,26 +2,19 @@ package org.thoughtcrime.securesms;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity;
import org.thoughtcrime.securesms.conversation.ConversationIntents;
import org.thoughtcrime.securesms.conversationlist.ConversationListArchiveFragment;
import org.thoughtcrime.securesms.conversationlist.ConversationListFragment;
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity;
import org.thoughtcrime.securesms.insights.InsightsLauncher;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.stories.landing.StoriesLandingFragment;
public class MainNavigator {
public static final String STORIES_TAG = "STORIES";
public static final int REQUEST_CONFIG_CHANGES = 901;
private final MainActivity activity;
@@ -38,16 +31,6 @@ public class MainNavigator {
return ((MainActivity) activity).getNavigator();
}
public void onCreate(@Nullable Bundle savedInstanceState) {
if (savedInstanceState != null) {
return;
}
getFragmentManager().beginTransaction()
.add(R.id.fragment_container, ConversationListFragment.newInstance())
.commit();
}
/**
* @return True if the back pressed was handled in our own custom way, false if it should be given
* to the system to do the default behavior.
@@ -76,29 +59,6 @@ public class MainNavigator {
activity.startActivityForResult(AppSettingsActivity.home(activity), REQUEST_CONFIG_CHANGES);
}
public void goToArchiveList() {
getFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_from_end, R.anim.slide_to_start, R.anim.slide_from_start, R.anim.slide_to_end)
.replace(R.id.fragment_container, ConversationListArchiveFragment.newInstance())
.addToBackStack(null)
.commit();
}
public void goToStories() {
if (getFragmentManager().findFragmentByTag(STORIES_TAG) == null) {
getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, new StoriesLandingFragment(), STORIES_TAG)
.addToBackStack(null)
.commit();
}
}
public void goToChats() {
if (getFragmentManager().findFragmentByTag(STORIES_TAG) != null) {
getFragmentManager().popBackStack();
}
}
public void goToGroupCreation() {
activity.startActivity(CreateGroupActivity.newIntent(activity));
}

View File

@@ -55,6 +55,10 @@ import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.animation.DepthPageTransformer;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.components.viewpager.ExtendedOnPageChangedListener;
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController;
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner;
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment;
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs;
import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader;
@@ -67,7 +71,6 @@ import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.sharing.ShareActivity;
import org.thoughtcrime.securesms.util.AttachmentUtil;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.FullscreenHelper;
@@ -87,7 +90,8 @@ import java.util.Objects;
public final class MediaPreviewActivity extends PassphraseRequiredActivity
implements LoaderManager.LoaderCallbacks<Pair<Cursor, Integer>>,
MediaRailAdapter.RailItemListener,
MediaPreviewFragment.Events
MediaPreviewFragment.Events,
VoiceNoteMediaControllerOwner
{
private final static String TAG = Log.tag(MediaPreviewActivity.class);
@@ -127,6 +131,8 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
private MediaDatabase.Sorting sorting;
private FullscreenHelper fullscreenHelper;
private VoiceNoteMediaController voiceNoteMediaController;
private @Nullable Cursor cursor = null;
public static @NonNull Intent intentFromMediaRecord(@NonNull Context context,
@@ -159,6 +165,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
setSupportActionBar(findViewById(R.id.toolbar));
voiceNoteMediaController = new VoiceNoteMediaController(this);
viewModel = ViewModelProviders.of(this).get(MediaPreviewViewModel.class);
fullscreenHelper = new FullscreenHelper(this);
@@ -203,23 +210,25 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
else from = "";
if (showThread) {
String to = null;
String titleText = null;
Recipient threadRecipient = mediaItem.threadRecipient;
if (threadRecipient != null) {
if (mediaItem.outgoing || threadRecipient.isGroup()) {
if (mediaItem.outgoing) {
if (threadRecipient.isSelf()) {
from = getString(R.string.note_to_self);
titleText = getString(R.string.note_to_self);
} else {
to = threadRecipient.getDisplayName(this);
titleText = getString(R.string.MediaPreviewActivity_you_to_s, threadRecipient.getDisplayName(this));
}
} else {
to = getString(R.string.MediaPreviewActivity_you);
if (threadRecipient.isGroup()) {
titleText = getString(R.string.MediaPreviewActivity_s_to_s, from, threadRecipient.getDisplayName(this));
} else {
titleText = getString(R.string.MediaPreviewActivity_s_to_you, from);
}
}
}
return to != null ? getString(R.string.MediaPreviewActivity_s_to_s, from, to)
: from;
return titleText != null ? titleText : from;
} else {
return from;
}
@@ -286,7 +295,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
anchorMarginsToBottomInsets(detailsContainer);
fullscreenHelper.configureToolbarSpacer(findViewById(R.id.toolbar_cutout_spacer));
fullscreenHelper.configureToolbarLayout(findViewById(R.id.toolbar_cutout_spacer), findViewById(R.id.toolbar));
fullscreenHelper.showAndHideWithSystemUI(getWindow(), detailsContainer, toolbarLayout);
}
@@ -386,10 +395,12 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
MediaItem mediaItem = getCurrentMediaItem();
if (mediaItem != null) {
Intent composeIntent = new Intent(this, ShareActivity.class);
composeIntent.putExtra(Intent.EXTRA_STREAM, mediaItem.uri);
composeIntent.setType(mediaItem.type);
startActivity(composeIntent);
MultiselectForwardFragmentArgs.create(
this,
mediaItem.uri,
mediaItem.type,
args -> MultiselectForwardFragment.showBottomSheet(getSupportFragmentManager(), args)
);
}
}
@@ -597,6 +608,15 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
finish();
}
@Override
public void onMediaReady() {
}
@Override
public @NonNull VoiceNoteMediaController getVoiceNoteMediaController() {
return voiceNoteMediaController;
}
private class ViewPagerListener extends ExtendedOnPageChangedListener {
@Override

View File

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

View File

@@ -25,7 +25,7 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery;
import org.thoughtcrime.securesms.conversation.ConversationIntents;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity;
@@ -33,11 +33,11 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.signal.core.util.concurrent.SimpleTask;
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
import java.util.Optional;
import java.util.function.Consumer;
/**
@@ -79,7 +79,7 @@ public class NewConversationActivity extends ContactSelectionActivity
if (!resolved.isRegistered() || !resolved.hasServiceId()) {
Log.i(TAG, "[onContactSelected] Not registered or no UUID. Doing a directory refresh.");
try {
DirectoryHelper.refreshDirectoryFor(this, resolved, false);
ContactDiscovery.refresh(this, resolved, false);
resolved = Recipient.resolved(resolved.getId());
} catch (IOException e) {
Log.w(TAG, "[onContactSelected] Failed to refresh directory for new contact.");

View File

@@ -61,7 +61,7 @@ public class PassphraseCreateActivity extends PassphraseActivity {
passphrase);
MasterSecretUtil.generateAsymmetricMasterSecret(PassphraseCreateActivity.this, masterSecret);
SignalStore.account().generateAciIdentityKey();
SignalStore.account().generateAciIdentityKeyIfNecessary();
SignalStore.account().generatePniIdentityKeyIfNecessary();
VersionTracker.updateLastSeenVersion(PassphraseCreateActivity.this);

View File

@@ -9,7 +9,9 @@ import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.util.CharacterCalculator;
import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Optional;
public class TransportOption implements Parcelable {
@@ -18,8 +20,8 @@ public class TransportOption implements Parcelable {
TEXTSECURE
}
private final int drawable;
private final int backgroundColor;
private final int drawable;
private final int backgroundColor;
private final @NonNull String text;
private final @NonNull Type type;
private final @NonNull String composeHint;
@@ -35,7 +37,7 @@ public class TransportOption implements Parcelable {
@NonNull CharacterCalculator characterCalculator)
{
this(type, drawable, backgroundColor, text, composeHint, characterCalculator,
Optional.<CharSequence>absent(), Optional.<Integer>absent());
Optional.empty(), Optional.empty());
}
public TransportOption(@NonNull Type type,
@@ -64,8 +66,8 @@ public class TransportOption implements Parcelable {
in.readString(),
in.readString(),
CharacterCalculator.readFromParcel(in),
Optional.fromNullable(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in)),
in.readInt() == 1 ? Optional.of(in.readInt()) : Optional.absent());
Optional.ofNullable(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in)),
in.readInt() == 1 ? Optional.of(in.readInt()) : Optional.empty());
}
public @NonNull Type getType() {
@@ -123,7 +125,7 @@ public class TransportOption implements Parcelable {
dest.writeString(text);
dest.writeString(composeHint);
CharacterCalculator.writeToParcel(dest, characterCalculator);
TextUtils.writeToParcel(simName.orNull(), dest, flags);
TextUtils.writeToParcel(simName.orElse(null), dest, flags);
if (simSubscriptionId.isPresent()) {
dest.writeInt(1);

View File

@@ -14,13 +14,14 @@ import org.thoughtcrime.securesms.util.PushCharacterCalculator;
import org.thoughtcrime.securesms.util.SmsCharacterCalculator;
import org.thoughtcrime.securesms.util.dualsim.SubscriptionInfoCompat;
import org.thoughtcrime.securesms.util.dualsim.SubscriptionManagerCompat;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.util.OptionalUtil;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import static org.thoughtcrime.securesms.TransportOption.Type;
@@ -33,8 +34,8 @@ public class TransportOptions {
private final List<TransportOption> enabledTransports;
private Type defaultTransportType = Type.SMS;
private Optional<Integer> defaultSubscriptionId = Optional.absent();
private Optional<TransportOption> selectedOption = Optional.absent();
private Optional<Integer> defaultSubscriptionId = Optional.empty();
private Optional<TransportOption> selectedOption = Optional.empty();
private final Optional<Integer> systemSubscriptionId;
@@ -54,7 +55,7 @@ public class TransportOptions {
setSelectedTransport(null);
} else {
this.defaultTransportType = Type.SMS;
this.defaultSubscriptionId = Optional.absent();
this.defaultSubscriptionId = Optional.empty();
notifyTransportChangeListeners();
}
@@ -81,7 +82,7 @@ public class TransportOptions {
}
public void setSelectedTransport(@Nullable TransportOption transportOption) {
this.selectedOption = Optional.fromNullable(transportOption);
this.selectedOption = Optional.ofNullable(transportOption);
notifyTransportChangeListeners();
}
@@ -93,7 +94,7 @@ public class TransportOptions {
if (selectedOption.isPresent()) return selectedOption.get();
if (defaultTransportType == Type.SMS) {
TransportOption transportOption = findEnabledSmsTransportOption(defaultSubscriptionId.or(systemSubscriptionId));
TransportOption transportOption = findEnabledSmsTransportOption(OptionalUtil.or(defaultSubscriptionId, systemSubscriptionId));
if (transportOption != null) {
return transportOption;
}
@@ -124,7 +125,7 @@ public class TransportOptions {
for (TransportOption transportOption : enabledTransports) {
if (transportOption.getType() == Type.SMS &&
subId == transportOption.getSimSubscriptionId().or(-1)) {
subId == transportOption.getSimSubscriptionId().orElse(-1)) {
return transportOption;
}
}
@@ -133,7 +134,7 @@ public class TransportOptions {
}
public void disableTransport(Type type) {
TransportOption selected = selectedOption.orNull();
TransportOption selected = selectedOption.orElse(null);
Iterator<TransportOption> iterator = enabledTransports.iterator();
while (iterator.hasNext()) {

View File

@@ -49,6 +49,7 @@ import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.protocol.IdentityKey;
import org.thoughtcrime.securesms.components.TooltipPopup;
import org.thoughtcrime.securesms.components.sensors.DeviceOrientationMonitor;
import org.thoughtcrime.securesms.components.webrtc.CallParticipantsListUpdatePopupWindow;
@@ -75,16 +76,19 @@ import org.thoughtcrime.securesms.util.FullscreenHelper;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ThrottledDebouncer;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.VibrateUtil;
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
import org.thoughtcrime.securesms.webrtc.CallParticipantsViewState;
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.signalservice.api.messages.calls.HangupMessage;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.Disposable;
import static org.thoughtcrime.securesms.components.sensors.Orientation.PORTRAIT_BOTTOM_EDGE;
public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChangeDialog.Callback {
@@ -92,6 +96,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
private static final String TAG = Log.tag(WebRtcCallActivity.class);
private static final int STANDARD_DELAY_FINISH = 1000;
private static final int VIBRATE_DURATION = 50;
public static final String ANSWER_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".ANSWER_ACTION";
public static final String DENY_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".DENY_ACTION";
@@ -111,6 +116,8 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
private WindowLayoutInfoConsumer windowLayoutInfoConsumer;
private ThrottledDebouncer requestNewSizesThrottle;
private Disposable ephemeralStateDisposable = Disposable.empty();
@Override
protected void attachBaseContext(@NonNull Context newBase) {
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);
@@ -153,6 +160,18 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
requestNewSizesThrottle = new ThrottledDebouncer(TimeUnit.SECONDS.toMillis(1));
}
@Override
protected void onStart() {
super.onStart();
ephemeralStateDisposable = ApplicationDependencies.getSignalCallManager()
.ephemeralStates()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(state -> {
viewModel.updateFromEphemeralState(state);
});
}
@Override
public void onResume() {
Log.i(TAG, "onResume()");
@@ -193,6 +212,8 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
Log.i(TAG, "onStop");
super.onStop();
ephemeralStateDisposable.dispose();
if (!isInPipMode() || isFinishing()) {
EventBus.getDefault().unregister(this);
requestNewSizesThrottle.clear();
@@ -295,7 +316,8 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
LiveDataUtil.combineLatest(viewModel.getCallParticipantsState(),
viewModel.getOrientationAndLandscapeEnabled(),
(s, o) -> new CallParticipantsViewState(s, o.first == PORTRAIT_BOTTOM_EDGE, o.second))
viewModel.getEphemeralState(),
(s, o, e) -> new CallParticipantsViewState(s, e, o.first == PORTRAIT_BOTTOM_EDGE, o.second))
.observe(this, p -> callScreen.updateCallParticipants(p));
viewModel.getCallParticipantListUpdate().observe(this, participantUpdateWindow::addCallParticipantListUpdate);
viewModel.getSafetyNumberChangeEvent().observe(this, this::handleSafetyNumberChangeEvent);
@@ -501,6 +523,11 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
}
}
private void handleCallReconnecting() {
callScreen.setStatus(getString(R.string.WebRtcCallActivity__reconnecting));
VibrateUtil.vibrate(this, VIBRATE_DURATION);
}
private void handleRecipientUnavailable() {
EventBus.getDefault().removeStickyEvent(WebRtcViewModel.class);
callScreen.setStatus(getString(R.string.RedPhone_recipient_unavailable));
@@ -623,6 +650,8 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
handleCallPreJoin(event); break;
case CALL_CONNECTED:
handleCallConnected(event); break;
case CALL_RECONNECTING:
handleCallReconnecting(); break;
case NETWORK_FAILURE:
handleServerFailure(); break;
case CALL_RINGING:
@@ -807,7 +836,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
if (feature.isPresent()) {
FoldingFeature foldingFeature = (FoldingFeature) feature.get();
Rect bounds = foldingFeature.getBounds();
if (foldingFeature.getState() == FoldingFeature.State.HALF_OPENED && bounds.top == bounds.bottom) {
if (foldingFeature.isSeparating()) {
Log.d(TAG, "OnWindowLayoutInfo accepted: ensure call view is in table-top display mode");
viewModel.setFoldableState(WebRtcControls.FoldableState.folded(bounds.top));
} else {

View File

@@ -0,0 +1,63 @@
package org.thoughtcrime.securesms.animation.transitions
import android.animation.Animator
import android.animation.ValueAnimator
import android.content.Context
import android.transition.Transition
import android.transition.TransitionValues
import android.util.AttributeSet
import android.view.ViewGroup
import androidx.annotation.RequiresApi
import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart
@RequiresApi(21)
class CrossfaderTransition(context: Context, attrs: AttributeSet?) : Transition(context, attrs) {
companion object {
private const val WIDTH = "CrossfaderTransition.WIDTH"
}
override fun captureStartValues(transitionValues: TransitionValues) {
if (transitionValues.view is Crossfadeable) {
transitionValues.values[WIDTH] = transitionValues.view.width
}
}
override fun captureEndValues(transitionValues: TransitionValues) {
if (transitionValues.view is Crossfadeable) {
transitionValues.values[WIDTH] = transitionValues.view.width
}
}
override fun createAnimator(sceneRoot: ViewGroup?, startValues: TransitionValues?, endValues: TransitionValues?): Animator? {
if (startValues == null || endValues == null) {
return null
}
val startWidth = (startValues.values[WIDTH] ?: 0) as Int
val endWidth = (endValues.values[WIDTH] ?: 0) as Int
val view: Crossfadeable = endValues.view as? Crossfadeable ?: return null
val reverse = startWidth > endWidth
return ValueAnimator.ofFloat(0f, 1f).apply {
addUpdateListener {
view.onCrossfadeAnimationUpdated(it.animatedValue as Float, reverse)
}
doOnStart {
view.onCrossfadeStarted(reverse)
}
doOnEnd {
view.onCrossfadeFinished(reverse)
}
}
}
interface Crossfadeable {
fun onCrossfadeAnimationUpdated(progress: Float, reverse: Boolean)
fun onCrossfadeStarted(reverse: Boolean)
fun onCrossfadeFinished(reverse: Boolean)
}
}

View File

@@ -0,0 +1,53 @@
package org.thoughtcrime.securesms.animation.transitions
import android.animation.Animator
import android.animation.ValueAnimator
import android.content.Context
import android.transition.Transition
import android.transition.TransitionValues
import android.util.AttributeSet
import android.view.ViewGroup
import androidx.annotation.RequiresApi
import com.google.android.material.floatingactionbutton.FloatingActionButton
@RequiresApi(21)
class FabElevationFadeTransform(context: Context, attrs: AttributeSet?) : Transition(context, attrs) {
companion object {
private const val ELEVATION = "CrossfaderTransition.ELEVATION"
}
override fun captureStartValues(transitionValues: TransitionValues) {
if (transitionValues.view is FloatingActionButton) {
transitionValues.values[ELEVATION] = transitionValues.view.elevation
}
}
override fun captureEndValues(transitionValues: TransitionValues) {
if (transitionValues.view is FloatingActionButton) {
transitionValues.values[ELEVATION] = transitionValues.view.elevation
}
}
override fun createAnimator(sceneRoot: ViewGroup?, startValues: TransitionValues?, endValues: TransitionValues?): Animator? {
if (startValues?.view !is FloatingActionButton || endValues?.view !is FloatingActionButton) {
return null
}
val startElevation = startValues.view.elevation
val endElevation = endValues.view.elevation
if (startElevation == endElevation) {
return null
}
return ValueAnimator.ofFloat(
startValues.values[ELEVATION] as Float,
endValues.values[ELEVATION] as Float
).apply {
addUpdateListener {
val elevation = it.animatedValue as Float
endValues.view.elevation = elevation
}
}
}
}

View File

@@ -9,12 +9,12 @@ import org.thoughtcrime.securesms.blurhash.BlurHash;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.stickers.StickerLocator;
import org.thoughtcrime.securesms.util.Base64;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
public class PointerAttachment extends Attachment {
@@ -93,7 +93,7 @@ public class PointerAttachment extends Attachment {
}
public static Optional<Attachment> forPointer(Optional<SignalServiceAttachment> pointer, @Nullable StickerLocator stickerLocator, @Nullable String fastPreflightId) {
if (!pointer.isPresent() || !pointer.get().isPointer()) return Optional.absent();
if (!pointer.isPresent() || !pointer.get().isPointer()) return Optional.empty();
String encodedKey = null;
@@ -103,12 +103,12 @@ public class PointerAttachment extends Attachment {
return Optional.of(new PointerAttachment(pointer.get().getContentType(),
AttachmentDatabase.TRANSFER_PROGRESS_PENDING,
pointer.get().asPointer().getSize().or(0),
pointer.get().asPointer().getFileName().orNull(),
pointer.get().asPointer().getSize().orElse(0),
pointer.get().asPointer().getFileName().orElse(null),
pointer.get().asPointer().getCdnNumber(),
pointer.get().asPointer().getRemoteId().toString(),
encodedKey, null,
pointer.get().asPointer().getDigest().orNull(),
pointer.get().asPointer().getDigest().orElse(null),
fastPreflightId,
pointer.get().asPointer().getVoiceNote(),
pointer.get().asPointer().isBorderless(),
@@ -116,9 +116,9 @@ public class PointerAttachment extends Attachment {
pointer.get().asPointer().getWidth(),
pointer.get().asPointer().getHeight(),
pointer.get().asPointer().getUploadTimestamp(),
pointer.get().asPointer().getCaption().orNull(),
pointer.get().asPointer().getCaption().orElse(null),
stickerLocator,
BlurHash.parseOrNull(pointer.get().asPointer().getBlurHash().orNull())));
BlurHash.parseOrNull(pointer.get().asPointer().getBlurHash().orElse(null))));
}
@@ -127,13 +127,13 @@ public class PointerAttachment extends Attachment {
return Optional.of(new PointerAttachment(pointer.getContentType(),
AttachmentDatabase.TRANSFER_PROGRESS_PENDING,
thumbnail != null ? thumbnail.asPointer().getSize().or(0) : 0,
thumbnail != null ? thumbnail.asPointer().getSize().orElse(0) : 0,
pointer.getFileName(),
thumbnail != null ? thumbnail.asPointer().getCdnNumber() : 0,
thumbnail != null ? thumbnail.asPointer().getRemoteId().toString() : "0",
thumbnail != null && thumbnail.asPointer().getKey() != null ? Base64.encodeBytes(thumbnail.asPointer().getKey()) : null,
null,
thumbnail != null ? thumbnail.asPointer().getDigest().orNull() : null,
thumbnail != null ? thumbnail.asPointer().getDigest().orElse(null) : null,
null,
false,
false,
@@ -141,7 +141,7 @@ public class PointerAttachment extends Attachment {
thumbnail != null ? thumbnail.asPointer().getWidth() : 0,
thumbnail != null ? thumbnail.asPointer().getHeight() : 0,
thumbnail != null ? thumbnail.asPointer().getUploadTimestamp() : 0,
thumbnail != null ? thumbnail.asPointer().getCaption().orNull() : null,
thumbnail != null ? thumbnail.asPointer().getCaption().orElse(null) : null,
null,
null));
}

View File

@@ -12,7 +12,6 @@ import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.components.voice.VoiceNoteDraft;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;

View File

@@ -14,10 +14,10 @@ import org.thoughtcrime.securesms.mms.PartAuthority
import org.thoughtcrime.securesms.profiles.AvatarHelper
import org.thoughtcrime.securesms.providers.BlobProvider
import org.thoughtcrime.securesms.util.MediaUtil
import org.whispersystems.libsignal.util.guava.Optional
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.util.Optional
import javax.annotation.meta.Exhaustive
/**
@@ -128,6 +128,6 @@ object AvatarRenderer {
}
private fun createMedia(uri: Uri, size: Long): Media {
return Media(uri, MediaUtil.IMAGE_JPEG, System.currentTimeMillis(), DIMENSIONS, DIMENSIONS, size, 0, false, false, Optional.absent(), Optional.absent(), Optional.absent())
return Media(uri, MediaUtil.IMAGE_JPEG, System.currentTimeMillis(), DIMENSIONS, DIMENSIONS, size, 0, false, false, Optional.empty(), Optional.empty(), Optional.empty())
}
}

View File

@@ -31,8 +31,8 @@ class TextAvatarDrawable(
}
override fun draw(canvas: Canvas) {
val textSize = Avatars.getTextSizeForLength(context, avatar.text, size * 0.8f, size * 0.45f)
val width = bounds.width()
val textSize = Avatars.getTextSizeForLength(context, avatar.text, width * 0.8f, width * 0.45f)
val candidates = EmojiProvider.getCandidates(avatar.text)
textPaint.textSize = textSize

View File

@@ -26,7 +26,10 @@ class AvatarView @JvmOverloads constructor(
isClickable = false
}
private val avatar: AvatarImageView = findViewById(R.id.avatar_image_view)
private val avatar: AvatarImageView = findViewById<AvatarImageView>(R.id.avatar_image_view).apply {
initialize(context, attrs)
}
private val storyRing: View = findViewById(R.id.avatar_story_ring)
private fun showStoryRing(hasUnreadStory: Boolean) {
@@ -37,8 +40,8 @@ class AvatarView @JvmOverloads constructor(
storyRing.visible = true
storyRing.isActivated = hasUnreadStory
avatar.scaleX = 0.82f
avatar.scaleY = 0.82f
avatar.scaleX = 0.8f
avatar.scaleY = 0.8f
}
private fun hideStoryRing() {
@@ -48,6 +51,10 @@ class AvatarView @JvmOverloads constructor(
avatar.scaleY = 1f
}
fun hasStory(): Boolean {
return storyRing.visible
}
fun setStoryRingFromState(storyViewState: StoryViewState) {
when (storyViewState) {
StoryViewState.NONE -> hideStoryRing()

View File

@@ -23,6 +23,8 @@ import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
@@ -110,7 +112,7 @@ public class BackupDialog {
@RequiresApi(29)
public static void showChooseBackupLocationDialog(@NonNull Fragment fragment, int requestCode) {
new AlertDialog.Builder(fragment.requireContext())
new MaterialAlertDialogBuilder(fragment.requireContext())
.setView(R.layout.backup_choose_location_dialog)
.setCancelable(true)
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {
@@ -141,7 +143,7 @@ public class BackupDialog {
}
public static void showDisableBackupDialog(@NonNull Context context, @NonNull Runnable onBackupsDisabled) {
new AlertDialog.Builder(context)
new MaterialAlertDialogBuilder(context)
.setTitle(R.string.BackupDialog_delete_backups)
.setMessage(R.string.BackupDialog_disable_and_delete_all_local_backups)
.setNegativeButton(android.R.string.cancel, null)

View File

@@ -5,8 +5,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.greenrobot.eventbus.EventBus;
import org.signal.core.util.logging.Log;
import org.whispersystems.libsignal.util.ByteUtil;
import org.signal.libsignal.protocol.util.ByteUtil;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

View File

@@ -18,10 +18,11 @@ import net.zetetic.database.sqlcipher.SQLiteDatabase;
import org.greenrobot.eventbus.EventBus;
import org.signal.core.util.Conversions;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.protocol.kdf.HKDFv3;
import org.signal.libsignal.protocol.util.ByteUtil;
import org.thoughtcrime.securesms.attachments.AttachmentId;
import org.thoughtcrime.securesms.crypto.AttachmentSecret;
import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.EmojiSearchDatabase;
@@ -47,13 +48,11 @@ import org.thoughtcrime.securesms.keyvalue.KeyValueDataSet;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.CursorUtil;
import org.thoughtcrime.securesms.util.SetUtil;
import org.signal.core.util.CursorUtil;
import org.signal.core.util.SetUtil;
import org.thoughtcrime.securesms.util.Stopwatch;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.kdf.HKDFv3;
import org.whispersystems.libsignal.util.ByteUtil;
import java.io.File;
import java.io.FileOutputStream;

View File

@@ -17,6 +17,8 @@ import org.greenrobot.eventbus.EventBus;
import org.signal.core.util.Conversions;
import org.signal.core.util.StreamUtil;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.protocol.kdf.HKDFv3;
import org.signal.libsignal.protocol.util.ByteUtil;
import org.thoughtcrime.securesms.backup.BackupProtos.Attachment;
import org.thoughtcrime.securesms.backup.BackupProtos.BackupFrame;
import org.thoughtcrime.securesms.backup.BackupProtos.DatabaseVersion;
@@ -36,9 +38,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.BackupUtil;
import org.thoughtcrime.securesms.util.SqlUtil;
import org.whispersystems.libsignal.kdf.HKDFv3;
import org.whispersystems.libsignal.util.ByteUtil;
import org.signal.core.util.SqlUtil;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -69,6 +69,18 @@ public class FullBackupImporter extends FullBackupBase {
@SuppressWarnings("unused")
private static final String TAG = Log.tag(FullBackupImporter.class);
private static final String[] TABLES_TO_DROP_FIRST = {
"distribution_list_member",
"distribution_list",
"message_send_log_recipients",
"msl_recipient",
"msl_message",
"reaction",
"notification_profile_schedule",
"notification_profile_allowed_members",
"story_sends"
};
public static void importFile(@NonNull Context context, @NonNull AttachmentSecret attachmentSecret,
@NonNull SQLiteDatabase db, @NonNull Uri uri, @NonNull String passphrase)
throws IOException
@@ -85,12 +97,12 @@ public class FullBackupImporter extends FullBackupBase {
int count = 0;
SQLiteDatabase keyValueDatabase = KeyValueDatabase.getInstance(ApplicationDependencies.getApplication()).getSqlCipherDatabase();
db.beginTransaction();
keyValueDatabase.beginTransaction();
try {
BackupRecordInputStream inputStream = new BackupRecordInputStream(is, passphrase);
db.beginTransaction();
keyValueDatabase.beginTransaction();
dropAllTables(db);
BackupFrame frame;
@@ -272,12 +284,17 @@ public class FullBackupImporter extends FullBackupBase {
}
private static void dropAllTables(@NonNull SQLiteDatabase db) {
for (String name : TABLES_TO_DROP_FIRST) {
db.execSQL("DROP TABLE IF EXISTS " + name);
}
try (Cursor cursor = db.rawQuery("SELECT name, type FROM sqlite_master", null)) {
while (cursor != null && cursor.moveToNext()) {
String name = cursor.getString(0);
String type = cursor.getString(1);
if ("table".equals(type) && !name.startsWith("sqlite_")) {
Log.i(TAG, "Dropping table: " + name);
db.execSQL("DROP TABLE IF EXISTS " + name);
}
}

View File

@@ -12,7 +12,6 @@ import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.ThemeUtil
import java.lang.IllegalArgumentException
class BadgeImageView @JvmOverloads constructor(
context: Context,

View File

@@ -9,13 +9,13 @@ import com.google.android.flexbox.FlexboxLayoutManager
import com.google.android.flexbox.JustifyContent
import org.signal.core.util.DimensionUnit
import org.signal.core.util.logging.Log
import org.signal.libsignal.protocol.util.Pair
import org.thoughtcrime.securesms.BuildConfig
import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.badges.models.Badge.Category.Companion.fromCode
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
import org.thoughtcrime.securesms.database.model.databaseprotos.BadgeList
import org.thoughtcrime.securesms.util.ScreenDensity
import org.whispersystems.libsignal.util.Pair
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile
import java.math.BigDecimal
import java.sql.Timestamp

View File

@@ -45,6 +45,7 @@ class CantProcessSubscriptionPaymentBottomSheetDialogFragment : DSLSettingsBotto
text = DSLSettingsText.from(R.string.CantProcessSubscriptionPaymentBottomSheetDialogFragment__dont_show_this_again)
) {
SignalStore.donationsValues().showCantProcessDialog = false
dismissAllowingStateLoss()
}
}
}

View File

@@ -41,9 +41,9 @@ class ExpiredBadgeBottomSheetDialogFragment : DSLSettingsBottomSheetFragment(
sectionHeaderPref(
DSLSettingsText.from(
if (badge.isBoost()) {
R.string.ExpiredBadgeBottomSheetDialogFragment__your_badge_has_expired
R.string.ExpiredBadgeBottomSheetDialogFragment__boost_badge_expired
} else {
R.string.ExpiredBadgeBottomSheetDialogFragment__subscription_cancelled
R.string.ExpiredBadgeBottomSheetDialogFragment__monthly_donation_cancelled
},
DSLSettingsText.CenterModifier
)
@@ -54,11 +54,11 @@ class ExpiredBadgeBottomSheetDialogFragment : DSLSettingsBottomSheetFragment(
noPadTextPref(
DSLSettingsText.from(
if (badge.isBoost()) {
getString(R.string.ExpiredBadgeBottomSheetDialogFragment__your_boost_badge_has_expired)
getString(R.string.ExpiredBadgeBottomSheetDialogFragment__your_boost_badge_has_expired_and)
} else if (inactive) {
getString(R.string.ExpiredBadgeBottomSheetDialogFragment__your_sustainer_subscription_was_automatically, badge.name)
getString(R.string.ExpiredBadgeBottomSheetDialogFragment__your_recurring_monthly_donation_was_automatically, badge.name)
} else {
getString(R.string.ExpiredBadgeBottomSheetDialogFragment__your_sustainer_subscription_was_canceled)
getString(R.string.ExpiredBadgeBottomSheetDialogFragment__your_recurring_monthly_donation_was_canceled)
},
DSLSettingsText.CenterModifier
)
@@ -72,7 +72,7 @@ class ExpiredBadgeBottomSheetDialogFragment : DSLSettingsBottomSheetFragment(
if (isLikelyASustainer) {
R.string.ExpiredBadgeBottomSheetDialogFragment__you_can_reactivate
} else {
R.string.ExpiredBadgeBottomSheetDialogFragment__to_continue_supporting_technology
R.string.ExpiredBadgeBottomSheetDialogFragment__you_can_keep
}
} else {
R.string.ExpiredBadgeBottomSheetDialogFragment__you_can

View File

@@ -17,7 +17,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.InternetConnectionObserver
import org.thoughtcrime.securesms.util.livedata.Store
import org.whispersystems.libsignal.util.guava.Optional
import java.util.Optional
private val TAG = Log.tag(BadgesOverviewViewModel::class.java)
@@ -54,13 +54,13 @@ class BadgesOverviewViewModel(
subscriptionsRepository.getSubscriptions()
) { active, all ->
if (!active.isActive && active.activeSubscription?.willCancelAtPeriodEnd() == true) {
Optional.fromNullable<String>(all.firstOrNull { it.level == active.activeSubscription?.level }?.badge?.id)
Optional.ofNullable<String>(all.firstOrNull { it.level == active.activeSubscription?.level }?.badge?.id)
} else {
Optional.absent()
Optional.empty()
}
}.subscribeBy(
onSuccess = { badgeId ->
store.update { it.copy(fadedBadgeId = badgeId.orNull()) }
store.update { it.copy(fadedBadgeId = badgeId.orElse(null)) }
},
onError = { throwable ->
Log.w(TAG, "Could not retrieve data from server", throwable)

View File

@@ -4,7 +4,6 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import org.thoughtcrime.securesms.badges.BadgeRepository
import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.recipients.Recipient

View File

@@ -24,8 +24,8 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Optional;
import java.util.function.Consumer;
public class BlockedUsersActivity extends PassphraseRequiredActivity implements BlockedUsersFragment.Listener, ContactSelectionListFragment.OnContactSelectedListener {
@@ -88,7 +88,7 @@ public class BlockedUsersActivity extends PassphraseRequiredActivity implements
@Override
public void onBeforeContactSelected(@NonNull Optional<RecipientId> recipientId, String number, @NonNull Consumer<Boolean> callback) {
final String displayName = recipientId.transform(id -> Recipient.resolved(id).getDisplayName(this)).or(number);
final String displayName = recipientId.map(id -> Recipient.resolved(id).getDisplayName(this)).orElse(number);
AlertDialog confirmationDialog = new MaterialAlertDialogBuilder(this)
.setTitle(R.string.BlockedUsersActivity__block_user)

View File

@@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.signalservice.api.util.OptionalUtil;
import java.util.Objects;
@@ -64,7 +65,9 @@ final class BlockedUsersAdapter extends ListAdapter<Recipient, BlockedUsersAdapt
displayName.setText(recipient.getDisplayName(itemView.getContext()));
if (recipient.hasAUserSetDisplayName(itemView.getContext())) {
String identifier = recipient.getE164().transform(PhoneNumberFormatter::prettyPrint).or(recipient.getUsername()).orNull();
String identifier = OptionalUtil.or(recipient.getE164().map(PhoneNumberFormatter::prettyPrint),
recipient.getUsername())
.orElse(null);
if (identifier != null) {
numberOrUsername.setText(identifier);

View File

@@ -95,7 +95,7 @@ public final class AvatarImageView extends AppCompatImageView {
initialize(context, attrs);
}
private void initialize(@NonNull Context context, @Nullable AttributeSet attrs) {
public void initialize(@NonNull Context context, @Nullable AttributeSet attrs) {
setScaleType(ScaleType.CENTER_CROP);
if (attrs != null) {

View File

@@ -37,7 +37,7 @@ import org.thoughtcrime.securesms.components.mention.MentionValidatorWatcher;
import org.thoughtcrime.securesms.database.model.Mention;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.StringUtil;
import org.signal.core.util.StringUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.util.List;

View File

@@ -41,10 +41,10 @@ import org.thoughtcrime.securesms.util.SignalLocalMetrics;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.dualsim.SubscriptionInfoCompat;
import org.thoughtcrime.securesms.util.dualsim.SubscriptionManagerCompat;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
public class ConversationItemFooter extends ConstraintLayout {

View File

@@ -11,15 +11,11 @@ import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.ContextCompat;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.badges.BadgeImageView;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.libsignal.util.Pair;
import java.util.LinkedList;
import java.util.List;
public class ConversationTypingView extends ConstraintLayout {

View File

@@ -7,11 +7,9 @@ import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.drawable.shapes.RoundRectShape;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class CornerMask {

View File

@@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.events.PartProgressEvent;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideClickListener;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.api.util.OptionalUtil;
public class DocumentView extends FrameLayout {
@@ -105,11 +106,11 @@ public class DocumentView extends FrameLayout {
this.documentSlide = documentSlide;
this.fileName.setText(documentSlide.getFileName()
.or(documentSlide.getCaption())
.or(getContext().getString(R.string.DocumentView_unnamed_file)));
this.fileName.setText(OptionalUtil.or(documentSlide.getFileName(),
documentSlide.getCaption())
.orElse(getContext().getString(R.string.DocumentView_unnamed_file)));
this.fileSize.setText(Util.getPrettyFileSize(documentSlide.getFileSize()));
this.document.setText(documentSlide.getFileType(getContext()).or("").toLowerCase());
this.document.setText(documentSlide.getFileType(getContext()).orElse("").toLowerCase());
this.setOnClickListener(new OpenClickedListener(documentSlide));
}

View File

@@ -1,9 +1,11 @@
package org.thoughtcrime.securesms.components
import android.app.Dialog
import android.graphics.Color
import android.os.Bundle
import android.view.ContextThemeWrapper
import android.view.View
import androidx.annotation.ColorInt
import androidx.annotation.StyleRes
import androidx.core.view.ViewCompat
import com.google.android.material.bottomsheet.BottomSheetBehavior
@@ -26,6 +28,9 @@ abstract class FixedRoundedCornerBottomSheetDialogFragment : BottomSheetDialogFr
@StyleRes
protected open val themeResId: Int = R.style.Widget_Signal_FixedRoundedCorners
@ColorInt
protected var backgroundColor: Int = Color.TRANSPARENT
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NORMAL, themeResId)
@@ -44,7 +49,8 @@ abstract class FixedRoundedCornerBottomSheetDialogFragment : BottomSheetDialogFr
val dialogBackground = MaterialShapeDrawable(shapeAppearanceModel)
val bottomSheetStyle = ThemeUtil.getThemedResourceId(ContextThemeWrapper(requireContext(), themeResId), R.attr.bottomSheetStyle)
dialogBackground.setTint(ThemeUtil.getThemedColor(ContextThemeWrapper(requireContext(), bottomSheetStyle), R.attr.backgroundTint))
backgroundColor = ThemeUtil.getThemedColor(ContextThemeWrapper(requireContext(), bottomSheetStyle), R.attr.backgroundTint)
dialogBackground.setTint(backgroundColor)
dialog.behavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {

View File

@@ -62,7 +62,7 @@ public class FromTextView extends SimpleEmojiTextView {
builder.append(suffix);
}
if (recipient.isReleaseNotes()) {
if (recipient.showVerified()) {
Drawable official = ContextUtil.requireDrawable(getContext(), R.drawable.ic_official_20);
official.setBounds(0, 0, ViewUtil.dpToPx(20), ViewUtil.dpToPx(20));

View File

@@ -13,7 +13,6 @@ import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.DialogFragment;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.ThemeUtil;
/**
* Base dialog fragment for rendering as a full screen dialog with animation

View File

@@ -53,11 +53,11 @@ import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
public class InputPanel extends LinearLayout
@@ -181,13 +181,19 @@ public class InputPanel extends LinearLayout
@NonNull CharSequence body,
@NonNull SlideDeck attachments)
{
this.quoteView.setQuote(glideRequests, id, author, body, false, attachments, null);
this.quoteView.setQuote(glideRequests, id, author, body, false, attachments, null, null);
int originalHeight = this.quoteView.getVisibility() == VISIBLE ? this.quoteView.getMeasuredHeight()
: 0;
this.quoteView.setVisibility(VISIBLE);
this.quoteView.measure(0, 0);
int maxWidth = composeContainer.getWidth();
if (quoteView.getLayoutParams() instanceof MarginLayoutParams) {
MarginLayoutParams layoutParams = (MarginLayoutParams) quoteView.getLayoutParams();
maxWidth -= layoutParams.leftMargin + layoutParams.rightMargin;
}
this.quoteView.measure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST), 0);
if (quoteAnimator != null) {
quoteAnimator.cancel();
@@ -252,7 +258,7 @@ public class InputPanel extends LinearLayout
if (quoteView.getQuoteId() > 0 && quoteView.getVisibility() == View.VISIBLE) {
return Optional.of(new QuoteModel(quoteView.getQuoteId(), quoteView.getAuthor().getId(), quoteView.getBody().toString(), false, quoteView.getAttachments(), quoteView.getMentions()));
} else {
return Optional.absent();
return Optional.empty();
}
}

View File

@@ -13,7 +13,6 @@ import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Guideline;
import org.signal.glide.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.ViewUtil;

View File

@@ -35,7 +35,7 @@ abstract class KeyboardEntryDialogFragment(@LayoutRes contentLayoutId: Int) :
dialog.window?.setDimAmount(0f)
}
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE or WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
return dialog
}

View File

@@ -22,13 +22,13 @@ import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.ImageSlide;
import org.thoughtcrime.securesms.mms.SlidesClickedListener;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Locale;
import okhttp3.HttpUrl;
import org.thoughtcrime.securesms.util.ViewUtil;
/**
* The view shown in the compose box or conversation that represents the state of the link preview.

View File

@@ -19,7 +19,6 @@ import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.permissions.Permissions;
@@ -101,7 +100,7 @@ public final class MicrophoneRecorderView extends FrameLayout implements View.On
case MotionEvent.ACTION_DOWN:
if (!Permissions.hasAll(getContext(), Manifest.permission.RECORD_AUDIO)) {
if (listener != null) listener.onRecordPermissionRequired();
} else {
} else if (state == State.NOT_RUNNING) {
state = State.RUNNING_HELD;
floatingRecordButton.display(event.getX(), event.getY());
lockDropTarget.display();

View File

@@ -1,174 +0,0 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.RelativeLayout;
import androidx.annotation.NonNull;
import com.annimon.stream.Stream;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.RecipientsAdapter;
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
/**
* Panel component combining both an editable field with a button for
* a list-based contact selector.
*
* @author Moxie Marlinspike
*/
public class PushRecipientsPanel extends RelativeLayout implements RecipientForeverObserver {
private final String TAG = Log.tag(PushRecipientsPanel.class);
private RecipientsPanelChangedListener panelChangeListener;
private RecipientsEditor recipientsText;
private View panel;
private static final int RECIPIENTS_MAX_LENGTH = 312;
public PushRecipientsPanel(Context context) {
super(context);
initialize();
}
public PushRecipientsPanel(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public PushRecipientsPanel(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Stream.of(getRecipients()).map(Recipient::live).forEach(r -> r.removeForeverObserver(this));
}
public List<Recipient> getRecipients() {
String rawText = recipientsText.getText().toString();
return getRecipientsFromString(getContext(), rawText);
}
public void disable() {
recipientsText.setText("");
panel.setVisibility(View.GONE);
}
public void setPanelChangeListener(RecipientsPanelChangedListener panelChangeListener) {
this.panelChangeListener = panelChangeListener;
}
private void initialize() {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.push_recipients_panel, this, true);
View imageButton = findViewById(R.id.contacts_button);
((MarginLayoutParams) imageButton.getLayoutParams()).topMargin = 0;
panel = findViewById(R.id.recipients_panel);
initRecipientsEditor();
}
private void initRecipientsEditor() {
this.recipientsText = (RecipientsEditor)findViewById(R.id.recipients_text);
List<Recipient> recipients = getRecipients();
Stream.of(recipients).map(Recipient::live).forEach(r -> r.observeForever(this));
recipientsText.setAdapter(new RecipientsAdapter(this.getContext()));
recipientsText.populate(recipients);
recipientsText.setOnFocusChangeListener(new FocusChangedListener());
recipientsText.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
if (panelChangeListener != null) {
panelChangeListener.onRecipientsPanelUpdate(getRecipients());
}
recipientsText.setText("");
}
});
}
private @NonNull List<Recipient> getRecipientsFromString(Context context, @NonNull String rawText) {
StringTokenizer tokenizer = new StringTokenizer(rawText, ",");
List<Recipient> recipients = new LinkedList<>();
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken().trim();
if (!TextUtils.isEmpty(token)) {
if (hasBracketedNumber(token)) recipients.add(Recipient.external(context, parseBracketedNumber(token)));
else recipients.add(Recipient.external(context, token));
}
}
return recipients;
}
private boolean hasBracketedNumber(String recipient) {
int openBracketIndex = recipient.indexOf('<');
return (openBracketIndex != -1) &&
(recipient.indexOf('>', openBracketIndex) != -1);
}
private String parseBracketedNumber(String recipient) {
int begin = recipient.indexOf('<');
int end = recipient.indexOf('>', begin);
String value = recipient.substring(begin + 1, end);
return value;
}
@Override
public void onRecipientChanged(@NonNull Recipient recipient) {
recipientsText.populate(getRecipients());
}
private class FocusChangedListener implements View.OnFocusChangeListener {
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus && (panelChangeListener != null)) {
panelChangeListener.onRecipientsPanelUpdate(getRecipients());
}
}
}
public interface RecipientsPanelChangedListener {
public void onRecipientsPanelUpdate(List<Recipient> recipients);
}
}

View File

@@ -24,6 +24,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.components.emoji.EmojiImageView;
import org.thoughtcrime.securesms.components.mention.MentionAnnotation;
import org.thoughtcrime.securesms.conversation.colors.ChatColors;
import org.thoughtcrime.securesms.database.model.Mention;
@@ -34,10 +35,13 @@ import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import org.thoughtcrime.securesms.stories.StoryTextPostModel;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.Projection;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.Util;
import java.io.IOException;
import java.util.List;
public class QuoteView extends FrameLayout implements RecipientForeverObserver {
@@ -49,7 +53,9 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
PREVIEW(0),
OUTGOING(1),
INCOMING(2),
STORY_REPLY(3);
STORY_REPLY_OUTGOING(3),
STORY_REPLY_INCOMING(4),
STORY_REPLY_PREVIEW(5);
private final int code;
@@ -68,16 +74,18 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
}
}
private ViewGroup mainView;
private ViewGroup footerView;
private TextView authorView;
private TextView bodyView;
private View quoteBarView;
private ImageView thumbnailView;
private View attachmentVideoOverlayView;
private ViewGroup attachmentContainerView;
private TextView attachmentNameView;
private ImageView dismissView;
private ViewGroup mainView;
private ViewGroup footerView;
private TextView authorView;
private TextView bodyView;
private View quoteBarView;
private ImageView thumbnailView;
private View attachmentVideoOverlayView;
private ViewGroup attachmentContainerView;
private TextView attachmentNameView;
private ImageView dismissView;
private EmojiImageView missingStoryReaction;
private EmojiImageView storyReactionEmoji;
private long id;
private LiveRecipient author;
@@ -129,6 +137,8 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
this.dismissView = findViewById(R.id.quote_dismiss);
this.mediaDescriptionText = findViewById(R.id.media_type);
this.missingLinkText = findViewById(R.id.quote_missing_text);
this.missingStoryReaction = findViewById(R.id.quote_missing_story_reaction_emoji);
this.storyReactionEmoji = findViewById(R.id.quote_story_reaction_emoji);
this.largeCornerRadius = getResources().getDimensionPixelSize(R.dimen.quote_corner_radius_large);
this.smallCornerRadius = getResources().getDimensionPixelSize(R.dimen.quote_corner_radius_bottom);
@@ -177,13 +187,11 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
int radius = getResources().getDimensionPixelOffset(R.dimen.quote_corner_radius_preview);
cornerMask.setTopLeftRadius(radius);
cornerMask.setTopRightRadius(radius);
} else if (messageType == MessageType.STORY_REPLY) {
} else if (isStoryReply()) {
thumbWidth = getResources().getDimensionPixelOffset(R.dimen.quote_story_thumb_width);
thumbHeight = getResources().getDimensionPixelOffset(R.dimen.quote_story_thumb_height);
}
mainView.setMinimumHeight(thumbHeight);
ViewGroup.LayoutParams params = thumbnailView.getLayoutParams();
params.height = thumbHeight;
params.width = thumbWidth;
@@ -197,7 +205,8 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
@Nullable CharSequence body,
boolean originalMissing,
@NonNull SlideDeck attachments,
@Nullable ChatColors chatColors)
@Nullable ChatColors chatColors,
@Nullable String storyReaction)
{
if (this.author != null) this.author.removeForeverObserver(this);
@@ -208,8 +217,8 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
this.author.observeForever(this);
setQuoteAuthor(author);
setQuoteText(body, attachments);
setQuoteAttachment(glideRequests, attachments);
setQuoteText(body, attachments, originalMissing, storyReaction);
setQuoteAttachment(glideRequests, body, attachments, originalMissing);
setQuoteMissingFooter(originalMissing);
if (Build.VERSION.SDK_INT < 21 && messageType == MessageType.INCOMING && chatColors != null) {
@@ -248,10 +257,10 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
}
private void setQuoteAuthor(@NonNull Recipient author) {
boolean outgoing = messageType != MessageType.INCOMING;
boolean preview = messageType == MessageType.PREVIEW || messageType == MessageType.STORY_REPLY;
boolean outgoing = messageType != MessageType.INCOMING && messageType != MessageType.STORY_REPLY_INCOMING;
boolean preview = messageType == MessageType.PREVIEW || messageType == MessageType.STORY_REPLY_PREVIEW;
if (messageType == MessageType.STORY_REPLY) {
if (isStoryReply()) {
authorView.setText(author.isSelf() ? getContext().getString(R.string.QuoteView_your_story)
: getContext().getString(R.string.QuoteView_s_story, author.getDisplayName(getContext())));
} else {
@@ -259,14 +268,70 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
: author.getDisplayName(getContext()));
}
quoteBarView.setBackgroundColor(ContextCompat.getColor(getContext(), outgoing ? R.color.core_white : android.R.color.transparent));
mainView.setBackgroundColor(ContextCompat.getColor(getContext(), preview ? R.color.quote_preview_background : R.color.quote_view_background));
quoteBarView.setBackgroundColor(ContextCompat.getColor(getContext(), outgoing || isStoryReply() ? R.color.core_white : android.R.color.transparent));
int mainViewColor;
if (preview) {
mainViewColor = R.color.quote_preview_background;
} else if (!outgoing && isStoryReply()) {
mainViewColor = R.color.quote_incoming_story_background;
} else {
mainViewColor = R.color.quote_view_background;
}
mainView.setBackgroundColor(ContextCompat.getColor(getContext(), mainViewColor));
}
private void setQuoteText(@Nullable CharSequence body, @NonNull SlideDeck attachments) {
private boolean isStoryReply() {
return messageType == MessageType.STORY_REPLY_OUTGOING ||
messageType == MessageType.STORY_REPLY_INCOMING ||
messageType == MessageType.STORY_REPLY_PREVIEW;
}
private void setQuoteText(@Nullable CharSequence body,
@NonNull SlideDeck attachments,
boolean originalMissing,
@Nullable String storyReaction)
{
if (originalMissing && isStoryReply()) {
bodyView.setVisibility(GONE);
storyReactionEmoji.setVisibility(View.GONE);
mediaDescriptionText.setVisibility(VISIBLE);
mediaDescriptionText.setText(R.string.QuoteView_no_longer_available);
if (storyReaction != null) {
missingStoryReaction.setVisibility(View.VISIBLE);
missingStoryReaction.setImageEmoji(body);
} else {
missingStoryReaction.setVisibility(View.GONE);
}
return;
}
if (storyReaction != null) {
storyReactionEmoji.setImageEmoji(storyReaction);
storyReactionEmoji.setVisibility(View.VISIBLE);
missingStoryReaction.setVisibility(View.INVISIBLE);
} else {
storyReactionEmoji.setVisibility(View.GONE);
missingStoryReaction.setVisibility(View.GONE);
}
boolean isTextStory = !attachments.containsMediaSlide() && isStoryReply();
if (!TextUtils.isEmpty(body) || !attachments.containsMediaSlide()) {
if (isTextStory && body != null) {
try {
bodyView.setText(getStoryTextPost(body).getText());
} catch (Exception e) {
Log.w(TAG, "Could not parse body of text post.", e);
bodyView.setText("");
}
} else {
bodyView.setText(body == null ? "" : body);
}
bodyView.setVisibility(VISIBLE);
bodyView.setText(body == null ? "" : body);
mediaDescriptionText.setVisibility(GONE);
return;
}
@@ -305,7 +370,22 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
}
}
private void setQuoteAttachment(@NonNull GlideRequests glideRequests, @NonNull SlideDeck slideDeck) {
private void setQuoteAttachment(@NonNull GlideRequests glideRequests, @NonNull CharSequence body, @NonNull SlideDeck slideDeck, boolean originalMissing) {
mainView.setMinimumHeight(isStoryReply() && originalMissing ? 0 : thumbHeight);
if (!attachments.containsMediaSlide() && isStoryReply()) {
StoryTextPostModel model = getStoryTextPost(body);
attachmentVideoOverlayView.setVisibility(GONE);
attachmentContainerView.setVisibility(GONE);
thumbnailView.setVisibility(VISIBLE);
glideRequests.load(model)
.centerCrop()
.override(thumbWidth, thumbHeight)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.into(thumbnailView);
return;
}
Slide imageVideoSlide = slideDeck.getSlides().stream().filter(s -> s.hasImage() || s.hasVideo() || s.hasSticker()).findFirst().orElse(null);
Slide documentSlide = slideDeck.getSlides().stream().filter(Slide::hasDocument).findFirst().orElse(null);
Slide viewOnceSlide = slideDeck.getSlides().stream().filter(Slide::hasViewOnce).findFirst().orElse(null);
@@ -330,7 +410,7 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
} else if (documentSlide != null){
thumbnailView.setVisibility(GONE);
attachmentContainerView.setVisibility(VISIBLE);
attachmentNameView.setText(documentSlide.getFileName().or(""));
attachmentNameView.setText(documentSlide.getFileName().orElse(""));
} else {
thumbnailView.setVisibility(GONE);
attachmentContainerView.setVisibility(GONE);
@@ -343,10 +423,22 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
}
private void setQuoteMissingFooter(boolean missing) {
footerView.setVisibility(missing ? VISIBLE : GONE);
footerView.setVisibility(missing && !isStoryReply() ? VISIBLE : GONE);
footerView.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.quote_view_background));
}
private @Nullable StoryTextPostModel getStoryTextPost(@Nullable CharSequence body) {
if (Util.isEmpty(body)) {
return null;
}
try {
return StoryTextPostModel.parseFrom(body.toString(), id, author.getId());
} catch (IOException ioException) {
return null;
}
}
public void setTextSize(int unit, float size) {
bodyView.setTextSize(unit, size);
}

View File

@@ -1,11 +1,7 @@
package org.thoughtcrime.securesms.components;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;

View File

@@ -58,6 +58,7 @@ public class SearchToolbar extends LinearLayout {
EditText searchText = searchView.findViewById(R.id.search_src_text);
searchView.setSubmitButtonEnabled(false);
searchView.setMaxWidth(Integer.MAX_VALUE);
if (searchText != null) searchText.setHint(R.string.SearchToolbar_search);
else searchView.setQueryHint(getResources().getString(R.string.SearchToolbar_search));
@@ -65,7 +66,9 @@ public class SearchToolbar extends LinearLayout {
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
if (listener != null) listener.onSearchTextChange(query);
if (listener != null) {
listener.onSearchTextChange(query);
}
return true;
}

View File

@@ -12,7 +12,9 @@ import org.thoughtcrime.securesms.TransportOptions;
import org.thoughtcrime.securesms.TransportOptions.OnTransportChangedListener;
import org.thoughtcrime.securesms.TransportOptionsPopup;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Optional;
public class SendButton extends AppCompatImageButton
implements TransportOptions.OnTransportChangedListener,
@@ -22,7 +24,7 @@ public class SendButton extends AppCompatImageButton
private final TransportOptions transportOptions;
private Optional<TransportOptionsPopup> transportOptionsPopup = Optional.absent();
private Optional<TransportOptionsPopup> transportOptionsPopup = Optional.empty();
@SuppressWarnings("unused")
public SendButton(Context context) {

View File

@@ -34,16 +34,17 @@ import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideClickListener;
import org.thoughtcrime.securesms.mms.SlidesClickedListener;
import org.thoughtcrime.securesms.stories.StoryTextPostModel;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Collections;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
@@ -68,7 +69,7 @@ public class ThumbnailView extends FrameLayout {
private final int[] bounds = new int[4];
private final int[] measureDimens = new int[2];
private Optional<TransferControlView> transferControls = Optional.absent();
private Optional<TransferControlView> transferControls = Optional.empty();
private SlideClickListener thumbnailClickListener = null;
private SlidesClickedListener downloadClickListener = null;
private Slide slide = null;
@@ -391,6 +392,32 @@ public class ThumbnailView extends FrameLayout {
return future;
}
public ListenableFuture<Boolean> setImageResource(@NonNull GlideRequests glideRequests, @NonNull StoryTextPostModel model, int width, int height) {
SettableFuture<Boolean> future = new SettableFuture<>();
if (transferControls.isPresent()) getTransferControls().setVisibility(View.GONE);
GlideRequest request = glideRequests.load(model)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.placeholder(model.getPlaceholder())
.transition(withCrossFade());
if (width > 0 && height > 0) {
request = request.override(width, height);
}
if (radius > 0) {
request = request.transforms(new CenterCrop(), new RoundedCorners(radius));
} else {
request = request.transforms(new CenterCrop());
}
request.into(new GlideDrawableListeningTarget(image, future));
blurhash.setImageDrawable(null);
return future;
}
public void setThumbnailClickListener(SlideClickListener listener) {
this.thumbnailClickListener = listener;
}
@@ -401,11 +428,15 @@ public class ThumbnailView extends FrameLayout {
public void clear(GlideRequests glideRequests) {
glideRequests.clear(image);
image.setImageDrawable(null);
if (transferControls.isPresent()) {
getTransferControls().clear();
}
glideRequests.clear(blurhash);
blurhash.setImageDrawable(null);
slide = null;
}

View File

@@ -2,15 +2,20 @@ package org.thoughtcrime.securesms.components;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.exifinterface.media.ExifInterface;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import com.davemorrissey.labs.subscaleview.ImageSource;
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
@@ -24,11 +29,12 @@ import org.thoughtcrime.securesms.components.subsampling.AttachmentRegionDecoder
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.util.ActionRequestListener;
import org.thoughtcrime.securesms.util.BitmapDecodingException;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.signal.core.util.concurrent.SimpleTask;
import java.io.IOException;
import java.io.InputStream;
@@ -79,7 +85,7 @@ public class ZoomingImageView extends FrameLayout {
}
@SuppressLint("StaticFieldLeak")
public void setImageUri(@NonNull GlideRequests glideRequests, @NonNull Uri uri, @NonNull String contentType)
public void setImageUri(@NonNull GlideRequests glideRequests, @NonNull Uri uri, @NonNull String contentType, @NonNull Runnable onMediaReady)
{
final Context context = getContext();
final int maxTextureSize = BitmapUtil.getMaxTextureSize();
@@ -101,15 +107,16 @@ public class ZoomingImageView extends FrameLayout {
if (dimensions == null || (dimensions.first <= maxTextureSize && dimensions.second <= maxTextureSize)) {
Log.i(TAG, "Loading in standard image view...");
setImageViewUri(glideRequests, uri);
setImageViewUri(glideRequests, uri, onMediaReady);
} else {
Log.i(TAG, "Loading in subsampling image view...");
setSubsamplingImageViewUri(uri);
onMediaReady.run();
}
});
}
private void setImageViewUri(@NonNull GlideRequests glideRequests, @NonNull Uri uri) {
private void setImageViewUri(@NonNull GlideRequests glideRequests, @NonNull Uri uri, @NonNull Runnable onMediaReady) {
photoView.setVisibility(View.VISIBLE);
subsamplingImageView.setVisibility(View.GONE);
@@ -117,6 +124,7 @@ public class ZoomingImageView extends FrameLayout {
.diskCacheStrategy(DiskCacheStrategy.NONE)
.dontTransform()
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.addListener(ActionRequestListener.onEither(onMediaReady))
.into(photoView);
}

View File

@@ -42,12 +42,12 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
@SuppressWarnings("deprecation")
public class CameraView extends ViewGroup {
@@ -56,8 +56,8 @@ public class CameraView extends ViewGroup {
private final CameraSurfaceView surface;
private final OnOrientationChange onOrientationChange;
private volatile Optional<Camera> camera = Optional.absent();
private volatile int cameraId = CameraInfo.CAMERA_FACING_BACK;
private volatile Optional<Camera> camera = Optional.empty();
private volatile int cameraId = CameraInfo.CAMERA_FACING_BACK;
private volatile int displayOrientation = -1;
private @NonNull State state = State.PAUSED;
@@ -104,7 +104,7 @@ public class CameraView extends ViewGroup {
Void onRunBackground() {
try {
long openStartMillis = System.currentTimeMillis();
camera = Optional.fromNullable(Camera.open(cameraId));
camera = Optional.ofNullable(Camera.open(cameraId));
Log.i(TAG, "camera.open() -> " + (System.currentTimeMillis() - openStartMillis) + "ms");
synchronized (CameraView.this) {
CameraView.this.notifyAll();
@@ -145,7 +145,7 @@ public class CameraView extends ViewGroup {
@Override
protected void onPreMain() {
cameraToDestroy = camera;
camera = Optional.absent();
camera = Optional.empty();
}
@Override

View File

@@ -14,7 +14,6 @@ import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
public class EmojiEditText extends AppCompatEditText {

View File

@@ -21,8 +21,8 @@ import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.Emoj
import org.thoughtcrime.securesms.components.emoji.EmojiPageViewGridAdapter.VariationSelectorListener;
import org.thoughtcrime.securesms.util.ContextUtil;
import org.thoughtcrime.securesms.util.DrawableUtil;
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel;
import java.util.List;
import java.util.Optional;

View File

@@ -10,6 +10,7 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -19,7 +20,6 @@ import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.components.emoji.parsing.EmojiDrawInfo;
import org.thoughtcrime.securesms.components.emoji.parsing.EmojiParser;
import org.thoughtcrime.securesms.emoji.EmojiFiles;
import org.thoughtcrime.securesms.emoji.EmojiPageCache;
import org.thoughtcrime.securesms.emoji.EmojiSource;
import org.thoughtcrime.securesms.emoji.JumboEmoji;
@@ -101,6 +101,10 @@ public class EmojiProvider {
}
static @Nullable Drawable getEmojiDrawable(@NonNull Context context, @Nullable CharSequence emoji, boolean jumboEmoji) {
if (TextUtils.isEmpty(emoji)) {
return null;
}
EmojiDrawInfo drawInfo = EmojiSource.getLatest().getEmojiTree().getEmoji(emoji, 0, emoji.length());
return getEmojiDrawable(context, drawInfo, null, jumboEmoji);
}

View File

@@ -33,9 +33,10 @@ import org.thoughtcrime.securesms.components.mention.MentionRendererDelegate;
import org.thoughtcrime.securesms.emoji.JumboEmoji;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import kotlin.Unit;
@@ -141,7 +142,7 @@ public class EmojiTextView extends AppCompatTextView {
previousTransformationMethod = getTransformationMethod();
if (useSystemEmoji || candidates == null || candidates.size() == 0) {
super.setText(new SpannableStringBuilder(Optional.fromNullable(text).or("")), BufferType.SPANNABLE);
super.setText(new SpannableStringBuilder(Optional.ofNullable(text).orElse("")), BufferType.SPANNABLE);
} else {
CharSequence emojified = EmojiProvider.emojify(candidates, text, this, isJumbomoji || forceJumboEmoji);
super.setText(new SpannableStringBuilder(emojified), BufferType.SPANNABLE);
@@ -149,7 +150,7 @@ public class EmojiTextView extends AppCompatTextView {
// Android fails to ellipsize spannable strings. (https://issuetracker.google.com/issues/36991688)
// We ellipsize them ourselves by manually truncating the appropriate section.
if (getText() != null && getText().length() > 0 && getEllipsize() == TextUtils.TruncateAt.END) {
if (getText() != null && getText().length() > 0 && isEllipsizedAtEnd()) {
if (maxLength > 0) {
ellipsizeAnyTextForMaxLength();
} else if (getMaxLines() > 0) {
@@ -162,6 +163,17 @@ public class EmojiTextView extends AppCompatTextView {
}
}
/**
* Used to determine whether to apply custom ellipsizing logic without necessarily having the
* ellipsize property set. This allows us to work around implementations of Layout which apply an
* ellipsis even when maxLines is not set.
*/
private boolean isEllipsizedAtEnd() {
return getEllipsize() == TextUtils.TruncateAt.END ||
(getMaxLines() > 0 && getMaxLines() < Integer.MAX_VALUE) ||
maxLength > 0;
}
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
widthMeasureSpec = applyWidthMeasureRoundingFix(widthMeasureSpec);
@@ -201,7 +213,7 @@ public class EmojiTextView extends AppCompatTextView {
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
float measuredTextWidth = hasMetricAffectingSpan(text) ? Layout.getDesiredWidth(text, getPaint()) : getPaint().measureText(text, 0, text.length());
float measuredTextWidth = hasMetricAffectingSpan(text) ? Layout.getDesiredWidth(text, getPaint()) : getLongestLineWidth(text);
int desiredWidth = (int) measuredTextWidth + getPaddingLeft() + getPaddingRight();
if (widthSpecMode == MeasureSpec.AT_MOST && desiredWidth < widthSpecSize) {
@@ -221,6 +233,20 @@ public class EmojiTextView extends AppCompatTextView {
return ((Spanned) text).nextSpanTransition(-1, text.length(), CharacterStyle.class) != text.length();
}
private float getLongestLineWidth(@NonNull CharSequence text) {
if (TextUtils.isEmpty(text)) {
return 0f;
}
long maxLines = getMaxLines() > 0 ? getMaxLines() : Long.MAX_VALUE;
return Arrays.stream(text.toString().split("\n"))
.limit(maxLines)
.map(s -> getPaint().measureText(s, 0, s.length()))
.max(Float::compare)
.orElse(0f);
}
public int getLastLineWidth() {
return lastLineWidth;
}
@@ -291,7 +317,7 @@ public class EmojiTextView extends AppCompatTextView {
SpannableStringBuilder newContent = new SpannableStringBuilder();
newContent.append(getText().subSequence(0, overflowStart))
.append(ellipsized.subSequence(0, ellipsized.length()))
.append(Optional.fromNullable(overflowText).or(""));
.append(Optional.ofNullable(overflowText).orElse(""));
EmojiParser.CandidateList newCandidates = isInEditMode() ? null : EmojiProvider.getCandidates(newContent);
CharSequence emojified = EmojiProvider.emojify(newCandidates, newContent, this, isJumbomoji || forceJumboEmoji);

View File

@@ -1,10 +1,12 @@
package org.thoughtcrime.securesms.components.emoji;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageButton;
import org.thoughtcrime.securesms.R;
@@ -24,17 +26,17 @@ public class EmojiToggle extends AppCompatImageButton implements MediaKeyboard.M
public EmojiToggle(Context context) {
super(context);
initialize();
initialize(null);
}
public EmojiToggle(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
initialize(attrs);
}
public EmojiToggle(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize();
initialize(attrs);
}
public void setToMedia() {
@@ -45,11 +47,18 @@ public class EmojiToggle extends AppCompatImageButton implements MediaKeyboard.M
setImageDrawable(imeToggle);
}
private void initialize() {
this.emojiToggle = ContextUtil.requireDrawable(getContext(), R.drawable.ic_emoji);
private void initialize(@Nullable AttributeSet attrs) {
boolean forceOutline = false;
if (attrs != null) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.EmojiToggle, 0, 0);
forceOutline = typedArray.getBoolean(R.styleable.EmojiToggle_force_outline, false);
typedArray.recycle();
}
this.emojiToggle = ContextUtil.requireDrawable(getContext(), forceOutline ? R.drawable.ic_emoji_outline : R.drawable.ic_emoji);
this.stickerToggle = ContextUtil.requireDrawable(getContext(), R.drawable.ic_sticker_24);
this.gifToggle = ContextUtil.requireDrawable(getContext(), R.drawable.ic_gif_24);
this.imeToggle = ContextUtil.requireDrawable(getContext(), R.drawable.ic_keyboard_24);
this.imeToggle = ContextUtil.requireDrawable(getContext(), forceOutline ? R.drawable.ic_keyboard_outline_24 : R.drawable.ic_keyboard_24);
this.mediaToggle = emojiToggle;
setToMedia();

View File

@@ -9,7 +9,7 @@ import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.components.emoji.parsing.EmojiParser;
import org.thoughtcrime.securesms.emoji.EmojiSource;
import org.thoughtcrime.securesms.emoji.ObsoleteEmoji;
import org.thoughtcrime.securesms.util.StringUtil;
import org.signal.core.util.StringUtil;
import org.thoughtcrime.securesms.util.Util;
import java.util.HashSet;
@@ -18,6 +18,7 @@ import java.util.regex.Pattern;
public final class EmojiUtil {
private static final Pattern EMOJI_PATTERN = Pattern.compile("^(?:(?:[\u00a9\u00ae\u203c\u2049\u2122\u2139\u2194-\u2199\u21a9-\u21aa\u231a-\u231b\u2328\u23cf\u23e9-\u23f3\u23f8-\u23fa\u24c2\u25aa-\u25ab\u25b6\u25c0\u25fb-\u25fe\u2600-\u2604\u260e\u2611\u2614-\u2615\u2618\u261d\u2620\u2622-\u2623\u2626\u262a\u262e-\u262f\u2638-\u263a\u2648-\u2653\u2660\u2663\u2665-\u2666\u2668\u267b\u267f\u2692-\u2694\u2696-\u2697\u2699\u269b-\u269c\u26a0-\u26a1\u26aa-\u26ab\u26b0-\u26b1\u26bd-\u26be\u26c4-\u26c5\u26c8\u26ce-\u26cf\u26d1\u26d3-\u26d4\u26e9-\u26ea\u26f0-\u26f5\u26f7-\u26fa\u26fd\u2702\u2705\u2708-\u270d\u270f\u2712\u2714\u2716\u271d\u2721\u2728\u2733-\u2734\u2744\u2747\u274c\u274e\u2753-\u2755\u2757\u2763-\u2764\u2795-\u2797\u27a1\u27b0\u27bf\u2934-\u2935\u2b05-\u2b07\u2b1b-\u2b1c\u2b50\u2b55\u3030\u303d\u3297\u3299\ud83c\udc04\ud83c\udccf\ud83c\udd70-\ud83c\udd71\ud83c\udd7e-\ud83c\udd7f\ud83c\udd8e\ud83c\udd91-\ud83c\udd9a\ud83c\ude01-\ud83c\ude02\ud83c\ude1a\ud83c\ude2f\ud83c\ude32-\ud83c\ude3a\ud83c\ude50-\ud83c\ude51\u200d\ud83c\udf00-\ud83d\uddff\ud83d\ude00-\ud83d\ude4f\ud83d\ude80-\ud83d\udeff\ud83e\udd00-\ud83e\uddff\udb40\udc20-\udb40\udc7f]|\u200d[\u2640\u2642]|[\ud83c\udde6-\ud83c\uddff]{2}|.[\u20e0\u20e3\ufe0f]+)+)+$");
private static final String EMOJI_REGEX = "[^\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\p{Cs}\\s]";
private EmojiUtil() {}
@@ -84,4 +85,12 @@ public final class EmojiUtil {
return (candidates != null && candidates.size() > 0) || EMOJI_PATTERN.matcher(text).matches();
}
public static String stripEmoji(@Nullable String text) {
if (text == null) {
return text;
}
return text.replaceAll(EMOJI_REGEX, "");
}
}

View File

@@ -10,8 +10,6 @@ import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.core.content.res.TypedArrayUtils;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
@@ -23,7 +21,6 @@ import org.thoughtcrime.securesms.components.InputAwareLayout.InputView;
import org.thoughtcrime.securesms.keyboard.KeyboardPage;
import org.thoughtcrime.securesms.keyboard.KeyboardPagerFragment;
import org.thoughtcrime.securesms.keyboard.emoji.search.EmojiSearchFragment;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.ThemedFragment;
public class MediaKeyboard extends FrameLayout implements InputView {
@@ -149,6 +146,10 @@ public class MediaKeyboard extends FrameLayout implements InputView {
.commitAllowingStateLoss();
}
public boolean isEmojiSearchMode() {
return keyboardState == State.EMOJI_SEARCH;
}
private void initView() {
if (!isInitialised) {

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