diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 71943c3bb..de2084ee1 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -3,7 +3,7 @@ name: Bug report
about: Report something that isn't working.
title: ''
assignees: ''
-
+labels: bug
---
-
+
+
+
+
@@ -267,7 +270,11 @@
android:clearTaskOnLaunch="true"
android:finishOnTaskLaunch="true" />
-
+
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bcfc4ccda..07a6987ec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,93 @@
# Delta Chat Android Changelog
+## v1.41.0 Testrun
+2023-10
+
+* keep screen on while playing voice messages
+* pause background music when starting voice messages
+* use the system camera as default; the old built-in camera can be enabled at "Settings / Advanced"
+* add "Verified by" information to contact profiles
+* screen reader: read out message types
+* screen reader: allow tapping anywhere in the message to start voice or audio playback
+* set different wallpapers for different accounts
+* add "Select All" to gallery and to file lists
+* resend attachments from profile (long tap, then "Resend" in the menu)
+* allow to import a key file instead of a folder containing keys
+* search in "Attach Contact" dialog
+* improve landscape mode for webxdc apps
+* adapt webxdc loading screen to dark mode
+* add file name to dialog shown if a webxdc app wants to share information
+* add app icon to webxdc info messages and improve webxdc app icon layout
+* improve layout of input bar when system emojis are used
+* ask for permissions before adding notifications on Android 13 (needed by the required update to API 33)
+* switch account if needed when opening webxdc app on the system's home screen
+* improve video error messages and logging
+* fix sometimes wrong avatar shown in notifications when using multiple accounts
+* fix: save map preferences per account to avoid resetting location and zoom
+* fix: play audio and voice messages: do not show progress in unrelated messages
+* fix: update relative times directly after entering chatlist, do not wait for a minute
+* fix issues when after selecting a non-system-language, system-language strings still show up
+* fix: only jump to message if info message is from webxdc
+* fix: update webxdc document name in titles immediately
+* fix: do not open Connectivity when tapping forward/share titles
+* fix starting conversation with contact from the phone contacts app
+* fix WASM support for some webxdc apps
+* fix off-by-one mismatch in manual language selection
+* fix: sanitize invalid filename we get from some camera apps
+* fix: display sticker footer properly
+* fix: webxdc apps starting twice sometimes
+* fix sending images and other files in location steaming mode
+* fix connectivity view layout if eg. storage shows values larger than 100%
+* fix scanning account-QR-codes on older phones that miss the Let's Encrypt system certificate
+* fix: make Thunderbird show encrypted subjects
+* fix: do not forward document name when forwarding only a webxdc app
+* fix: do not create new groups if someone replies to a group message with status "failed"
+* fix: do not block new group chats if 1:1 chat is blocked
+* fix "Show full message" showing a black screen for some messages received from Microsoft Exchange
+* fix: skip read-only mailing lists from forwarding/share chat lists
+* fix: do not allow dots at the end of email addresses
+* fix: do not send images pasted from the keyboard unconditionally as stickers
+* fix: forbid membership changes from possible non-members, allow from possible members
+* fix: improve group consistency across members
+* fix: delete messages from SMTP queue only on user demand
+* fix: improve wrapping of email messages on the wire
+* fix memory leak in IMAP
+* update translations and local help
+* update to core 1.124.1
+
+
+## v1.40.1
+2023-08
+
+* fix: correct core-submodule picked up by f-droid
+* update to core119.1
+
+
+## v1.40.0
+2023-08
+
+* use image editor for avatar selection when possible
+* allow media from blob: and data: in webxdc
+* optimized native library size
+* improve loading screen in dark mode
+* improve IMAP logs
+* update "verified icon"
+* fix webxdc issues with dark mode
+* fix crash in android 4.2 or older when opening a HTML message in full message view
+* fix: avoid IMAP move loops when DeltaChat folder is aliased
+* fix: accept webxdc updates in mailing lists
+* fix: delete webxdc status updates together with webxdc instance
+* fix: prevent corruption of large unencrypted webxdc updates
+* fix "Member added by me" message appearing sometimes within wrong context
+* fix core panic after sending 29 offline messages
+* fix: make avatar in qr-codes work on more platforms
+* fix: preserve indentation when converting plaintext to HTML
+* fix: remove superfluous spaces at start of lines when converting HTML to plaintext
+* fix: always rewrite and translate member added/removed messages
+* add Luri Bakhtiari translation, update other translations and local help
+* update to core119
+
+
## v1.38.2
2023-06
diff --git a/androidTest/com/b44t/messenger/uitests/offline/ForwardingTest.java b/androidTest/com/b44t/messenger/uitests/offline/ForwardingTest.java
index 9b7755dbd..c8c7f6483 100644
--- a/androidTest/com/b44t/messenger/uitests/offline/ForwardingTest.java
+++ b/androidTest/com/b44t/messenger/uitests/offline/ForwardingTest.java
@@ -43,7 +43,7 @@ public class ForwardingTest {
}
@Rule
- public ActivityScenarioRule activityRule = TestUtils.getOfflineActivityRule(false);
+ public final ActivityScenarioRule activityRule = TestUtils.getOfflineActivityRule(false);
@Before
public void createChats() {
diff --git a/androidTest/com/b44t/messenger/uitests/offline/SharingTest.java b/androidTest/com/b44t/messenger/uitests/offline/SharingTest.java
index 360bac152..17fdd680f 100644
--- a/androidTest/com/b44t/messenger/uitests/offline/SharingTest.java
+++ b/androidTest/com/b44t/messenger/uitests/offline/SharingTest.java
@@ -22,7 +22,6 @@ import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.ShareActivity;
import org.thoughtcrime.securesms.connect.DcHelper;
-import org.thoughtcrime.securesms.util.Util;
import java.io.File;
@@ -32,7 +31,6 @@ import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
-import static androidx.test.espresso.matcher.ViewMatchers.withChild;
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
@@ -46,13 +44,11 @@ public class SharingTest {
// PLEASE BACKUP YOUR ACCOUNT BEFORE RUNNING THIS!
// ==============================================================================================
- @SuppressWarnings("unused")
- private final static String TAG = SharingTest.class.getSimpleName();
private static int createdGroupId;
private static int createdSingleChatId;
@Rule
- public ActivityScenarioRule activityRule = TestUtils.getOfflineActivityRule(false);
+ public final ActivityScenarioRule activityRule = TestUtils.getOfflineActivityRule(false);
@Before
public void createGroup() {
diff --git a/assets/help/cs/help.html b/assets/help/cs/help.html
index 9f3fe913b..20ff62894 100644
--- a/assets/help/cs/help.html
+++ b/assets/help/cs/help.html
@@ -839,6 +839,9 @@ Pro ostatní programy by mělo jít nalézt řešení na Interentu.
Yes. Delta Chat 1.36 comes with a new, experimental function for using the same account on different devices:
+ -
+
Make sure both devices are on the same Wi-Fi or network
+
-
On the first device, go to Settings → Add Second Device, unlock the screen if needed
and wait a moment until a QR code is shown
@@ -1030,10 +1033,11 @@ you only open attachments from senders you trust, and not from spammers.
- - In general, there is no curated appstore and Delta Chat is not responsible
-for private apps or their content. Anyone can share private apps with each
+
- In general, anyone can share private apps with each
other without restrictions.
- - You can find some example apps on webxdc.org.
+ - You can send ‘hi’ to xstore@testrun.org
+to see an experimental webxdc appstore.
+All of the apps are open source and for free.
- Many people write their own private apps and post them to the Delta Chat
forum.
diff --git a/assets/help/de/help.html b/assets/help/de/help.html
index 79d25303c..d5cab13e9 100644
--- a/assets/help/de/help.html
+++ b/assets/help/de/help.html
@@ -746,6 +746,9 @@ Für andere Programme können Sie online eine Lösung finden.
Ja. Delta Chat 1.36 enthält eine neue, experimentelle Funktion zur Verwendung desselben Kontos auf verschiedenen Geräten:
+ -
+
Make sure both devices are on the same Wi-Fi or network
+
-
Gehen Sie auf dem ersten Gerät zu Einstellungen → Zweites Gerät hinzufügen, entsperren Sie den Bildschirm, falls erforderlich und warten Sie einen Moment, bis ein QR-Code angezeigt wird
@@ -895,9 +898,13 @@ Für andere Programme können Sie online eine Lösung finden.
- - Im Allgemeinen gibt es keinen kuratierten Appstore und Delta Chat ist nicht verantwortlich für Private Apps oder deren Inhalt. Jeder kann Private Apps uneingeschränkt miteinander teilen.
- - Einige Beispiel-Apps finden Sie auf webxdc.org.
- - Viele Leute schreiben ihre eigenen Privaten Apps und posten sie im Delta Chat-Forum.
+ - In general, anyone can share private apps with each
+other without restrictions.
+ - You can send ‘hi’ to xstore@testrun.org
+to see an experimental webxdc appstore.
+All of the apps are open source and for free.
+ - Many people write their own private apps and post them to the Delta Chat
+forum.
diff --git a/assets/help/en/help.html b/assets/help/en/help.html
index 3576ea88b..1efec25d2 100644
--- a/assets/help/en/help.html
+++ b/assets/help/en/help.html
@@ -28,18 +28,22 @@
- What happens if I turn on “Delete old messages from device”?
- - Encryption
+
- Encryption and Security
@@ -55,17 +59,17 @@
- Why can I choose not to watch the DeltaChat folder?
- Private Apps / webxdc
+ webxdc apps
Experimental Features
@@ -375,7 +379,7 @@ of a contact. It means they were “recently seen”.
because they wrote something to a group you are both a member of,
because they sent you a read receipt for a message you wrote,
or because they sent data to your Delta Chat app by using a
-private app.
+webxdc app.
So this is not a real time online status - and if someone doesn’t answer
@@ -534,199 +538,292 @@ this way, all messages will be deleted from your device as soon as they
older than that.
-
+
- Encryption
+ Encryption and Security
-
+
- Does Delta Chat support end-to-end-encryption?
+ Which standards are used for end-to-end encryption?
-
- -
-
Yes. Delta Chat implements the Autocrypt Level 1 standard and can
-thus E2E-encrypt messages with other Autocrypt-capable apps.
-
- -
-
Delta Chat also supports a strong form of end-to-end encryption that is
-even safe against active attacks, see “verified groups” further below.
-
-
+
Autocrypt is used for automatically
+establishing E2E-encryption with contacts and group chats.
+Autocrypt uses a limited and secure subset of the OpenPGP standard.
+
+Secure-Join protocols
+are used to implement verified groups
+which provide pervasive protection against network attacks and compromised servers.
+Verified groups enforce all messages in a chat to be safely E2E-encrypted
+with an unparalleled ease of use that avoids users having to learn about
+public key cryptography, key management or key verification.
-
+
- What do I have to do to activate the end-to-end-encryption?
+ When will messages be E2E-encrypted?
-
- -
-
Nothing.
-
- -
-
Delta Chat apps (and other Autocrypt-compatible
-e-mail apps) share the keys required for end-to-end-encryption automatically
-as the first messages are sent.
-After this, all subsequent messages are encrypted end-to-end automatically.
-If one of the chat partners uses a non-Autocrypt e-mail app, subsequent
-messages are not encrypted until an Autocrypt-compliant app is available again.
-
- -
-
If you want to rather avoid end-to-end-encrypted e-mails by default,
-use the corresponding Autocrypt setting in “Settings” or “Advanced settings”.
-
-
+
After a first message was received from a Delta Chat or other Autocrypt-capable contact,
+Delta Chat defaults to using E2E-encryption with that contact (and vice versa).
+When creating a group chat with contacts where E2E-encrypted is individually in place,
+all group members will automatically use E2E-encryption with each other and in the group.
+However, if you add a contact that lacks E2E-encryption,
+the group chat will not use E2E-encryption.
+
+If you want to be sure to always and only use E2E-encryption in a group
+use verified chat groups
+which additionally protects against compromised or malfeasant e-mail servers.
-
+
- If end-to-end-encryption is not available, is the connection not encrypted at all?
+ Are attachments (pictures, files, audio etc.) E2E-encrypted?
-
- - With most mail servers, Delta Chat establishes transport encryption
-(TLS).
-This only secures the connection between your device and your e-mail
-server. Whereas e2e-encryption provides safety between
-your device and a friend’s device.
-
+
Yes.
+When we talk about an “E2E-encrypted message”
+we always mean a whole message is encrypted,
+including all the attachments
+and attachment metadata such as filenames.
-
+
- How can I verify cryptographic status with a sender?
+ Is OpenPGP secure?
-
If you are within immediate distance of the chat partner:
+Yes, Delta Chat uses a secure subset of OpenPGP
+and only displays a padlock security indicator on a message
+if the whole message is properly encrypted and signed.
+For example, “Detached signatures” are not treated as secure.
+
+OpenPGP is not insecure by itself.
+Most publically discussed OpenPGP security problems
+actually stem from bad usability or bad implementations of tools or apps (or both).
+It is particularly important to distinguish between OpenPGP, the IETF encryption standard,
+and GnuPG (GPG), a command line tool implementing OpenPGP.
+Many public critiques of OpenPGP actually discuss GnuPG which Delta Chat has never used.
+Delta Chat rather uses the OpenPGP Rust implementation rPGP,
+available as an independent “pgp” package,
+and security-audited in 2019.
+
+We aim, along with other OpenPGP implementors,
+to further improve security characteristics by implementing the
+new IETF OpenPGP Crypto-Refresh
+which was thankfully adopted in summer 2023.
+
+
+
+
+ Did you consider using alternatives to OpenPGP for E2E-encryption?
+
+
+
+
+Yes, we are following efforts like MLS
+or Saltpack
+but adopting them would mean breaking E2E-encryption interoperability
+with all other e-mail apps that typically support OpenPGP encryption.
+So it would not be a light decision to take
+and there must be tangible improvements for users.
+
+Delta Chat takes a holistic “usable security” approach
+and works with a wide range of activist groupings as well as
+renowned researchers such as TeamUSEC
+to improve actual user outcomes against security threats.
+The wire protocol and standard for establishing E2E-encryption is
+only one part of “user outcomes”,
+see also our answers to device-seizure
+and message-metadata questions.
+
+
+
+
+ Is Delta Chat vulnerable to EFAIL?
+
+
+
+
+No, Delta Chat never was vulnerable to EFAIL
+because its OpenPGP implementation rPGP
+uses Modification Detection Code when encrypting messages
+and returns an error
+if the Modification Detection Code is incorrect.
+
+Delta Chat also never was vulnerable to the “Direct Exfiltration” EFAIL attack
+because it only decrypts multipart/encrypted messages
+which contain exactly one encrypted and signed part,
+as defined by the Autocrypt Level 1 specification.
+
+
+
+
+ Is a message exposed in cleartext if E2E-encryption is not available?
+
+
+
+
+No,
+this does not necessarily mean that the message is exposed in cleartext.
+
+Delta Chat always uses (TLS) encryption
+which secures connections between your device and your e-mail provider
+unless you explicitly disable it.
+All of Delta Chat’s TLS-handling has been independently security audited.
+
+Moreover, the connection between your and the recipient’s e-mail provider
+will today typically be transport-encrypted as well.
+If the involved e-mail servers support MTA-STS
+then transport encryption will be enforced in all inter e-mail server communications
+in which case Delta Chat communications will never be exposed in cleartext to the network
+even if the message was not E2E-encrypted.
+
+Note that maintaining E2E-encryption on top of TLS encryption is highly advisable
+because it provides safety between your device and a contact’s device,
+irrespective of any hops over potentially compromised e-mail servers.
+
+
+
+Delta Chat protects most message metadata by putting the following information
+into the E2E-encrypted part of messages:
- - Select QR Invite code on one device and then Scan QR code
-on the other one and scan the code. If both devices are online,
-they will introduce a chat channel with each-other (if it doesn’t exist already)
-and the encryption keys will also be verified. Both will see a
-“sender verified” system message in their 1:1 chat.
+ - Subject line
+ - Group avatar and name
+ - MDN (read receipt) requests (
Chat-Disposition-Notification-To)
+ - Disappearing message timer (
Ephemeral-Timer)
+ Chat-Group-Member-Removed, Chat-Group-Member-Added
+ Secure-Join header containing secure join commands
+ - Notification about enabling location streaming
+ - WebRTC room URL
-If you are not near the chat partner, you can check the status manually in the “Encryption” dialog
-(user profile on Android/iOS or right-click a user’s chat-list item on desktop):
+E-Mail servers do not get access to this protected metadata
+but they do see the message date as well as the message size,
+and, more importantly, the sender and receiver addresses.
+E-mail servers need receiver addresses to route and
+deliver messages to recipient’s devices.
+
+
+
+
+ How to protect metadata and contacts when a device is seized?
+
+
+
-
- -
-
For end-to-end-encryption, Delta Chat shows two fingerprints there.
-If the same fingerprints appear on your chat partner’s device,
+
Both for protecting against metadata-collecting e-mail servers
+as well as against the threat of device seizure
+we recommend to use a Delta Chat optimized e-mail server instance
+to create pseudonymous temporary accounts through QR-code scans.
+Note that Delta Chat apps on all platforms support multiple accounts
+so you can easily use action-specific “1-week” or “1-month” accounts next to your “main” account
+with the knowledge that all temporary account data, along with all metadata, will be deleted.
+Moreover, if a device is seized then contacts using temporary e-mail accounts
+can not be identified easily, as compared to messengers which reveal
+phone numbers in chat groups which in turn are often associated with passport identities.
+
+
+
+If you are within immediate distance of your contact,
+select QR Invite code on one device and then Scan QR code
+from within Delta Chat on the other device.
+If both devices are online,
+they will setup a chat with each-other (if it doesn’t exist already)
+and both will see a “sender verified” system message in their chat.
+Showing and scanning a QR code can also happen in any “second channel”
+such as a video call or another messenger.
+
+If QR code scanning is for some reason not viable,
+you may check the E2E encryption status manually in the “Encryption” dialog
+(user profile on Android/iOS or right-click a user’s chat-list item on desktop).
+Delta Chat shows two fingerprints there.
+If the same fingerprints appear on your own and your contact’s device,
the connection is safe.
-
- -
-
For transport encryption, this state is just shown there
-
-
-
+
- How can I check the encryption of messages?
+ How can I check the encryption status of messages?
-
- -
-
A little padlock shown beside a message denotes whether the message is end-to-end-encrypted from from the given sender.
-
- -
-
If there is no padlock, the message is usually transported unencrypted e.g. because you or the sender have turned off end-to-end-encryption, or the sender uses an app without support for end-to-end-encryption.
-
-
+
A little padlock in a message bubble denotes
+that the message was properly E2E-encrypted from the given sender.
+If there is no padlock, the message was not properly E2E-encrypted
+most likely because the sender uses an app or webmail interface
+without support for E2E–encryption.
-
+
- How can I ensure message encryption and deletion?
+ Why do I see unencrypted messages?
+
+
+
+
+
If a contact uses a non-Autocrypt e-mail app,
+all messages involving this contact (in a group or 1:1 chat)
+will not be E2E-encrypted, and thus not show a “padlock” with messages.
+Note that even if your contacts use Delta Chat on their account,
+they might also use a non-Autocrypt e-mail app on that account
+which then may cause intermittently unencrypted messages.
+Replying unencrypted to unencrypted messages is mandated by Autocrypt
+to prevent unreadable messages on the side of your contacts
+and their non-Autocrypt e-mail app.
+
+If you need a safely E2E-encrypted chat with contacts
+who are using their account also with Autocrypt-incapable apps (e.g. webmail)
+it’s best to create a verified group chat with them.
+Any message sent into a verified group chat will be E2E-encrypted
+irrespective of the last incoming message from a contact.
+
+
+
+
+ How can I ensure message E2E-encryption and deletion?
The best way to ensure every message is encrypted,
and metadata deleted as quickly as possible
-is creating a verified group and turning on
-disappearing messages.
+is using verified groups
+and turning on disappearing messages.
-Verified groups are always encrypted and protected against MITM
-attacks.
+Verified groups are always encrypted and protected against MITM attacks
+and turning on “disappearing messages” deletes the messages
+on the server after a user-configured time.
-Metadata can’t be encrypted, as the server needs to know where to deliver your
-messages. But turning on “disappearing messages” deletes the messages on
-the server after they were delivered.
-
-If you need the messages on your device, but not on the server, you can also
-agree in the group to turn on “delete messages from server
-automatically”.
-
-If you want to protect a 1:1 conversation like this, you should create a
-verified group with only 2 people. If the other person loses their device but
-not their account, you can still communicate in the 1:1 chat. (Read more)
-
-
-
-
- Which standards are used for end-to-end-encryption?
-
-
-
-
-
- -
-
Autocrypt is used for establishing
-e2e-encryption with other Delta Chat and other Autocrypt-capable mail apps.
-Autocrypt uses a limited subset of OpenPGP functionality.
-
- -
-
Delta Chat implements countermitm setup-contact and verified-group protocols to achieve protection against active network attacks. This goes beyond the opportunistic
-base protection of Autocrypt Level 1, while maintaining its ease of use.
-
-
-
-
-
-
- What is the difference between verified groups and 1:1 chats with verified contacts?
-
-
-
-
-
- -
-
1:1 chats with a verified contact and verified groups are not the same, even
-if there are only 2 people in the verified group. One difference is that you
-could easily add more people to the group, but there are other implications as
-well.
-
- -
-
Verified groups are invariably secured. Any breakage (cleartext or wrongly
-signed messages etc.) will be flagged and such messages will not be shown in
-this chat. You can trust all messages in this verified-checkmark chat to have
-not been read/altered by middle parties.
-
- -
-
1:1 chats are opportunistic, it is meant to allow people to communicate no
-matter if they change e-mail clients, devices, setups etc. That’s why there
-is no verification checkmark, even if you have verified the contact.
-
-
+If you don’t need a longer-lived copy of your messages on the server,
+you can also turn on “delete messages from server automatically”.
@@ -736,57 +833,23 @@ is no verification checkmark, even if you have verified the contact.
-
- -
-
No, OpenPGP doesn’t support Perfect Forward Secrecy. Perfect Forward Secrecy
-works session-oriented, but E-Mail is asynchronous by nature
-and often used from multiple devices independently. This means that if your
-Delta Chat private key is leaked, and someone has a record
-of all your in-transit messages, they will be able to read them.
-
- -
-
Note that if anyone has seized or hacked your running phone, they will
-typically be able to read all messages, no matter if Perfect Forward Secrecy
-is in place or not. Having access to a single device from a member of a group,
-will typically expose a lot of the social graph. Using e-mail addresses that
-are not easily tracked back to persons helps group members to stay safer from
-the effects of device seizure.
-
- -
-
We are sketching ways to protect communications better against the event
-of device seizure.
-
-
-
-
+No, Delta Chat doesn’t support Perfect Forward Secrecy (PFS).
+This means that if your Delta Chat private decryption key is leaked,
+and someone has collected your prior in-transit messages,
+they will be able to decrypt and read them using the leaked decryption key.
-
- -
-
As Delta Chat is a decentralized messenger, the metadata of Delta Chat users
-are not stored on a single central server. However, they are stored on the mail
-servers of the sender and the recipient of a message.
-
- -
-
Each mail server currently knows about who sent and who received a message by
-inspecting the unencrypted To/Cc headers and thus determine which e-mail addresses
-are part of a group. Delta Chat itself could avoid unencrypted To/Cc headers quite
-and always put them only into the encrypted section. See
-Avoid sending To/CC headers for verified groups.
-For opportunistic chats the main concern is how it affects other mail apps who
-might participate in chats.
-
- -
-
Many other e-mail headers, in particular the “Subject” header, are
-end-to-end-encryption protected, see also this upcoming IETF
-RFC.
-
-
+Note, however, that if anyone obtains to your decryption keys,
+they will typically also be able to obtain your messages,
+irrespective if Perfect Forward Secrecy is in place or not.
+The typical real-world situation for leaked decryption keys is device seizure
+which we discuss in our answer on metadata and device seizure.
+
+It is possible that Delta Chat evolves to support Perfect Forward Secrecy,
+because OpenPGP is just a container for encrypted messages
+but encryption key management (and thus key rotation or key “ratcheting”)
+could be organized in flexible ways.
+See Seqouia’s PFS prototype
+for existing experiments in the OpenPGP implementor community.
@@ -796,14 +859,12 @@ RFC.
-
- -
-
Yes. The best way is to send an Autocrypt Setup Message from the other e-mail client. Look for something like Start Autocrypt Setup Transfer in the settings of the other client and follow the instructions shown there.
-
- -
-
Alternatively, you can import the key manually in “Settings -> Advanced settings -> Import secret keys”. Caution: Make sure the key is not protected by a password, or remove the password beforehand.
-
-
+Yes.
+The best way is to send an Autocrypt Setup Message from the other e-mail client.
+Look for something like Start Autocrypt Setup Transfer in the settings of the other client and follow the instructions shown there.
+
+Alternatively, you can import the key manually in “Settings -> Advanced settings -> Import secret keys”.
+Caution: Make sure the key is not protected by a password, or remove the password beforehand.
If you don’t have a key or don’t even know you would need one - don’t worry: Delta Chat generates keys as needed, you don’t have to hit a button for it.
@@ -853,6 +914,9 @@ For other programs, you should be able to find a solution online.
Yes. Delta Chat 1.36 comes with a new, experimental function for using the same account on different devices:
+ -
+
Make sure both devices are on the same Wi-Fi or network
+
-
On the first device, go to Settings → Add Second Device, unlock the screen if needed
and wait a moment until a QR code is shown
@@ -1008,62 +1072,63 @@ Otherwise, deleting messages or multi-device setups might not work properly.
- Private Apps / webxdc
+ webxdc apps
-In Delta Chat, you can share “private apps”, attachments with an .xdc file
+
In Delta Chat, you can share webxdc apps, attachments with an .xdc file
extension. They can do very different things, and make Delta Chat a truly
-extendable messenger. The technical term is webxdc.
+extendable messenger.
-
+
- How private are private apps?
+ How private are webxdc apps?
- - Private apps can not send data to the Internet, or download anything.
- - A private app can only exchange data within a Delta Chat chat, with its
+
- webxdc apps can not send data to the Internet, or download anything.
+ - A webxdc app can only exchange data within a Delta Chat chat, with its
copies on the devices of your chat partners. Other than that, it’s completely
isolated from the Internet.
- - The privacy a private app offers is the privacy of your chat - as long as you
-trust the people you chat with, you can trust the private app as well.
- - This also means: it can be a privacy risk to open private apps in chats where
+
- The privacy a webxdc app offers is the privacy of your chat - as long as you
+trust the people you chat with, you can trust the webxdc app as well.
+ - This also means: it can be a privacy risk to open webxdc apps in chats where
you don’t trust the members - as you know it from e-mail attachments, where
you only open attachments from senders you trust, and not from spammers.
-
+
- Where can I get private apps?
+ Where can I get webxdc apps?
-
+
- How can I create my own private apps?
+ How can I create my own webxdc apps?
- - Private apps are just zip files containing html, css, and javascript code.
+ - webxdc apps are just zip files containing html, css, and javascript code.
- You can extend the Hello World example app
to get started.
- All else you need to know is written in the
@@ -1107,31 +1172,32 @@ link in the settings.
this way, you will have a new random jitsi room every time you call someone.
-
+
- What is a verified group? Why is it experimental?
+ What is a verified group? Why is it experimental?
-
- -
-
A verified group is a chat that guarantees safety against an active
-attacker. All Messages in a verified chat view are e2e-encrypted, and
-members can join by scanning a “QR invite code”. All members are thus
-connected with each other through a chain of invites, which guarantee
-cryptographic consistency against active network or provider attacks.
+
Verified groups carry a green verification checkmark in the group title that guarantees
+that all messages are E2E-encrypted and can not be read or altered by e-mail servers.
+Each member in a verified group chat can add already verified contacts
+or tap “QR Invite code” to let invitees scan the code to get verified and added (“secure-join”).
+This “secure-join” protocol ensures that all verified group chat members
+are connected with each other through a chain of verifications (“web of trust”),
+guaranteeing E2E-encryption consistency even if e-mail servers are compromised or malfeasant.
See countermitm.readthedocs.io
-for the R&D behind this feature.
-
- -
-
As of Oct 2022, “verified groups” remain an experimental feature. It is
-continuously improved and many bugs have been fixed since the original
-introduction in 2018. However, there remain cases, especially with large
-groups where inconsistencies can occur, or messages become unreadable.
-
-
+for a detailed security discussion.
+
+
Note that “1:1” chats are currently only opportunistically encrypted (Autocrypt).
+You need to create a verified group with your contact
+to ensure that all messages will be safely E2E-encrypted between you two.
+We plan to introduce verified 1:1 chats around the end of 2023,
+simplifying and extending the guarantees of verified E2E-encryption for all chat types.
+Until then verified groups will remain classified as an experimental feature
+although they are widely and successfully used already,
+and reported bugs have been continously fixed in the last years.
@@ -1343,10 +1409,10 @@ uses.
See Standards used in Delta Chat.
-
+
- Was Delta Chat independently audited for security vulnerabilities?
+ Was Delta Chat independently audited for security vulnerabilities?
diff --git a/assets/help/es/help.html b/assets/help/es/help.html
index df850c05e..aecea3c28 100644
--- a/assets/help/es/help.html
+++ b/assets/help/es/help.html
@@ -843,6 +843,9 @@ For other programs, you should be able to find a solution online.
Yes. Delta Chat 1.36 comes with a new, experimental function for using the same account on different devices:
+ -
+
Make sure both devices are on the same Wi-Fi or network
+
-
On the first device, go to Settings → Add Second Device, unlock the screen if needed
and wait a moment until a QR code is shown
@@ -1028,10 +1031,11 @@ you only open attachments from senders you trust, and not from spammers.
- - In general, there is no curated appstore and Delta Chat is not responsible
-for private apps or their content. Anyone can share private apps with each
+
- In general, anyone can share private apps with each
other without restrictions.
- - You can find some example apps on webxdc.org.
+ - You can send ‘hi’ to xstore@testrun.org
+to see an experimental webxdc appstore.
+All of the apps are open source and for free.
- Many people write their own private apps and post them to the Delta Chat
forum.
diff --git a/assets/help/fr/help.html b/assets/help/fr/help.html
index 84c2dca2d..44a3fd0e6 100644
--- a/assets/help/fr/help.html
+++ b/assets/help/fr/help.html
@@ -7,14 +7,14 @@
- Quels sont les messages qui apparaissent dans Delta Chat ?
- Et si j’attends un message de quelqu’un avec qui je n’ai encore jamais échangé ?
- Delta Chat prend-il en charge les images, vidéos et autres pièces jointes ?
- - Comment naviguer entre mes différents comptes et comment en ajouter ?
+ - Comment naviguer entre plusieurs comptes et comment en ajouter ?
- Qui peut voir ma photo de profil ?
- Est-ce que Delta Chat gère les courriels en HTML ?
- Pourquoi dois-je entrer le mot de passe de mon compte de courriel dans Delta Chat ? Est-ce sécurisé ?
- De quelles autorisations Delta Chat a-t-il besoin?
- Que signifient “épingler”, “sourdine” et “archiver” ?
- Que signifie le point vert ?
- - How can I delete my account?
+ - Comment supprimer mon compte ?
Groupes
@@ -35,7 +35,7 @@
Si le chiffrement de bout en bout n’est pas disponible, la connexion n’est-elle pas chiffrée du tout ?
Comment puis-je vérifier le statut de chiffrement auprès d’un expéditeur ?
Comment puis-je vérifier le chiffrement des messages ?
- Comment puis-je faire en sorte que les messages soient chiffrés et supprimés ?
+ Comment m’assurer que les messages sont chiffrés et supprimés ?
Quels standards sont utilisés pour le chiffrement de bout en bout ?
Quelle est la différence entre des groupes vérifiés et des discussions à deux avec des contacts vérifiés ?
Delta Chat prend-il en charge la confidentialité persistante parfaite (Perfect Forward Secrecy) ?
@@ -57,20 +57,20 @@
Applications privées / webxdc
Fonctionnalités expérimentales
Divers
@@ -80,8 +80,8 @@
Si Delta Chat se base sur le courrier électronique, est-ce vraiment une messagerie instantanée?
Delta Chat est-il compatible avec Protonmail / Tutanota / Criptext ?
Les détails techniques m’intéressent. Pouvez-vous m’en dire plus ?
- Was Delta Chat independently audited for security vulnerabilities?
- Comment est financé le développement de Delta Chat?
+ Est-ce qu’un audit indépendant des failles de sécurité a été réalisé sur Delta Chat ?
+ Comment est financé le développement de Delta Chat ?
@@ -141,12 +141,12 @@
Par défaut, Delta Chat affiche tous les courriels.
-Vous pouvez changer cette préférence en allant dans “Paramètres -> Discussions & fichiers multimédia -> Voir les courriels classiques”. Trois options sont disponibles :
+Vous pouvez changer cette préférence en vous rendant dans vos “Paramètres -> Discussions & fichiers multimédia -> Voir les courriels classiques”. Trois options sont disponibles :
- “Non, seulement les discussions”: vous verrez seulement les messages envoyés par d’autres utilisateurs de Delta Chat et les réponses à vos messages Delta Chat. Cette option est recommandée si vous utilisez le même compte pour recevoir aussi des courriels normaux.
- “Tout”: vous verrez dans Delta Chat tous les courriels envoyés à votre adresse. Cette option est recommandée si vous souhaitez utiliser Delta Chat pour tous vos courriels, afin de ne rater aucun message.
- - “Pour les contacts acceptés”: vous verrez dans Delta Chat tous les courriels envoyés par les personnes avec qui vous avez déjà ouvert une discussion, mais les notifications de nouvelles conversations ne s’afficheront que pour des messages envoyés via Delta Chat. Cette option vous permet de décider au cas par cas si vous souhaitez échanger via Delta Chat ou via une application de courriel “normale”.
+ - “Pour les contacts acceptés”: vous verrez dans Delta Chat tous les courriels envoyés par les personnes avec qui vous avez déjà ouvert une discussion, mais les notifications de nouvelles conversations ne s’afficheront que pour des messages envoyés via Delta Chat. Cette option vous permet de décider au cas par cas si vous souhaitez échanger via Delta Chat ou via une application de courriel “classique”.
@@ -179,21 +179,21 @@ recevoir de messages de cette personne, nous vous conseillons de la bloquer<
Oui. En plus du texte brut, toutes les pièces jointes aux courriels sont affichées sous forme de messages séparés. Les messages sortants expédient automatiquement les pièces jointes que vous ajoutez.
- - Pour de meilleures performances, les images sont optimisées et envoyées en taille réduite par défaut, mais vous pouvez les envoyer en tant que “fichier” pour en conserver la taille originale.
+ - Pour améliorer les performances, les images sont redimensionnées et envoyées en taille réduite par défaut ; mais vous pouvez les envoyer en tant que “fichier” pour en conserver la taille originale.
- Comment naviguer entre mes différents comptes et comment en ajouter ?
+ Comment naviguer entre plusieurs comptes et comment en ajouter ?
-Vous pouvez facilement gérer des comptes supplémentaires sur les clients Delta Chat mobile ou de bureau en cliquant sur :
+Vous pouvez facilement gérer des comptes supplémentaires sur les clients Delta Chat mobile ou de bureau en cliquant :
- - le bouton du menu, puis sur “Changer de compte” (Android et bureau) ;
- - l’icône de profil, puis sur “Ajouter un compte” (iOS).
+ - soit sur le bouton du menu, puis sur “Changer de compte” (Android et PC) ;
+ - soit sur l’icône de profil, puis sur “Ajouter un compte” (iOS).
Peut-être voudrez-vous aussi savoir comment ajouter un compte sur plusieurs appareils.
@@ -340,41 +340,31 @@ pour mettre une discussion en sourdine, ouvrez le menu de la conversation (Andro
- - Depuis la version 1.34 de Delta Chat, vous voyez parfois un “point vert” à côté de la photo de profil
-d’un contact. Cela signifie qu’il a été “vu récemment”.
+ - Depuis la version 1.34 de Delta Chat, vous voyez parfois un “point vert” à côté de la photo de profil d’un contact. Cela signifie qu’il a été “vu récemment”.
- Plus précisément, cela signifie que Delta Chat a détecté l’activité de ce contact au cours des 10 dernières minutes :
- soit parce qu’il vous a écrit personnellement ;
- soit parce qu’il a écrit dans un groupe dont vous êtres tous les deux membres ;
- - soit parce qu’il a envoyé l’accusé de lecture d’un message que vous lui avez écrit ;
- - ou bien parce qu’il a envoyé des données à votre appli Delta Chat via une
-appli privée.
+ - soit parce qu’il a renvoyé l’accusé de lecture d’un message que vous lui avez écrit ;
+ - ou bien parce qu’il a envoyé des données à votre appli Delta Chat via une appli privée.
- - Le “point vert” n’est donc pas un statut en temps réel. Aussi, si quelqu’un ne vous répond pas
-immédiatement alors qu’il semble être en ligne, ne vous en faites pas et
-patientez ;-)
- - D’un autre côté, les autres ne vous voient pas forcément “en ligne”. En particulier,
- si vous avez désactivé les accusés de lecture, ils ne verront pas de point vert
-tant que vous ne leur écrivez pas directement ou n’écrivez pas dans un groupe dont ils font aussi partie.
+ - Le “point vert” n’est donc pas un statut en temps réel. Aussi, si quelqu’un ne vous répond pas immédiatement alors qu’il semble être en ligne, ne vous inquiétez pas et donnez-lui du temps ;-)
+ - D’un autre côté, vos contacts ne vous voient pas forcément “en ligne”. Si vous avez désactivé les accusés de lecture, ils ne verront pas de point vert tant que vous ne leur écrivez pas directement ou n’écrivez pas dans un groupe dont ils font aussi partie.
-
+
-
As you use an e-mail account for Delta Chat,
-how you can delete your account depends on your e-mail provider.
-We don’t have any control over your e-mail account,
-so unfortunately we can’t help you with that.
+Étant donné que vous utilisez Delta Chat à travers un compte courriel, la façon dont vous pouvez supprimer votre compte dépend de votre fournisseur de courriel.
+Comme nous n’exerçons aucun contrôle sur votre compte courriel, nous ne sommes pas en mesure de vous aider à le supprimer.
-If you want to keep the account,
-but uninstall Delta Chat,
-it is recommended to leave any group chat before uninstalling Delta Chat.
+Si vous souhaitez conserver votre compte mais désinstaller Delta Chat, nous vous recommandons de quitter toutes les discussions de groupe avant de procéder à la désinstallation.
@@ -470,15 +460,15 @@ qu’un humain a lu ou compris le message ;)
-
-
Par défaut, Delta Chat stocke tous les messages sur votre appareil. Vous pouvez donc choisir de supprimer les anciens messages automatiquement pour libérer de l’espace de stockage chez votre fournisseur de courriel. Ces messages seront conservés localement jusqu’à ce que vous les supprimiez aussi de votre appareil.
+ Par défaut, Delta Chat stocke tous les messages sur votre appareil. Vous pouvez donc configurer Delta Chat pour qu’il supprime automatiquement les anciens messages reçus, si vous souhaitez libérer de l’espace de stockage chez votre fournisseur de courriel. Ces messages seront conservés localement jusqu’à ce que vous les supprimiez aussi de votre appareil.
-
-
Pour activer cette option, allez dans les paramètres de “Discussions et fichiers multimédia” et Supprimer les anciens messages → Supprimer les messages du serveur.
+
Pour activer cette option, accédez à vos paramètres de “Discussions et fichiers multimédia”, puis à Supprimer les anciens messages → Supprimer les messages du serveur.
Vous pouvez régler un délai allant de “Immédiatement” à “Après 1 année”.
Tous les courriels reçus sur Delta Chat seront supprimés du serveur une fois le délai écoulé.
-
-
Attention : si vous utilisez Delta Chat sur plusieurs appareils, laissez les messages sur le serveur jusqu’à ce que l’(es) autre(s) appareil(s) ai(en)t pu les télécharger aussi. Dans ce cas, nous vous conseillons de paramétrer la suppression automatique sur “Après 1 jour” ou plus, en fonction de la fréquence à laquelle vous vous servez de ces autres appareils.
+ Attention : si vous utilisez Delta Chat sur plusieurs appareils, vous devez laisser vos messages sur le serveur suffisamment longtemps pour permettre à votre/vos autre(s) appareil(s) de les télécharger aussi.
@@ -593,15 +583,15 @@ Ce type de connexion sécurise les échanges seulement entre votre appareil et v
-
La meilleure façon de garantir que chaque message soit chiffré et les métadonnées effacées aussi vite que possible c’est de créer un groupe vérifié et d’activer les messages éphémères.
+Créer un groupe vérifié et activer les messages éphémères constitue le meilleur moyen de garantir que chaque message est chiffré et les métadonnées effacées aussi tôt que possible.
Les groupes vérifiés sont toujours chiffrés et protégés contre les attaques de l’homme du milieu.
@@ -609,7 +599,7 @@ Ce type de connexion sécurise les échanges seulement entre votre appareil et v
Si vous avez besoin de conserver vos messages sur votre appareil, mais pas sur le serveur, vous pouvez aussi vous mettre d’accord dans le groupe pour activer l’option “Supprimer les messages du serveur immédiatement”.
-Si vous souhaitez protéger une conversation à deux de cette manière, nous vous conseillons de créer un groupe vérifié avec seulement deux personnes. Si jamais l’autre personne perdait son appareil, mais pas son compte, vous pourriez toujours communiquer dans la conversation à deux. (En savoir plus)
+Si vous souhaitez protéger une discussion à deux de cette manière, nous vous conseillons de créer un groupe vérifié avec seulement deux personnes. Si jamais l’autre personne perdait son appareil mais pas son compte, vous pourriez toujours communiquer dans la conversation à deux. (En savoir plus)
@@ -750,6 +740,9 @@ Pour les autres programmes, vous devriez pouvoir trouver la solution en ligne.
Oui. Delta Chat 1.36 propose une nouvelle fonctionnalité expérimentale pour utiliser le même compte sur plusieurs appareils.
+ -
+
Make sure both devices are on the same Wi-Fi or network
+
-
Sur le premier appareil, allez dans Paramètres → Ajouter un deuxième appareil, déverrouillez l’écran si nécessaire, et patientez un peu jusqu’à ce qu’un code QR s’affiche.
@@ -794,8 +787,8 @@ Ne quittez pas Delta Chat !
(Nous faisons de notre mieux pour que l’application fonctionne en arrière-plan, mais les systèmes ont une fâcheuse tendance à tuer les applis.)
-
-
Delta Chat est déjà connecté sur l’appareil de destination.
-Vous pouvez utiliser plusieurs comptes sur tous vos appareils : ajoutez un nouveau compte.
+ Si Delta Chat est déjà connecté sur l’appareil de destination.
+Vous pouvez utiliser plusieurs comptes par appareil : ajoutez un nouveau compte.
-
Si les problèmes persistent, ou si vous ne pouvez pas scanner de code QR, essayez la méthode de transfert manuel décrite ci-dessous.
@@ -884,10 +877,10 @@ Sinon, la suppression de messages dans une configuration multi-appareils pourrai
Dans Delta Chat, vous pouvez partager des “applications privées” : ce sont des pièces jointes avec une extension de fichier .xdc. Elles possèdent des fonctionnalités très variées et peuvent faire de Delta Chat une messagerie vraiment extensible. En jargon technique, on parle de webxdc.
-
+
- Dans quelle mesure les applications privées sont-elles privées ?
+ À quel point les applications privées sont-elles privées ?
@@ -896,7 +889,7 @@ Sinon, la suppression de messages dans une configuration multi-appareils pourrai
- Les applications privées ne peuvent ni envoyer des données sur internet ni télécharger quoi que ce soit.
- Une application privée peut échanger des données uniquement au sein d’une discussion Delta Chat, ainsi qu’avec les copies sur les différents appareils des autres membres de la discussion. En dehors de cela, l’appli privée est complètement isolée d’internet.
- La confidentialité offerte par une application privée est celle de votre discussion : si vous faites confiance aux personnes avec qui vous discutez, vous pouvez aussi vous fier à l’application privée.
- - Cela signifie aussi qu’ouvrir des applications privées dans des discussions où se trouvent des membres à qui vous ne faites pas confiance peut représenter un risque en matière de confidentialité. En fait, c’est la même situation qu’avec les courriels : vous ouvrez uniquement les pièces jointes envoyées par une adresse que vous connaissez et pas celles envoyées par un émetteur malveillant.
+ - Cela signifie aussi qu’ouvrir des applications privées dans des discussions où se trouvent des membres à qui vous ne faites pas confiance peut représenter un risque en matière de confidentialité. En fait, la situation est la même qu’avec les courriels : vous ouvrez uniquement les pièces jointes envoyées par une adresse que vous connaissez et pas celles contenues dans les pourriels.
@@ -908,9 +901,13 @@ Sinon, la suppression de messages dans une configuration multi-appareils pourrai
- - De manière générale, il n’existe aucun magasin d’applications présélectionnées et Delta Chat n’est pas responsable des applications privées ou de leur contenu. Tout le monde peut partager des applications privées avec n’importe qui, sans restrictions.
- - Vous trouverez des exemples d’applications sur webxdc.org.
- - Beaucoup de gens développent leurs propres applications privées et les postent ensuite sur le forum Delta Chat.
+ - In general, anyone can share private apps with each
+other without restrictions.
+ - You can send ‘hi’ to xstore@testrun.org
+to see an experimental webxdc appstore.
+All of the apps are open source and for free.
+ - Many people write their own private apps and post them to the Delta Chat
+forum.
Nous apprécions tout particulièrement les retours sur ces fonctionnalités. Vous voulez donner votre avis ? Rejoignez-nous sur le Forum pour contribuer. (Vous aimez faire des expériences ? Inscrivez-vous en cliquant sur “Sign up -> with Delta Chat” !)
-
- - In generale, non esiste un appstore curato e Delta Chat non è responsabile delle applicazioni private o del loro contenuto.
-Chiunque può condividere apps private con
-gli altri senza restrizioni.
- - Potete trovare alcuni esempi di app su webxdc.org.
- - Molte persone scrivono le proprie applicazioni private e le pubblicano sul forum di Delta Chat
-.
+ - In general, anyone can share private apps with each
+other without restrictions.
+ - You can send ‘hi’ to xstore@testrun.org
+to see an experimental webxdc appstore.
+All of the apps are open source and for free.
+ - Many people write their own private apps and post them to the Delta Chat
+forum.
@@ -1214,8 +1216,8 @@ indirizzo e-mail
gruppo verificato, lo riconosceranno automaticamente.
-
Per conoscere i dettagli alla base di questo, leggi il nostro post sul blog
-it.
+Per conoscere i dettagli alla base di questo, leggi il nostro post
+sul blog.
@@ -1317,7 +1319,7 @@ problema per molte app legittime. Per ulteriori informazioni, vedi
-
The Delta Chat project underwent four independent security audits in the last years:
+Il progetto Delta Chat è stato sottoposto a quattro verifiche di sicurezza indipendenti negli ultimi anni:
-
@@ -1354,11 +1356,11 @@ Puoi leggere ulteriori informazioni sull’audit rapporto completo qui.
-
-
Beginning 2023, we fixed security and privacy issues with the “web
-apps shared in a chat” feature, related to failures of sandboxing
-especially with Chromium. We subsequently got an independent security
-audit from Cure53 and all issues found were fixed in the 1.36 app series released in April 2023.
-See here for the full background story on E2E security in the web.
+ A partire dal 2023, abbiamo risolto i problemi di sicurezza e privacy con le “web
+app condivise in una chat”, relativa ai fallimenti del sandboxing
+soprattutto con Chromium. Successivamente abbiamo ottenuto una sicurezza indipendente
+verificata da Cure53 e tutti i problemi rilevati sono stati risolti nella serie di app 1.36 rilasciata nell’Aprile 2023.
+Vedi qui per la storia completa sulla sicurezza E2E nel web.
diff --git a/assets/help/nl/help.html b/assets/help/nl/help.html
index 9792f197c..6b413ecb0 100644
--- a/assets/help/nl/help.html
+++ b/assets/help/nl/help.html
@@ -14,7 +14,7 @@
Welke Android-rechten heeft Delta Chat nodig?
Wat is vastmaken, negeren en archiveren?
Wat betekent die groene stip?
- How can I delete my account?
+ Hoe kan ik mijn account verwijderen?
Groepen
@@ -375,22 +375,22 @@ leesbevestigingen hebt uitgeschakeld, dan wordt er geen groen stipje getoond tot
iemand benaderd of iets in een groep plaatst..
-
+
- How can I delete my account?
+ Hoe kan ik mijn account verwijderen?
-
As you use an e-mail account for Delta Chat,
-how you can delete your account depends on your e-mail provider.
-We don’t have any control over your e-mail account,
-so unfortunately we can’t help you with that.
+Delta Chat maakt gebruik van je e-mailaccount,
+dus de verwijderprocedure is afhankelijk van je provider.
+We hebben geen zeggenschap over je account,
+dus helaas kunnen we je daar niet bij helpen.
-If you want to keep the account,
-but uninstall Delta Chat,
-it is recommended to leave any group chat before uninstalling Delta Chat.
+Als je je account wilt behouden maar Delta Chat
+niet wilt verwijderen, verlaat dan groeps-
+gesprekken voordat je Delta Chat verwijdert.
@@ -835,6 +835,9 @@ Voor andere programma’s kun je zélf een oplossing zoeken op internet.
Ja, Delta Chat 1.36 bevat een nieuwe, experimentele functie om hetzelfde account op meerdere apparaten te gebruiken:
+ -
+
Make sure both devices are on the same Wi-Fi or network
+
-
Ga op het eerste apparaat naar Instellingen → Tweede apparaat toevoegen, ontgrendel - indien nodig - het scherm
en wacht totdat de QR-code in beeld verschijnt.
@@ -1019,10 +1022,13 @@ niet per definitie veilig. Open dus alléén privé-apps van personen die je ver
- - Er is in principe geen appwinkel met privé-apps en Delta Chat is niet verantwoordelijk voor
-de apps of inhoud ervan. Iedereen kan ongelimiteerd privé-apps met elkaar delen.
- - Op webxdc.org staan wel enkele voorbeeldapps.
- - De meeste mensen maken hun eigen privé-apps en delen ze op het Delta Chat-forum.
+ - In general, anyone can share private apps with each
+other without restrictions.
+ - You can send ‘hi’ to xstore@testrun.org
+to see an experimental webxdc appstore.
+All of the apps are open source and for free.
+ - Many people write their own private apps and post them to the Delta Chat
+forum.
diff --git a/assets/help/pl/help.html b/assets/help/pl/help.html
index feb919a51..bf6a856f6 100644
--- a/assets/help/pl/help.html
+++ b/assets/help/pl/help.html
@@ -14,7 +14,7 @@
- Jakich uprawnień potrzebuje Delta Chat?
- Co oznacza przypinanie, wyciszanie i archiwizowanie?
- Co oznacza zielona kropka?
- - How can I delete my account?
+ - Jak mogę usunąć swoje konto?
Grupy
@@ -332,22 +332,18 @@ aby wyciszyć czat, użyj menu czatu (Android/Desktop) lub profilu czatu (iOS).<
Z drugiej strony inni nie zawsze „zobaczą, że jesteś online”. Jeśli wyłączyłeś potwierdzenia odczytu, nie zobaczą oni zielonej kropki, dopóki nie wyślesz do nich wiadomości lub nie napiszesz do grupy, w której również się znajdują.
-
+
- How can I delete my account?
+ Jak mogę usunąć swoje konto?
-
As you use an e-mail account for Delta Chat,
-how you can delete your account depends on your e-mail provider.
-We don’t have any control over your e-mail account,
-so unfortunately we can’t help you with that.
+Gdy korzystasz z konta e-mail w Delta Chat, sposób usunięcia konta zależy od dostawcy poczty e-mail.
+Nie mamy żadnej kontroli nad twoim kontem e-mail, więc niestety nie możemy ci w tym pomóc.
-If you want to keep the account,
-but uninstall Delta Chat,
-it is recommended to leave any group chat before uninstalling Delta Chat.
+Jeśli chcesz zachować konto, ale odinstalować Delta Chat, zaleca się opuszczenie każdego czatu grupowego przed odinstalowaniem Delta Chat.
@@ -715,6 +711,9 @@ W przypadku innych programów można znaleźć rozwiązanie online.
Tak. Delta Chat 1.36 zawiera nową, eksperymentalną funkcję korzystania z tego samego konta na różnych urządzeniach:
+ -
+
Make sure both devices are on the same Wi-Fi or network
+
-
Na pierwszym urządzeniu przejdź do Ustawienia → Dodaj kolejne urządzenie, w razie potrzeby odblokuj ekran i poczekaj chwilę, aż pojawi się kod QR
@@ -864,9 +863,13 @@ W przypadku innych programów można znaleźć rozwiązanie online.
- - Ogólnie rzecz biorąc, nie ma wyselekcjonowanego sklepu z aplikacjami, a Delta Chat nie ponosi odpowiedzialności za prywatne aplikacje ani ich zawartość. Każdy może udostępniać sobie prywatne aplikacje bez ograniczeń.
- - Przykładowe aplikacje można znaleźć na stronie webxdc.org.
- - Wiele osób pisze własne prywatne aplikacje i publikuje je na forum Delta Chat.
+ - In general, anyone can share private apps with each
+other without restrictions.
+ - You can send ‘hi’ to xstore@testrun.org
+to see an experimental webxdc appstore.
+All of the apps are open source and for free.
+ - Many people write their own private apps and post them to the Delta Chat
+forum.
diff --git a/assets/help/pt/help.html b/assets/help/pt/help.html
index b30eabd43..164262782 100644
--- a/assets/help/pt/help.html
+++ b/assets/help/pt/help.html
@@ -816,6 +816,9 @@ Para outros programas, você deveria achar uma solução online.
Yes. Delta Chat 1.36 comes with a new, experimental function for using the same account on different devices:
+ -
+
Make sure both devices are on the same Wi-Fi or network
+
-
On the first device, go to Settings → Add Second Device, unlock the screen if needed
and wait a moment until a QR code is shown
@@ -1009,10 +1012,11 @@ you only open attachments from senders you trust, and not from spammers.
- - In general, there is no curated appstore and Delta Chat is not responsible
-for private apps or their content. Anyone can share private apps with each
+
- In general, anyone can share private apps with each
other without restrictions.
- - You can find some example apps on webxdc.org.
+ - You can send ‘hi’ to xstore@testrun.org
+to see an experimental webxdc appstore.
+All of the apps are open source and for free.
- Many people write their own private apps and post them to the Delta Chat
forum.
diff --git a/assets/help/ru/help.html b/assets/help/ru/help.html
index 137340f3c..5dc850b57 100644
--- a/assets/help/ru/help.html
+++ b/assets/help/ru/help.html
@@ -852,6 +852,9 @@ RFC.
Yes. Delta Chat 1.36 comes with a new, experimental function for using the same account on different devices:
+ -
+
Make sure both devices are on the same Wi-Fi or network
+
-
On the first device, go to Settings → Add Second Device, unlock the screen if needed
and wait a moment until a QR code is shown
@@ -1044,10 +1047,11 @@ you only open attachments from senders you trust, and not from spammers.
- - In general, there is no curated appstore and Delta Chat is not responsible
-for private apps or their content. Anyone can share private apps with each
+
- In general, anyone can share private apps with each
other without restrictions.
- - You can find some example apps on webxdc.org.
+ - You can send ‘hi’ to xstore@testrun.org
+to see an experimental webxdc appstore.
+All of the apps are open source and for free.
- Many people write their own private apps and post them to the Delta Chat
forum.
diff --git a/assets/help/sk/help.html b/assets/help/sk/help.html
index 36ff5fa4a..71fe78055 100644
--- a/assets/help/sk/help.html
+++ b/assets/help/sk/help.html
@@ -852,6 +852,9 @@ Pre iné programy by ste mali vedieť nájsť riešenie online.
Yes. Delta Chat 1.36 comes with a new, experimental function for using the same account on different devices:
+ -
+
Make sure both devices are on the same Wi-Fi or network
+
-
On the first device, go to Settings → Add Second Device, unlock the screen if needed
and wait a moment until a QR code is shown
@@ -1042,10 +1045,11 @@ you only open attachments from senders you trust, and not from spammers.
- - In general, there is no curated appstore and Delta Chat is not responsible
-for private apps or their content. Anyone can share private apps with each
+
- In general, anyone can share private apps with each
other without restrictions.
- - You can find some example apps on webxdc.org.
+ - You can send ‘hi’ to xstore@testrun.org
+to see an experimental webxdc appstore.
+All of the apps are open source and for free.
- Many people write their own private apps and post them to the Delta Chat
forum.
diff --git a/assets/help/sq/help.html b/assets/help/sq/help.html
index 585596348..4da15ebcc 100644
--- a/assets/help/sq/help.html
+++ b/assets/help/sq/help.html
@@ -14,7 +14,7 @@
- Ç’leje lyp Delta Chat-i?
- Ç’do të thotë Fiksim, Heshtim, Arkivim?
- Ç’do të thotë pika e gjelbër?
- - How can I delete my account?
+ - Si mund ta fshij llogarinë time?
- Grupe
@@ -390,22 +390,22 @@ u merakosni dhe lëruni ca kohë ;-)
t’u dërgoni një mesazh, ose të shkruani te një grup ku bëjnë pjesë edhe ata.
-
+
- How can I delete my account?
+ Si mund ta fshij llogarinë time?
-
As you use an e-mail account for Delta Chat,
-how you can delete your account depends on your e-mail provider.
-We don’t have any control over your e-mail account,
-so unfortunately we can’t help you with that.
+Ngaqë për Delta Chat-in përdorni një llogari email,
+se si mund të fshini llogarinë tuaj, varet nga shërbimi juaj email.
+Nuk kemi fuqi mbi llogarinë tuaj email,
+ndaj, mjerisht, s’mund t’ju ndihmojmë për këtë.
-If you want to keep the account,
-but uninstall Delta Chat,
-it is recommended to leave any group chat before uninstalling Delta Chat.
+Nëse doni ta mbani llogarinë,
+por të çinstaloni Delta Chat-in,
+rekomandohet të braktisni çfarëdo grupi fjalosjeje, para çinstalimit të Delta Chat-it.
@@ -509,10 +509,10 @@ të zgjidhni të fshihen automatikisht mesazhet e vjetër. Mbeten ende në pajis
deri sa t’i fshini edhe prej andej.
-
-
To turn it on, go to Delete Old Messages → Delete Messages from Server
-in the “Chats and Media” settings.
-You can set a timeframe between “At once” and “After 1 year”.
-All e-mails received by Delta Chat will be deleted from the server after this timeframe.
+ Për ta vënë në punë, kaloni te Fshi Mesazhe të Vjetër → Fshiji Mesazhet te Shërbyesi
+te rregullimet “Fjalosje dhe Media”.
+Mund të caktoni një interval kohor, mes “Menjëherë” dhe “Pas 1 viti”.
+Krejt email-et e marrë nga Delta Chat-i do të fshihen te shërbyesi pas këtij intervali kohor.
-
Kini parasysh se, nëse e përdorni Delta Chat-in në më shumë se një pajisje, duhet t’i
@@ -865,6 +865,9 @@ Për programe të tjera, mund të gjeni një zgjidhje në internet.
Po. Delta Chat 1.36 vjen me një funksion të ri eksperimental për përdorimin e të njëjtës llogari në pajisje të ndryshme:
+ -
+
Make sure both devices are on the same Wi-Fi or network
+
-
Te pajisja e parë, kaloni te Rregullime → Shtoni Pajisje të Dytë, shkyçni ekranin, në u dashtë
dhe prisni një çast sa të shfaqet një kod QR
@@ -1063,12 +1066,13 @@ hapni bashkëngjitje vetëm nga dërgues që u besoni dhe jo prej kujtdo.
- - Në përgjithësi, s’ka shitore aplikacionesh nën përkujdesje dhe Delta Chat-i
-s’është përgjegjës për aplikacionet private apo lëndën e tyre. Cilido mund
-të ndajë me të tjerët aplikacione private pa kufizime.
- - Mund të gjeni disa aplikacione shembuj te webxdc.org.
- - Mjaft vetë shkruajnë aplikacionet e tyre private dhe i postojnë te forumi i
-Delta Chat-it.
+ - In general, anyone can share private apps with each
+other without restrictions.
+ - You can send ‘hi’ to xstore@testrun.org
+to see an experimental webxdc appstore.
+All of the apps are open source and for free.
+ - Many people write their own private apps and post them to the Delta Chat
+forum.
@@ -1369,7 +1373,7 @@ email-i me çfarëdo aplikacioni
-The Delta Chat project underwent four independent security audits in the last years:
+Projekti Delta Chat ka pasur katër auditime të pavarur sigurie gjatë viteve të fundit:
-
@@ -1404,11 +1408,12 @@ Mund të lexoni më tepër rreth auditimit raportin e plotë këtu.
-
-
Beginning 2023, we fixed security and privacy issues with the “web
-apps shared in a chat” feature, related to failures of sandboxing
-especially with Chromium. We subsequently got an independent security
-audit from Cure53 and all issues found were fixed in the 1.36 app series released in April 2023.
-See here for the full background story on E2E security in the web.
+ Në fillim të 2023-shit, ndreqëm probleme sigurie dhe privatësie me
+veçorinë “aplikacione web dhënë në një fjalosje”, të lidhura me dështime
+në izolimin e tyre, veçanërisht nën Chromium. Në vazhdim kaluam një auditim
+të pavarur sigurie prej Cure53 dhe krejt problemet e gjetura u ndreqën në
+seritë 1.36 të hedhura në qarkullim në prill të 2023-shit.
+Shihni këtu, për shpjegim të plotë të sfondit për sigurinë lidhur me E2E në web.
diff --git a/assets/help/uk/help.html b/assets/help/uk/help.html
index dd141d250..5159094b0 100644
--- a/assets/help/uk/help.html
+++ b/assets/help/uk/help.html
@@ -14,7 +14,7 @@
- Яких дозволів потребує Delta Chat?
- Що значить Закріплення, Приглушення, Архівування?
- Що означає зелена точка?
- - How can I delete my account?
+ - Як мені видалити свій обліковий запис?
- Групи
@@ -345,22 +345,17 @@
– З іншого боку, інші не завжди «побачать, що ви онлайн». Якщо ви вимкнули сповіщення про прочитання, вони не побачать зелену крапку, доки ви не надішлете їм повідомлення або напишете у групу, до якої вони входять.
-
+
- How can I delete my account?
+ Як мені видалити свій обліковий запис?
-
As you use an e-mail account for Delta Chat,
-how you can delete your account depends on your e-mail provider.
-We don’t have any control over your e-mail account,
-so unfortunately we can’t help you with that.
+Оскільки ви використовуєте обліковий запис електронної пошти для Delta Chat, спосіб видалення облікового запису залежить від вашого постачальника послуг електронної пошти. Ми не маємо жодного контролю над вашим обліковим записом електронної пошти, тому, на жаль, ми не можемо вам у цьому допомогти.
-If you want to keep the account,
-but uninstall Delta Chat,
-it is recommended to leave any group chat before uninstalling Delta Chat.
+Якщо ви хочете зберегти обліковий запис, але видалити Delta Chat, перед видаленням Delta Chat рекомендується покинути будь-які групові чати.
@@ -721,6 +716,9 @@ it is recommended to leave any group chat before uninstalling Delta Chat.
Так. У Delta Chat 1.36 є нова експериментальна функція для використання одного облікового запису на різних пристроях:
- 群组
@@ -182,7 +182,7 @@
- How can I add or switch between multiple accounts?
+ 如何添加账户或在多个账户之间切换?
@@ -348,10 +348,10 @@ Archived chats remain accessible above the chat list or via search.
- 另一方面,其他人并不总是能“看到您在线”。如果您关掉了已读回执,那么在您发消息给其他人以及和他们的共有群组前,其他人将不会看到绿色圆点。
-
+
- How can I delete my account?
+ 如何删除我的账户?
@@ -738,6 +738,9 @@ not their account, you can still communicate in the 1:1 chat. (webxdc.org 找到一些示例应用。
-
很多人编写了他们自己的私人应用,并将应用发布到了 Delta Chat 论坛。
+ In general, anyone can share private apps with each
+other without restrictions.
+ You can send ‘hi’ to xstore@testrun.org
+to see an experimental webxdc appstore.
+All of the apps are open source and for free.
+ Many people write their own private apps and post them to the Delta Chat
+forum.
diff --git a/build.gradle b/build.gradle
index 03f4f4628..4278127da 100644
--- a/build.gradle
+++ b/build.gradle
@@ -82,7 +82,7 @@ dependencies {
android {
namespace "org.thoughtcrime.securesms"
flavorDimensions "none"
- compileSdkVersion 32
+ compileSdkVersion 33
// Set NDK version to strip native libraries.
// Even though we compile our libraries outside Gradle with `scripts/ndk-make.sh`,
@@ -94,14 +94,14 @@ android {
useLibrary 'org.apache.http.legacy'
defaultConfig {
- versionCode 658
- versionName "1.38.2"
+ versionCode 661
+ versionName "1.41.0"
applicationId "com.b44t.messenger"
multiDexEnabled true
minSdkVersion 16
- targetSdkVersion 32
+ targetSdkVersion 33
vectorDrawables.useSupportLibrary = true
diff --git a/docs/release-checklist.md b/docs/release-checklist.md
index a5848f96d..c52d566ed 100644
--- a/docs/release-checklist.md
+++ b/docs/release-checklist.md
@@ -97,11 +97,12 @@ This may take some days.
on :
-12. a) for "Delta Chat", select tab "Add upcoming version"
- b) at "App Information" hit "Edit" abottom and then "Replace APK" atop,
- upload the APK from above
- c) on the same tab, add "Release notes" from CHANGELOG.md, "Save"
- d) hit "Submit app" at the upper right corner
+12. a) for "Delta Chat", select "Add upcoming version" on the left
+ b) at "Step 1 / Existing file(s)" hit "Replace", upload the APK from above
+ c) on the "Step 1" page, add "Release notes" from CHANGELOG.md, hit "Next"
+ d) on "Step 2" page: "Does your app collect or transfer user data to third parties?" -> No, then "Next"
+ e) on "Step 3" page: "Next"
+ f) on "Step 4" page: "Submit app"
## Releases on Apklis, Passkoocheh
diff --git a/flake.lock b/flake.lock
index 8d336f07d..7fd8b23b9 100644
--- a/flake.lock
+++ b/flake.lock
@@ -7,11 +7,11 @@
"nixpkgs": "nixpkgs"
},
"locked": {
- "lastModified": 1687378940,
- "narHash": "sha256-xGPTFVCWzbQh3DwuKedetTIpq2p6vq36xHe7HrasqPU=",
+ "lastModified": 1692995047,
+ "narHash": "sha256-L7PzSUM0bomAax+i+L+PoHokIgkD03m7MaiirIU5VDA=",
"owner": "tadfisher",
"repo": "android-nixpkgs",
- "rev": "28e855d3273cf858a3e233c7a591ad19b3066aed",
+ "rev": "6529bfb4b0d5fc38de7ac9093a14edf04a2258c5",
"type": "github"
},
"original": {
@@ -29,11 +29,11 @@
"systems": "systems"
},
"locked": {
- "lastModified": 1687173957,
- "narHash": "sha256-GOds2bAQcZ94fb9/Nl/aM+r+0wGSi4EKYuZYR8Dw4R8=",
+ "lastModified": 1692793255,
+ "narHash": "sha256-yVyj0AE280JkccDHuG1XO9oGxN6bW8ksr/xttXcXzK0=",
"owner": "numtide",
"repo": "devshell",
- "rev": "2cf83bb31720fcc29a999aee28d6da101173e66a",
+ "rev": "2aa26972b951bc05c3632d4e5ae683cb6771a7c6",
"type": "github"
},
"original": {
@@ -47,11 +47,11 @@
"systems": "systems_2"
},
"locked": {
- "lastModified": 1687171271,
- "narHash": "sha256-BJlq+ozK2B1sJDQXS3tzJM5a+oVZmi1q0FlBK/Xqv7M=",
+ "lastModified": 1692799911,
+ "narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=",
"owner": "numtide",
"repo": "flake-utils",
- "rev": "abfb11bd1aec8ced1c9bb9adfe68018230f4fb3c",
+ "rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44",
"type": "github"
},
"original": {
@@ -65,11 +65,11 @@
"systems": "systems_3"
},
"locked": {
- "lastModified": 1687171271,
- "narHash": "sha256-BJlq+ozK2B1sJDQXS3tzJM5a+oVZmi1q0FlBK/Xqv7M=",
+ "lastModified": 1692799911,
+ "narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=",
"owner": "numtide",
"repo": "flake-utils",
- "rev": "abfb11bd1aec8ced1c9bb9adfe68018230f4fb3c",
+ "rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44",
"type": "github"
},
"original": {
@@ -98,11 +98,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1686960236,
- "narHash": "sha256-AYCC9rXNLpUWzD9hm+askOfpliLEC9kwAo7ITJc4HIw=",
+ "lastModified": 1692734709,
+ "narHash": "sha256-SCFnyHCyYjwEmgUsHDDuU0TsbVMKeU1vwkR+r7uS2Rg=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "04af42f3b31dba0ef742d254456dc4c14eedac86",
+ "rev": "b85ed9dcbf187b909ef7964774f8847d554fab3b",
"type": "github"
},
"original": {
@@ -114,11 +114,11 @@
},
"nixpkgs_2": {
"locked": {
- "lastModified": 1687488839,
- "narHash": "sha256-7JDjuyHwUvGJJge9jxfRJkuYyL5G5yipspc4J3HwjGA=",
+ "lastModified": 1692808169,
+ "narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "f9e94676ce6c7531c44d38da61d2669ebec0f603",
+ "rev": "9201b5ff357e781bf014d0330d18555695df7ba8",
"type": "github"
},
"original": {
@@ -158,11 +158,11 @@
"nixpkgs": "nixpkgs_3"
},
"locked": {
- "lastModified": 1687400833,
- "narHash": "sha256-rVENiSupjAE8o1+ZXNRIqewUzM2brm+aeme8MUrwl0U=",
+ "lastModified": 1692929460,
+ "narHash": "sha256-zdN6UVtEml7t0WQHVy0avsE+TWJLklXnqJyiPaOa8u0=",
"owner": "oxalica",
"repo": "rust-overlay",
- "rev": "fc0a266e836c079a9131108f4334e5af219dbb93",
+ "rev": "673e2d3d2a3951adc6f5e3351c9fce6ad130baed",
"type": "github"
},
"original": {
diff --git a/flake.nix b/flake.nix
index df197d0b5..9ee6f14a4 100644
--- a/flake.nix
+++ b/flake.nix
@@ -33,7 +33,7 @@
buildInputs = [
android-sdk
pkgs.openjdk17
- (pkgs.buildPackages.rust-bin.stable."1.70.0".minimal.override {
+ (pkgs.buildPackages.rust-bin.stable."1.72.0".minimal.override {
targets = [
"armv7-linux-androideabi"
"aarch64-linux-android"
diff --git a/jni/Android.mk b/jni/Android.mk
index 8597475da..e2867679c 100644
--- a/jni/Android.mk
+++ b/jni/Android.mk
@@ -23,7 +23,10 @@ LOCAL_C_INCLUDES := $(JNI_DIR)/utils/
LOCAL_LDLIBS := -llog
LOCAL_STATIC_LIBRARIES := deltachat-core
-LOCAL_CFLAGS := -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast -DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
+# -Werror flag is important to catch incompatibilities between the JNI bindings and the core.
+# Otherwise passing a variable of different type such as char * instead of int
+# causes only a -Wint-conversion warning.
+LOCAL_CFLAGS := -Werror -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast -DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
LOCAL_CFLAGS += -Drestrict='' -D__EMX__ -DFIXED_POINT -DUSE_ALLOCA -DHAVE_LRINT -DHAVE_LRINTF -fno-math-errno
LOCAL_CFLAGS += -DANDROID_NDK -DDISABLE_IMPORTGL -fno-strict-aliasing -DAVOID_TABLES -DANDROID_TILE_BASED_DECODE -DANDROID_ARMV6_IDCT -ffast-math -D__STDC_CONSTANT_MACROS
diff --git a/jni/dc_wrapper.c b/jni/dc_wrapper.c
index 01d01f21d..802fce7bb 100644
--- a/jni/dc_wrapper.c
+++ b/jni/dc_wrapper.c
@@ -1026,15 +1026,6 @@ JNIEXPORT jlong Java_com_b44t_messenger_DcContext_getProviderFromEmailWithDnsCPt
}
-JNIEXPORT jlong Java_com_b44t_messenger_DcContext_getHttpResponseCPtr(JNIEnv *env, jobject obj, jstring url)
-{
- CHAR_REF(url);
- jlong ret = (jlong)dc_get_http_response(get_dc_context(env, obj), urlPtr);
- CHAR_UNREF(url);
- return ret;
-}
-
-
/*******************************************************************************
* DcEventEmitter
******************************************************************************/
@@ -1923,6 +1914,12 @@ JNIEXPORT jboolean Java_com_b44t_messenger_DcContact_isVerified(JNIEnv *env, job
}
+JNIEXPORT jint Java_com_b44t_messenger_DcContact_getVerifierId(JNIEnv *env, jobject obj)
+{
+ return dc_contact_get_verifier_id(get_dc_contact(env, obj));
+}
+
+
/*******************************************************************************
* DcLot
******************************************************************************/
@@ -2088,66 +2085,6 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcProvider_getOverviewPage(JNIEnv *env
}
-/*******************************************************************************
- * DcHttpResponse
- ******************************************************************************/
-
-
-static dc_http_response_t* get_dc_http_response(JNIEnv *env, jobject obj)
-{
- static jfieldID fid = 0;
- if (fid==0) {
- jclass cls = (*env)->GetObjectClass(env, obj);
- fid = (*env)->GetFieldID(env, cls, "httpResponseCPtr", "J" /*Signature, J=long*/);
- }
- if (fid) {
- return (dc_http_response_t*)(*env)->GetLongField(env, obj, fid);
- }
- return NULL;
-}
-
-
-JNIEXPORT void Java_com_b44t_messenger_DcHttpResponse_unrefHttpResponseCPtr(JNIEnv *env, jobject obj)
-{
- dc_http_response_unref(get_dc_http_response(env, obj));
-}
-
-
-JNIEXPORT jstring Java_com_b44t_messenger_DcHttpResponse_getMimetype(JNIEnv *env, jobject obj)
-{
- char* temp = dc_http_response_get_mimetype(get_dc_http_response(env, obj));
- jstring ret = NULL;
- if (temp) {
- ret = JSTRING_NEW(temp);
- }
- dc_str_unref(temp);
- return ret;
-}
-
-
-JNIEXPORT jstring Java_com_b44t_messenger_DcHttpResponse_getEncoding(JNIEnv *env, jobject obj)
-{
- char* temp = dc_http_response_get_encoding(get_dc_http_response(env, obj));
- jstring ret = NULL;
- if (temp) {
- ret = JSTRING_NEW(temp);
- }
- dc_str_unref(temp);
- return ret;
-}
-
-
-JNIEXPORT jbyteArray Java_com_b44t_messenger_DcHttpResponse_getBlob(JNIEnv *env, jobject obj)
-{
- jbyteArray ret = NULL;
- dc_http_response_t* http_response = get_dc_http_response(env, obj);
- size_t ptr_size = dc_http_response_get_size(http_response);
- uint8_t* ptr = dc_http_response_get_blob(http_response);
- ret = ptr2jbyteArray(env, ptr, ptr_size);
- dc_str_unref((char*)ptr);
- return ret;
-}
-
/*******************************************************************************
* DcJsonrpcInstance
******************************************************************************/
diff --git a/jni/deltachat-core-rust b/jni/deltachat-core-rust
index 8ee07e792..40d19f181 160000
--- a/jni/deltachat-core-rust
+++ b/jni/deltachat-core-rust
@@ -1 +1 @@
-Subproject commit 8ee07e7920b4288149bf1db6ec228a32efa1cd92
+Subproject commit 40d19f181db38b947620434c6094ce089708904c
diff --git a/metadata/en-US/changelogs/6594.txt b/metadata/en-US/changelogs/6594.txt
new file mode 100644
index 000000000..a0ea3e8f0
--- /dev/null
+++ b/metadata/en-US/changelogs/6594.txt
@@ -0,0 +1,5 @@
+* use image editor for avatar selection when possible
+* improve loading screen in dark mode
+* update "verified icon"
+* add Luri Bakhtiari translation, update other translations
+* lots of bug fixes
diff --git a/metadata/en-US/changelogs/6600.txt b/metadata/en-US/changelogs/6600.txt
new file mode 100644
index 000000000..a0ea3e8f0
--- /dev/null
+++ b/metadata/en-US/changelogs/6600.txt
@@ -0,0 +1,5 @@
+* use image editor for avatar selection when possible
+* improve loading screen in dark mode
+* update "verified icon"
+* add Luri Bakhtiari translation, update other translations
+* lots of bug fixes
diff --git a/res/drawable/ic_verified.xml b/res/drawable/ic_verified.xml
index a92c98538..c8b706df6 100644
--- a/res/drawable/ic_verified.xml
+++ b/res/drawable/ic_verified.xml
@@ -3,6 +3,6 @@
-
diff --git a/res/drawable/reaction_pill_background.xml b/res/drawable/reaction_pill_background.xml
new file mode 100644
index 000000000..bad81d54c
--- /dev/null
+++ b/res/drawable/reaction_pill_background.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/res/drawable/reaction_pill_background_selected.xml b/res/drawable/reaction_pill_background_selected.xml
new file mode 100644
index 000000000..b52af9b3d
--- /dev/null
+++ b/res/drawable/reaction_pill_background_selected.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/res/layout/audio_view.xml b/res/layout/audio_view.xml
index 6253cbe7b..15786f1c4 100644
--- a/res/layout/audio_view.xml
+++ b/res/layout/audio_view.xml
@@ -96,4 +96,15 @@
-
\ No newline at end of file
+
+
+
+
diff --git a/res/layout/conversation_input_panel.xml b/res/layout/conversation_input_panel.xml
index 963f3924a..fb5da9742 100644
--- a/res/layout/conversation_input_panel.xml
+++ b/res/layout/conversation_input_panel.xml
@@ -40,6 +40,10 @@
@@ -50,10 +54,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:minHeight="40dp"
- android:paddingLeft="12dp"
- android:paddingStart="12dp"
+ android:paddingLeft="6dp"
android:paddingRight="6dp"
- android:paddingEnd="6dp"
android:background="@drawable/touch_highlight_background"
android:contentDescription="@string/menu_toggle_keyboard" />
diff --git a/res/layout/conversation_item_footer.xml b/res/layout/conversation_item_footer.xml
index 170f60666..4ac5a5791 100644
--- a/res/layout/conversation_item_footer.xml
+++ b/res/layout/conversation_item_footer.xml
@@ -15,7 +15,7 @@
android:src="@drawable/msg_encr_out"
android:visibility="gone"
android:layout_gravity="center_vertical|end"
- android:contentDescription="@string/chat_input_placeholder"
+ android:importantForAccessibility="no"
tools:visibility="visible"/>
+ android:layout="@layout/conversation_item_sticker" />
@@ -200,25 +201,17 @@
android:alpha="0.7"
app:footer_text_color="?conversation_item_incoming_text_secondary_color"/>
-
-
+
+
diff --git a/res/layout/conversation_item_sent.xml b/res/layout/conversation_item_sent.xml
index 9cc41ebcb..6defb23cc 100644
--- a/res/layout/conversation_item_sent.xml
+++ b/res/layout/conversation_item_sent.xml
@@ -100,7 +100,7 @@
android:id="@+id/sticker_view_stub"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout="@layout/conversation_item_received_sticker" />
+ android:layout="@layout/conversation_item_sticker" />
@@ -177,26 +178,18 @@
android:clipToPadding="false"
app:footer_text_color="?attr/conversation_item_outgoing_text_secondary_color"/>
-
-
+
+
-
diff --git a/res/layout/conversation_item_received_sticker.xml b/res/layout/conversation_item_sticker.xml
similarity index 85%
rename from res/layout/conversation_item_received_sticker.xml
rename to res/layout/conversation_item_sticker.xml
index 6e24028f8..f597ffdc0 100644
--- a/res/layout/conversation_item_received_sticker.xml
+++ b/res/layout/conversation_item_sticker.xml
@@ -4,4 +4,4 @@
android:id="@+id/image_view"
android:layout_width="@dimen/media_bubble_sticker_dimens"
android:layout_height="@dimen/media_bubble_sticker_dimens"
- android:contentDescription="@string/image" />
+ android:contentDescription="@string/sticker" />
diff --git a/res/layout/profile_settings_item.xml b/res/layout/profile_settings_item.xml
index 37b3c06b4..1a57eec3a 100644
--- a/res/layout/profile_settings_item.xml
+++ b/res/layout/profile_settings_item.xml
@@ -10,6 +10,7 @@
diff --git a/res/layout/reaction_recipient_item.xml b/res/layout/reaction_recipient_item.xml
new file mode 100644
index 000000000..e4944fc9b
--- /dev/null
+++ b/res/layout/reaction_recipient_item.xml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/reactions_details_fragment.xml b/res/layout/reactions_details_fragment.xml
new file mode 100644
index 000000000..cb161704b
--- /dev/null
+++ b/res/layout/reactions_details_fragment.xml
@@ -0,0 +1,7 @@
+
diff --git a/res/layout/reactions_pill.xml b/res/layout/reactions_pill.xml
new file mode 100644
index 000000000..c6f4cddf6
--- /dev/null
+++ b/res/layout/reactions_pill.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/recent_photo_view_item.xml b/res/layout/recent_photo_view_item.xml
index b59d2ab48..d713a2cec 100644
--- a/res/layout/recent_photo_view_item.xml
+++ b/res/layout/recent_photo_view_item.xml
@@ -11,7 +11,8 @@
+ android:scaleType="centerCrop"
+ android:contentDescription="@string/image"/>
diff --git a/res/layout/registration_activity.xml b/res/layout/registration_activity.xml
index c22ac042a..66c31e899 100644
--- a/res/layout/registration_activity.xml
+++ b/res/layout/registration_activity.xml
@@ -151,7 +151,7 @@
app:layout_constraintStart_toEndOf="@id/advanced_icon"
app:layout_constraintTop_toBottomOf="@id/no_servers_hint" />
-
+
+
diff --git a/res/layout/webxdc_compact_view.xml b/res/layout/webxdc_compact_view.xml
index a1357eae4..5aa6a6622 100644
--- a/res/layout/webxdc_compact_view.xml
+++ b/res/layout/webxdc_compact_view.xml
@@ -1,6 +1,7 @@
-
+ android:scaleType="centerCrop"
+ app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.CornerSize10Percent" />
-
+
+ android:scaleType="centerCrop"
+ app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.CornerSize10Percent" />
+
+
+
+
diff --git a/res/raw/webxdc.js b/res/raw/webxdc.js
index 014ad4144..2b99c2760 100644
--- a/res/raw/webxdc.js
+++ b/res/raw/webxdc.js
@@ -45,15 +45,13 @@ window.webxdc = (() => {
if (!message.file && !message.text) {
return Promise.reject("sendToChat() error: file or text missing");
}
- /** @type {(file: Blob) => Promise} */
+
const blobToBase64 = (file) => {
const dataStart = ";base64,";
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
- /** @type {string} */
- //@ts-ignore
let data = reader.result;
resolve(data.slice(data.indexOf(dataStart) + dataStart.length));
};
@@ -64,7 +62,6 @@ window.webxdc = (() => {
data.text = message.text;
}
- /** @type {{file_name: string, file_message: string} | null} */
if (message.file) {
let base64content;
if (!message.file.name) {
@@ -78,20 +75,12 @@ window.webxdc = (() => {
return Promise.reject("sendToChat() error: only one of blob, base64 or plainText allowed");
}
- // @ts-ignore - needed because typescript imagines that blob would not exist
if (message.file.blob instanceof Blob) {
- // @ts-ignore - needed because typescript imagines that blob would not exist
base64content = await blobToBase64(message.file.blob);
- // @ts-ignore - needed because typescript imagines that base64 would not exist
} else if (typeof message.file.base64 === "string") {
- // @ts-ignore - needed because typescript imagines that base64 would not exist
base64content = message.file.base64;
- // @ts-ignore - needed because typescript imagines that plainText would not exist
} else if (typeof message.file.plainText === "string") {
- base64content = await blobToBase64(
- // @ts-ignore - needed because typescript imagines that plainText would not exist
- new Blob([message.file.plainText])
- );
+ base64content = await blobToBase64(new Blob([message.file.plainText]));
} else {
return Promise.reject("sendToChat() error: none of blob, base64 or plainText set correctly");
}
diff --git a/res/raw/webxdc_wrapper.html b/res/raw/webxdc_wrapper.html
index ab20e2fa1..9dd7898c6 100644
--- a/res/raw/webxdc_wrapper.html
+++ b/res/raw/webxdc_wrapper.html
@@ -1,6 +1,7 @@
+
+
diff --git a/res/xml/preferences_advanced.xml b/res/xml/preferences_advanced.xml
index d43b1d39a..78888de4c 100644
--- a/res/xml/preferences_advanced.xml
+++ b/res/xml/preferences_advanced.xml
@@ -57,6 +57,12 @@
android:key="pref_incognito_keyboard"
android:summary="@string/pref_incognito_keyboard_explain"
android:title="@string/pref_incognito_keyboard" />
+
+
diff --git a/scripts/rust-toolchain b/scripts/rust-toolchain
index 832e9afb6..0834888f5 100644
--- a/scripts/rust-toolchain
+++ b/scripts/rust-toolchain
@@ -1 +1 @@
-1.70.0
+1.72.0
diff --git a/src/com/b44t/messenger/DcChat.java b/src/com/b44t/messenger/DcChat.java
index 4b4522a39..e87a407e7 100644
--- a/src/com/b44t/messenger/DcChat.java
+++ b/src/com/b44t/messenger/DcChat.java
@@ -17,7 +17,10 @@ public class DcChat {
public final static int DC_CHAT_VISIBILITY_ARCHIVED = 1;
public final static int DC_CHAT_VISIBILITY_PINNED = 2;
- public DcChat(long chatCPtr) {
+ private int accountId;
+
+ public DcChat(int accountId, long chatCPtr) {
+ this.accountId = accountId;
this.chatCPtr = chatCPtr;
}
@@ -27,6 +30,7 @@ public class DcChat {
chatCPtr = 0;
}
+ public int getAccountId () { return accountId; }
public native int getId ();
public native int getType ();
public native int getVisibility ();
diff --git a/src/com/b44t/messenger/DcChatlist.java b/src/com/b44t/messenger/DcChatlist.java
index 0479abb95..4a250d15d 100644
--- a/src/com/b44t/messenger/DcChatlist.java
+++ b/src/com/b44t/messenger/DcChatlist.java
@@ -2,7 +2,10 @@ package com.b44t.messenger;
public class DcChatlist {
- public DcChatlist(long chatlistCPtr) {
+ private int accountId;
+
+ public DcChatlist(int accountId, long chatlistCPtr) {
+ this.accountId = accountId;
this.chatlistCPtr = chatlistCPtr;
}
@@ -12,9 +15,10 @@ public class DcChatlist {
chatlistCPtr = 0;
}
+ public int getAccountId() { return accountId; }
public native int getCnt ();
public native int getChatId (int index);
- public DcChat getChat (int index) { return new DcChat(getChatCPtr(index)); }
+ public DcChat getChat (int index) { return new DcChat(accountId, getChatCPtr(index)); }
public native int getMsgId (int index);
public DcMsg getMsg (int index) { return new DcMsg(getMsgCPtr(index)); }
public DcLot getSummary(int index, DcChat chat) { return new DcLot(getSummaryCPtr(index, chat==null? 0 : chat.getChatCPtr())); }
diff --git a/src/com/b44t/messenger/DcContact.java b/src/com/b44t/messenger/DcContact.java
index 3fc164b3f..a8353e6c1 100644
--- a/src/com/b44t/messenger/DcContact.java
+++ b/src/com/b44t/messenger/DcContact.java
@@ -58,6 +58,7 @@ public class DcContact {
public native boolean wasSeenRecently();
public native boolean isBlocked ();
public native boolean isVerified ();
+ public native int getVerifierId ();
// working with raw c-data
private long contactCPtr; // CAVE: the name is referenced in the JNI
diff --git a/src/com/b44t/messenger/DcContext.java b/src/com/b44t/messenger/DcContext.java
index e0314d584..33736f654 100644
--- a/src/com/b44t/messenger/DcContext.java
+++ b/src/com/b44t/messenger/DcContext.java
@@ -11,6 +11,7 @@ public class DcContext {
public final static int DC_EVENT_ERROR = 400;
public final static int DC_EVENT_ERROR_SELF_NOT_IN_GROUP = 410;
public final static int DC_EVENT_MSGS_CHANGED = 2000;
+ public final static int DC_EVENT_REACTIONS_CHANGED = 2001;
public final static int DC_EVENT_INCOMING_MSG = 2005;
public final static int DC_EVENT_MSGS_NOTICED = 2008;
public final static int DC_EVENT_MSG_DELIVERED = 2010;
@@ -101,10 +102,6 @@ public class DcContext {
@Override
protected void finalize() throws Throwable {
super.finalize();
- unref();
- }
-
- public void unref() {
if (contextCPtr != 0) {
unrefContextCPtr();
contextCPtr = 0;
@@ -161,8 +158,8 @@ public class DcContext {
public native String getContactEncrInfo (int contact_id);
public native boolean deleteContact (int id);
public native int addAddressBook (String adrbook);
- public DcChatlist getChatlist (int listflags, String query, int queryId) { return new DcChatlist(getChatlistCPtr(listflags, query, queryId)); }
- public DcChat getChat (int chat_id) { return new DcChat(getChatCPtr(chat_id)); }
+ public DcChatlist getChatlist (int listflags, String query, int queryId) { return new DcChatlist(getAccountId(), getChatlistCPtr(listflags, query, queryId)); }
+ public DcChat getChat (int chat_id) { return new DcChat(getAccountId(), getChatCPtr(chat_id)); }
public native String getChatEncrInfo (int chat_id);
public native void markseenMsgs (int msg_ids[]);
public native void marknoticedChat (int chat_id);
@@ -216,7 +213,6 @@ public class DcContext {
public DcArray getLocations (int chat_id, int contact_id, long timestamp_start, long timestamp_end) { return new DcArray(getLocationsCPtr(chat_id, contact_id, timestamp_start, timestamp_end)); }
public native void deleteAllLocations ();
public DcProvider getProviderFromEmailWithDns (String email) { long cptr = getProviderFromEmailWithDnsCPtr(email); return cptr!=0 ? new DcProvider(cptr) : null; }
- public DcHttpResponse getHttpResponse (String url) { long cptr = getHttpResponseCPtr(url); return cptr!=0 ? new DcHttpResponse(cptr) : null; }
public String getNameNAddr() {
String displayname = getConfig("displayname");
@@ -256,5 +252,4 @@ public class DcContext {
private native long checkQrCPtr (String qr);
private native long getProviderFromEmailWithDnsCPtr (String addr);
private native long newBackupProviderCPtr();
- private native long getHttpResponseCPtr(String url);
}
diff --git a/src/com/b44t/messenger/DcHttpResponse.java b/src/com/b44t/messenger/DcHttpResponse.java
deleted file mode 100644
index f41f9cd1c..000000000
--- a/src/com/b44t/messenger/DcHttpResponse.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.b44t.messenger;
-
-public class DcHttpResponse {
-
- public DcHttpResponse(long httpResponseCPtr) {
- this.httpResponseCPtr = httpResponseCPtr;
- }
-
- @Override protected void finalize() throws Throwable {
- super.finalize();
- unrefHttpResponseCPtr();
- httpResponseCPtr = 0;
- }
-
- public native String getMimetype ();
- public native String getEncoding ();
- public native byte[] getBlob ();
-
- // working with raw c-data
- private long httpResponseCPtr; // CAVE: the name is referenced in the JNI
- private native void unrefHttpResponseCPtr();
-}
diff --git a/src/com/b44t/messenger/rpc/HttpResponse.java b/src/com/b44t/messenger/rpc/HttpResponse.java
index bd07a1bb5..ccc6b8c6b 100644
--- a/src/com/b44t/messenger/rpc/HttpResponse.java
+++ b/src/com/b44t/messenger/rpc/HttpResponse.java
@@ -4,11 +4,11 @@ import android.util.Base64;
public class HttpResponse {
// base64-encoded response body.
- private String blob;
+ private final String blob;
// MIME type, e.g. "text/plain" or "text/html".
- private String mimetype;
+ private final String mimetype;
// Encoding, e.g. "utf-8".
- private String encoding;
+ private final String encoding;
public HttpResponse(String blob, String mimetype, String encoding) {
this.blob = blob;
diff --git a/src/com/b44t/messenger/rpc/Reaction.java b/src/com/b44t/messenger/rpc/Reaction.java
new file mode 100644
index 000000000..493cb4ec5
--- /dev/null
+++ b/src/com/b44t/messenger/rpc/Reaction.java
@@ -0,0 +1,39 @@
+package com.b44t.messenger.rpc;
+
+import androidx.annotation.Nullable;
+
+public class Reaction {
+ // The reaction emoji string.
+ private final String emoji;
+ // The count of users that have reacted with this reaction.
+ private final int count;
+ // true if self-account reacted with this reaction, false otherwise.
+ private final boolean isFromSelf;
+
+ public Reaction(String emoji, int count, boolean isFromSelf) {
+ this.emoji = emoji;
+ this.count = count;
+ this.isFromSelf = isFromSelf;
+ }
+
+ public String getEmoji() {
+ return emoji;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public boolean isFromSelf() {
+ return isFromSelf;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (obj instanceof Reaction) {
+ Reaction reaction = (Reaction) obj;
+ return emoji.equals(reaction.getEmoji()) && count == reaction.getCount() && isFromSelf == reaction.isFromSelf();
+ }
+ return false;
+ }
+}
diff --git a/src/com/b44t/messenger/rpc/Reactions.java b/src/com/b44t/messenger/rpc/Reactions.java
new file mode 100644
index 000000000..7b8849868
--- /dev/null
+++ b/src/com/b44t/messenger/rpc/Reactions.java
@@ -0,0 +1,26 @@
+package com.b44t.messenger.rpc;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Reactions {
+ // Map from a contact to it's reaction to message.
+ private final HashMap reactionsByContact;
+ // Unique reactions, sorted in descending order.
+ private final ArrayList reactions;
+
+ public Reactions(HashMap reactionsByContact, ArrayList reactions) {
+ this.reactionsByContact = reactionsByContact;
+ this.reactions = reactions;
+ }
+
+ public Map getReactionsByContact() {
+ return reactionsByContact;
+ }
+
+ public List getReactions() {
+ return reactions;
+ }
+}
diff --git a/src/com/b44t/messenger/rpc/Rpc.java b/src/com/b44t/messenger/rpc/Rpc.java
index 8f255c68c..ae8c4e61a 100644
--- a/src/com/b44t/messenger/rpc/Rpc.java
+++ b/src/com/b44t/messenger/rpc/Rpc.java
@@ -8,7 +8,6 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
-import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
@@ -41,7 +40,7 @@ public class Rpc {
} else if (response.result != null) {
future.set(response.result);
} else {
- future.setException(new RpcException("Got JSON-RPC response witout result or error: " + jsonResponse));
+ future.setException(new RpcException("Got JSON-RPC response without result or error: " + jsonResponse));
}
}
@@ -100,12 +99,16 @@ public class Rpc {
return gson.fromJson(getResult("get_http_response", accountId, url), HttpResponse.class);
}
+ public Reactions getMsgReactions(int accountId, int msgId) throws RpcException {
+ return gson.fromJson(getResult("get_message_reactions", accountId, msgId), Reactions.class);
+ }
+
private static class Request {
- public String jsonrpc = "2.0";
- public String method;
- public Object[] params;
- public int id;
+ private final String jsonrpc = "2.0";
+ public final String method;
+ public final Object[] params;
+ public final int id;
public Request(String method, Object[] params, int id) {
this.method = method;
@@ -115,9 +118,9 @@ public class Rpc {
}
private static class Response {
- public int id = 0;
- public JsonElement result;
- public JsonElement error;
+ public final int id;
+ public final JsonElement result;
+ public final JsonElement error;
public Response(int id, JsonElement result, JsonElement error) {
this.id = id;
diff --git a/src/com/b44t/messenger/util/concurrent/SettableFuture.java b/src/com/b44t/messenger/util/concurrent/SettableFuture.java
index 1255b907b..003f93b8a 100644
--- a/src/com/b44t/messenger/util/concurrent/SettableFuture.java
+++ b/src/com/b44t/messenger/util/concurrent/SettableFuture.java
@@ -3,7 +3,6 @@ package com.b44t.messenger.util.concurrent;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
diff --git a/src/org/thoughtcrime/securesms/ApplicationContext.java b/src/org/thoughtcrime/securesms/ApplicationContext.java
index 98227cc7f..e34f6646c 100644
--- a/src/org/thoughtcrime/securesms/ApplicationContext.java
+++ b/src/org/thoughtcrime/securesms/ApplicationContext.java
@@ -45,6 +45,7 @@ import org.thoughtcrime.securesms.notifications.NotificationCenter;
import org.thoughtcrime.securesms.util.AndroidSignalProtocolLogger;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
+import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.SignalProtocolLoggerProvider;
import java.io.File;
@@ -133,6 +134,16 @@ public class ApplicationContext extends MultiDexApplication {
dcAccounts.getAccount(accountId).setConfig(CONFIG_VERIFIED_ONE_ON_ONE_CHATS, "1");
}
+ // migrating chat backgrounds, added 04/10/23, can be removed after some versions
+ String backgroundImagePath = Prefs.getStringPreference(this, "pref_chat_background", "");
+ if (!backgroundImagePath.isEmpty()) {
+ for (int accId : dcAccounts.getAll()) {
+ Prefs.setBackgroundImagePath(this, accId, backgroundImagePath);
+ }
+ Prefs.setStringPreference(this, "pref_chat_background", "");
+ }
+ // /migrating chat backgrounds
+
// set translations before starting I/O to avoid sending untranslated MDNs (issue #2288)
DcHelper.setStockTranslations(this);
diff --git a/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java b/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java
index 89c669678..75e96d619 100644
--- a/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java
+++ b/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java
@@ -17,6 +17,7 @@
*/
package org.thoughtcrime.securesms;
+import android.Manifest;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
@@ -30,6 +31,7 @@ import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
+import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.fragment.app.Fragment;
@@ -49,7 +51,6 @@ import org.thoughtcrime.securesms.preferences.ChatsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment;
import org.thoughtcrime.securesms.preferences.NotificationsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.widgets.ProfilePreference;
-import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.qr.BackupTransferActivity;
@@ -65,9 +66,6 @@ import org.thoughtcrime.securesms.util.ScreenLockUtil;
public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarActivity
implements SharedPreferences.OnSharedPreferenceChangeListener
{
- @SuppressWarnings("unused")
- private static final String TAG = ApplicationPreferencesActivity.class.getSimpleName();
-
private static final String PREFERENCE_CATEGORY_PROFILE = "preference_category_profile";
private static final String PREFERENCE_CATEGORY_NOTIFICATIONS = "preference_category_notifications";
private static final String PREFERENCE_CATEGORY_APPEARANCE = "preference_category_appearance";
@@ -79,15 +77,6 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
public static final int REQUEST_CODE_SET_BACKGROUND = 11;
- private final DynamicTheme dynamicTheme = new DynamicTheme();
- private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
-
- @Override
- protected void onPreCreate() {
- dynamicTheme.onCreate(this);
- dynamicLanguage.onCreate(this);
- }
-
@Override
protected void onCreate(Bundle icicle, boolean ready) {
//noinspection ConstantConditions
@@ -98,13 +87,6 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
}
}
- @Override
- public void onResume() {
- super.onResume();
- dynamicTheme.onResume(this);
- dynamicLanguage.onResume(this);
- }
-
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
@@ -137,7 +119,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
DynamicTheme.setDefaultDayNightMode(this);
recreate();
} else if (key.equals(Prefs.LANGUAGE_PREF)) {
- recreate();
+ finish();
}
}
@@ -252,7 +234,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
}
private class CategoryClickListener implements Preference.OnPreferenceClickListener {
- private String category;
+ private final String category;
CategoryClickListener(String category) {
this.category = category;
@@ -264,7 +246,17 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
switch (category) {
case PREFERENCE_CATEGORY_NOTIFICATIONS:
- fragment = new NotificationsPreferenceFragment();
+ NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getActivity());
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || notificationManager.areNotificationsEnabled()) {
+ fragment = new NotificationsPreferenceFragment();
+ } else {
+ new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.notifications_disabled)
+ .setMessage(R.string.perm_explain_access_to_notifications_denied)
+ .setPositiveButton(R.string.perm_continue, (dialog, which) -> getActivity().startActivity(Permissions.getApplicationSettingsIntent(getActivity())))
+ .setNegativeButton(android.R.string.cancel, null)
+ .show();
+ }
break;
case PREFERENCE_CATEGORY_CONNECTIVITY:
startActivity(new Intent(getActivity(), ConnectivityActivity.class));
diff --git a/src/org/thoughtcrime/securesms/AttachContactActivity.java b/src/org/thoughtcrime/securesms/AttachContactActivity.java
new file mode 100644
index 000000000..1cdbf9c81
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/AttachContactActivity.java
@@ -0,0 +1,28 @@
+package org.thoughtcrime.securesms;
+
+import android.content.Intent;
+
+import com.b44t.messenger.DcContext;
+
+import org.thoughtcrime.securesms.connect.DcHelper;
+
+public class AttachContactActivity extends ContactSelectionActivity {
+
+ public static final String NAME_EXTRA = "name_extra";
+ public static final String ADDR_EXTRA = "addr_extra";
+
+ @Override
+ public void onContactSelected(int specialId, String addr) {
+ String name = "";
+ DcContext dcContext = DcHelper.getContext(this);
+ int contactId = dcContext.lookupContactIdByAddr(addr);
+ if (contactId != 0) {
+ name = dcContext.getContact(contactId).getDisplayName();
+ }
+ Intent intent = new Intent();
+ intent.putExtra(NAME_EXTRA, name);
+ intent.putExtra(ADDR_EXTRA, addr);
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/BaseActionBarActivity.java b/src/org/thoughtcrime/securesms/BaseActionBarActivity.java
index d63654125..6297cd58c 100644
--- a/src/org/thoughtcrime/securesms/BaseActionBarActivity.java
+++ b/src/org/thoughtcrime/securesms/BaseActionBarActivity.java
@@ -1,5 +1,6 @@
package org.thoughtcrime.securesms;
+import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
@@ -15,6 +16,8 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
+import org.thoughtcrime.securesms.util.DynamicLanguage;
+import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.Prefs;
import java.lang.reflect.Field;
@@ -25,9 +28,17 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
private static final String TAG = BaseActionBarActivity.class.getSimpleName();
public static final String LOCALE_EXTRA = "locale_extra";
+ protected final DynamicLanguage dynamicLanguage = new DynamicLanguage();
+ protected DynamicTheme dynamicTheme = new DynamicTheme();
+
+ protected void onPreCreate() {
+ dynamicTheme.onCreate(this);
+ dynamicLanguage.onCreate(this);
+ }
@Override
protected void onCreate(Bundle savedInstanceState) {
+ onPreCreate();
if (BaseActivity.isMenuWorkaroundRequired()) {
forceOverflowMenu();
}
@@ -38,6 +49,15 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
protected void onResume() {
super.onResume();
initializeScreenshotSecurity();
+ dynamicTheme.onResume(this);
+ dynamicLanguage.onResume(this);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ // on orientation changes locale is reset in the context/activity so set locale as onCreate()
+ dynamicLanguage.onCreate(this);
}
@Override
diff --git a/src/org/thoughtcrime/securesms/BaseConversationItem.java b/src/org/thoughtcrime/securesms/BaseConversationItem.java
index 6d0d36e38..35d353fbd 100644
--- a/src/org/thoughtcrime/securesms/BaseConversationItem.java
+++ b/src/org/thoughtcrime/securesms/BaseConversationItem.java
@@ -13,9 +13,11 @@ import androidx.appcompat.app.AlertDialog;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcMsg;
+import com.b44t.messenger.rpc.Rpc;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.recipients.Recipient;
+import org.thoughtcrime.securesms.util.Util;
import java.util.HashSet;
import java.util.Set;
@@ -23,7 +25,7 @@ import java.util.Set;
public abstract class BaseConversationItem extends LinearLayout
implements BindableConversationItem
{
- static long PULSE_HIGHLIGHT_MILLIS = 500;
+ static final long PULSE_HIGHLIGHT_MILLIS = 500;
protected DcMsg messageRecord;
protected DcChat dcChat;
@@ -32,6 +34,7 @@ public abstract class BaseConversationItem extends LinearLayout
protected final Context context;
protected final DcContext dcContext;
protected Recipient conversationRecipient;
+ protected final Rpc rpc;
protected @NonNull Set batchSelected = new HashSet<>();
@@ -41,6 +44,7 @@ public abstract class BaseConversationItem extends LinearLayout
super(context, attrs);
this.context = context;
this.dcContext = DcHelper.getContext(context);
+ this.rpc = DcHelper.getRpc(context);
}
protected void bind(@NonNull DcMsg messageRecord,
@@ -81,6 +85,8 @@ public abstract class BaseConversationItem extends LinearLayout
|| messageRecord.getInfoType() == DcMsg.DC_INFO_PROTECTION_ENABLED);
}
+ protected void onAccessibilityClick() {}
+
protected class PassthroughClickListener implements View.OnLongClickListener, View.OnClickListener {
@Override
@@ -99,7 +105,7 @@ public abstract class BaseConversationItem extends LinearLayout
}
protected class ClickListener implements View.OnClickListener {
- private OnClickListener parent;
+ private final OnClickListener parent;
ClickListener(@Nullable OnClickListener parent) {
this.parent = parent;
@@ -107,6 +113,9 @@ public abstract class BaseConversationItem extends LinearLayout
public void onClick(View v) {
if (!shouldInterceptClicks(messageRecord) && parent != null) {
+ if (batchSelected.isEmpty() && Util.isTouchExplorationEnabled(context)) {
+ BaseConversationItem.this.onAccessibilityClick();
+ }
parent.onClick(v);
} else if (messageRecord.isFailed()) {
View view = View.inflate(context, R.layout.message_details_view, null);
diff --git a/src/org/thoughtcrime/securesms/BindableConversationItem.java b/src/org/thoughtcrime/securesms/BindableConversationItem.java
index 39cc245ad..c59d9a73e 100644
--- a/src/org/thoughtcrime/securesms/BindableConversationItem.java
+++ b/src/org/thoughtcrime/securesms/BindableConversationItem.java
@@ -9,7 +9,6 @@ import com.b44t.messenger.DcMsg;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
-import java.util.List;
import java.util.Locale;
import java.util.Set;
@@ -30,5 +29,6 @@ public interface BindableConversationItem extends Unbindable {
void onQuoteClicked(DcMsg messageRecord);
void onShowFullClicked(DcMsg messageRecord);
void onDownloadClicked(DcMsg messageRecord);
+ void onReactionClicked(DcMsg messageRecord);
}
}
diff --git a/src/org/thoughtcrime/securesms/BlockedAndShareContactsActivity.java b/src/org/thoughtcrime/securesms/BlockedContactsActivity.java
similarity index 65%
rename from src/org/thoughtcrime/securesms/BlockedAndShareContactsActivity.java
rename to src/org/thoughtcrime/securesms/BlockedContactsActivity.java
index 43d2332db..261cc22c8 100644
--- a/src/org/thoughtcrime/securesms/BlockedAndShareContactsActivity.java
+++ b/src/org/thoughtcrime/securesms/BlockedContactsActivity.java
@@ -1,11 +1,9 @@
package org.thoughtcrime.securesms;
-import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.appcompat.app.AlertDialog;
@@ -26,42 +24,17 @@ import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter;
import org.thoughtcrime.securesms.contacts.ContactSelectionListItem;
import org.thoughtcrime.securesms.mms.GlideApp;
-import org.thoughtcrime.securesms.util.DynamicLanguage;
-import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.ViewUtil;
-import java.util.Objects;
-
-public class BlockedAndShareContactsActivity extends PassphraseRequiredActionBarActivity {
-
- public static final String SHOW_ONLY_BLOCKED_EXTRA = "show_only_blocked";
- public static final String SHARE_CONTACT_NAME_EXTRA = "share_contact_name";
- public static final String SHARE_CONTACT_MAIL_EXTRA = "share_contact_mail";
-
- private final DynamicTheme dynamicTheme = new DynamicTheme();
- private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
-
- @Override
- public void onPreCreate() {
- dynamicTheme.onCreate(this);
- dynamicLanguage.onCreate(this);
- }
+public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity {
@Override
public void onCreate(Bundle bundle, boolean ready) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- boolean showOnlyBlocked = getIntent().getBooleanExtra(SHOW_ONLY_BLOCKED_EXTRA, false);
- getSupportActionBar().setTitle(showOnlyBlocked ? R.string.pref_blocked_contacts : R.string.contacts_headline);
+ getSupportActionBar().setTitle(R.string.pref_blocked_contacts);
initFragment(android.R.id.content, new BlockedAndShareContactsFragment(), null, getIntent().getExtras());
}
- @Override
- public void onResume() {
- super.onResume();
- dynamicTheme.onResume(this);
- dynamicLanguage.onResume(this);
- }
-
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
@@ -80,8 +53,6 @@ public class BlockedAndShareContactsActivity extends PassphraseRequiredActionBar
private RecyclerView recyclerView;
private TextView emptyStateView;
- private boolean showOnlyBlocked;
-
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
View view = inflater.inflate(R.layout.contact_selection_list_fragment, container, false);
@@ -95,7 +66,6 @@ public class BlockedAndShareContactsActivity extends PassphraseRequiredActionBar
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
- showOnlyBlocked = getArguments().getBoolean(SHOW_ONLY_BLOCKED_EXTRA, false);
getLoaderManager().initLoader(0, null, this);
}
@@ -116,7 +86,7 @@ public class BlockedAndShareContactsActivity extends PassphraseRequiredActionBar
@Override
public Loader onCreateLoader(int id, Bundle args) {
- return new DcContactsLoader(getActivity(), showOnlyBlocked ? -1 : DcContext.DC_GCL_ADD_SELF, null, false, showOnlyBlocked);
+ return new DcContactsLoader(getActivity(), -1, null, false, true, true);
}
@Override
@@ -152,15 +122,11 @@ public class BlockedAndShareContactsActivity extends PassphraseRequiredActionBar
@Override
public void onItemClick(ContactSelectionListItem item, boolean handleActionMode) {
- if(showOnlyBlocked) {
- new AlertDialog.Builder(getActivity())
- .setMessage(R.string.ask_unblock_contact)
- .setCancelable(true)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(R.string.menu_unblock_contact, (dialog, which) -> unblockContact(item.getContactId())).show();
- } else {
- shareContact(item.getName(), item.getNumber());
- }
+ new AlertDialog.Builder(getActivity())
+ .setMessage(R.string.ask_unblock_contact)
+ .setCancelable(true)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(R.string.menu_unblock_contact, (dialog, which) -> unblockContact(item.getContactId())).show();
}
private void unblockContact(int contactId) {
@@ -169,19 +135,8 @@ public class BlockedAndShareContactsActivity extends PassphraseRequiredActionBar
restartLoader();
}
- private void shareContact(String name, String mail) {
- Intent intent = new Intent();
- intent.putExtra(BlockedAndShareContactsActivity.SHARE_CONTACT_NAME_EXTRA, name);
- intent.putExtra(BlockedAndShareContactsActivity.SHARE_CONTACT_MAIL_EXTRA, mail);
- FragmentActivity activity = Objects.requireNonNull(getActivity());
- activity.setResult(RESULT_OK, intent);
- activity.finish();
- }
-
@Override
- public void onItemLongClick(ContactSelectionListItem view) {
- // Not needed
- }
+ public void onItemLongClick(ContactSelectionListItem view) {}
}
}
diff --git a/src/org/thoughtcrime/securesms/ConnectivityActivity.java b/src/org/thoughtcrime/securesms/ConnectivityActivity.java
index 81207a98f..ebf0cc06f 100644
--- a/src/org/thoughtcrime/securesms/ConnectivityActivity.java
+++ b/src/org/thoughtcrime/securesms/ConnectivityActivity.java
@@ -35,7 +35,8 @@ public class ConnectivityActivity extends WebViewActivity implements DcEventCent
}
private void refresh() {
- String connectivityHtml = DcHelper.getContext(this).getConnectivityHtml();
+ final String connectivityHtml = DcHelper.getContext(this).getConnectivityHtml()
+ .replace("", " html { color-scheme: dark light; }");
webView.loadDataWithBaseURL(null, connectivityHtml, "text/html", "utf-8", null);
}
diff --git a/src/org/thoughtcrime/securesms/ContactMultiSelectionActivity.java b/src/org/thoughtcrime/securesms/ContactMultiSelectionActivity.java
index d7090f5f1..81ae4d653 100644
--- a/src/org/thoughtcrime/securesms/ContactMultiSelectionActivity.java
+++ b/src/org/thoughtcrime/securesms/ContactMultiSelectionActivity.java
@@ -33,9 +33,6 @@ import java.util.List;
*/
public class ContactMultiSelectionActivity extends ContactSelectionActivity {
- @SuppressWarnings("unused")
- private final static String TAG = ContactMultiSelectionActivity.class.getSimpleName();
-
@Override
protected void onCreate(Bundle icicle, boolean ready) {
getIntent().putExtra(ContactSelectionListFragment.MULTI_SELECT, true);
@@ -65,9 +62,6 @@ public class ContactMultiSelectionActivity extends ContactSelectionActivity {
saveSelection();
finish();
return true;
- case android.R.id.home:
- finish();
- return true;
}
return false;
diff --git a/src/org/thoughtcrime/securesms/ContactSelectionActivity.java b/src/org/thoughtcrime/securesms/ContactSelectionActivity.java
index 62e6cb825..cff905c4b 100644
--- a/src/org/thoughtcrime/securesms/ContactSelectionActivity.java
+++ b/src/org/thoughtcrime/securesms/ContactSelectionActivity.java
@@ -17,11 +17,10 @@
package org.thoughtcrime.securesms;
import android.os.Bundle;
+import android.view.MenuItem;
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
-import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
-import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.ViewUtil;
/**
@@ -35,17 +34,14 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
{
private static final String TAG = ContactSelectionActivity.class.getSimpleName();
- private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
- private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
-
protected ContactSelectionListFragment contactsFragment;
private ContactFilterToolbar toolbar;
@Override
protected void onPreCreate() {
- dynamicTheme.onCreate(this);
- dynamicLanguage.onCreate(this);
+ dynamicTheme = new DynamicNoActionBarTheme();
+ super.onPreCreate();
}
@Override
@@ -57,13 +53,6 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
initializeSearch();
}
- @Override
- public void onResume() {
- super.onResume();
- dynamicTheme.onResume(this);
- dynamicLanguage.onResume(this);
- }
-
protected ContactFilterToolbar getToolbar() {
return toolbar;
}
@@ -88,6 +77,17 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
toolbar.setOnFilterChangedListener(filter -> contactsFragment.setQueryFilter(filter));
}
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ super.onOptionsItemSelected(item);
+
+ switch (item.getItemId()) {
+ case android.R.id.home: super.onBackPressed(); return true;
+ }
+
+ return false;
+ }
+
@Override
public void onContactSelected(int specialId, String number) {}
diff --git a/src/org/thoughtcrime/securesms/ContactSelectionListFragment.java b/src/org/thoughtcrime/securesms/ContactSelectionListFragment.java
index 4c0791403..fbfb26495 100644
--- a/src/org/thoughtcrime/securesms/ContactSelectionListFragment.java
+++ b/src/org/thoughtcrime/securesms/ContactSelectionListFragment.java
@@ -77,12 +77,11 @@ public class ContactSelectionListFragment extends Fragment
implements LoaderManager.LoaderCallbacks,
DcEventCenter.DcEventDelegate
{
- @SuppressWarnings("unused")
private static final String TAG = ContactSelectionListFragment.class.getSimpleName();
public static final String MULTI_SELECT = "multi_select";
public static final String SELECT_VERIFIED_EXTRA = "select_verified";
- public static final String FROM_SHARE_ACTIVITY_EXTRA = "from_share_activity";
+ public static final String ALLOW_CREATION = "allow_creation";
public static final String PRESELECTED_CONTACTS = "preselected_contacts";
private DcContext dcContext;
@@ -257,10 +256,6 @@ public class ContactSelectionListFragment extends Fragment
return getActivity().getIntent().getBooleanExtra(SELECT_VERIFIED_EXTRA, false);
}
- private boolean isFromShareActivity() {
- return getActivity().getIntent().getBooleanExtra(FROM_SHARE_ACTIVITY_EXTRA, false);
- }
-
private void initializeCursor() {
ContactSelectionListAdapter adapter = new ContactSelectionListAdapter(getActivity(),
GlideApp.with(this),
@@ -282,12 +277,15 @@ public class ContactSelectionListFragment extends Fragment
@Override
public Loader onCreateLoader(int id, Bundle args) {
- boolean addCreateGroupLinks = isFromShareActivity() || isRelayingMessageContent(getActivity()) ? false : !isMulti();
+ boolean allowCreation = getActivity().getIntent().getBooleanExtra(ALLOW_CREATION, true);
+ boolean addCreateContactLink = allowCreation;
+ boolean addCreateGroupLinks = allowCreation && !isRelayingMessageContent(getActivity()) && !isMulti();
+
int listflags = DcContext.DC_GCL_ADD_SELF;
if(isSelectVerfied()) {
listflags = DcContext.DC_GCL_VERIFIED_ONLY;
}
- return new DcContactsLoader(getActivity(), listflags, cursorFilter, addCreateGroupLinks, false);
+ return new DcContactsLoader(getActivity(), listflags, cursorFilter, addCreateGroupLinks, addCreateContactLink, false);
}
@Override
diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java
index b1daeda69..cf415364c 100644
--- a/src/org/thoughtcrime/securesms/ConversationActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationActivity.java
@@ -105,7 +105,6 @@ import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.scribbles.ScribbleActivity;
-import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.Prefs;
@@ -194,23 +193,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private ApplicationContext context;
private Recipient recipient;
private DcContext dcContext;
- private DcChat dcChat = new DcChat(0);
+ private DcChat dcChat = new DcChat(0, 0);
private int chatId;
private final boolean isSecureText = true;
private boolean isDefaultSms = true;
private boolean isSecurityInitialized = false;
private boolean successfulForwardingAttempt = false;
-
- private final DynamicTheme dynamicTheme = new DynamicTheme();
- private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
-
- @Override
- protected void onPreCreate() {
- dynamicTheme.onCreate(this);
- dynamicLanguage.onCreate(this);
- }
-
@Override
protected void onCreate(Bundle state, boolean ready) {
this.context = ApplicationContext.getInstance(getApplicationContext());
@@ -305,16 +294,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
ConversationListRelayingActivity.finishActivity();
}
- @Override
- protected void onStart() {
- super.onStart();
- }
-
@Override
protected void onResume() {
super.onResume();
- dynamicTheme.onResume(this);
- dynamicLanguage.onResume(this);
quickAttachmentDrawer.onResume();
initializeEnabledCheck();
@@ -340,11 +322,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
AudioSlidePlayer.stopAll();
}
- @Override
- protected void onStop() {
- super.onStop();
- }
-
@Override
public void onConfigurationChanged(Configuration newConfig) {
Log.i(TAG, "onConfigurationChanged(" + newConfig.orientation + ")");
@@ -870,19 +847,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
composeText.setOnClickListener(composeKeyPressedListener);
composeText.setOnFocusChangeListener(composeKeyPressedListener);
- if (QuickAttachmentDrawer.isDeviceSupported(this)) {
- quickAttachmentDrawer.setListener(this);
- quickCameraToggle.setOnClickListener(new QuickCameraToggleListener());
- } else {
- quickCameraToggle.setVisibility(View.GONE);
- quickCameraToggle.setEnabled(false);
- }
+ quickAttachmentDrawer.setListener(this);
+ quickCameraToggle.setOnClickListener(new QuickCameraToggleListener());
initializeBackground();
}
private void initializeBackground() {
- String backgroundImagePath = Prefs.getBackgroundImagePath(this);
+ String backgroundImagePath = Prefs.getBackgroundImagePath(this, dcContext.getAccountId());
Drawable background;
if(!backgroundImagePath.isEmpty()) {
background = Drawable.createFromPath(backgroundImagePath);
@@ -916,6 +888,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
AccountManager.getInstance().switchAccount(context, accountId);
dcContext = context.dcContext;
fragment.dcContext = context.dcContext;
+ initializeBackground();
}
chatId = getIntent().getIntExtra(CHAT_ID_EXTRA, -1);
if(chatId == DcChat.DC_CHAT_NO_CHAT)
@@ -974,7 +947,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void startContactChooserActivity() {
- Intent intent = new Intent(ConversationActivity.this, BlockedAndShareContactsActivity.class);
+ Intent intent = new Intent(ConversationActivity.this, AttachContactActivity.class);
+ intent.putExtra(ContactSelectionListFragment.ALLOW_CREATION, false);
startActivityForResult(intent, PICK_CONTACT);
}
@@ -991,8 +965,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void addAttachmentContactInfo(Intent data) {
- String name = data.getStringExtra(BlockedAndShareContactsActivity.SHARE_CONTACT_NAME_EXTRA);
- String mail = data.getStringExtra(BlockedAndShareContactsActivity.SHARE_CONTACT_MAIL_EXTRA);
+ String name = data.getStringExtra(AttachContactActivity.NAME_EXTRA);
+ String mail = data.getStringExtra(AttachContactActivity.ADDR_EXTRA);
composeText.append(name + "\n" + mail);
}
@@ -1388,18 +1362,23 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private class QuickCameraToggleListener implements OnClickListener {
@Override
public void onClick(View v) {
- if (!quickAttachmentDrawer.isShowing()) {
- Permissions.with(ConversationActivity.this)
- .request(Manifest.permission.CAMERA)
- .ifNecessary()
- .withPermanentDenialDialog(getString(R.string.perm_explain_access_to_camera_denied))
- .onAllGranted(() -> {
- composeText.clearFocus();
- container.show(composeText, quickAttachmentDrawer);
- })
- .execute();
+ if (Prefs.isBuiltInCameraPreferred(ConversationActivity.this)
+ && QuickAttachmentDrawer.isDeviceSupported(ConversationActivity.this)) {
+ if (!quickAttachmentDrawer.isShowing()) {
+ Permissions.with(ConversationActivity.this)
+ .request(Manifest.permission.CAMERA)
+ .ifNecessary()
+ .withPermanentDenialDialog(getString(R.string.perm_explain_access_to_camera_denied))
+ .onAllGranted(() -> {
+ composeText.clearFocus();
+ container.show(composeText, quickAttachmentDrawer);
+ })
+ .execute();
+ } else {
+ container.hideAttachedInput(false);
+ }
} else {
- container.hideAttachedInput(false);
+ attachmentManager.capturePhoto(ConversationActivity.this, TAKE_PHOTO);
}
}
}
diff --git a/src/org/thoughtcrime/securesms/ConversationAdapter.java b/src/org/thoughtcrime/securesms/ConversationAdapter.java
index a1b5a80b1..3db9b468c 100644
--- a/src/org/thoughtcrime/securesms/ConversationAdapter.java
+++ b/src/org/thoughtcrime/securesms/ConversationAdapter.java
@@ -22,7 +22,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -94,7 +93,7 @@ public class ConversationAdapter
private final @NonNull Context context;
private final @NonNull Calendar calendar;
- private DcContext dcContext;
+ private final DcContext dcContext;
private @NonNull DcChat dcChat;
private @NonNull int[] dcMsgList = new int[0];
private int positionToPulseHighlight = -1;
@@ -188,7 +187,7 @@ public class ConversationAdapter
}
static class HeaderViewHolder extends RecyclerView.ViewHolder {
- TextView textView;
+ final TextView textView;
HeaderViewHolder(View itemView) {
super(itemView);
diff --git a/src/org/thoughtcrime/securesms/ConversationFragment.java b/src/org/thoughtcrime/securesms/ConversationFragment.java
index 879e24704..74645d826 100644
--- a/src/org/thoughtcrime/securesms/ConversationFragment.java
+++ b/src/org/thoughtcrime/securesms/ConversationFragment.java
@@ -64,6 +64,7 @@ import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.recipients.Recipient;
+import org.thoughtcrime.securesms.reactions.ReactionsDetailsFragment;
import org.thoughtcrime.securesms.util.AccessibilityUtil;
import org.thoughtcrime.securesms.util.Debouncer;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
@@ -119,6 +120,7 @@ public class ConversationFragment extends MessageSelectorFragment
DcEventCenter eventCenter = DcHelper.getEventCenter(getContext());
eventCenter.addObserver(DcContext.DC_EVENT_INCOMING_MSG, this);
eventCenter.addObserver(DcContext.DC_EVENT_MSGS_CHANGED, this);
+ eventCenter.addObserver(DcContext.DC_EVENT_REACTIONS_CHANGED, this);
eventCenter.addObserver(DcContext.DC_EVENT_MSG_DELIVERED, this);
eventCenter.addObserver(DcContext.DC_EVENT_MSG_FAILED, this);
eventCenter.addObserver(DcContext.DC_EVENT_MSG_READ, this);
@@ -476,20 +478,6 @@ public class ConversationFragment extends MessageSelectorFragment
}
}
- private void handleResendMessage(final Set dcMsgsSet) {
- int[] ids = DcMsg.msgSetToIds(dcMsgsSet);
- if (dcContext.resendMsgs(ids)) {
- actionMode.finish();
- Toast.makeText(getContext(), R.string.sending, Toast.LENGTH_SHORT).show();
- } else {
- new AlertDialog.Builder(getContext())
- .setMessage(dcContext.getLastError())
- .setCancelable(false)
- .setPositiveButton(android.R.string.ok, null)
- .show();
- }
- }
-
private void reloadList() {
reloadList(false);
}
@@ -802,7 +790,7 @@ public class ConversationFragment extends MessageSelectorFragment
else if(DozeReminder.isDozeReminderMsg(getContext(), messageRecord)) {
DozeReminder.dozeReminderTapped(getContext());
}
- else if(messageRecord.isInfo() && messageRecord.getParent() != null && messageRecord.getParent().getType() == DcMsg.DC_MSG_WEBXDC) {
+ else if(messageRecord.getInfoType() == DcMsg.DC_INFO_WEBXDC_INFO_MESSAGE) {
scrollMaybeSmoothToMsgId(messageRecord.getParent().getId());
}
else {
@@ -866,6 +854,12 @@ public class ConversationFragment extends MessageSelectorFragment
public void onDownloadClicked(DcMsg messageRecord) {
dcContext.downloadFullMsg(messageRecord.getId());
}
+
+ @Override
+ public void onReactionClicked(DcMsg messageRecord) {
+ ReactionsDetailsFragment dialog = new ReactionsDetailsFragment(messageRecord.getId());
+ dialog.show(getActivity().getSupportFragmentManager(), null);
+ }
}
@Override
@@ -972,6 +966,7 @@ public class ConversationFragment extends MessageSelectorFragment
}
break;
+ case DcContext.DC_EVENT_REACTIONS_CHANGED:
case DcContext.DC_EVENT_INCOMING_MSG:
case DcContext.DC_EVENT_MSG_DELIVERED:
case DcContext.DC_EVENT_MSG_FAILED:
diff --git a/src/org/thoughtcrime/securesms/ConversationItem.java b/src/org/thoughtcrime/securesms/ConversationItem.java
index 4ceac173d..7305a091b 100644
--- a/src/org/thoughtcrime/securesms/ConversationItem.java
+++ b/src/org/thoughtcrime/securesms/ConversationItem.java
@@ -22,6 +22,7 @@ import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.Rect;
+import android.os.Build;
import android.text.SpannableString;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -39,6 +40,8 @@ import androidx.annotation.Nullable;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContact;
import com.b44t.messenger.DcMsg;
+import com.b44t.messenger.rpc.Reactions;
+import com.b44t.messenger.rpc.RpcException;
import org.thoughtcrime.securesms.audio.AudioSlidePlayer;
import org.thoughtcrime.securesms.components.AudioView;
@@ -59,6 +62,7 @@ import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideClickListener;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.mms.StickerSlide;
+import org.thoughtcrime.securesms.reactions.ReactionsConversationView;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.LongClickMovementMethod;
import org.thoughtcrime.securesms.util.MediaUtil;
@@ -96,7 +100,7 @@ public class ConversationItem extends BaseConversationItem
protected View replyView;
@Nullable private QuoteView quoteView;
private ConversationItemFooter footer;
- private ConversationItemFooter stickerFooter;
+ private ReactionsConversationView reactionsView;
private TextView groupSender;
private View groupSenderHolder;
private AvatarImageView contactPhoto;
@@ -132,7 +136,7 @@ public class ConversationItem extends BaseConversationItem
this.bodyText = findViewById(R.id.conversation_item_body);
this.footer = findViewById(R.id.conversation_item_footer);
- this.stickerFooter = findViewById(R.id.conversation_item_sticker_footer);
+ this.reactionsView = findViewById(R.id.reactions_view);
this.groupSender = findViewById(R.id.group_message_sender);
this.contactPhoto = findViewById(R.id.contact_photo);
this.contactPhotoHolder = findViewById(R.id.contact_photo_container);
@@ -183,8 +187,12 @@ public class ConversationItem extends BaseConversationItem
setGroupMessageStatus();
setAuthor(messageRecord, showSender);
setMessageSpacing(context);
+ setReactions(messageRecord);
setFooter(messageRecord, locale);
setQuote(messageRecord);
+ if (Util.isTouchExplorationEnabled(context)) {
+ setContentDescription();
+ }
}
@@ -281,9 +289,7 @@ public class ConversationItem extends BaseConversationItem
}
if (audioViewStub.resolved()) {
- audioViewStub.get().setFocusable(!shouldInterceptClicks(messageRecord) && batchSelected.isEmpty());
- audioViewStub.get().setClickable(batchSelected.isEmpty());
- audioViewStub.get().setEnabled(batchSelected.isEmpty());
+ audioViewStub.get().disablePlayer(!batchSelected.isEmpty());
}
if (documentViewStub.resolved()) {
@@ -297,6 +303,35 @@ public class ConversationItem extends BaseConversationItem
}
}
+ private void setContentDescription() {
+ String desc = "";
+ if (groupSenderHolder.getVisibility() == View.VISIBLE) {
+ desc = groupSender.getText() + "\n";
+ }
+
+ if (audioViewStub.resolved() && audioViewStub.get().getVisibility() == View.VISIBLE) {
+ desc += audioViewStub.get().getDescription() + "\n";
+ } else if (documentViewStub.resolved() && documentViewStub.get().getVisibility() == View.VISIBLE) {
+ desc += documentViewStub.get().getDescription() + "\n";
+ } else if (webxdcViewStub.resolved() && webxdcViewStub.get().getVisibility() == View.VISIBLE) {
+ desc += webxdcViewStub.get().getDescription() + "\n";
+ } else if (mediaThumbnailStub.resolved() && mediaThumbnailStub.get().getVisibility() == View.VISIBLE) {
+ desc += mediaThumbnailStub.get().getDescription() + "\n";
+ } else if (stickerStub.resolved() && stickerStub.get().getVisibility() == View.VISIBLE) {
+ desc += stickerStub.get().getDescription() + "\n";
+ }
+
+ if (bodyText.getVisibility() == View.VISIBLE) {
+ desc += bodyText.getText() + "\n";
+ }
+
+ if (footer.getVisibility() == View.VISIBLE) {
+ desc += footer.getDescription();
+ }
+
+ this.setContentDescription(desc);
+ }
+
private boolean hasAudio(DcMsg messageRecord) {
int type = messageRecord.getType();
return type==DcMsg.DC_MSG_AUDIO || type==DcMsg.DC_MSG_VOICE;
@@ -414,7 +449,7 @@ public class ConversationItem extends BaseConversationItem
public void onStop() {}
@Override
- public void onProgress(double progress, long millis) {}
+ public void onProgress(AudioSlide slide, double progress, long millis) {}
@Override
public void onReceivedDuration(int millis) {
@@ -438,7 +473,11 @@ public class ConversationItem extends BaseConversationItem
}
audioViewStub.get().setAudio(new AudioSlide(context, messageRecord), duration);
+ audioViewStub.get().setOnClickListener(passthroughClickListener);
audioViewStub.get().setOnLongClickListener(passthroughClickListener);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ audioViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ }
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
@@ -455,6 +494,9 @@ public class ConversationItem extends BaseConversationItem
documentViewStub.get().setDocument(new DocumentSlide(context, messageRecord));
documentViewStub.get().setDocumentClickListener(new ThumbnailClickListener());
documentViewStub.get().setOnLongClickListener(passthroughClickListener);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ documentViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ }
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
@@ -470,6 +512,9 @@ public class ConversationItem extends BaseConversationItem
webxdcViewStub.get().setWebxdc(messageRecord, context.getString(R.string.webxdc_app));
webxdcViewStub.get().setWebxdcClickListener(new ThumbnailClickListener());
webxdcViewStub.get().setOnLongClickListener(passthroughClickListener);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ webxdcViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ }
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
@@ -504,6 +549,9 @@ public class ConversationItem extends BaseConversationItem
mediaThumbnailStub.get().setOnLongClickListener(passthroughClickListener);
mediaThumbnailStub.get().setOnClickListener(passthroughClickListener);
mediaThumbnailStub.get().showShade(TextUtils.isEmpty(messageRecord.getText()));
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ mediaThumbnailStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ }
setThumbnailOutlineCorners(messageRecord, showSender);
@@ -523,9 +571,11 @@ public class ConversationItem extends BaseConversationItem
stickerStub.get().setSlide(glideRequests, new StickerSlide(context, messageRecord));
stickerStub.get().setThumbnailClickListener(new StickerClickListener());
-
stickerStub.get().setOnLongClickListener(passthroughClickListener);
stickerStub.get().setOnClickListener(passthroughClickListener);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ stickerStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ }
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
@@ -657,7 +707,6 @@ public class ConversationItem extends BaseConversationItem
ViewUtil.updateLayoutParams(footer, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
footer.setVisibility(GONE);
- stickerFooter.setVisibility(GONE);
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().getFooter().setVisibility(GONE);
ConversationItemFooter activeFooter = getActiveFooter(current);
@@ -665,9 +714,29 @@ public class ConversationItem extends BaseConversationItem
activeFooter.setMessageRecord(current, locale);
}
+ private void setReactions(@NonNull DcMsg current) {
+ try {
+ Reactions reactions = rpc.getMsgReactions(dcContext.getAccountId(), current.getId());
+ if (reactions == null) {
+ reactionsView.clear();
+ } else {
+ reactionsView.setReactions(reactions.getReactions());
+ reactionsView.setOnClickListener(view -> {
+ if (eventListener != null && batchSelected.isEmpty()) {
+ eventListener.onReactionClicked(current);
+ } else {
+ passthroughClickListener.onClick(view);
+ }
+ });
+ }
+ } catch (RpcException e) {
+ reactionsView.clear();
+ }
+ }
+
private ConversationItemFooter getActiveFooter(@NonNull DcMsg messageRecord) {
if (hasSticker(messageRecord)) {
- return stickerFooter;
+ return stickerStub.get().getFooter();
} else if (hasOnlyThumbnail(messageRecord) && TextUtils.isEmpty(messageRecord.getText())) {
return mediaThumbnailStub.get().getFooter();
} else {
@@ -764,6 +833,14 @@ public class ConversationItem extends BaseConversationItem
return availableWidth;
}
+ @Override
+ public void onAccessibilityClick() {
+ if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().performClick();
+ else if (audioViewStub.resolved()) audioViewStub.get().togglePlay();
+ else if (documentViewStub.resolved()) documentViewStub.get().performClick();
+ else if (webxdcViewStub.resolved()) webxdcViewStub.get().performClick();
+ }
+
/// Event handlers
private class ThumbnailClickListener implements SlideClickListener {
diff --git a/src/org/thoughtcrime/securesms/ConversationItemSwipeCallback.java b/src/org/thoughtcrime/securesms/ConversationItemSwipeCallback.java
index 2b8f1a030..b252c9f28 100644
--- a/src/org/thoughtcrime/securesms/ConversationItemSwipeCallback.java
+++ b/src/org/thoughtcrime/securesms/ConversationItemSwipeCallback.java
@@ -19,8 +19,8 @@ import org.thoughtcrime.securesms.util.ServiceUtil;
class ConversationItemSwipeCallback extends ItemTouchHelper.SimpleCallback {
- private static float SWIPE_SUCCESS_DX = ConversationSwipeAnimationHelper.TRIGGER_DX;
- private static long SWIPE_SUCCESS_VIBE_TIME_MS = 10;
+ private static final float SWIPE_SUCCESS_DX = ConversationSwipeAnimationHelper.TRIGGER_DX;
+ private static final long SWIPE_SUCCESS_VIBE_TIME_MS = 10;
private boolean swipeBack;
private boolean shouldTriggerSwipeFeedback;
diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java
index 8acd36844..9a8472d38 100644
--- a/src/org/thoughtcrime/securesms/ConversationListActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java
@@ -20,6 +20,7 @@ import static org.thoughtcrime.securesms.ConversationActivity.CHAT_ID_EXTRA;
import static org.thoughtcrime.securesms.ConversationActivity.STARTING_POSITION_EXTRA;
import static org.thoughtcrime.securesms.util.RelayUtil.acquireRelayMessageContent;
import static org.thoughtcrime.securesms.util.RelayUtil.getDirectSharingChatId;
+import static org.thoughtcrime.securesms.util.RelayUtil.getSharedTitle;
import static org.thoughtcrime.securesms.util.RelayUtil.isDirectSharing;
import static org.thoughtcrime.securesms.util.RelayUtil.isForwarding;
import static org.thoughtcrime.securesms.util.RelayUtil.isRelayingMessageContent;
@@ -39,6 +40,7 @@ import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
+import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.appcompat.widget.TooltipCompat;
@@ -55,30 +57,25 @@ import org.thoughtcrime.securesms.connect.AccountManager;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.connect.DirectShareUtil;
import org.thoughtcrime.securesms.mms.GlideApp;
+import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.qr.QrActivity;
import org.thoughtcrime.securesms.qr.QrCodeHandler;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.search.SearchFragment;
-import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.Prefs;
-import org.thoughtcrime.securesms.util.RelayUtil;
import org.thoughtcrime.securesms.util.SendRelayedMessageUtil;
public class ConversationListActivity extends PassphraseRequiredActionBarActivity
implements ConversationListFragment.ConversationSelectedListener
{
- @SuppressWarnings("unused")
private static final String TAG = ConversationListActivity.class.getSimpleName();
private static final String OPENPGP4FPR = "openpgp4fpr";
private static final String NDK_ARCH_WARNED = "ndk_arch_warned";
public static final String CLEAR_NOTIFICATIONS = "clear_notifications";
public static final String ACCOUNT_ID_EXTRA = "account_id";
- private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
- private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
-
private ConversationListFragment conversationListFragment;
public TextView title;
private AvatarImageView selfAvatar;
@@ -89,18 +86,18 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
@Override
protected void onPreCreate() {
- dynamicTheme.onCreate(this);
- dynamicLanguage.onCreate(this);
+ dynamicTheme = new DynamicNoActionBarTheme();
+ super.onPreCreate();
}
@Override
protected void onCreate(Bundle icicle, boolean ready) {
// update messages - for new messages, do not reuse or modify strings but create new ones.
// it is not needed to keep all past update messages, however, when deleted, also the strings should be deleted.
- DcContext dcContext = DcHelper.getContext(this);
- DcMsg msg = new DcMsg(dcContext, DcMsg.DC_MSG_TEXT);
- msg.setText(getString(R.string.update_1_38_android, "https://get.delta.chat/#changelogs"));
- dcContext.addDeviceMsg("update_1_38c_android", msg);
+ //DcContext dcContext = DcHelper.getContext(this);
+ //DcMsg msg = new DcMsg(dcContext, DcMsg.DC_MSG_TEXT);
+ //msg.setText(getString(R.string.update_1_38_android, "https://get.delta.chat/#changelogs"));
+ //dcContext.addDeviceMsg("update_1_38c_android", msg);
// create view
setContentView(R.layout.conversation_list_activity);
@@ -213,7 +210,16 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
public void refreshTitle() {
if (isRelayingMessageContent(this)) {
- title.setText(isForwarding(this) ? R.string.forward_to : R.string.chat_share_with_title);
+ if (isForwarding(this)) {
+ title.setText(R.string.forward_to);
+ } else {
+ String titleStr = getSharedTitle(this);
+ if (titleStr != null) { // sharing from sendToChat
+ title.setText(titleStr);
+ } else { // normal sharing
+ title.setText(R.string.chat_share_with_title);
+ }
+ }
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} else {
title.setText(DcHelper.getConnectivitySummary(this, R.string.app_name));
@@ -239,9 +245,6 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
@Override
public void onResume() {
super.onResume();
- dynamicTheme.onResume(this);
- dynamicLanguage.onResume(this);
-
DirectShareUtil.triggerRefreshDirectShare(this);
}
@@ -306,7 +309,11 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
}
private void initializeTitleListener() {
- title.setOnClickListener(v -> startActivity(new Intent(this, ConnectivityActivity.class)));
+ title.setOnClickListener(v -> {
+ if (!isRelayingMessageContent(this)) {
+ startActivity(new Intent(this, ConnectivityActivity.class));
+ }
+ });
}
@Override
@@ -432,4 +439,9 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
break;
}
}
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
+ }
}
diff --git a/src/org/thoughtcrime/securesms/ConversationListAdapter.java b/src/org/thoughtcrime/securesms/ConversationListAdapter.java
index 909d4ae27..8eb5156c1 100644
--- a/src/org/thoughtcrime/securesms/ConversationListAdapter.java
+++ b/src/org/thoughtcrime/securesms/ConversationListAdapter.java
@@ -92,7 +92,7 @@ class ConversationListAdapter extends RecyclerView.Adapter {
this.context = new WeakReference<>(context);
this.glideRequests = glideRequests;
this.dcContext = DcHelper.getContext(context);
- this.dcChatlist = new DcChatlist(0);
+ this.dcChatlist = new DcChatlist(0, 0);
this.locale = locale;
this.inflater = LayoutInflater.from(context);
this.clickListener = clickListener;
@@ -195,7 +195,7 @@ class ConversationListAdapter extends RecyclerView.Adapter {
}
void changeData(@Nullable DcChatlist chatlist) {
- dcChatlist = chatlist == null ? new DcChatlist(0) : chatlist;
+ dcChatlist = chatlist == null ? new DcChatlist(0, 0) : chatlist;
notifyDataSetChanged();
}
}
diff --git a/src/org/thoughtcrime/securesms/ConversationListArchiveActivity.java b/src/org/thoughtcrime/securesms/ConversationListArchiveActivity.java
index 6505ce94c..8909aac72 100644
--- a/src/org/thoughtcrime/securesms/ConversationListArchiveActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationListArchiveActivity.java
@@ -9,8 +9,6 @@ import android.view.MenuItem;
import com.b44t.messenger.DcChat;
import org.thoughtcrime.securesms.connect.DcHelper;
-import org.thoughtcrime.securesms.util.DynamicLanguage;
-import org.thoughtcrime.securesms.util.DynamicTheme;
import static org.thoughtcrime.securesms.ConversationActivity.CHAT_ID_EXTRA;
import static org.thoughtcrime.securesms.ConversationActivity.FROM_ARCHIVED_CHATS_EXTRA;
@@ -23,15 +21,6 @@ public class ConversationListArchiveActivity extends PassphraseRequiredActionBar
{
private ConversationListFragment conversationListFragment;
- private final DynamicTheme dynamicTheme = new DynamicTheme();
- private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
-
- @Override
- protected void onPreCreate() {
- dynamicTheme.onCreate(this);
- dynamicLanguage.onCreate(this);
- }
-
@Override
protected void onCreate(Bundle icicle, boolean ready) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@@ -60,13 +49,6 @@ public class ConversationListArchiveActivity extends PassphraseRequiredActionBar
if (isFinishing()) overridePendingTransition(R.anim.fade_scale_in, R.anim.slide_to_right);
}
- @Override
- public void onResume() {
- super.onResume();
- dynamicTheme.onResume(this);
- dynamicLanguage.onResume(this);
- }
-
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuInflater inflater = this.getMenuInflater();
diff --git a/src/org/thoughtcrime/securesms/ConversationListFragment.java b/src/org/thoughtcrime/securesms/ConversationListFragment.java
index 07d98acec..20e96ef28 100644
--- a/src/org/thoughtcrime/securesms/ConversationListFragment.java
+++ b/src/org/thoughtcrime/securesms/ConversationListFragment.java
@@ -16,6 +16,7 @@
*/
package org.thoughtcrime.securesms;
+import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
@@ -50,6 +51,7 @@ import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcChatlist;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcEvent;
+import com.b44t.messenger.DcMsg;
import com.google.android.material.snackbar.Snackbar;
import org.thoughtcrime.securesms.ConversationListAdapter.ItemClickListener;
@@ -60,6 +62,8 @@ import org.thoughtcrime.securesms.connect.DcEventCenter;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.connect.DirectShareUtil;
import org.thoughtcrime.securesms.mms.GlideApp;
+import org.thoughtcrime.securesms.permissions.Permissions;
+import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.RelayUtil;
import org.thoughtcrime.securesms.util.SendRelayedMessageUtil;
import org.thoughtcrime.securesms.util.Util;
@@ -86,7 +90,6 @@ public class ConversationListFragment extends Fragment
public static final String ARCHIVE = "archive";
public static final String RELOAD_LIST = "reload_list";
- @SuppressWarnings("unused")
private static final String TAG = ConversationListFragment.class.getSimpleName();
private ActionMode actionMode;
@@ -95,10 +98,11 @@ public class ConversationListFragment extends Fragment
private TextView emptySearch;
private PulsingFloatingActionButton fab;
private Locale locale;
- private String queryFilter = "";
+ private final String queryFilter = "";
private boolean archive;
private Timer reloadTimer;
private boolean chatlistJustLoaded;
+ private boolean reloadTimerInstantly;
@Override
public void onCreate(Bundle icicle) {
@@ -171,23 +175,33 @@ public class ConversationListFragment extends Fragment
if (getActivity().getIntent().getIntExtra(RELOAD_LIST, 0) == 1
&& !chatlistJustLoaded) {
+ Log.i(TAG, "🤠 resuming chatlist: loading chatlist");
loadChatlist();
+ reloadTimerInstantly = false;
}
chatlistJustLoaded = false;
+ Log.i(TAG, "🤠 resuming chatlist: starting update timer");
reloadTimer = new Timer();
reloadTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
- Util.runOnMain(() -> { list.getAdapter().notifyDataSetChanged(); });
+ Util.runOnMain(() -> {
+ Log.i(TAG, "🤠 update timer: refreshing chatlist");
+ list.getAdapter().notifyDataSetChanged();
+ });
}
- }, 60 * 1000, 60 * 1000);
+ }, reloadTimerInstantly? 0 : 60 * 1000, 60 * 1000);
}
@Override
public void onPause() {
super.onPause();
+
+ Log.i(TAG, "🤠 pausing chatlist: cancel update timer");
reloadTimer.cancel();
+ reloadTimerInstantly = true;
+
fab.stopPulse();
}
@@ -256,7 +270,28 @@ public class ConversationListFragment extends Fragment
@Override
protected void onPostExecute(Void result) {
- DozeReminder.maybeAskDirectly(getActivity());
+ Activity activity = ConversationListFragment.this.getActivity();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ if (!Prefs.getBooleanPreference(activity, Prefs.ASKED_FOR_NOTIFICATION_PERMISSION, false)) {
+ Prefs.setBooleanPreference(activity, Prefs.ASKED_FOR_NOTIFICATION_PERMISSION, true);
+ Permissions.with(activity)
+ .request(Manifest.permission.POST_NOTIFICATIONS)
+ .ifNecessary()
+ .onAllGranted(() -> {
+ DozeReminder.maybeAskDirectly(getActivity());
+ })
+ .onAnyDenied(() -> {
+ final DcContext dcContext = DcHelper.getContext(activity);
+ DcMsg msg = new DcMsg(dcContext, DcMsg.DC_MSG_TEXT);
+ msg.setText("\uD83D\uDC49 "+activity.getString(R.string.notifications_disabled)+" \uD83D\uDC48\n\n"
+ +activity.getString(R.string.perm_explain_access_to_notifications_denied));
+ dcContext.addDeviceMsg("android.notifications-disabled", msg);
+ })
+ .execute();
+ }
+ } else {
+ DozeReminder.maybeAskDirectly(getActivity());
+ }
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, getActivity());
}
diff --git a/src/org/thoughtcrime/securesms/ConversationListItem.java b/src/org/thoughtcrime/securesms/ConversationListItem.java
index 47904089e..1cbd7eff7 100644
--- a/src/org/thoughtcrime/securesms/ConversationListItem.java
+++ b/src/org/thoughtcrime/securesms/ConversationListItem.java
@@ -61,9 +61,6 @@ import java.util.Set;
public class ConversationListItem extends RelativeLayout
implements BindableConversationListItem, Unbindable
{
- @SuppressWarnings("unused")
- private final static String TAG = ConversationListItem.class.getSimpleName();
-
private final static Typeface BOLD_TYPEFACE = Typeface.create("sans-serif-medium", Typeface.NORMAL);
private final static Typeface LIGHT_TYPEFACE = Typeface.create("sans-serif", Typeface.NORMAL);
diff --git a/src/org/thoughtcrime/securesms/ConversationTitleView.java b/src/org/thoughtcrime/securesms/ConversationTitleView.java
index cc0aabda7..f3a0dd14e 100644
--- a/src/org/thoughtcrime/securesms/ConversationTitleView.java
+++ b/src/org/thoughtcrime/securesms/ConversationTitleView.java
@@ -23,9 +23,6 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class ConversationTitleView extends RelativeLayout {
- @SuppressWarnings("unused")
- private static final String TAG = ConversationTitleView.class.getSimpleName();
-
private View content;
private ImageView back;
private AvatarView avatar;
diff --git a/src/org/thoughtcrime/securesms/CreateProfileActivity.java b/src/org/thoughtcrime/securesms/CreateProfileActivity.java
index fa8e91209..b6a8c654d 100644
--- a/src/org/thoughtcrime/securesms/CreateProfileActivity.java
+++ b/src/org/thoughtcrime/securesms/CreateProfileActivity.java
@@ -42,16 +42,10 @@ import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints;
import org.thoughtcrime.securesms.scribbles.ScribbleActivity;
-import org.thoughtcrime.securesms.util.BitmapDecodingException;
-import org.thoughtcrime.securesms.util.BitmapUtil;
-import org.thoughtcrime.securesms.util.DynamicLanguage;
-import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.Prefs;
-import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
import java.security.SecureRandom;
@@ -65,9 +59,6 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Emoj
private static final int REQUEST_CODE_AVATAR = 1;
- private final DynamicTheme dynamicTheme = new DynamicTheme();
- private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
-
private InputAwareLayout container;
private ImageView avatar;
private EditText name;
@@ -88,9 +79,6 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Emoj
super.onCreate(bundle);
this.fromWelcome = getIntent().getBooleanExtra(FROM_WELCOME, false);
- dynamicTheme.onCreate(this);
- dynamicLanguage.onCreate(this);
-
setContentView(R.layout.profile_create_activity);
getSupportActionBar().setTitle(R.string.pref_profile_info_headline);
@@ -113,13 +101,6 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Emoj
return true;
}
- @Override
- public void onResume() {
- super.onResume();
- dynamicTheme.onResume(this);
- dynamicLanguage.onResume(this);
- }
-
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
diff --git a/src/org/thoughtcrime/securesms/EphemeralMessagesDialog.java b/src/org/thoughtcrime/securesms/EphemeralMessagesDialog.java
index e116397f0..235527664 100644
--- a/src/org/thoughtcrime/securesms/EphemeralMessagesDialog.java
+++ b/src/org/thoughtcrime/securesms/EphemeralMessagesDialog.java
@@ -1,7 +1,6 @@
package org.thoughtcrime.securesms;
import android.content.Context;
-import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RadioButton;
diff --git a/src/org/thoughtcrime/securesms/FullMsgActivity.java b/src/org/thoughtcrime/securesms/FullMsgActivity.java
index 9a04e2f14..d5ed6f1e4 100644
--- a/src/org/thoughtcrime/securesms/FullMsgActivity.java
+++ b/src/org/thoughtcrime/securesms/FullMsgActivity.java
@@ -40,7 +40,7 @@ public class FullMsgActivity extends WebViewActivity
protected void onCreate(Bundle state, boolean ready) {
super.onCreate(state, ready);
- blockLoadingRemote = getIntent().getBooleanExtra(BLOCK_LOADING_REMOTE, false);;
+ blockLoadingRemote = getIntent().getBooleanExtra(BLOCK_LOADING_REMOTE, false);
loadRemoteContent = !blockLoadingRemote && Prefs.getAlwaysLoadRemoteContent(this);
webView.getSettings().setBlockNetworkLoads(!loadRemoteContent);
diff --git a/src/org/thoughtcrime/securesms/GroupCreateActivity.java b/src/org/thoughtcrime/securesms/GroupCreateActivity.java
index 0be74cef2..5dd52197a 100644
--- a/src/org/thoughtcrime/securesms/GroupCreateActivity.java
+++ b/src/org/thoughtcrime/securesms/GroupCreateActivity.java
@@ -42,8 +42,6 @@ import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.qr.QrShowActivity;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.scribbles.ScribbleActivity;
-import org.thoughtcrime.securesms.util.DynamicLanguage;
-import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter;
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter.OnRecipientDeletedListener;
import org.thoughtcrime.securesms.util.ThemeUtil;
@@ -68,9 +66,6 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
public static final String CREATE_BROADCAST = "group_create_broadcast";
public static final String SUGGESTED_CONTACT_IDS = "suggested_contact_ids";
- private final DynamicTheme dynamicTheme = new DynamicTheme();
- private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
-
private static final int PICK_CONTACT = 1;
public static final int AVATAR_SIZE = 210;
private static final int REQUEST_CODE_AVATAR = 2759;
@@ -90,12 +85,6 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private boolean hasSuggestedContacts;
private AttachmentManager attachmentManager;
- @Override
- protected void onPreCreate() {
- dynamicTheme.onCreate(this);
- dynamicLanguage.onCreate(this);
- }
-
@Override
protected void onCreate(Bundle state, boolean ready) {
dcContext = DcHelper.getContext(this);
@@ -130,8 +119,6 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
@Override
public void onResume() {
super.onResume();
- dynamicTheme.onResume(this);
- dynamicLanguage.onResume(this);
updateViewState();
}
@@ -179,8 +166,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private static class AddMembersTask extends AsyncTask> {
static class Result {
- Optional recipient;
- String reason;
+ final Optional recipient;
+ final String reason;
public Result(@Nullable Recipient recipient, @Nullable String reason) {
this.recipient = Optional.fromNullable(recipient);
@@ -188,7 +175,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
}
- private GroupCreateActivity activity;
+ private final GroupCreateActivity activity;
public AddMembersTask(@NonNull GroupCreateActivity activity) {
this.activity = activity;
@@ -442,7 +429,6 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
@Override
public void onActivityResult(int reqCode, int resultCode, final Intent data) {
super.onActivityResult(reqCode, resultCode, data);
- Uri outputFile = Uri.fromFile(new File(getCacheDir(), "cropped"));
if (resultCode != Activity.RESULT_OK)
return;
@@ -531,7 +517,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private static class FillExistingGroupInfoAsyncTask extends ProgressDialogAsyncTask {
- GroupCreateActivity activity;
+ final GroupCreateActivity activity;
FillExistingGroupInfoAsyncTask(GroupCreateActivity activity) {
super(activity,
diff --git a/src/org/thoughtcrime/securesms/LogViewActivity.java b/src/org/thoughtcrime/securesms/LogViewActivity.java
index 6bdf38ec3..315390b59 100644
--- a/src/org/thoughtcrime/securesms/LogViewActivity.java
+++ b/src/org/thoughtcrime/securesms/LogViewActivity.java
@@ -12,24 +12,17 @@ import android.view.MenuItem;
import android.widget.Toast;
import org.thoughtcrime.securesms.permissions.Permissions;
-import org.thoughtcrime.securesms.util.DynamicLanguage;
-import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.Util;
public class LogViewActivity extends BaseActionBarActivity {
private static final String TAG = LogViewActivity.class.getSimpleName();
- private final DynamicTheme dynamicTheme = new DynamicTheme();
- private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
-
LogViewFragment logViewFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- dynamicTheme.onCreate(this);
- dynamicLanguage.onCreate(this);
setContentView(R.layout.log_view_activity);
logViewFragment = new LogViewFragment(dynamicLanguage);
@@ -40,13 +33,6 @@ public class LogViewActivity extends BaseActionBarActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
- @Override
- protected void onResume() {
- super.onResume();
- dynamicTheme.onResume(this);
- dynamicLanguage.onResume(this);
- }
-
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuInflater inflater = this.getMenuInflater();
diff --git a/src/org/thoughtcrime/securesms/LogViewFragment.java b/src/org/thoughtcrime/securesms/LogViewFragment.java
index c60ce3416..ce9567dd5 100644
--- a/src/org/thoughtcrime/securesms/LogViewFragment.java
+++ b/src/org/thoughtcrime/securesms/LogViewFragment.java
@@ -61,7 +61,7 @@ public class LogViewFragment extends Fragment {
private static final String TAG = LogViewFragment.class.getSimpleName();
private EditText logPreview;
- private @NonNull DynamicLanguage dynamicLanguage;
+ private final @NonNull DynamicLanguage dynamicLanguage;
public LogViewFragment(DynamicLanguage dynamicLanguage) {
this.dynamicLanguage = dynamicLanguage;
@@ -154,7 +154,7 @@ public class LogViewFragment extends Fragment {
}
private class PopulateLogcatAsyncTask extends AsyncTask {
- private WeakReference weakFragment;
+ private final WeakReference weakFragment;
public PopulateLogcatAsyncTask(LogViewFragment fragment) {
this.weakFragment = new WeakReference<>(fragment);
diff --git a/src/org/thoughtcrime/securesms/MediaPreviewActivity.java b/src/org/thoughtcrime/securesms/MediaPreviewActivity.java
index cfc50f6a0..26acf07b6 100644
--- a/src/org/thoughtcrime/securesms/MediaPreviewActivity.java
+++ b/src/org/thoughtcrime/securesms/MediaPreviewActivity.java
@@ -16,6 +16,7 @@
*/
package org.thoughtcrime.securesms;
+import android.app.Activity;
import android.Manifest;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
@@ -62,7 +63,7 @@ import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.DateUtils;
-import org.thoughtcrime.securesms.util.DynamicLanguage;
+import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment;
import org.thoughtcrime.securesms.util.StorageUtil;
@@ -93,8 +94,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
/** USE ONLY IF YOU HAVE NO MESSAGE ID! */
public static final String SIZE_EXTRA = "size";
- private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
-
@Nullable
private DcMsg messageRecord;
private DcContext dcContext;
@@ -107,12 +106,20 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
private int editAvatarChatId = 0;
+ @Override
+ protected void onPreCreate() {
+ dynamicTheme = new DynamicTheme() {
+ public void onCreate(Activity activity) {
+ activity.setTheme(R.style.TextSecure_DarkTheme); // force dark theme
+ }
+ public void onResume(Activity activity) {}
+ };
+ super.onPreCreate();
+ }
+
@SuppressWarnings("ConstantConditions")
@Override
protected void onCreate(Bundle bundle, boolean ready) {
- this.setTheme(R.style.TextSecure_DarkTheme);
- dynamicLanguage.onCreate(this);
-
setFullscreenIfPossible();
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
@@ -173,8 +180,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
@Override
public void onResume() {
super.onResume();
-
- dynamicLanguage.onResume(this);
initializeMedia();
}
diff --git a/src/org/thoughtcrime/securesms/MessageSelectorFragment.java b/src/org/thoughtcrime/securesms/MessageSelectorFragment.java
index e3ce36c9c..3451ceb05 100644
--- a/src/org/thoughtcrime/securesms/MessageSelectorFragment.java
+++ b/src/org/thoughtcrime/securesms/MessageSelectorFragment.java
@@ -7,6 +7,7 @@ import android.os.AsyncTask;
import android.view.Menu;
import android.view.View;
import android.widget.TextView;
+import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.view.ActionMode;
@@ -115,4 +116,18 @@ public abstract class MessageSelectorFragment
protected void handleShare(final DcMsg dcMsg) {
DcHelper.openForViewOrShare(getContext(), dcMsg.getId(), Intent.ACTION_SEND);
}
+
+ protected void handleResendMessage(final Set dcMsgsSet) {
+ int[] ids = DcMsg.msgSetToIds(dcMsgsSet);
+ if (dcContext.resendMsgs(ids)) {
+ actionMode.finish();
+ Toast.makeText(getContext(), R.string.sending, Toast.LENGTH_SHORT).show();
+ } else {
+ new AlertDialog.Builder(getContext())
+ .setMessage(dcContext.getLastError())
+ .setCancelable(false)
+ .setPositiveButton(android.R.string.ok, null)
+ .show();
+ }
+ }
}
diff --git a/src/org/thoughtcrime/securesms/NewConversationActivity.java b/src/org/thoughtcrime/securesms/NewConversationActivity.java
index 15f657239..aa48935b5 100644
--- a/src/org/thoughtcrime/securesms/NewConversationActivity.java
+++ b/src/org/thoughtcrime/securesms/NewConversationActivity.java
@@ -22,7 +22,6 @@ import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
@@ -51,7 +50,6 @@ import static org.thoughtcrime.securesms.util.RelayUtil.isRelayingMessageContent
*/
public class NewConversationActivity extends ContactSelectionActivity {
- @SuppressWarnings("unused")
private static final String TAG = NewConversationActivity.class.getSimpleName();
public static final String MAILTO = "mailto";
private static final String SUBJECT = "subject";
@@ -186,17 +184,6 @@ public class NewConversationActivity extends ContactSelectionActivity {
finish();
}
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- super.onOptionsItemSelected(item);
-
- switch (item.getItemId()) {
- case android.R.id.home: super.onBackPressed(); return true;
- }
-
- return false;
- }
-
@Override
protected boolean onPrepareOptionsPanel(View view, Menu menu) {
/* currently not needed
diff --git a/src/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java b/src/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java
index e8fb9d93a..d01cf6faf 100644
--- a/src/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java
+++ b/src/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java
@@ -13,7 +13,6 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
@Override
protected final void onCreate(Bundle savedInstanceState) {
Log.w(TAG, "onCreate(" + savedInstanceState + ")");
- onPreCreate();
if (GenericForegroundService.isForegroundTaskStarted()) {
// this does not prevent intent set by onNewIntent(),
@@ -27,16 +26,16 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
if (!DcHelper.isConfigured(getApplicationContext())) {
Intent intent = new Intent(this, WelcomeActivity.class);
startActivity(intent);
+ super.onCreate(savedInstanceState);
finish();
+ } else {
+ super.onCreate(savedInstanceState);
}
- super.onCreate(savedInstanceState);
-
if (!isFinishing()) {
onCreate(savedInstanceState, true);
}
}
- protected void onPreCreate() {}
protected void onCreate(Bundle savedInstanceState, boolean ready) {}
}
diff --git a/src/org/thoughtcrime/securesms/ProfileActivity.java b/src/org/thoughtcrime/securesms/ProfileActivity.java
index 91a2d09aa..8e443dab6 100644
--- a/src/org/thoughtcrime/securesms/ProfileActivity.java
+++ b/src/org/thoughtcrime/securesms/ProfileActivity.java
@@ -32,9 +32,7 @@ import com.google.android.material.tabs.TabLayout;
import org.thoughtcrime.securesms.connect.DcEventCenter;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.mms.GlideApp;
-import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
-import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
@@ -46,9 +44,6 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity
implements DcEventCenter.DcEventDelegate
{
- @SuppressWarnings("unused")
- private final static String TAG = ProfileActivity.class.getSimpleName();
-
public static final String CHAT_ID_EXTRA = "chat_id";
public static final String CONTACT_ID_EXTRA = "contact_id";
public static final String FORCE_TAB_EXTRA = "force_tab";
@@ -64,9 +59,6 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity
private static final int REQUEST_CODE_PICK_RINGTONE = 1;
- private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
- private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
-
private DcContext dcContext;
private int chatId;
private boolean chatIsMultiUser;
@@ -76,7 +68,7 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity
private int contactId;
private boolean fromChat;
- private ArrayList tabs = new ArrayList<>();
+ private final ArrayList tabs = new ArrayList<>();
private Toolbar toolbar;
private ConversationTitleView titleView;
private TabLayout tabLayout;
@@ -84,8 +76,8 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity
@Override
protected void onPreCreate() {
- dynamicTheme.onCreate(this);
- dynamicLanguage.onCreate(this);
+ dynamicTheme = new DynamicNoActionBarTheme();
+ super.onPreCreate();
dcContext = DcHelper.getContext(this);
}
@@ -188,13 +180,6 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity
return true;
}
- @Override
- public void onResume() {
- super.onResume();
- dynamicTheme.onResume(this);
- dynamicLanguage.onResume(this);
- }
-
boolean backPressed = false;
@Override
public void onBackPressed() {
diff --git a/src/org/thoughtcrime/securesms/ProfileDocumentsAdapter.java b/src/org/thoughtcrime/securesms/ProfileDocumentsAdapter.java
index 6ebedaa0d..5aecf166f 100644
--- a/src/org/thoughtcrime/securesms/ProfileDocumentsAdapter.java
+++ b/src/org/thoughtcrime/securesms/ProfileDocumentsAdapter.java
@@ -27,9 +27,6 @@ import java.util.Set;
class ProfileDocumentsAdapter extends StickyHeaderGridAdapter {
- @SuppressWarnings("unused")
- private static final String TAG = ProfileDocumentsAdapter.class.getSimpleName();
-
private final Context context;
private final Locale locale;
private final ItemClickListener itemClickListener;
@@ -53,7 +50,7 @@ class ProfileDocumentsAdapter extends StickyHeaderGridAdapter {
}
private static class HeaderHolder extends StickyHeaderGridAdapter.HeaderViewHolder {
- TextView textView;
+ final TextView textView;
HeaderHolder(View itemView) {
super(itemView);
@@ -106,9 +103,7 @@ class ProfileDocumentsAdapter extends StickyHeaderGridAdapter {
viewHolder.audioView.setAudio((AudioSlide)slide, dcMsg.getDuration());
viewHolder.audioView.setOnClickListener(view -> itemClickListener.onMediaClicked(dcMsg));
viewHolder.audioView.setOnLongClickListener(view -> { itemClickListener.onMediaLongClicked(dcMsg); return true; });
- viewHolder.audioView.setFocusable(selected.isEmpty());
- viewHolder.audioView.setClickable(selected.isEmpty());
- viewHolder.audioView.setLongClickable(selected.isEmpty());
+ viewHolder.audioView.disablePlayer(!selected.isEmpty());
viewHolder.itemView.setOnClickListener(view -> itemClickListener.onMediaClicked(dcMsg));
}
else if (slide != null && slide.isWebxdcDocument()) {
@@ -160,6 +155,12 @@ class ProfileDocumentsAdapter extends StickyHeaderGridAdapter {
notifyDataSetChanged();
}
+ public void selectAll() {
+ selected.clear();
+ selected.addAll(media.getAll());
+ notifyDataSetChanged();
+ }
+
public int getSelectedMediaCount() {
return selected.size();
}
diff --git a/src/org/thoughtcrime/securesms/ProfileDocumentsFragment.java b/src/org/thoughtcrime/securesms/ProfileDocumentsFragment.java
index 9900ef350..054cf958d 100644
--- a/src/org/thoughtcrime/securesms/ProfileDocumentsFragment.java
+++ b/src/org/thoughtcrime/securesms/ProfileDocumentsFragment.java
@@ -1,5 +1,7 @@
package org.thoughtcrime.securesms;
+import static com.b44t.messenger.DcChat.DC_CHAT_NO_CHAT;
+
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
@@ -45,7 +47,7 @@ public class ProfileDocumentsFragment
protected TextView noMedia;
protected RecyclerView recyclerView;
private StickyHeaderGridLayoutManager gridManager;
- private ActionModeCallback actionModeCallback = new ActionModeCallback();
+ private final ActionModeCallback actionModeCallback = new ActionModeCallback();
private boolean showAudio;
private boolean showWebxdc;
@@ -124,7 +126,7 @@ public class ProfileDocumentsFragment
((ProfileDocumentsAdapter) recyclerView.getAdapter()).notifyAllSectionsDataSetChanged();
noMedia.setVisibility(recyclerView.getAdapter().getItemCount() > 0 ? View.GONE : View.VISIBLE);
- if (chatId == 0) {
+ if (chatId == DC_CHAT_NO_CHAT) {
noMedia.setText(R.string.tab_all_media_empty_hint);
} else if (showAudio) {
noMedia.setText(R.string.tab_audio_empty_hint);
@@ -148,6 +150,11 @@ public class ProfileDocumentsFragment
}
}
+ private void updateActionModeBar() {
+ actionMode.setTitle(String.valueOf(getListAdapter().getSelectedMediaCount()));
+ setCorrectMenuVisibility(actionMode.getMenu());
+ }
+
private void handleMediaMultiSelectClick(@NonNull DcMsg mediaRecord) {
ProfileDocumentsAdapter adapter = getListAdapter();
@@ -156,8 +163,7 @@ public class ProfileDocumentsFragment
actionMode.finish();
actionMode = null;
} else {
- actionMode.setTitle(String.valueOf(adapter.getSelectedMediaCount()));
- setCorrectMenuVisibility(actionMode.getMenu());
+ updateActionModeBar();
}
}
@@ -202,6 +208,15 @@ public class ProfileDocumentsFragment
menu.findItem(R.id.show_in_chat).setVisible(singleSelection);
menu.findItem(R.id.share).setVisible(singleSelection);
+ boolean canResend = true;
+ for (DcMsg messageRecord : messageRecords) {
+ if (!messageRecord.isOutgoing()) {
+ canResend = false;
+ break;
+ }
+ }
+ menu.findItem(R.id.menu_resend).setVisible(canResend);
+
boolean webxdcApp = singleSelection && messageRecords.iterator().next().getType() == DcMsg.DC_MSG_WEBXDC;
menu.findItem(R.id.menu_add_to_home_screen).setVisible(webxdcApp);
}
@@ -257,6 +272,13 @@ public class ProfileDocumentsFragment
case R.id.save:
handleSaveAttachment(getListAdapter().getSelectedMedia());
return true;
+ case R.id.menu_resend:
+ handleResendMessage(getListAdapter().getSelectedMedia());
+ return true;
+ case R.id.menu_select_all:
+ getListAdapter().selectAll();
+ updateActionModeBar();
+ return true;
}
return false;
}
diff --git a/src/org/thoughtcrime/securesms/ProfileGalleryAdapter.java b/src/org/thoughtcrime/securesms/ProfileGalleryAdapter.java
index 9ab9a1264..5bd775337 100644
--- a/src/org/thoughtcrime/securesms/ProfileGalleryAdapter.java
+++ b/src/org/thoughtcrime/securesms/ProfileGalleryAdapter.java
@@ -23,9 +23,6 @@ import java.util.Set;
class ProfileGalleryAdapter extends StickyHeaderGridAdapter {
- @SuppressWarnings("unused")
- private static final String TAG = ProfileGalleryAdapter.class.getSimpleName();
-
private final Context context;
private final GlideRequests glideRequests;
private final Locale locale;
@@ -35,8 +32,8 @@ class ProfileGalleryAdapter extends StickyHeaderGridAdapter {
private BucketedThreadMedia media;
private static class ViewHolder extends StickyHeaderGridAdapter.ItemViewHolder {
- ThumbnailView imageView;
- View selectedIndicator;
+ final ThumbnailView imageView;
+ final View selectedIndicator;
ViewHolder(View v) {
super(v);
@@ -46,7 +43,7 @@ class ProfileGalleryAdapter extends StickyHeaderGridAdapter {
}
private static class HeaderHolder extends StickyHeaderGridAdapter.HeaderViewHolder {
- TextView textView;
+ final TextView textView;
HeaderHolder(View itemView) {
super(itemView);
@@ -124,6 +121,12 @@ class ProfileGalleryAdapter extends StickyHeaderGridAdapter {
notifyDataSetChanged();
}
+ public void selectAll() {
+ selected.clear();
+ selected.addAll(media.getAll());
+ notifyDataSetChanged();
+ }
+
public int getSelectedMediaCount() {
return selected.size();
}
diff --git a/src/org/thoughtcrime/securesms/ProfileGalleryFragment.java b/src/org/thoughtcrime/securesms/ProfileGalleryFragment.java
index 06d550ce4..6fa61d3da 100644
--- a/src/org/thoughtcrime/securesms/ProfileGalleryFragment.java
+++ b/src/org/thoughtcrime/securesms/ProfileGalleryFragment.java
@@ -1,5 +1,7 @@
package org.thoughtcrime.securesms;
+import static com.b44t.messenger.DcChat.DC_CHAT_NO_CHAT;
+
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
@@ -45,7 +47,7 @@ public class ProfileGalleryFragment
protected TextView noMedia;
protected RecyclerView recyclerView;
private StickyHeaderGridLayoutManager gridManager;
- private ActionModeCallback actionModeCallback = new ActionModeCallback();
+ private final ActionModeCallback actionModeCallback = new ActionModeCallback();
private int chatId;
private Locale locale;
@@ -120,7 +122,7 @@ public class ProfileGalleryFragment
((ProfileGalleryAdapter) recyclerView.getAdapter()).notifyAllSectionsDataSetChanged();
noMedia.setVisibility(recyclerView.getAdapter().getItemCount() > 0 ? View.GONE : View.VISIBLE);
- if (chatId == 0) {
+ if (chatId == DC_CHAT_NO_CHAT) {
noMedia.setText(R.string.tab_all_media_empty_hint);
}
getActivity().invalidateOptionsMenu();
@@ -140,6 +142,11 @@ public class ProfileGalleryFragment
}
}
+ private void updateActionModeBar() {
+ actionMode.setTitle(String.valueOf(getListAdapter().getSelectedMediaCount()));
+ setCorrectMenuVisibility(actionMode.getMenu());
+ }
+
private void handleMediaMultiSelectClick(@NonNull DcMsg mediaRecord) {
ProfileGalleryAdapter adapter = getListAdapter();
@@ -148,8 +155,7 @@ public class ProfileGalleryFragment
actionMode.finish();
actionMode = null;
} else {
- actionMode.setTitle(String.valueOf(adapter.getSelectedMediaCount()));
- setCorrectMenuVisibility(actionMode.getMenu());
+ updateActionModeBar();
}
}
@@ -195,6 +201,15 @@ public class ProfileGalleryFragment
menu.findItem(R.id.details).setVisible(singleSelection);
menu.findItem(R.id.show_in_chat).setVisible(singleSelection);
menu.findItem(R.id.share).setVisible(singleSelection);
+
+ boolean canResend = true;
+ for (DcMsg messageRecord : messageRecords) {
+ if (!messageRecord.isOutgoing()) {
+ canResend = false;
+ break;
+ }
+ }
+ menu.findItem(R.id.menu_resend).setVisible(canResend);
}
private ProfileGalleryAdapter getListAdapter() {
@@ -244,6 +259,13 @@ public class ProfileGalleryFragment
case R.id.save:
handleSaveAttachment(getListAdapter().getSelectedMedia());
return true;
+ case R.id.menu_resend:
+ handleResendMessage(getListAdapter().getSelectedMedia());
+ return true;
+ case R.id.menu_select_all:
+ getListAdapter().selectAll();
+ updateActionModeBar();
+ return true;
}
return false;
}
diff --git a/src/org/thoughtcrime/securesms/ProfileSettingsAdapter.java b/src/org/thoughtcrime/securesms/ProfileSettingsAdapter.java
index 68766932a..553479eb4 100644
--- a/src/org/thoughtcrime/securesms/ProfileSettingsAdapter.java
+++ b/src/org/thoughtcrime/securesms/ProfileSettingsAdapter.java
@@ -32,13 +32,15 @@ import java.util.Set;
public class ProfileSettingsAdapter extends RecyclerView.Adapter
implements StickyHeaderAdapter
{
- public static final int SETTING_SEND_MESSAGE = 120;
+ public static final int INFO_VERIFIED = 118;
+ public static final int INFO_LAST_SEEN = 119;
+ public static final int INFO_SEND_MESSAGE_BUTTON = 120;
private final @NonNull Context context;
private final @NonNull Locale locale;
private final @NonNull DcContext dcContext;
- private @NonNull ArrayList itemData = new ArrayList<>();
+ private final @NonNull ArrayList itemData = new ArrayList<>();
private int itemDataMemberCount;
private DcChatlist itemDataSharedChats;
private DcContact itemDataContact;
@@ -52,30 +54,34 @@ public class ProfileSettingsAdapter extends RecyclerView.Adapter
private final GlideRequests glideRequests;
static class ItemData {
- static final int TYPE_PRIMARY_SETTING = 1;
- static final int TYPE_STATUS = 2;
- static final int TYPE_MEMBER = 3;
- static final int TYPE_SHARED_CHAT = 4;
- int type;
- int contactId;
- int chatlistIndex;
- int settingsId;
- String label;
+ static final int CATEGORY_INFO = 1;
+ static final int CATEGORY_SIGNATURE = 2;
+ static final int CATEGORY_MEMBERS = 3;
+ static final int CATEGORY_SHARED_CHATS = 4;
+ final int type;
+ final int contactId;
+ final int chatlistIndex;
+ final int settingsId;
+ final String label;
+ final int labelColor;
+ final int iconLeft;
- ItemData(int type, int settingsId, String label) {
- this(type, 0, 0, settingsId, label);
+ ItemData(int type, int settingsId, String label, int labelColor, int iconLeft) {
+ this(type, 0, 0, settingsId, label, labelColor, iconLeft);
}
ItemData(int type, int contactId, int chatlistIndex) {
- this(type, contactId, chatlistIndex, 0, null);
+ this(type, contactId, chatlistIndex, 0, null, 0, 0);
}
- ItemData(int type, int contactId, int chatlistIndex, int settingsId, @Nullable String label) {
+ ItemData(int type, int contactId, int chatlistIndex, int settingsId, @Nullable String label, int labelColor, int iconLeft) {
this.type = type;
this.contactId = contactId;
this.chatlistIndex = chatlistIndex;
this.settingsId = settingsId;
this.label = label;
+ this.labelColor = labelColor;
+ this.iconLeft = iconLeft;
}
};
@@ -106,7 +112,7 @@ public class ProfileSettingsAdapter extends RecyclerView.Adapter
}
static class HeaderViewHolder extends RecyclerView.ViewHolder {
- TextView textView;
+ final TextView textView;
HeaderViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.label);
@@ -115,17 +121,17 @@ public class ProfileSettingsAdapter extends RecyclerView.Adapter
@Override
public ProfileSettingsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- if (viewType == ItemData.TYPE_MEMBER) {
+ if (viewType == ItemData.CATEGORY_MEMBERS) {
final ContactSelectionListItem item = (ContactSelectionListItem)layoutInflater.inflate(R.layout.contact_selection_list_item, parent, false);
item.setNoHeaderPadding();
return new ViewHolder(item);
}
- else if (viewType == ItemData.TYPE_SHARED_CHAT) {
+ else if (viewType == ItemData.CATEGORY_SHARED_CHATS) {
final ConversationListItem item = (ConversationListItem)layoutInflater.inflate(R.layout.conversation_list_item_view, parent, false);
item.hideItemDivider();
return new ViewHolder(item);
}
- else if (viewType == ItemData.TYPE_STATUS) {
+ else if (viewType == ItemData.CATEGORY_SIGNATURE) {
final ProfileStatusItem item = (ProfileStatusItem)layoutInflater.inflate(R.layout.profile_status_item, parent, false);
return new ViewHolder(item);
}
@@ -191,7 +197,7 @@ public class ProfileSettingsAdapter extends RecyclerView.Adapter
int settingsId = itemData.get(i).settingsId;
ProfileSettingsItem profileSettingsItem = (ProfileSettingsItem) holder.itemView;
profileSettingsItem.setOnClickListener(view -> clickListener.onSettingsClicked(settingsId));
- profileSettingsItem.set(itemData.get(i).label);
+ profileSettingsItem.set(itemData.get(i).label, itemData.get(i).labelColor, itemData.get(i).iconLeft);
}
}
@@ -222,7 +228,7 @@ public class ProfileSettingsAdapter extends RecyclerView.Adapter
public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) {
String txt = "";
switch(getItemViewType(position)) {
- case ItemData.TYPE_MEMBER:
+ case ItemData.CATEGORY_MEMBERS:
if (isMailingList) {
txt = context.getString(R.string.contacts_headline);
} else if (isBroadcast) {
@@ -231,19 +237,13 @@ public class ProfileSettingsAdapter extends RecyclerView.Adapter
txt = context.getResources().getQuantityString(R.plurals.n_members, (int) itemDataMemberCount, (int) itemDataMemberCount);
}
break;
- case ItemData.TYPE_SHARED_CHAT:
+ case ItemData.CATEGORY_SHARED_CHATS:
txt = context.getString(R.string.profile_shared_chats);
break;
- case ItemData.TYPE_PRIMARY_SETTING:
- long lastSeen = (itemDataContact!=null? itemDataContact.getLastSeen() : 0);
- if (lastSeen == 0) {
- txt = context.getString(R.string.last_seen_unknown);
- }
- else {
- txt = context.getString(R.string.last_seen_at, DateUtils.getExtendedTimeSpanString(context, locale, lastSeen));
- }
+ case ItemData.CATEGORY_INFO:
+ txt = context.getString(R.string.info);
break;
- case ItemData.TYPE_STATUS:
+ case ItemData.CATEGORY_SIGNATURE:
txt = context.getString(R.string.pref_default_status_label);
break;
default:
@@ -297,14 +297,14 @@ public class ProfileSettingsAdapter extends RecyclerView.Adapter
if (dcChat.isMailingList()) {
isMailingList = true;
} else if (dcChat.canSend()) {
- itemData.add(new ItemData(ItemData.TYPE_MEMBER, DcContact.DC_CONTACT_ID_ADD_MEMBER, 0));
+ itemData.add(new ItemData(ItemData.CATEGORY_MEMBERS, DcContact.DC_CONTACT_ID_ADD_MEMBER, 0));
if (!isBroadcast) {
- itemData.add(new ItemData(ItemData.TYPE_MEMBER, DcContact.DC_CONTACT_ID_QR_INVITE, 0));
+ itemData.add(new ItemData(ItemData.CATEGORY_MEMBERS, DcContact.DC_CONTACT_ID_QR_INVITE, 0));
}
}
for (int value : memberList) {
- itemData.add(new ItemData(ItemData.TYPE_MEMBER, value, 0));
+ itemData.add(new ItemData(ItemData.CATEGORY_MEMBERS, value, 0));
}
}
else if (sharedChats!=null && dcContact!=null) {
@@ -312,19 +312,44 @@ public class ProfileSettingsAdapter extends RecyclerView.Adapter
itemDataContact = dcContact;
if (!chatIsDeviceTalk) {
- itemData.add(new ItemData(ItemData.TYPE_PRIMARY_SETTING, SETTING_SEND_MESSAGE, context.getString(R.string.send_message)));
+ if (dcContact.isVerified()) {
+ int verifierId = dcContact.getVerifierId();
+
+ String verifiedInfo;
+ if (verifierId == DcContact.DC_CONTACT_ID_SELF) {
+ verifiedInfo = context.getString(R.string.verified_by_you);
+ } else if (verifierId != 0) {
+ verifiedInfo = context.getString(R.string.verified_by, dcContext.getContact(verifierId).getAddr());
+ } else {
+ verifiedInfo = context.getString(R.string.verified);
+ }
+ itemData.add(new ItemData(ItemData.CATEGORY_INFO, INFO_VERIFIED, verifiedInfo, 0, R.drawable.ic_verified));
+ }
+
+ long lastSeenTimestamp = (itemDataContact!=null? itemDataContact.getLastSeen() : 0);
+ String lastSeenTxt;
+ if (lastSeenTimestamp == 0) {
+ lastSeenTxt = context.getString(R.string.last_seen_unknown);
+ }
+ else {
+ lastSeenTxt = context.getString(R.string.last_seen_at, DateUtils.getExtendedTimeSpanString(context, locale, lastSeenTimestamp));
+ }
+ itemData.add(new ItemData(ItemData.CATEGORY_INFO, INFO_LAST_SEEN, lastSeenTxt, 0, 0));
+
+
+ itemData.add(new ItemData(ItemData.CATEGORY_INFO, INFO_SEND_MESSAGE_BUTTON, context.getString(R.string.send_message), R.color.delta_accent, 0));
}
itemDataStatusText = dcContact.getStatus();
if (!itemDataStatusText.isEmpty()) {
- itemData.add(new ItemData(ItemData.TYPE_STATUS, 0, itemDataStatusText));
+ itemData.add(new ItemData(ItemData.CATEGORY_SIGNATURE, 0, itemDataStatusText, 0, 0));
}
itemDataSharedChats = sharedChats;
if (!chatIsDeviceTalk) {
int sharedChatsCnt = sharedChats.getCnt();
for (int i = 0; i < sharedChatsCnt; i++) {
- itemData.add(new ItemData(ItemData.TYPE_SHARED_CHAT, 0, i));
+ itemData.add(new ItemData(ItemData.CATEGORY_SHARED_CHATS, 0, i));
}
}
}
diff --git a/src/org/thoughtcrime/securesms/ProfileSettingsFragment.java b/src/org/thoughtcrime/securesms/ProfileSettingsFragment.java
index 2030e4a15..58cbb3754 100644
--- a/src/org/thoughtcrime/securesms/ProfileSettingsFragment.java
+++ b/src/org/thoughtcrime/securesms/ProfileSettingsFragment.java
@@ -54,7 +54,7 @@ public class ProfileSettingsFragment extends Fragment
private StickyHeaderDecoration listDecoration;
private ProfileSettingsAdapter adapter;
private ActionMode actionMode;
- private ActionModeCallback actionModeCallback = new ActionModeCallback();
+ private final ActionModeCallback actionModeCallback = new ActionModeCallback();
private Locale locale;
@@ -143,9 +143,12 @@ public class ProfileSettingsFragment extends Fragment
@Override
public void onSettingsClicked(int settingsId) {
switch(settingsId) {
- case ProfileSettingsAdapter.SETTING_SEND_MESSAGE:
+ case ProfileSettingsAdapter.INFO_SEND_MESSAGE_BUTTON:
onSendMessage();
break;
+ case ProfileSettingsAdapter.INFO_VERIFIED:
+ onVerifiedByClicked();
+ break;
}
}
@@ -233,6 +236,18 @@ public class ProfileSettingsFragment extends Fragment
getActivity().finish();
}
+ private void onVerifiedByClicked() {
+ DcContact dcContact = dcContext.getContact(contactId);
+ if (dcContact.isVerified()) {
+ int verifierId = dcContact.getVerifierId();
+ if (verifierId != 0 && verifierId != DcContact.DC_CONTACT_ID_SELF && verifierId != contactId) {
+ Intent intent = new Intent(getContext(), ProfileActivity.class);
+ intent.putExtra(ProfileActivity.CONTACT_ID_EXTRA, verifierId);
+ startActivity(intent);
+ }
+ }
+ }
+
private void onSendMessage() {
DcContact dcContact = dcContext.getContact(contactId);
int chatId = dcContext.createChatByContactId(dcContact.getId());
@@ -256,6 +271,8 @@ public class ProfileSettingsFragment extends Fragment
menu.findItem(R.id.show_in_chat).setVisible(false);
menu.findItem(R.id.save).setVisible(false);
menu.findItem(R.id.share).setVisible(false);
+ menu.findItem(R.id.menu_resend).setVisible(false);
+ menu.findItem(R.id.menu_select_all).setVisible(false);
mode.setTitle("1");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
diff --git a/src/org/thoughtcrime/securesms/ProfileSettingsItem.java b/src/org/thoughtcrime/securesms/ProfileSettingsItem.java
index c14a029a0..8b113c29d 100644
--- a/src/org/thoughtcrime/securesms/ProfileSettingsItem.java
+++ b/src/org/thoughtcrime/securesms/ProfileSettingsItem.java
@@ -5,6 +5,10 @@ import android.util.AttributeSet;
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.core.content.ContextCompat;
+
+import org.thoughtcrime.securesms.util.ResUtil;
+
public class ProfileSettingsItem extends LinearLayout {
private TextView labelView;
@@ -23,7 +27,15 @@ public class ProfileSettingsItem extends LinearLayout {
labelView = findViewById(R.id.label);
}
- public void set(String label) {
+ public void set(String label, int labelColor, int iconLeft) {
labelView.setText(label==null? "" : label);
+ labelView.setCompoundDrawablesWithIntrinsicBounds(iconLeft, 0,0,0);
+
+ // we need different color getters as `labelColor` is `R.color.name` while default is `R.attr.name`
+ if (labelColor != 0) {
+ labelView.setTextColor(ContextCompat.getColor(getContext(), labelColor));
+ } else {
+ labelView.setTextColor(ResUtil.getColor(getContext(), R.attr.emoji_text_color));
+ }
}
}
diff --git a/src/org/thoughtcrime/securesms/ResolveMediaTask.java b/src/org/thoughtcrime/securesms/ResolveMediaTask.java
index 3f2d46d01..3bf5c6d09 100644
--- a/src/org/thoughtcrime/securesms/ResolveMediaTask.java
+++ b/src/org/thoughtcrime/securesms/ResolveMediaTask.java
@@ -6,7 +6,6 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Process;
import android.provider.OpenableColumns;
-import androidx.annotation.Nullable;
import android.util.Log;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
@@ -29,10 +28,10 @@ public class ResolveMediaTask extends AsyncTask {
void onMediaResolved(Uri uri);
}
- private WeakReference contextRef;
- private WeakReference listenerWeakReference;
+ private final WeakReference contextRef;
+ private final WeakReference listenerWeakReference;
- private static HashSet instances = new HashSet<>();
+ private static final HashSet instances = new HashSet<>();
ResolveMediaTask(Activity activityContext, ResolveMediaTask.OnMediaResolvedListener listener) {
this.contextRef = new WeakReference<>(activityContext);
diff --git a/src/org/thoughtcrime/securesms/ShareActivity.java b/src/org/thoughtcrime/securesms/ShareActivity.java
index 3df404a73..92710d00f 100644
--- a/src/org/thoughtcrime/securesms/ShareActivity.java
+++ b/src/org/thoughtcrime/securesms/ShareActivity.java
@@ -18,6 +18,7 @@
package org.thoughtcrime.securesms;
import static org.thoughtcrime.securesms.util.RelayUtil.setSharedText;
+import static org.thoughtcrime.securesms.util.RelayUtil.setSharedTitle;
import android.Manifest;
import android.content.Intent;
@@ -38,9 +39,7 @@ import com.b44t.messenger.DcContext;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.permissions.Permissions;
-import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
-import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.RelayUtil;
@@ -57,9 +56,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity implement
private static final String TAG = ShareActivity.class.getSimpleName();
public static final String EXTRA_CHAT_ID = "chat_id";
-
- private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
- private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
+ public static final String EXTRA_TITLE = "extra_title";
private ArrayList resolvedExtras;
private DcContext dcContext;
@@ -67,8 +64,8 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity implement
@Override
protected void onPreCreate() {
- dynamicTheme.onCreate(this);
- dynamicLanguage.onCreate(this);
+ dynamicTheme = new DynamicNoActionBarTheme();
+ super.onPreCreate();
}
@Override
@@ -89,14 +86,6 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity implement
initializeMedia();
}
- @Override
- public void onResume() {
- Log.w(TAG, "onResume()");
- super.onResume();
- dynamicTheme.onResume(this);
- dynamicLanguage.onResume(this);
- }
-
@Override
protected void onDestroy() {
super.onDestroy();
@@ -274,6 +263,12 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity implement
private Intent getBaseShareIntent(final @NonNull Class> target) {
final Intent intent = new Intent(this, target);
+
+ String title = getIntent().getStringExtra(EXTRA_TITLE);
+ if (title != null) {
+ setSharedTitle(intent, title);
+ }
+
String text = getIntent().getStringExtra(Intent.EXTRA_TEXT);
if (text==null) {
CharSequence cs = getIntent().getCharSequenceExtra(Intent.EXTRA_TEXT);
diff --git a/src/org/thoughtcrime/securesms/WebViewActivity.java b/src/org/thoughtcrime/securesms/WebViewActivity.java
index 12fc967ef..676b83794 100644
--- a/src/org/thoughtcrime/securesms/WebViewActivity.java
+++ b/src/org/thoughtcrime/securesms/WebViewActivity.java
@@ -18,13 +18,13 @@ import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.RequiresApi;
+import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.SearchView;
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;
import androidx.webkit.ProxyController;
import androidx.webkit.ProxyConfig;
-import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import java.util.concurrent.Executor;
@@ -36,13 +36,10 @@ public class WebViewActivity extends PassphraseRequiredActionBarActivity
private static final String TAG = WebViewActivity.class.getSimpleName();
protected WebView webView;
- private final DynamicTheme dynamicTheme = new DynamicTheme();
- protected final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@Override
protected void onPreCreate() {
- dynamicTheme.onCreate(this);
- dynamicLanguage.onCreate(this);
+ super.onPreCreate();
if (WebViewFeature.isFeatureSupported(WebViewFeature.PROXY_OVERRIDE)) {
// Set proxy to non-routable address.
@@ -71,7 +68,10 @@ public class WebViewActivity extends PassphraseRequiredActionBarActivity
@Override
protected void onCreate(Bundle state, boolean ready) {
setContentView(R.layout.web_view_activity);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
webView = findViewById(R.id.webview);
webView.setWebViewClient(new WebViewClient() {
@@ -103,7 +103,6 @@ public class WebViewActivity extends PassphraseRequiredActionBarActivity
}
@Override
- @SuppressWarnings("deprecation")
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
WebResourceResponse res = interceptRequest(url);
if (res!=null) {
@@ -134,9 +133,10 @@ public class WebViewActivity extends PassphraseRequiredActionBarActivity
}
protected void setForceDark() {
- if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+ if (Build.VERSION.SDK_INT <= 32 && WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+ // needed for older API (tested on android7) that do not set `color-scheme` without the following hint
WebSettingsCompat.setForceDark(webView.getSettings(),
- DynamicTheme.isDarkTheme(this) ? WebSettingsCompat.FORCE_DARK_ON : WebSettingsCompat.FORCE_DARK_OFF);
+ DynamicTheme.isDarkTheme(this) ? WebSettingsCompat.FORCE_DARK_ON : WebSettingsCompat.FORCE_DARK_OFF);
}
}
@@ -149,8 +149,6 @@ public class WebViewActivity extends PassphraseRequiredActionBarActivity
@Override
public void onResume() {
super.onResume();
- dynamicTheme.onResume(this);
- dynamicLanguage.onResume(this);
webView.onResume();
}
diff --git a/src/org/thoughtcrime/securesms/WebxdcActivity.java b/src/org/thoughtcrime/securesms/WebxdcActivity.java
index 303b68f48..6efbe6d55 100644
--- a/src/org/thoughtcrime/securesms/WebxdcActivity.java
+++ b/src/org/thoughtcrime/securesms/WebxdcActivity.java
@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
@@ -14,6 +15,7 @@ import android.util.Base64;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.View;
import android.webkit.JavascriptInterface;
import android.webkit.MimeTypeMap;
import android.webkit.ValueCallback;
@@ -25,6 +27,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
+import androidx.appcompat.app.ActionBar;
import androidx.core.app.TaskStackBuilder;
import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.content.pm.ShortcutManagerCompat;
@@ -36,6 +39,7 @@ import com.b44t.messenger.DcEvent;
import com.b44t.messenger.DcMsg;
import org.json.JSONObject;
+import org.thoughtcrime.securesms.connect.AccountManager;
import org.thoughtcrime.securesms.connect.DcEventCenter;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.util.JsonUtils;
@@ -44,7 +48,6 @@ import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.Util;
import java.io.ByteArrayInputStream;
-import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
@@ -61,13 +64,15 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
private boolean internetAccess = false;
public static void openWebxdcActivity(Context context, DcMsg instance) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- if (Prefs.isDeveloperModeEnabled(context)) {
- WebView.setWebContentsDebuggingEnabled(true);
+ if (!Util.isClickedRecently()) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ if (Prefs.isDeveloperModeEnabled(context)) {
+ WebView.setWebContentsDebuggingEnabled(true);
+ }
+ context.startActivity(getWebxdcIntent(context, instance.getId()));
+ } else {
+ Toast.makeText(context, "At least Android 5.0 (Lollipop) required for Webxdc.", Toast.LENGTH_LONG).show();
}
- context.startActivity(getWebxdcIntent(context, instance.getId()));
- } else {
- Toast.makeText(context, "At least Android 5.0 (Lollipop) required for Webxdc.", Toast.LENGTH_LONG).show();
}
}
@@ -99,6 +104,10 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
protected void onCreate(Bundle state, boolean ready) {
super.onCreate(state, ready);
+ // enter fullscreen mode if necessary,
+ // this is needed here because if the app is opened while already in landscape mode, onConfigurationChanged() is not triggered
+ setScreenMode();
+
webView.setWebChromeClient(new WebChromeClient() {
@Override
@RequiresApi(21)
@@ -118,15 +127,16 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
DcEventCenter eventCenter = DcHelper.getEventCenter(WebxdcActivity.this.getApplicationContext());
eventCenter.addObserver(DcContext.DC_EVENT_WEBXDC_STATUS_UPDATE, this);
+ eventCenter.addObserver(DcContext.DC_EVENT_MSGS_CHANGED, this);
Bundle b = getIntent().getExtras();
int appMessageId = b.getInt("appMessageId");
+ int accountId = b.getInt("accountId");
this.dcContext = DcHelper.getContext(getApplicationContext());
- if (dcContext.getAccountId() != b.getInt("accountId")) {
- Toast.makeText(this, "Switch to belonging account first.", Toast.LENGTH_LONG).show();
- finish();
- return;
+ if (accountId != dcContext.getAccountId()) {
+ AccountManager.getInstance().switchAccount(getApplicationContext(), accountId);
+ this.dcContext = DcHelper.getContext(getApplicationContext());
}
this.dcAppMsg = this.dcContext.getMsg(appMessageId);
@@ -197,6 +207,29 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
return false;
}
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ Log.i(TAG, "onConfigurationChanged(" + newConfig.orientation + ")");
+ super.onConfigurationChanged(newConfig);
+ // orientation might have changed, enter/exit fullscreen mode if needed
+ setScreenMode();
+ }
+
+ private void setScreenMode() {
+ // enter/exit fullscreen mode depending on orientation (landscape/portrait),
+ // on tablets there is enough height so fullscreen mode is never enabled there
+ boolean enable = getResources().getBoolean(R.bool.isLandscape) && !getResources().getBoolean(R.bool.isBigScreen);
+ getWindow().getDecorView().setSystemUiVisibility(enable? View.SYSTEM_UI_FLAG_FULLSCREEN : 0);
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ if (enable) {
+ actionBar.hide();
+ } else {
+ actionBar.show();
+ }
+ }
+ }
+
@Override
protected boolean openOnlineUrl(String url) {
if (url.startsWith("mailto:")) {
@@ -238,6 +271,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
if (mimeType == null) {
switch (ext) {
case "js": mimeType = "text/javascript"; break;
+ case "wasm": mimeType = "application/wasm"; break;
default: mimeType = "application/octet-stream"; Log.i(TAG, "unknown mime type for " + rawUrl); break;
}
}
@@ -251,7 +285,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
res = new WebResourceResponse("text/plain", "UTF-8", targetStream);
}
- if (res != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Map headers = new HashMap<>();
headers.put("Content-Security-Policy",
"default-src 'self'; "
@@ -276,6 +310,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
Log.i(TAG, "handleEvent");
webView.loadUrl("javascript:document.getElementById('frame').contentWindow.__webxdcUpdate();");
} else if ((eventId == DcContext.DC_EVENT_MSGS_CHANGED && event.getData2Int() == dcAppMsg.getId())) {
+ this.dcAppMsg = this.dcContext.getMsg(event.getData2Int()); // msg changed, reload data from db
Util.runOnAnyBackgroundThread(() -> {
final JSONObject info = dcAppMsg.getWebxdcInfo();
final DcChat chat = dcContext.getChat(dcAppMsg.getChatId());
@@ -409,7 +444,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
return "provided file is invalid, you need to set both name and base64 content";
}
- DcHelper.share(WebxdcActivity.this, data, "application/octet-stream", name, text);
+ DcHelper.sendToChat(WebxdcActivity.this, data, "application/octet-stream", name, text);
return null;
} catch (Exception e) {
e.printStackTrace();
diff --git a/src/org/thoughtcrime/securesms/WelcomeActivity.java b/src/org/thoughtcrime/securesms/WelcomeActivity.java
index 2d4d18beb..8f8505caf 100644
--- a/src/org/thoughtcrime/securesms/WelcomeActivity.java
+++ b/src/org/thoughtcrime/securesms/WelcomeActivity.java
@@ -38,7 +38,6 @@ import org.thoughtcrime.securesms.qr.RegistrationQrActivity;
import org.thoughtcrime.securesms.service.GenericForegroundService;
import org.thoughtcrime.securesms.service.NotificationController;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
-import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.StorageUtil;
import org.thoughtcrime.securesms.util.StreamUtil;
import org.thoughtcrime.securesms.util.Util;
@@ -65,10 +64,14 @@ public class WelcomeActivity extends BaseActionBarActivity implements DcEventCen
DcContext dcContext;
private NotificationController notificationController;
+ @Override
+ protected void onPreCreate() {
+ dynamicTheme = new DynamicNoActionBarTheme();
+ super.onPreCreate();
+ }
+
@Override
public void onCreate(Bundle bundle) {
- DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
- dynamicTheme.onCreate(this);
super.onCreate(bundle);
setContentView(R.layout.welcome_activity);
diff --git a/src/org/thoughtcrime/securesms/accounts/AccountSelectionListAdapter.java b/src/org/thoughtcrime/securesms/accounts/AccountSelectionListAdapter.java
index a61fc92fa..66d9cb891 100644
--- a/src/org/thoughtcrime/securesms/accounts/AccountSelectionListAdapter.java
+++ b/src/org/thoughtcrime/securesms/accounts/AccountSelectionListAdapter.java
@@ -20,9 +20,6 @@ import org.thoughtcrime.securesms.mms.GlideRequests;
public class AccountSelectionListAdapter extends RecyclerView.Adapter
{
- @SuppressWarnings("unused")
- private final static String TAG = AccountSelectionListAdapter.class.getSimpleName();
-
private final @NonNull Context context;
private final @NonNull DcAccounts accounts;
private @NonNull int[] accountList = new int[0];
diff --git a/src/org/thoughtcrime/securesms/accounts/AccountSelectionListFragment.java b/src/org/thoughtcrime/securesms/accounts/AccountSelectionListFragment.java
index 1ad3161cc..be83c84bc 100644
--- a/src/org/thoughtcrime/securesms/accounts/AccountSelectionListFragment.java
+++ b/src/org/thoughtcrime/securesms/accounts/AccountSelectionListFragment.java
@@ -24,9 +24,6 @@ import static com.b44t.messenger.DcContact.DC_CONTACT_ID_ADD_ACCOUNT;
public class AccountSelectionListFragment extends DialogFragment
{
- @SuppressWarnings("unused")
- private static final String TAG = AccountSelectionListFragment.class.getSimpleName();
-
private RecyclerView recyclerView;
@Override
diff --git a/src/org/thoughtcrime/securesms/accounts/AccountSelectionListItem.java b/src/org/thoughtcrime/securesms/accounts/AccountSelectionListItem.java
index 726be41ce..79d3f37b2 100644
--- a/src/org/thoughtcrime/securesms/accounts/AccountSelectionListItem.java
+++ b/src/org/thoughtcrime/securesms/accounts/AccountSelectionListItem.java
@@ -24,9 +24,6 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class AccountSelectionListItem extends LinearLayout {
- @SuppressWarnings("unused")
- private static final String TAG = AccountSelectionListItem.class.getSimpleName();
-
private AvatarImageView contactPhotoImage;
private View addrContainer;
private TextView addrView;
diff --git a/src/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java b/src/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java
index 4e745773d..dcddc008d 100644
--- a/src/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java
+++ b/src/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java
@@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.audio;
+import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@@ -7,6 +8,7 @@ import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.util.Pair;
+import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -151,6 +153,7 @@ public class AudioSlidePlayer {
setPlaying(AudioSlidePlayer.this);
}
+ keepScreenOn(true);
notifyOnStart();
progressEventHandler.sendEmptyMessage(0);
break;
@@ -163,6 +166,7 @@ public class AudioSlidePlayer {
mediaPlayer = null;
}
+ keepScreenOn(false);
notifyOnStop();
progressEventHandler.removeMessages(0);
}
@@ -200,6 +204,7 @@ public class AudioSlidePlayer {
public synchronized void stop() {
Log.i(TAG, "Stop called!");
+ keepScreenOn(false);
removePlaying(this);
if (this.mediaPlayer != null) {
@@ -264,7 +269,7 @@ public class AudioSlidePlayer {
Util.runOnMain(new Runnable() {
@Override
public void run() {
- getListener().onProgress(progress, millis);
+ getListener().onProgress(slide, progress, millis);
}
});
}
@@ -279,12 +284,22 @@ public class AudioSlidePlayer {
@Override
public void onStop() {}
@Override
- public void onProgress(double progress, long millis) {}
+ public void onProgress(AudioSlide slide, double progress, long millis) {}
@Override
public void onReceivedDuration(int millis) {}
};
}
+ public void keepScreenOn(boolean keepOn) {
+ if (context instanceof Activity) {
+ if (keepOn) {
+ ((Activity) context).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ } else {
+ ((Activity) context).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+ }
+ }
+
private synchronized static void setPlaying(@NonNull AudioSlidePlayer player) {
if (playing.isPresent() && playing.get() != player) {
playing.get().notifyOnStop();
@@ -303,7 +318,7 @@ public class AudioSlidePlayer {
public interface Listener {
void onStart();
void onStop();
- void onProgress(double progress, long millis);
+ void onProgress(AudioSlide slide, double progress, long millis);
void onReceivedDuration(int millis);
}
diff --git a/src/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java b/src/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java
index a0824f47c..930521977 100644
--- a/src/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java
+++ b/src/org/thoughtcrime/securesms/components/AttachmentTypeSelector.java
@@ -27,7 +27,6 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
-import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.permissions.Permissions;
@@ -46,9 +45,6 @@ public class AttachmentTypeSelector extends PopupWindow {
private static final int ANIMATION_DURATION = 300;
- @SuppressWarnings("unused")
- private static final String TAG = AttachmentTypeSelector.class.getSimpleName();
-
private final @NonNull LoaderManager loaderManager;
private final @NonNull RecentPhotoViewRail recentRail;
private final @NonNull ImageView imageButton;
@@ -62,7 +58,7 @@ public class AttachmentTypeSelector extends PopupWindow {
private @Nullable View currentAnchor;
private @Nullable AttachmentClickedListener listener;
- private int chatId;
+ private final int chatId;
public AttachmentTypeSelector(@NonNull Context context, @NonNull LoaderManager loaderManager, @Nullable AttachmentClickedListener listener, int chatId) {
super(context);
@@ -117,7 +113,7 @@ public class AttachmentTypeSelector extends PopupWindow {
}
public void show(@NonNull Activity activity, final @NonNull View anchor) {
- if (Permissions.hasAll(activity, Manifest.permission.READ_EXTERNAL_STORAGE)) {
+ if (Permissions.hasAll(activity, Permissions.galleryPermissions())) {
recentRail.setVisibility(View.VISIBLE);
loaderManager.restartLoader(1, null, recentRail);
} else {
diff --git a/src/org/thoughtcrime/securesms/components/AudioView.java b/src/org/thoughtcrime/securesms/components/AudioView.java
index e06345909..0a15ccda0 100644
--- a/src/org/thoughtcrime/securesms/components/AudioView.java
+++ b/src/org/thoughtcrime/securesms/components/AudioView.java
@@ -6,9 +6,10 @@ import android.content.res.ColorStateList;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.AnimatedVectorDrawable;
+import android.media.AudioAttributes;
+import android.media.AudioFocusRequest;
+import android.media.AudioManager;
import android.os.Build;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
@@ -18,6 +19,9 @@ import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.audio.AudioSlidePlayer;
import org.thoughtcrime.securesms.mms.AudioSlide;
@@ -36,8 +40,10 @@ public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener
private final @NonNull SeekBar seekBar;
private final @NonNull TextView timestamp;
private final @NonNull TextView title;
+ private final @NonNull View mask;
private @Nullable AudioSlidePlayer audioSlidePlayer;
+ private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener;
private int backwardsCounter;
public AudioView(Context context) {
@@ -58,6 +64,7 @@ public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener
this.seekBar = (SeekBar) findViewById(R.id.seek);
this.timestamp = (TextView) findViewById(R.id.timestamp);
this.title = (TextView) findViewById(R.id.title);
+ this.mask = findViewById(R.id.interception_mask);
this.timestamp.setText("00:00");
@@ -79,6 +86,7 @@ public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener
{
controlToggle.displayQuick(playButton);
seekBar.setEnabled(true);
+ seekBar.setProgress(0);
audioSlidePlayer = AudioSlidePlayer.createFor(getContext(), audio, this);
timestamp.setText(DateUtils.getFormatedDuration(duration));
@@ -91,6 +99,42 @@ public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener
}
}
+ @Override
+ public void setOnClickListener(OnClickListener listener) {
+ super.setOnClickListener(listener);
+ this.mask.setOnClickListener(listener);
+ }
+
+ @Override
+ public void setOnLongClickListener(OnLongClickListener listener) {
+ super.setOnLongClickListener(listener);
+ this.mask.setOnLongClickListener(listener);
+ this.playButton.setOnLongClickListener(listener);
+ this.pauseButton.setOnLongClickListener(listener);
+ }
+
+ public void togglePlay() {
+ if (this.playButton.getVisibility() == View.VISIBLE) {
+ playButton.performClick();
+ } else {
+ pauseButton.performClick();
+ }
+ }
+
+ public String getDescription() {
+ String desc;
+ if (this.title.getVisibility() == View.VISIBLE) {
+ desc = getContext().getString(R.string.audio);
+ } else {
+ desc = getContext().getString(R.string.voice_message);
+ }
+ desc += "\n" + this.timestamp.getText();
+ if (title.getVisibility() == View.VISIBLE) {
+ desc += "\n" + this.title.getText();
+ }
+ return desc;
+ }
+
public void setDuration(int duration) {
if (getProgress()==0)
this.timestamp.setText(DateUtils.getFormatedDuration(duration));
@@ -122,39 +166,20 @@ public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener
if (seekBar.getProgress() + 5 >= seekBar.getMax()) {
backwardsCounter = 4;
- onProgress(0.0, -1);
+ onProgress(audioSlidePlayer.getAudioSlide(), 0.0, -1);
}
}
- @Override
- public void setFocusable(boolean focusable) {
- super.setFocusable(focusable);
- this.playButton.setFocusable(focusable);
- this.pauseButton.setFocusable(focusable);
- this.seekBar.setFocusable(focusable);
- this.seekBar.setFocusableInTouchMode(focusable);
+ public void disablePlayer(boolean disable) {
+ this.mask.setVisibility(disable? View.VISIBLE : View.GONE);
}
@Override
- public void setClickable(boolean clickable) {
- super.setClickable(clickable);
- this.playButton.setClickable(clickable);
- this.pauseButton.setClickable(clickable);
- this.seekBar.setClickable(clickable);
- this.seekBar.setOnTouchListener(clickable ? null : new TouchIgnoringListener());
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- super.setEnabled(enabled);
- this.playButton.setEnabled(enabled);
- this.pauseButton.setEnabled(enabled);
- this.seekBar.setEnabled(enabled);
- }
-
- @Override
- public void onProgress(double progress, long millis) {
- int seekProgress = (int)Math.floor(progress * this.seekBar.getMax());
+ public void onProgress(AudioSlide slide, double progress, long millis) {
+ if (!audioSlidePlayer.getAudioSlide().equals(slide)) {
+ return;
+ }
+ int seekProgress = (int) Math.floor(progress * this.seekBar.getMax());
if (seekProgress > seekBar.getProgress() || backwardsCounter > 3) {
backwardsCounter = 0;
@@ -222,6 +247,31 @@ public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener
try {
Log.w(TAG, "playbutton onClick");
if (audioSlidePlayer != null) {
+ if (Build.VERSION.SDK_INT >= 26) {
+ if (audioFocusChangeListener == null) {
+ audioFocusChangeListener = focusChange -> {
+ if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
+ pauseButton.performClick();
+ }
+ };
+ }
+
+ AudioAttributes playbackAttributes = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
+ .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
+ .build();
+
+ AudioFocusRequest focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
+ .setAudioAttributes(playbackAttributes)
+ .setAcceptsDelayedFocusGain(false)
+ .setWillPauseWhenDucked(false)
+ .setOnAudioFocusChangeListener(audioFocusChangeListener)
+ .build();
+
+ AudioManager audioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+ audioManager.requestAudioFocus(focusRequest);
+ }
+
togglePlayToPause();
audioSlidePlayer.play(getProgress());
}
diff --git a/src/org/thoughtcrime/securesms/components/AvatarImageView.java b/src/org/thoughtcrime/securesms/components/AvatarImageView.java
index 685037ddc..fdd22011a 100644
--- a/src/org/thoughtcrime/securesms/components/AvatarImageView.java
+++ b/src/org/thoughtcrime/securesms/components/AvatarImageView.java
@@ -20,8 +20,6 @@ import org.thoughtcrime.securesms.util.ThemeUtil;
public class AvatarImageView extends AppCompatImageView {
- private static final String TAG = AvatarImageView.class.getSimpleName();
-
private OnClickListener listener = null;
public AvatarImageView(Context context) {
diff --git a/src/org/thoughtcrime/securesms/components/AvatarSelector.java b/src/org/thoughtcrime/securesms/components/AvatarSelector.java
index ecf987462..89f6336e7 100644
--- a/src/org/thoughtcrime/securesms/components/AvatarSelector.java
+++ b/src/org/thoughtcrime/securesms/components/AvatarSelector.java
@@ -31,9 +31,6 @@ public class AvatarSelector extends PopupWindow {
private static final int ANIMATION_DURATION = 300;
- @SuppressWarnings("unused")
- private static final String TAG = AttachmentTypeSelector.class.getSimpleName();
-
private final @NonNull LoaderManager loaderManager;
private final @NonNull RecentPhotoViewRail recentRail;
private final @NonNull ImageView imageButton;
@@ -80,7 +77,7 @@ public class AvatarSelector extends PopupWindow {
}
public void show(@NonNull Activity activity, final @NonNull View anchor) {
- if (Permissions.hasAll(activity, Manifest.permission.READ_EXTERNAL_STORAGE)) {
+ if (Permissions.hasAll(activity, Permissions.galleryPermissions())) {
recentRail.setVisibility(View.VISIBLE);
loaderManager.restartLoader(1, null, recentRail);
} else {
diff --git a/src/org/thoughtcrime/securesms/components/BorderlessImageView.java b/src/org/thoughtcrime/securesms/components/BorderlessImageView.java
index d4b2af102..4d134588d 100644
--- a/src/org/thoughtcrime/securesms/components/BorderlessImageView.java
+++ b/src/org/thoughtcrime/securesms/components/BorderlessImageView.java
@@ -18,6 +18,7 @@ public class BorderlessImageView extends FrameLayout {
private ThumbnailView image;
private View missingShade;
+ private ConversationItemFooter footer;
public BorderlessImageView(@NonNull Context context) {
super(context);
@@ -34,6 +35,7 @@ public class BorderlessImageView extends FrameLayout {
this.image = findViewById(R.id.sticker_thumbnail);
this.missingShade = findViewById(R.id.sticker_missing_shade);
+ this.footer = findViewById(R.id.sticker_footer);
}
@Override
@@ -65,6 +67,14 @@ public class BorderlessImageView extends FrameLayout {
missingShade.setVisibility(showControls ? View.VISIBLE : View.GONE);
}
+ public String getDescription() {
+ return getContext().getString(R.string.sticker) + "\n" + footer.getDescription();
+ }
+
+ public ConversationItemFooter getFooter() {
+ return footer;
+ }
+
public void setThumbnailClickListener(@NonNull SlideClickListener listener) {
image.setThumbnailClickListener(listener);
}
diff --git a/src/org/thoughtcrime/securesms/components/ContactFilterToolbar.java b/src/org/thoughtcrime/securesms/components/ContactFilterToolbar.java
index 4c2ffa397..c8147bb5a 100644
--- a/src/org/thoughtcrime/securesms/components/ContactFilterToolbar.java
+++ b/src/org/thoughtcrime/securesms/components/ContactFilterToolbar.java
@@ -19,10 +19,10 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class ContactFilterToolbar extends Toolbar {
private OnFilterChangedListener listener;
- private EditText searchText;
- private AnimatingToggle toggle;
- private ImageView clearToggle;
- private LinearLayout toggleContainer;
+ private final EditText searchText;
+ private final AnimatingToggle toggle;
+ private final ImageView clearToggle;
+ private final LinearLayout toggleContainer;
private boolean useClearButton;
public ContactFilterToolbar(Context context) {
diff --git a/src/org/thoughtcrime/securesms/components/ConversationItemFooter.java b/src/org/thoughtcrime/securesms/components/ConversationItemFooter.java
index b7f480cd0..2c8761f94 100644
--- a/src/org/thoughtcrime/securesms/components/ConversationItemFooter.java
+++ b/src/org/thoughtcrime/securesms/components/ConversationItemFooter.java
@@ -58,11 +58,6 @@ public class ConversationItemFooter extends LinearLayout {
}
}
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- }
-
public void setMessageRecord(@NonNull DcMsg messageRecord, @NonNull Locale locale) {
presentDate(messageRecord, locale);
secureIndicatorView.setVisibility(messageRecord.isSecure() ? View.VISIBLE : View.GONE);
@@ -105,4 +100,13 @@ public class ConversationItemFooter extends LinearLayout {
deliveryStatusView.setTint(textColor); // Reset the color to the standard color (because the footer is re-used in a RecyclerView)
}
}
+
+ public String getDescription() {
+ String desc = dateView.getText().toString();
+ String deliveryDesc = deliveryStatusView.getDescription();
+ if (!"".equals(deliveryDesc)) {
+ desc += "\n" + deliveryDesc;
+ }
+ return desc;
+ }
}
diff --git a/src/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java b/src/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java
index 8837ab6f6..2372b9e2c 100644
--- a/src/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java
+++ b/src/org/thoughtcrime/securesms/components/ConversationItemThumbnail.java
@@ -7,6 +7,7 @@ import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
+import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -86,6 +87,14 @@ public class ConversationItemThumbnail extends FrameLayout {
setTouchDelegate(thumbnail.getTouchDelegate());
}
+ public String getDescription() {
+ String desc = thumbnail.getDescription();
+ if (footer.getVisibility() == View.VISIBLE) {
+ desc += "\n" + footer.getDescription();
+ }
+ return desc;
+ }
+
@Override
protected void onMeasure(int originalWidthMeasureSpec, int originalHeightMeasureSpec) {
int width = MeasureSpec.getSize(originalWidthMeasureSpec);
@@ -185,6 +194,11 @@ public class ConversationItemThumbnail extends FrameLayout {
thumbnail.setThumbnailClickListener(listener);
}
+ @Override
+ public boolean performClick() {
+ return thumbnail.performClick();
+ }
+
@UiThread
public void setImageResource(@NonNull GlideRequests glideRequests, @NonNull Slide slide,
int naturalWidth, int naturalHeight)
diff --git a/src/org/thoughtcrime/securesms/components/CustomDefaultPreference.java b/src/org/thoughtcrime/securesms/components/CustomDefaultPreference.java
index c85f1804c..4c325c985 100644
--- a/src/org/thoughtcrime/securesms/components/CustomDefaultPreference.java
+++ b/src/org/thoughtcrime/securesms/components/CustomDefaultPreference.java
@@ -32,7 +32,7 @@ public class CustomDefaultPreference extends DialogPreference {
private final String customPreference;
private final String customToggle;
- private CustomPreferenceValidator validator;
+ private final CustomPreferenceValidator validator;
private String defaultValue;
public CustomDefaultPreference(Context context, AttributeSet attrs) {
diff --git a/src/org/thoughtcrime/securesms/components/DeliveryStatusView.java b/src/org/thoughtcrime/securesms/components/DeliveryStatusView.java
index 040631f57..b4db7fb68 100644
--- a/src/org/thoughtcrime/securesms/components/DeliveryStatusView.java
+++ b/src/org/thoughtcrime/securesms/components/DeliveryStatusView.java
@@ -123,4 +123,11 @@ public class DeliveryStatusView {
public void resetTint() {
deliveryIndicator.setColorFilter(null);
}
+
+ public String getDescription() {
+ if (deliveryIndicator.getVisibility() == View.VISIBLE) {
+ return deliveryIndicator.getContentDescription().toString();
+ }
+ return "";
+ }
}
diff --git a/src/org/thoughtcrime/securesms/components/DocumentView.java b/src/org/thoughtcrime/securesms/components/DocumentView.java
index 5e0ebf31d..b8769eb4a 100644
--- a/src/org/thoughtcrime/securesms/components/DocumentView.java
+++ b/src/org/thoughtcrime/securesms/components/DocumentView.java
@@ -59,6 +59,13 @@ public class DocumentView extends FrameLayout {
this.setOnClickListener(new OpenClickedListener(documentSlide));
}
+ public String getDescription() {
+ String desc = getContext().getString(R.string.file);
+ desc += "\n" + fileName.getText();
+ desc += "\n" + fileSize.getText();
+ return desc;
+ }
+
@Override
public void setFocusable(boolean focusable) {
super.setFocusable(focusable);
diff --git a/src/org/thoughtcrime/securesms/components/FromTextView.java b/src/org/thoughtcrime/securesms/components/FromTextView.java
index e96627e8e..06ff4487f 100644
--- a/src/org/thoughtcrime/securesms/components/FromTextView.java
+++ b/src/org/thoughtcrime/securesms/components/FromTextView.java
@@ -15,10 +15,8 @@ import android.util.AttributeSet;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
-import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.ResUtil;
-import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.spans.CenterAlignedRelativeSizeSpan;
public class FromTextView extends EmojiTextView {
diff --git a/src/org/thoughtcrime/securesms/components/InputPanel.java b/src/org/thoughtcrime/securesms/components/InputPanel.java
index 99f9076bd..a466a2b45 100644
--- a/src/org/thoughtcrime/securesms/components/InputPanel.java
+++ b/src/org/thoughtcrime/securesms/components/InputPanel.java
@@ -22,7 +22,6 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
-import androidx.core.view.ViewCompat;
import com.b44t.messenger.DcMsg;
import com.b44t.messenger.util.concurrent.ListenableFuture;
diff --git a/src/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java b/src/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java
index 263ab3cc9..4c9dfe638 100644
--- a/src/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java
+++ b/src/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java
@@ -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;
diff --git a/src/org/thoughtcrime/securesms/components/QuoteView.java b/src/org/thoughtcrime/securesms/components/QuoteView.java
index 17ae0cb9a..fa9d4f256 100644
--- a/src/org/thoughtcrime/securesms/components/QuoteView.java
+++ b/src/org/thoughtcrime/securesms/components/QuoteView.java
@@ -108,11 +108,6 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
dismissView.setOnClickListener(view -> setVisibility(GONE));
}
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- }
-
public void setQuote(GlideRequests glideRequests,
DcMsg msg,
@Nullable Recipient author,
diff --git a/src/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java b/src/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java
index 4e5eee16c..272cc5ed7 100644
--- a/src/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java
+++ b/src/org/thoughtcrime/securesms/components/RecentPhotoViewRail.java
@@ -80,9 +80,6 @@ public class RecentPhotoViewRail extends FrameLayout implements LoaderManager.Lo
private static class RecentPhotoAdapter extends CursorRecyclerViewAdapter {
- @SuppressWarnings("unused")
- private static final String TAG = RecentPhotoAdapter.class.getName();
-
@NonNull private final Uri baseUri;
@Nullable private OnItemClickedListener clickedListener;
@@ -132,7 +129,7 @@ public class RecentPhotoViewRail extends FrameLayout implements LoaderManager.Lo
static class RecentPhotoViewHolder extends RecyclerView.ViewHolder {
- ImageView imageView;
+ final ImageView imageView;
RecentPhotoViewHolder(View itemView) {
super(itemView);
diff --git a/src/org/thoughtcrime/securesms/components/RepeatableImageKey.java b/src/org/thoughtcrime/securesms/components/RepeatableImageKey.java
index 602db3d9f..48e071c94 100644
--- a/src/org/thoughtcrime/securesms/components/RepeatableImageKey.java
+++ b/src/org/thoughtcrime/securesms/components/RepeatableImageKey.java
@@ -6,11 +6,9 @@ import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.AttributeSet;
import android.view.HapticFeedbackConstants;
-import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
-import android.widget.EditText;
import android.widget.ImageButton;
public class RepeatableImageKey extends ImageButton {
@@ -71,7 +69,7 @@ public class RepeatableImageKey extends ImageButton {
}
private class RepeaterTouchListener implements OnTouchListener {
- private Repeater repeater;
+ private final Repeater repeater;
public RepeaterTouchListener() {
this.repeater = new Repeater();
diff --git a/src/org/thoughtcrime/securesms/components/ScaleStableImageView.java b/src/org/thoughtcrime/securesms/components/ScaleStableImageView.java
index 1f2a72901..1fc3f006e 100644
--- a/src/org/thoughtcrime/securesms/components/ScaleStableImageView.java
+++ b/src/org/thoughtcrime/securesms/components/ScaleStableImageView.java
@@ -38,7 +38,7 @@ public class ScaleStableImageView
private Drawable defaultDrawable;
private Drawable currentDrawable;
- private Map storedSizes = new HashMap<>();
+ private final Map storedSizes = new HashMap<>();
public ScaleStableImageView(Context context) {
this(context, null);
diff --git a/src/org/thoughtcrime/securesms/components/SendButton.java b/src/org/thoughtcrime/securesms/components/SendButton.java
index ef9c1a1e2..6de464b80 100644
--- a/src/org/thoughtcrime/securesms/components/SendButton.java
+++ b/src/org/thoughtcrime/securesms/components/SendButton.java
@@ -22,21 +22,18 @@ public class SendButton extends ImageButton
private Optional transportOptionsPopup = Optional.absent();
- @SuppressWarnings("unused")
public SendButton(Context context) {
super(context);
this.transportOptions = initializeTransportOptions();
ViewUtil.mirrorIfRtl(this, getContext());
}
- @SuppressWarnings("unused")
public SendButton(Context context, AttributeSet attrs) {
super(context, attrs);
this.transportOptions = initializeTransportOptions();
ViewUtil.mirrorIfRtl(this, getContext());
}
- @SuppressWarnings("unused")
public SendButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.transportOptions = initializeTransportOptions();
diff --git a/src/org/thoughtcrime/securesms/components/SquareFrameLayout.java b/src/org/thoughtcrime/securesms/components/SquareFrameLayout.java
index 5ee890358..0572c3481 100644
--- a/src/org/thoughtcrime/securesms/components/SquareFrameLayout.java
+++ b/src/org/thoughtcrime/securesms/components/SquareFrameLayout.java
@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.components;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.BitmapFactory;
import android.os.Build.VERSION_CODES;
import android.util.AttributeSet;
import android.widget.FrameLayout;
@@ -14,17 +13,15 @@ public class SquareFrameLayout extends FrameLayout {
private final boolean squareHeight;
- @SuppressWarnings("unused")
public SquareFrameLayout(Context context) {
this(context, null);
}
- @SuppressWarnings("unused")
public SquareFrameLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
- @TargetApi(VERSION_CODES.HONEYCOMB) @SuppressWarnings("unused")
+ @TargetApi(VERSION_CODES.HONEYCOMB)
public SquareFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
diff --git a/src/org/thoughtcrime/securesms/components/ThumbnailView.java b/src/org/thoughtcrime/securesms/components/ThumbnailView.java
index 41403f96f..6efd8702c 100644
--- a/src/org/thoughtcrime/securesms/components/ThumbnailView.java
+++ b/src/org/thoughtcrime/securesms/components/ThumbnailView.java
@@ -42,8 +42,8 @@ public class ThumbnailView extends FrameLayout {
private static final int MIN_HEIGHT = 2;
private static final int MAX_HEIGHT = 3;
- private ImageView image;
- private View playOverlay;
+ private final ImageView image;
+ private final View playOverlay;
private OnClickListener parentClickListener;
private final int[] dimens = new int[2];
@@ -82,6 +82,14 @@ public class ThumbnailView extends FrameLayout {
}
+ public String getDescription() {
+ if (slide != null && slide.hasPlayOverlay()) {
+ return getContext().getString(R.string.video);
+ } else {
+ return getContext().getString(R.string.image);
+ }
+ }
+
@Override
protected void onMeasure(int originalWidthMeasureSpec, int originalHeightMeasureSpec) {
fillTargetDimensions(measureDimens, dimens, bounds);
diff --git a/src/org/thoughtcrime/securesms/components/WebxdcView.java b/src/org/thoughtcrime/securesms/components/WebxdcView.java
index 552d0464a..a93904673 100644
--- a/src/org/thoughtcrime/securesms/components/WebxdcView.java
+++ b/src/org/thoughtcrime/securesms/components/WebxdcView.java
@@ -22,7 +22,6 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.mms.DocumentSlide;
import org.thoughtcrime.securesms.mms.SlideClickListener;
import org.thoughtcrime.securesms.util.JsonUtils;
-import org.thoughtcrime.securesms.util.Util;
import java.io.ByteArrayInputStream;
@@ -90,6 +89,15 @@ public class WebxdcView extends FrameLayout {
appSubtitle.setText(summary);
}
+ public String getDescription() {
+ String desc = getContext().getString(R.string.webxdc_app);
+ desc += "\n" + appName.getText();
+ if (appSubtitle.getText() != null && !appSubtitle.getText().toString().equals("") && !appSubtitle.getText().toString().equals(getContext().getString(R.string.webxdc_app))) {
+ desc += "\n" + appSubtitle.getText();
+ }
+ return desc;
+ }
+
private class OpenClickedListener implements View.OnClickListener {
private final @NonNull DocumentSlide slide;
diff --git a/src/org/thoughtcrime/securesms/components/camera/CameraView.java b/src/org/thoughtcrime/securesms/components/camera/CameraView.java
index 973967e3c..d5ce2efa0 100644
--- a/src/org/thoughtcrime/securesms/components/camera/CameraView.java
+++ b/src/org/thoughtcrime/securesms/components/camera/CameraView.java
@@ -64,7 +64,7 @@ public class CameraView extends ViewGroup {
private @NonNull State state = State.PAUSED;
private @Nullable Size previewSize;
- private @NonNull List listeners = Collections.synchronizedList(new LinkedList());
+ private final @NonNull List listeners = Collections.synchronizedList(new LinkedList());
private int outputOrientation = -1;
public CameraView(Context context) {
diff --git a/src/org/thoughtcrime/securesms/components/camera/QuickAttachmentDrawer.java b/src/org/thoughtcrime/securesms/components/camera/QuickAttachmentDrawer.java
index 249daaedc..96e7f5e2a 100644
--- a/src/org/thoughtcrime/securesms/components/camera/QuickAttachmentDrawer.java
+++ b/src/org/thoughtcrime/securesms/components/camera/QuickAttachmentDrawer.java
@@ -53,7 +53,7 @@ public class QuickAttachmentDrawer extends ViewGroup implements InputView, Camer
private ObjectAnimator animator;
private DrawerState drawerState = DrawerState.COLLAPSED;
- private Rect drawChildrenRect = new Rect();
+ private final Rect drawChildrenRect = new Rect();
private boolean paused = false;
public QuickAttachmentDrawer(Context context) {
@@ -310,7 +310,6 @@ public class QuickAttachmentDrawer extends ViewGroup implements InputView, Camer
}
}
- @SuppressWarnings("unused")
public void setSlideOffset(int slideOffset) {
this.slideOffset = slideOffset;
requestLayout();
diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiFilter.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiFilter.java
index 2b32d5e4d..f028454e0 100644
--- a/src/org/thoughtcrime/securesms/components/emoji/EmojiFilter.java
+++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiFilter.java
@@ -7,7 +7,7 @@ import android.text.TextUtils;
import android.widget.TextView;
public class EmojiFilter implements InputFilter {
- private TextView view;
+ private final TextView view;
public EmojiFilter(TextView view) {
this.view = view;
diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiKeyboardProvider.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiKeyboardProvider.java
index 3774e79ca..7949fe6eb 100644
--- a/src/org/thoughtcrime/securesms/components/emoji/EmojiKeyboardProvider.java
+++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiKeyboardProvider.java
@@ -111,10 +111,10 @@ public class EmojiKeyboardProvider implements MediaKeyboardProvider,
}
private static class EmojiPagerAdapter extends PagerAdapter {
- private Context context;
- private List pages;
- private EmojiEventListener emojiSelectionListener;
- private VariationSelectorListener variationSelectorListener;
+ private final Context context;
+ private final List pages;
+ private final EmojiEventListener emojiSelectionListener;
+ private final VariationSelectorListener variationSelectorListener;
public EmojiPagerAdapter(@NonNull Context context,
@NonNull List pages,
diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiPageView.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiPageView.java
index 3245394f3..f080ba695 100644
--- a/src/org/thoughtcrime/securesms/components/emoji/EmojiPageView.java
+++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiPageView.java
@@ -17,12 +17,12 @@ public class EmojiPageView extends FrameLayout implements VariationSelectorListe
private static final String TAG = EmojiPageView.class.getSimpleName();
private EmojiPageModel model;
- private EmojiPageViewGridAdapter adapter;
- private RecyclerView recyclerView;
- private GridLayoutManager layoutManager;
- private RecyclerView.OnItemTouchListener scrollDisabler;
- private VariationSelectorListener variationSelectorListener;
- private EmojiVariationSelectorPopup popup;
+ private final EmojiPageViewGridAdapter adapter;
+ private final RecyclerView recyclerView;
+ private final GridLayoutManager layoutManager;
+ private final RecyclerView.OnItemTouchListener scrollDisabler;
+ private final VariationSelectorListener variationSelectorListener;
+ private final EmojiVariationSelectorPopup popup;
public EmojiPageView(@NonNull Context context,
@NonNull EmojiEventListener emojiSelectionListener,
diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java
index 293a135a5..cd9c112b4 100644
--- a/src/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java
+++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java
@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.components.emoji;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
@@ -153,8 +152,8 @@ public class EmojiProvider {
class EmojiDrawable extends Drawable {
private final EmojiDrawInfo info;
private Bitmap bmp;
- private float intrinsicWidth;
- private float intrinsicHeight;
+ private final float intrinsicWidth;
+ private final float intrinsicHeight;
@Override
public int getIntrinsicWidth() {
diff --git a/src/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java b/src/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java
index 75a145d62..707d0c024 100644
--- a/src/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java
+++ b/src/org/thoughtcrime/securesms/components/emoji/EmojiTextView.java
@@ -41,7 +41,7 @@ public class EmojiTextView extends AppCompatTextView {
private float originalFontSize;
private boolean useSystemEmoji;
private boolean sizeChangeInProgress;
- private int maxLength;
+ private final int maxLength;
private CharSequence overflowText;
private CharSequence previousOverflowText;
diff --git a/src/org/thoughtcrime/securesms/components/emoji/parsing/EmojiTree.java b/src/org/thoughtcrime/securesms/components/emoji/parsing/EmojiTree.java
index af8caa568..4a8abe2b3 100644
--- a/src/org/thoughtcrime/securesms/components/emoji/parsing/EmojiTree.java
+++ b/src/org/thoughtcrime/securesms/components/emoji/parsing/EmojiTree.java
@@ -95,7 +95,7 @@ public class EmojiTree {
private static class EmojiTreeNode {
- private Map children = new HashMap<>();
+ private final Map children = new HashMap<>();
private EmojiDrawInfo emoji;
public void setEmoji(EmojiDrawInfo emoji) {
diff --git a/src/org/thoughtcrime/securesms/components/rangeslider/RangeSliderView.java b/src/org/thoughtcrime/securesms/components/rangeslider/RangeSliderView.java
index 4e40ccd02..c2d26c7c2 100644
--- a/src/org/thoughtcrime/securesms/components/rangeslider/RangeSliderView.java
+++ b/src/org/thoughtcrime/securesms/components/rangeslider/RangeSliderView.java
@@ -6,7 +6,7 @@ import android.content.res.TypedArray;
import android.graphics.Canvas;
import androidx.core.content.ContextCompat;
-import androidx.core.view.ViewCompat;
+
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
@@ -21,23 +21,23 @@ public class RangeSliderView extends View {
private static final String TAG = RangeSliderView.class.getName();
- protected int trackTintColor;
- protected int trackHighlightTintColor;
- protected float trackHeight;
- protected float thumbInnerRadius;
- protected float thumbOutlineSize;
- protected float displayTextFontSize;
- protected float displayTextBasicOffsetY;
- protected int sliderPaddingLeft;
- protected int sliderPaddingRight;
+ protected final int trackTintColor;
+ protected final int trackHighlightTintColor;
+ protected final float trackHeight;
+ protected final float thumbInnerRadius;
+ protected final float thumbOutlineSize;
+ protected final float displayTextFontSize;
+ protected final float displayTextBasicOffsetY;
+ protected final int sliderPaddingLeft;
+ protected final int sliderPaddingRight;
- private ThumbLayer minValueThumb;
- private TextLayer minValueDisplayLabel;
+ private final ThumbLayer minValueThumb;
+ private final TextLayer minValueDisplayLabel;
- private ThumbLayer maxValueThumb;
- private TextLayer maxValueDisplayLabel;
+ private final ThumbLayer maxValueThumb;
+ private final TextLayer maxValueDisplayLabel;
- private TrackLayer track;
+ private final TrackLayer track;
private ArrayList values;
@@ -56,7 +56,7 @@ public class RangeSliderView extends View {
private boolean isThumbViewLocked;
private OnValueChangedListener onValueChangedListener;
- GestureDetector longPressDetector;
+ final GestureDetector longPressDetector;
public interface OnValueChangedListener {
diff --git a/src/org/thoughtcrime/securesms/components/rangeslider/TextLayer.java b/src/org/thoughtcrime/securesms/components/rangeslider/TextLayer.java
index 4f6928aa2..0400d89fa 100644
--- a/src/org/thoughtcrime/securesms/components/rangeslider/TextLayer.java
+++ b/src/org/thoughtcrime/securesms/components/rangeslider/TextLayer.java
@@ -5,7 +5,7 @@ import android.graphics.Paint;
class TextLayer {
- private Paint paint;
+ private final Paint paint;
TextLayer(float fontSize, int color) {
paint = new Paint();
diff --git a/src/org/thoughtcrime/securesms/components/rangeslider/ThumbLayer.java b/src/org/thoughtcrime/securesms/components/rangeslider/ThumbLayer.java
index da0864971..0947eee2c 100644
--- a/src/org/thoughtcrime/securesms/components/rangeslider/ThumbLayer.java
+++ b/src/org/thoughtcrime/securesms/components/rangeslider/ThumbLayer.java
@@ -6,13 +6,13 @@ import android.graphics.Paint;
class ThumbLayer {
- private float radius;
- private float outlineSize;
+ private final float radius;
+ private final float outlineSize;
- private int highlightColor;
+ private final int highlightColor;
- private Paint outlinePaint;
- private Paint fillPaint;
+ private final Paint outlinePaint;
+ private final Paint fillPaint;
boolean isHighlight = false;
diff --git a/src/org/thoughtcrime/securesms/components/rangeslider/TimeRangeSlider.java b/src/org/thoughtcrime/securesms/components/rangeslider/TimeRangeSlider.java
index 21e13f4a5..c27dd9f41 100644
--- a/src/org/thoughtcrime/securesms/components/rangeslider/TimeRangeSlider.java
+++ b/src/org/thoughtcrime/securesms/components/rangeslider/TimeRangeSlider.java
@@ -18,12 +18,12 @@ import java.util.Locale;
public class TimeRangeSlider extends RangeSliderView implements RangeSliderView.OnValueChangedListener {
private static final int DEFAULT_TIMEFRAME_2D = 60 * 60 * 24 * 2; // 2d
private static final float DEFAULT_DELTA = 1000 * 60 * 30; // 30 min
- int timeFrame; // timeframe in seconds
+ final int timeFrame; // timeframe in seconds
- Locale locale;
+ final Locale locale;
OnTimestampChangedListener listener;
- private TextLayer minRangeDisplayLabel;
- private TextLayer maxRangeDisplayLabel;
+ private final TextLayer minRangeDisplayLabel;
+ private final TextLayer maxRangeDisplayLabel;
float displayLabelOffsetBelow;
diff --git a/src/org/thoughtcrime/securesms/components/rangeslider/TrackLayer.java b/src/org/thoughtcrime/securesms/components/rangeslider/TrackLayer.java
index 391932138..31d27e44d 100644
--- a/src/org/thoughtcrime/securesms/components/rangeslider/TrackLayer.java
+++ b/src/org/thoughtcrime/securesms/components/rangeslider/TrackLayer.java
@@ -6,13 +6,13 @@ import android.graphics.RectF;
class TrackLayer {
- private float height;
- private float radius;
+ private final float height;
+ private final float radius;
private int paddingLeft = 0;
private int paddingRight = 0;
- private Paint centerPaint;
- private Paint backgoundPaint;
+ private final Paint centerPaint;
+ private final Paint backgoundPaint;
TrackLayer(int paddingLeft, int paddingRight, float height, int color, int highlightColor) {
this.height = height;
diff --git a/src/org/thoughtcrime/securesms/components/reminder/DozeReminder.java b/src/org/thoughtcrime/securesms/components/reminder/DozeReminder.java
index 2925f9bdf..f9a57ca1d 100644
--- a/src/org/thoughtcrime/securesms/components/reminder/DozeReminder.java
+++ b/src/org/thoughtcrime/securesms/components/reminder/DozeReminder.java
@@ -10,7 +10,6 @@ import android.net.Uri;
import android.os.Build;
import android.os.PowerManager;
import android.provider.Settings;
-import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
diff --git a/src/org/thoughtcrime/securesms/connect/AccountManager.java b/src/org/thoughtcrime/securesms/connect/AccountManager.java
index fab0c753b..a149aeacf 100644
--- a/src/org/thoughtcrime/securesms/connect/AccountManager.java
+++ b/src/org/thoughtcrime/securesms/connect/AccountManager.java
@@ -20,7 +20,6 @@ import org.thoughtcrime.securesms.WelcomeActivity;
import org.thoughtcrime.securesms.accounts.AccountSelectionListFragment;
import org.thoughtcrime.securesms.crypto.DatabaseSecret;
import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider;
-import org.thoughtcrime.securesms.notifications.NotificationCenter;
import java.io.File;
diff --git a/src/org/thoughtcrime/securesms/connect/DcContactsLoader.java b/src/org/thoughtcrime/securesms/connect/DcContactsLoader.java
index 30e172885..19e985224 100644
--- a/src/org/thoughtcrime/securesms/connect/DcContactsLoader.java
+++ b/src/org/thoughtcrime/securesms/connect/DcContactsLoader.java
@@ -17,13 +17,15 @@ public class DcContactsLoader extends AsyncLoader {
private final int listflags;
private final String query;
private final boolean addCreateGroupLinks;
+ private final boolean addCreateContactLink;
private final boolean blockedContacts;
- public DcContactsLoader(Context context, int listflags, String query, boolean addCreateGroupLinks, boolean blockedContacts) {
+ public DcContactsLoader(Context context, int listflags, String query, boolean addCreateGroupLinks, boolean addCreateContactLink, boolean blockedContacts) {
super(context);
this.listflags = listflags;
this.query = (query==null||query.isEmpty())? null : query;
this.addCreateGroupLinks = addCreateGroupLinks;
+ this.addCreateContactLink= addCreateContactLink;
this.blockedContacts = blockedContacts;
}
@@ -39,7 +41,7 @@ public class DcContactsLoader extends AsyncLoader {
int[] contact_ids = dcContext.getContacts(listflags, query);
if(query!=null) {
// show the "new contact" link also for partly typed e-mail addresses, so that the user knows he can continue
- if (dcContext.lookupContactIdByAddr(query)==0 && (listflags&DcContext.DC_GCL_VERIFIED_ONLY)==0) {
+ if (addCreateContactLink && dcContext.lookupContactIdByAddr(query)==0 && (listflags&DcContext.DC_GCL_VERIFIED_ONLY)==0) {
contact_ids = Util.appendInt(contact_ids, DcContact.DC_CONTACT_ID_NEW_CONTACT);
}
return new DcContactsLoader.Ret(contact_ids, query);
@@ -65,8 +67,8 @@ public class DcContactsLoader extends AsyncLoader {
}
public class Ret {
- public int[] ids;
- public String query;
+ public final int[] ids;
+ public final String query;
Ret(int[] ids, String query) {
this.ids = ids;
diff --git a/src/org/thoughtcrime/securesms/connect/DcHelper.java b/src/org/thoughtcrime/securesms/connect/DcHelper.java
index 7ea578b6e..02a9024b9 100644
--- a/src/org/thoughtcrime/securesms/connect/DcHelper.java
+++ b/src/org/thoughtcrime/securesms/connect/DcHelper.java
@@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
import org.thoughtcrime.securesms.qr.QrActivity;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.IntentUtils;
+import org.thoughtcrime.securesms.util.FileUtils;
import org.thoughtcrime.securesms.util.MediaUtil;
import java.io.File;
@@ -264,7 +265,7 @@ public class DcHelper {
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
}
- public static HashMap sharedFiles = new HashMap<>();
+ public static final HashMap sharedFiles = new HashMap<>();
public static void openForViewOrShare(Context activity, int msg_id, String cmd) {
DcContext dcContext = getContext(activity);
@@ -319,7 +320,7 @@ public class DcHelper {
}
}
- public static void share(Context activity, byte[] data, String mimeType, String fileName, String text) {
+ public static void sendToChat(Context activity, byte[] data, String mimeType, String fileName, String text) {
Intent intent = new Intent(activity, ShareActivity.class);
intent.setAction(Intent.ACTION_SEND);
@@ -327,10 +328,14 @@ public class DcHelper {
Uri uri = PersistentBlobProvider.getInstance().create(activity, data, mimeType, fileName);
intent.setType(mimeType);
intent.putExtra(Intent.EXTRA_STREAM, uri);
+ intent.putExtra(ShareActivity.EXTRA_TITLE, activity.getString(R.string.send_file_to, fileName));
}
if (text != null) {
intent.putExtra(Intent.EXTRA_TEXT, text);
+ if (data == null) {
+ intent.putExtra(ShareActivity.EXTRA_TITLE, activity.getString(R.string.send_message_to));
+ }
}
activity.startActivity(intent);
@@ -358,6 +363,8 @@ public class DcHelper {
}
public static String getBlobdirFile(DcContext dcContext, String filename, String ext) {
+ filename = FileUtils.sanitizeFilename(filename);
+ ext = FileUtils.sanitizeFilename(ext);
String outPath = null;
for (int i = 0; i < 1000; i++) {
String test = dcContext.getBlobdir() + "/" + filename + (i == 0 ? "" : i < 100 ? "-" + i : "-" + (new Date().getTime() + i)) + ext;
diff --git a/src/org/thoughtcrime/securesms/connect/FetchWorker.java b/src/org/thoughtcrime/securesms/connect/FetchWorker.java
index 0f3f33481..355f55b6c 100644
--- a/src/org/thoughtcrime/securesms/connect/FetchWorker.java
+++ b/src/org/thoughtcrime/securesms/connect/FetchWorker.java
@@ -10,7 +10,7 @@ import androidx.work.WorkerParameters;
import org.thoughtcrime.securesms.util.Util;
public class FetchWorker extends Worker {
- private @NonNull Context context;
+ private final @NonNull Context context;
public FetchWorker(
@NonNull Context context,
diff --git a/src/org/thoughtcrime/securesms/connect/ForegroundDetector.java b/src/org/thoughtcrime/securesms/connect/ForegroundDetector.java
index 08fda2f3f..1546a04b3 100644
--- a/src/org/thoughtcrime/securesms/connect/ForegroundDetector.java
+++ b/src/org/thoughtcrime/securesms/connect/ForegroundDetector.java
@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.connect;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Application;
-import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
@@ -14,7 +13,7 @@ public class ForegroundDetector implements Application.ActivityLifecycleCallback
private int refs = 0;
private static ForegroundDetector Instance = null;
- private ApplicationContext application;
+ private final ApplicationContext application;
public static ForegroundDetector getInstance() {
return Instance;
diff --git a/src/org/thoughtcrime/securesms/connect/NetworkStateReceiver.java b/src/org/thoughtcrime/securesms/connect/NetworkStateReceiver.java
index 9b0fb03d8..97ac3423c 100644
--- a/src/org/thoughtcrime/securesms/connect/NetworkStateReceiver.java
+++ b/src/org/thoughtcrime/securesms/connect/NetworkStateReceiver.java
@@ -8,8 +8,6 @@ import android.net.NetworkInfo;
import android.os.Build;
import android.util.Log;
-import com.b44t.messenger.DcContext;
-
public class NetworkStateReceiver extends BroadcastReceiver {
private int debugConnectedCount;
diff --git a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java
index 053513fc3..0bf2e2006 100644
--- a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java
+++ b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java
@@ -17,7 +17,6 @@
package org.thoughtcrime.securesms.contacts;
import android.content.Context;
-import android.text.TextUtils;
import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.View;
diff --git a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java
index 3d0cd3891..6998e86e7 100644
--- a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java
+++ b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java
@@ -20,9 +20,6 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class ContactSelectionListItem extends LinearLayout implements RecipientModifiedListener {
- @SuppressWarnings("unused")
- private static final String TAG = ContactSelectionListItem.class.getSimpleName();
-
private AvatarView avatar;
private View numberContainer;
private TextView numberView;
diff --git a/src/org/thoughtcrime/securesms/contacts/avatars/GeneratedContactPhoto.java b/src/org/thoughtcrime/securesms/contacts/avatars/GeneratedContactPhoto.java
index ceb1fe443..d9e50f40f 100644
--- a/src/org/thoughtcrime/securesms/contacts/avatars/GeneratedContactPhoto.java
+++ b/src/org/thoughtcrime/securesms/contacts/avatars/GeneratedContactPhoto.java
@@ -4,7 +4,6 @@ import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
-import androidx.core.content.ContextCompat;
import androidx.appcompat.content.res.AppCompatResources;
import com.amulyakhare.textdrawable.TextDrawable;
diff --git a/src/org/thoughtcrime/securesms/contacts/avatars/GroupRecordContactPhoto.java b/src/org/thoughtcrime/securesms/contacts/avatars/GroupRecordContactPhoto.java
index 361a2d2cf..00b24e5c3 100644
--- a/src/org/thoughtcrime/securesms/contacts/avatars/GroupRecordContactPhoto.java
+++ b/src/org/thoughtcrime/securesms/contacts/avatars/GroupRecordContactPhoto.java
@@ -4,7 +4,6 @@ import android.content.Context;
import com.b44t.messenger.DcChat;
-import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.database.Address;
public class GroupRecordContactPhoto extends LocalFileContactPhoto {
diff --git a/src/org/thoughtcrime/securesms/contacts/avatars/ProfileContactPhoto.java b/src/org/thoughtcrime/securesms/contacts/avatars/ProfileContactPhoto.java
index 0644b56c3..45c8bef00 100644
--- a/src/org/thoughtcrime/securesms/contacts/avatars/ProfileContactPhoto.java
+++ b/src/org/thoughtcrime/securesms/contacts/avatars/ProfileContactPhoto.java
@@ -4,7 +4,6 @@ import android.content.Context;
import com.b44t.messenger.DcContact;
-import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.database.Address;
public class ProfileContactPhoto extends LocalFileContactPhoto {
diff --git a/src/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java b/src/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java
index 6bbe73cef..5a957c368 100644
--- a/src/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java
+++ b/src/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java
@@ -151,9 +151,6 @@ public final class KeyStoreHelper {
public static class SealedData {
- @SuppressWarnings("unused")
- private static final String TAG = KeyStoreHelper.class.getSimpleName();
-
@JsonProperty
@JsonSerialize(using = ByteArraySerializer.class)
@JsonDeserialize(using = ByteArrayDeserializer.class)
@@ -170,7 +167,7 @@ public final class KeyStoreHelper {
}
@SuppressWarnings("unused")
- public SealedData() {}
+ public SealedData() { /* needed by JsonUtils.fromJson() */ }
public String serialize() {
try {
diff --git a/src/org/thoughtcrime/securesms/database/loaders/BucketedThreadMediaLoader.java b/src/org/thoughtcrime/securesms/database/loaders/BucketedThreadMediaLoader.java
index 9567845fd..91b58ba9a 100644
--- a/src/org/thoughtcrime/securesms/database/loaders/BucketedThreadMediaLoader.java
+++ b/src/org/thoughtcrime/securesms/database/loaders/BucketedThreadMediaLoader.java
@@ -25,9 +25,6 @@ import java.util.Map;
public class BucketedThreadMediaLoader extends AsyncTaskLoader {
- @SuppressWarnings("unused")
- private static final String TAG = BucketedThreadMediaLoader.class.getSimpleName();
-
private final int chatId;
private final int msgType1;
private final int msgType2;
@@ -118,6 +115,17 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader getAll() {
+ LinkedList messages = new LinkedList<>();
+ for (TimeBucket section : TIME_SECTIONS) {
+ messages.addAll(section.records);
+ }
+ for (List records : OLDER.months.values()) {
+ messages.addAll(records);
+ }
+ return messages;
+ }
+
public int getSectionCount() {
return (int)Stream.of(TIME_SECTIONS)
.filter(timeBucket -> !timeBucket.isEmpty())
diff --git a/src/org/thoughtcrime/securesms/database/loaders/PagingMediaLoader.java b/src/org/thoughtcrime/securesms/database/loaders/PagingMediaLoader.java
index e288da2bf..ded87ae75 100644
--- a/src/org/thoughtcrime/securesms/database/loaders/PagingMediaLoader.java
+++ b/src/org/thoughtcrime/securesms/database/loaders/PagingMediaLoader.java
@@ -15,7 +15,6 @@ import org.thoughtcrime.securesms.util.AsyncLoader;
public class PagingMediaLoader extends AsyncLoader {
- @SuppressWarnings("unused")
private static final String TAG = PagingMediaLoader.class.getSimpleName();
private final DcMsg msg;
@@ -45,7 +44,6 @@ public class PagingMediaLoader extends AsyncLoader {
DcMsg unfound = context.getMsg(msg.getId());
Log.e(TAG, "did not find message in list: " + unfound.getId() + " / " + unfound.getFile() + " / " + unfound.getText());
}
- DcMediaGalleryElement retVal = new DcMediaGalleryElement(mediaMessages, currentIndex, context, leftIsRecent);
- return retVal;
+ return new DcMediaGalleryElement(mediaMessages, currentIndex, context, leftIsRecent);
}
}
diff --git a/src/org/thoughtcrime/securesms/database/loaders/RecentPhotosLoader.java b/src/org/thoughtcrime/securesms/database/loaders/RecentPhotosLoader.java
index c72696aee..b9dd34c74 100644
--- a/src/org/thoughtcrime/securesms/database/loaders/RecentPhotosLoader.java
+++ b/src/org/thoughtcrime/securesms/database/loaders/RecentPhotosLoader.java
@@ -14,7 +14,7 @@ import org.thoughtcrime.securesms.permissions.Permissions;
public class RecentPhotosLoader extends CursorLoader {
- public static Uri BASE_URL = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ public static final Uri BASE_URL = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
private static final String[] PROJECTION = new String[] {
MediaStore.Images.ImageColumns._ID,
@@ -36,7 +36,7 @@ public class RecentPhotosLoader extends CursorLoader {
@Override
public Cursor loadInBackground() {
- if (Permissions.hasAll(context, Manifest.permission.READ_EXTERNAL_STORAGE)) {
+ if (Permissions.hasAll(context, Permissions.galleryPermissions())) {
return context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
PROJECTION, SELECTION, null,
MediaStore.Images.ImageColumns.DATE_MODIFIED + " DESC");
diff --git a/src/org/thoughtcrime/securesms/geolocation/DcLocationManager.java b/src/org/thoughtcrime/securesms/geolocation/DcLocationManager.java
index 20d9c485e..675faf6ff 100644
--- a/src/org/thoughtcrime/securesms/geolocation/DcLocationManager.java
+++ b/src/org/thoughtcrime/securesms/geolocation/DcLocationManager.java
@@ -8,8 +8,6 @@ import android.location.Location;
import android.os.IBinder;
import android.util.Log;
-import com.b44t.messenger.DcChatlist;
-
import org.thoughtcrime.securesms.connect.DcHelper;
import java.util.LinkedList;
@@ -26,10 +24,10 @@ public class DcLocationManager implements Observer {
private static final String TAG = DcLocationManager.class.getSimpleName();
private LocationBackgroundService.LocationBackgroundServiceBinder serviceBinder;
- private Context context;
+ private final Context context;
private DcLocation dcLocation = DcLocation.getInstance();
- private LinkedList pendingShareLastLocation = new LinkedList<>();
- private ServiceConnection serviceConnection = new ServiceConnection() {
+ private final LinkedList pendingShareLastLocation = new LinkedList<>();
+ private final ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "background service connected");
diff --git a/src/org/thoughtcrime/securesms/imageeditor/Bounds.java b/src/org/thoughtcrime/securesms/imageeditor/Bounds.java
index 3b074dcce..6655071d6 100644
--- a/src/org/thoughtcrime/securesms/imageeditor/Bounds.java
+++ b/src/org/thoughtcrime/securesms/imageeditor/Bounds.java
@@ -33,7 +33,7 @@ public final class Bounds {
return new RectF(LEFT, TOP, RIGHT, BOTTOM);
}
- public static RectF FULL_BOUNDS = newFullBounds();
+ public static final RectF FULL_BOUNDS = newFullBounds();
public static boolean contains(float x, float y) {
return x >= FULL_BOUNDS.left && x <= FULL_BOUNDS.right &&
diff --git a/src/org/thoughtcrime/securesms/imageeditor/model/AnimationMatrix.java b/src/org/thoughtcrime/securesms/imageeditor/model/AnimationMatrix.java
index ea499c69c..202821cc3 100644
--- a/src/org/thoughtcrime/securesms/imageeditor/model/AnimationMatrix.java
+++ b/src/org/thoughtcrime/securesms/imageeditor/model/AnimationMatrix.java
@@ -19,7 +19,7 @@ final class AnimationMatrix {
private final static Interpolator interpolator = new DecelerateInterpolator();
private final static Interpolator pulseInterpolator = inverse(new CycleInterpolator(0.5f));
- static AnimationMatrix NULL = new AnimationMatrix();
+ static final AnimationMatrix NULL = new AnimationMatrix();
static {
new Matrix().getValues(iValues);
diff --git a/src/org/thoughtcrime/securesms/jobmanager/Job.java b/src/org/thoughtcrime/securesms/jobmanager/Job.java
index f231873cb..bfd145104 100644
--- a/src/org/thoughtcrime/securesms/jobmanager/Job.java
+++ b/src/org/thoughtcrime/securesms/jobmanager/Job.java
@@ -21,7 +21,6 @@ import android.os.PowerManager;
import org.thoughtcrime.securesms.jobmanager.requirements.Requirement;
import java.io.Serializable;
-import java.util.List;
/**
* An abstract class representing a unit of work that can be scheduled with
diff --git a/src/org/thoughtcrime/securesms/jobmanager/JobParameters.java b/src/org/thoughtcrime/securesms/jobmanager/JobParameters.java
index 14dae988e..5c950b93e 100644
--- a/src/org/thoughtcrime/securesms/jobmanager/JobParameters.java
+++ b/src/org/thoughtcrime/securesms/jobmanager/JobParameters.java
@@ -21,7 +21,6 @@ import org.thoughtcrime.securesms.jobmanager.requirements.Requirement;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
-import java.util.concurrent.TimeUnit;
/**
* The set of parameters that describe a {@link org.thoughtcrime.securesms.jobmanager.Job}.
@@ -82,12 +81,12 @@ public class JobParameters implements Serializable {
}
public static class Builder {
- private List requirements = new LinkedList<>();
- private int retryCount = 100;
- private long retryDuration = 0;
- private String groupId = null;
- private boolean wakeLock = false;
- private long wakeLockTimeout = 0;
+ private final List requirements = new LinkedList<>();
+ private final int retryCount = 100;
+ private final long retryDuration = 0;
+ private String groupId = null;
+ private final boolean wakeLock = false;
+ private final long wakeLockTimeout = 0;
/**
* Specify a groupId the job should belong to. Jobs with the same groupId are guaranteed to be
diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
index b57eb607d..f7897d489 100644
--- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
+++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
@@ -37,6 +37,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcMsg;
@@ -94,7 +95,7 @@ public class AttachmentManager {
private WebxdcView webxdcView;
//private SignalMapView mapView;
- private @NonNull List garbage = new LinkedList<>();
+ private final @NonNull List garbage = new LinkedList<>();
private @NonNull Optional slide = Optional.absent();
private @Nullable Uri imageCaptureUri;
private @Nullable Uri videoCaptureUri;
@@ -294,7 +295,7 @@ public class AttachmentManager {
public void onStop() {}
@Override
- public void onProgress(double progress, long millis) {}
+ public void onProgress(AudioSlide slide, double progress, long millis) {}
@Override
public void onReceivedDuration(int millis) {
@@ -422,13 +423,31 @@ public class AttachmentManager {
return deck;
}
+ public static @Nullable String getFileName(Context context, Uri uri) {
+ String result = null;
+ if (uri.getScheme().equals("content")) {
+ Cursor cursor = context.getContentResolver().query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null);
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ result = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
+ }
+ } finally {
+ if (cursor != null) cursor.close();
+ }
+ }
+ if (result == null) {
+ result = uri.getLastPathSegment();
+ }
+ return result;
+ }
+
public static void selectDocument(Activity activity, int requestCode) {
selectMediaType(activity, "*/*", null, requestCode);
}
public static void selectGallery(Activity activity, int requestCode) {
Permissions.with(activity)
- .request(Manifest.permission.READ_EXTERNAL_STORAGE)
+ .request(Permissions.galleryPermissions())
.ifNecessary()
.withPermanentDenialDialog(activity.getString(R.string.perm_explain_access_to_storage_denied))
.onAllGranted(() -> selectMediaType(activity, "image/*", new String[] {"image/*", "video/*"}, requestCode))
@@ -437,7 +456,7 @@ public class AttachmentManager {
public static void selectImage(Activity activity, int requestCode) {
Permissions.with(activity)
- .request(Manifest.permission.READ_EXTERNAL_STORAGE)
+ .request(Permissions.galleryPermissions())
.ifNecessary()
.withPermanentDenialDialog(activity.getString(R.string.perm_explain_access_to_storage_denied))
.onAllGranted(() -> selectMediaType(activity, "image/*", null, requestCode))
@@ -538,6 +557,12 @@ public class AttachmentManager {
captureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
captureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivityForResult(captureIntent, requestCode);
+ } else {
+ new AlertDialog.Builder(activity)
+ .setCancelable(false)
+ .setMessage("Video recording not available")
+ .setPositiveButton(android.R.string.ok, null)
+ .show();
}
} catch (Exception e) {
Log.w(TAG, e);
diff --git a/src/org/thoughtcrime/securesms/mms/AudioSlide.java b/src/org/thoughtcrime/securesms/mms/AudioSlide.java
index 4cbeac1c7..421c2db62 100644
--- a/src/org/thoughtcrime/securesms/mms/AudioSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/AudioSlide.java
@@ -18,13 +18,11 @@ package org.thoughtcrime.securesms.mms;
import android.content.Context;
import android.net.Uri;
-import androidx.annotation.DrawableRes;
-import androidx.annotation.NonNull;
+
import androidx.annotation.Nullable;
import com.b44t.messenger.DcMsg;
-import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.DcAttachment;
import org.thoughtcrime.securesms.attachments.UriAttachment;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
@@ -47,6 +45,13 @@ public class AudioSlide extends Slide {
super(context, new UriAttachment(uri, null, contentType, AttachmentDatabase.TRANSFER_PROGRESS_STARTED, dataSize, 0, 0, null, null, voiceNote));
}
+ @Override
+ public boolean equals(Object other) {
+ if (other == null) return false;
+ if (!(other instanceof AudioSlide)) return false;
+ return this.getDcMsgId() == ((AudioSlide)other).getDcMsgId();
+ }
+
@Override
@Nullable
public Uri getThumbnailUri() {
diff --git a/src/org/thoughtcrime/securesms/mms/DecryptableStreamLocalUriFetcher.java b/src/org/thoughtcrime/securesms/mms/DecryptableStreamLocalUriFetcher.java
index 043a6ed75..1a1cacf4f 100644
--- a/src/org/thoughtcrime/securesms/mms/DecryptableStreamLocalUriFetcher.java
+++ b/src/org/thoughtcrime/securesms/mms/DecryptableStreamLocalUriFetcher.java
@@ -2,16 +2,11 @@ package org.thoughtcrime.securesms.mms;
import android.content.ContentResolver;
import android.content.Context;
-import android.graphics.Bitmap;
import android.net.Uri;
import android.util.Log;
import com.bumptech.glide.load.data.StreamLocalUriFetcher;
-import org.thoughtcrime.securesms.util.MediaUtil;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -20,7 +15,7 @@ class DecryptableStreamLocalUriFetcher extends StreamLocalUriFetcher {
private static final String TAG = DecryptableStreamLocalUriFetcher.class.getSimpleName();
- private Context context;
+ private final Context context;
DecryptableStreamLocalUriFetcher(Context context, Uri uri) {
super(context.getContentResolver(), uri);
diff --git a/src/org/thoughtcrime/securesms/mms/DecryptableStreamUriLoader.java b/src/org/thoughtcrime/securesms/mms/DecryptableStreamUriLoader.java
index 054de985d..eab67bb90 100644
--- a/src/org/thoughtcrime/securesms/mms/DecryptableStreamUriLoader.java
+++ b/src/org/thoughtcrime/securesms/mms/DecryptableStreamUriLoader.java
@@ -55,7 +55,7 @@ public class DecryptableStreamUriLoader implements ModelLoader attachments;
- private DcMsg quotedMsg;
+ private final DcMsg quotedMsg;
public QuoteModel(DcContact author, String text, boolean missing, @Nullable List attachments, DcMsg quotedMsg) {
this.author = author;
diff --git a/src/org/thoughtcrime/securesms/mms/Slide.java b/src/org/thoughtcrime/securesms/mms/Slide.java
index c8f32df9f..89d40e9ef 100644
--- a/src/org/thoughtcrime/securesms/mms/Slide.java
+++ b/src/org/thoughtcrime/securesms/mms/Slide.java
@@ -18,7 +18,7 @@ package org.thoughtcrime.securesms.mms;
import android.content.Context;
import android.net.Uri;
-import androidx.annotation.DrawableRes;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
diff --git a/src/org/thoughtcrime/securesms/mms/SlideDeck.java b/src/org/thoughtcrime/securesms/mms/SlideDeck.java
index 7fe36e5f5..beee26df8 100644
--- a/src/org/thoughtcrime/securesms/mms/SlideDeck.java
+++ b/src/org/thoughtcrime/securesms/mms/SlideDeck.java
@@ -19,8 +19,6 @@ package org.thoughtcrime.securesms.mms;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.b44t.messenger.DcMsg;
-
import org.thoughtcrime.securesms.attachments.Attachment;
import java.util.LinkedList;
diff --git a/src/org/thoughtcrime/securesms/mms/StickerSlide.java b/src/org/thoughtcrime/securesms/mms/StickerSlide.java
index 5379e0ae8..2a491a161 100644
--- a/src/org/thoughtcrime/securesms/mms/StickerSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/StickerSlide.java
@@ -7,7 +7,6 @@ import androidx.annotation.NonNull;
import com.b44t.messenger.DcMsg;
-import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.DcAttachment;
diff --git a/src/org/thoughtcrime/securesms/mms/VideoSlide.java b/src/org/thoughtcrime/securesms/mms/VideoSlide.java
index 6726d188b..9abe76072 100644
--- a/src/org/thoughtcrime/securesms/mms/VideoSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/VideoSlide.java
@@ -18,8 +18,6 @@ package org.thoughtcrime.securesms.mms;
import android.content.Context;
import android.net.Uri;
-import androidx.annotation.DrawableRes;
-import androidx.annotation.NonNull;
import com.b44t.messenger.DcMsg;
diff --git a/src/org/thoughtcrime/securesms/notifications/InChatSounds.java b/src/org/thoughtcrime/securesms/notifications/InChatSounds.java
index dfad1ceaa..6838def9c 100644
--- a/src/org/thoughtcrime/securesms/notifications/InChatSounds.java
+++ b/src/org/thoughtcrime/securesms/notifications/InChatSounds.java
@@ -6,7 +6,6 @@ import android.media.SoundPool;
import android.util.Log;
import org.thoughtcrime.securesms.R;
-import org.thoughtcrime.securesms.util.Prefs;
public class InChatSounds {
private static final String TAG = InChatSounds.class.getSimpleName();
diff --git a/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java b/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java
index 641b5e9b4..2001d3d00 100644
--- a/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java
+++ b/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java
@@ -2,6 +2,8 @@
package org.thoughtcrime.securesms.notifications;
+import static com.b44t.messenger.DcChat.DC_CHAT_NO_CHAT;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -23,8 +25,8 @@ public class MarkReadReceiver extends BroadcastReceiver {
}
final int accountId = intent.getIntExtra(ACCOUNT_ID_EXTRA, 0);
- final int chatId = intent.getIntExtra(CHAT_ID_EXTRA, 0);
- if (accountId == 0 || chatId == 0) {
+ final int chatId = intent.getIntExtra(CHAT_ID_EXTRA, DC_CHAT_NO_CHAT);
+ if (accountId == 0 || chatId == DC_CHAT_NO_CHAT) {
return;
}
diff --git a/src/org/thoughtcrime/securesms/notifications/NotificationCenter.java b/src/org/thoughtcrime/securesms/notifications/NotificationCenter.java
index 28192047d..3bf8ce800 100644
--- a/src/org/thoughtcrime/securesms/notifications/NotificationCenter.java
+++ b/src/org/thoughtcrime/securesms/notifications/NotificationCenter.java
@@ -337,6 +337,11 @@ public class NotificationCenter {
return;
}
+ NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && !notificationManager.areNotificationsEnabled()) {
+ return;
+ }
+
if (Util.equals(visibleChat, chatData)) {
if (Prefs.isInChatNotifications(context)) {
InChatSounds.getInstance(context).playIncomingSound();
@@ -344,8 +349,6 @@ public class NotificationCenter {
return;
}
- NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
-
// get notification text as a single line
NotificationPrivacyPreference privacy = Prefs.getNotificationPrivacy(context);
@@ -443,8 +446,7 @@ public class NotificationCenter {
// add buttons that allow some actions without opening Delta Chat.
// if privacy options are enabled, the buttons are not added.
- if (privacy.isDisplayContact() && privacy.isDisplayMessage()
- && !Prefs.isScreenLockEnabled(context)) {
+ if (privacy.isDisplayContact() && privacy.isDisplayMessage()) {
try {
PendingIntent inNotificationReplyIntent = getRemoteReplyIntent(chatData);
PendingIntent markReadIntent = getMarkAsReadIntent(chatData, true);
@@ -602,8 +604,8 @@ public class NotificationCenter {
}
private class ChatData {
- public int accountId;
- public int chatId;
+ public final int accountId;
+ public final int chatId;
public ChatData(int accountId, int chatId) {
this.accountId = accountId;
diff --git a/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java b/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java
index 9122cfcd4..3233bd6d3 100644
--- a/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java
+++ b/src/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java
@@ -17,6 +17,8 @@
package org.thoughtcrime.securesms.notifications;
+import static com.b44t.messenger.DcChat.DC_CHAT_NO_CHAT;
+
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -47,9 +49,9 @@ public class RemoteReplyReceiver extends BroadcastReceiver {
if (!REPLY_ACTION.equals(intent.getAction())) return;
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
final int accountId = intent.getIntExtra(ACCOUNT_ID_EXTRA, 0);
- final int chatId = intent.getIntExtra(CHAT_ID_EXTRA, 0);
+ final int chatId = intent.getIntExtra(CHAT_ID_EXTRA, DC_CHAT_NO_CHAT);
- if (remoteInput == null || chatId == 0 || accountId == 0) return;
+ if (remoteInput == null || chatId == DC_CHAT_NO_CHAT || accountId == 0) return;
final CharSequence responseText = remoteInput.getCharSequence(EXTRA_REMOTE_REPLY);
diff --git a/src/org/thoughtcrime/securesms/permissions/Permissions.java b/src/org/thoughtcrime/securesms/permissions/Permissions.java
index e1aad5377..caee223ac 100644
--- a/src/org/thoughtcrime/securesms/permissions/Permissions.java
+++ b/src/org/thoughtcrime/securesms/permissions/Permissions.java
@@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.permissions;
+import android.Manifest;
import android.app.Activity;
import androidx.appcompat.app.AlertDialog;
import android.content.Context;
@@ -34,6 +35,18 @@ import java.util.Map;
public class Permissions {
+ public static String[] galleryPermissions() {
+ // on modern androids, the gallery picker works without permissions,
+ // however, the "camera roll" still requires permissions.
+ // to get that dialog at a UX-wise good moment, we still always ask for permission when opening gallery.
+ // just-always-asking this also avoids the mess with handling various paths for various apis.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ return new String[]{Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO};
+ } else {
+ return new String[]{Manifest.permission.READ_EXTERNAL_STORAGE};
+ }
+ }
+
private static final Map OUTSTANDING = new LRUCache<>(2);
public static PermissionsBuilder with(@NonNull Activity activity) {
@@ -134,6 +147,18 @@ public class Permissions {
}
public void execute() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ // READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE does not exist on modern androids
+ // as file access is done by pickers
+ String[] r = requestedPermissions;
+ Arrays.sort(r);
+ if ( (r.length == 1 && (r[0].equals(Manifest.permission.READ_EXTERNAL_STORAGE) || r[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)))
+ || (r.length == 2 && r[0].equals(Manifest.permission.READ_EXTERNAL_STORAGE) && r[1].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) ) {
+ allGrantedListener.run();
+ return;
+ }
+ }
+
PermissionsRequest request = new PermissionsRequest(allGrantedListener, anyDeniedListener, anyPermanentlyDeniedListener, anyResultListener,
someGrantedListener, someDeniedListener, somePermanentlyDeniedListener);
@@ -247,7 +272,7 @@ public class Permissions {
resultListener.onResult(permissions, grantResults, shouldShowRationaleDialog);
}
- private static Intent getApplicationSettingsIntent(@NonNull Context context) {
+ public static Intent getApplicationSettingsIntent(@NonNull Context context) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", context.getPackageName(), null);
@@ -275,7 +300,7 @@ public class Permissions {
private static class ActivityPermissionObject extends PermissionObject {
- private Activity activity;
+ private final Activity activity;
ActivityPermissionObject(@NonNull Activity activity) {
this.activity = activity;
@@ -304,7 +329,7 @@ public class Permissions {
private static class FragmentPermissionObject extends PermissionObject {
- private Fragment fragment;
+ private final Fragment fragment;
FragmentPermissionObject(@NonNull Fragment fragment) {
this.fragment = fragment;
diff --git a/src/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java
index c094b01fb..6297ab2c6 100644
--- a/src/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java
+++ b/src/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragment.java
@@ -12,6 +12,7 @@ import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
@@ -33,18 +34,25 @@ import org.thoughtcrime.securesms.LogViewActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.connect.DcEventCenter;
import org.thoughtcrime.securesms.connect.DcHelper;
+import org.thoughtcrime.securesms.mms.AttachmentManager;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.ScreenLockUtil;
+import org.thoughtcrime.securesms.util.StorageUtil;
+import org.thoughtcrime.securesms.util.StreamUtil;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment
implements DcEventCenter.DcEventDelegate
{
private static final String TAG = AdvancedPreferenceFragment.class.getSimpleName();
-
-
-
+ public static final int PICK_SELF_KEYS = 29923;
CheckBoxPreference preferE2eeCheckbox;
CheckBoxPreference sentboxWatchCheckbox;
@@ -167,9 +175,34 @@ public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
- if (resultCode == RESULT_OK && requestCode == REQUEST_CODE_CONFIRM_CREDENTIALS_KEYS) {
- exportKeys();
+ if (resultCode != RESULT_OK) return;
+ if (requestCode == REQUEST_CODE_CONFIRM_CREDENTIALS_KEYS) {
+ exportKeys();
+ } else if (requestCode == PICK_SELF_KEYS) {
+ Uri uri = (data != null ? data.getData() : null);
+ if (uri == null) {
+ Log.e(TAG, " Can't import null URI");
+ return;
+ }
+ try {
+ String name = AttachmentManager.getFileName(getContext(), uri);
+ if (name == null || name.isEmpty()) name = "FILE";
+ File file = copyToCacheDir(uri);
+ showImportKeysDialog(file.getAbsolutePath(), name);
+ } catch (IOException e) {
+ e.printStackTrace();
}
+ }
+ }
+
+ protected File copyToCacheDir(Uri uri) throws IOException {
+ try (InputStream inputStream = requireActivity().getContentResolver().openInputStream(uri)) {
+ File file = File.createTempFile("tmp-keys-file", ".tmp", requireActivity().getCacheDir());
+ try (OutputStream outputStream = new FileOutputStream(file)) {
+ StreamUtil.copy(inputStream, outputStream);
+ }
+ return file;
+ }
}
public static @NonNull String getVersion(@Nullable Context context) {
@@ -283,6 +316,15 @@ public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment
/***********************************************************************************************
* Key Import/Export
**********************************************************************************************/
+ protected void showImportKeysDialog(String imexPath, String pathAsDisplayedToUser) {
+ new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.pref_managekeys_import_secret_keys)
+ .setMessage(getActivity().getString(R.string.pref_managekeys_import_explain, pathAsDisplayedToUser))
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, (dialogInterface2, i2) -> startImex(DcContext.DC_IMEX_IMPORT_SELF_KEYS, imexPath, pathAsDisplayedToUser))
+ .show();
+ }
+
private class ManageKeysListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
@@ -316,12 +358,12 @@ public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment
.show();
}
else {
- new AlertDialog.Builder(getActivity())
- .setTitle(R.string.pref_managekeys_import_secret_keys)
- .setMessage(getActivity().getString(R.string.pref_managekeys_import_explain, DcHelper.getImexDir().getAbsolutePath()))
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok, (dialogInterface2, i2) -> startImex(DcContext.DC_IMEX_IMPORT_SELF_KEYS))
- .show();
+ if (Build.VERSION.SDK_INT >= 30) {
+ AttachmentManager.selectMediaType(getActivity(), "application/pgp-keys", new String[]{"text/plain"}, PICK_SELF_KEYS, StorageUtil.getDownloadUri());
+ } else {
+ String path = DcHelper.getImexDir().getAbsolutePath();
+ showImportKeysDialog(path, path);
+ }
}
}
)
diff --git a/src/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragment.java
index 944a464e9..5372c10e3 100644
--- a/src/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragment.java
+++ b/src/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragment.java
@@ -46,7 +46,7 @@ public class AppearancePreferenceFragment extends ListSummaryPreferenceFragment
public void onResume() {
super.onResume();
((ApplicationPreferencesActivity) getActivity()).getSupportActionBar().setTitle(R.string.pref_appearance);
- String imagePath = Prefs.getBackgroundImagePath(getContext());
+ String imagePath = Prefs.getBackgroundImagePath(getContext(), dcContext.getAccountId());
String backgroundString;
if(imagePath.isEmpty()){
backgroundString = this.getString(R.string.def);
@@ -75,7 +75,7 @@ public class AppearancePreferenceFragment extends ListSummaryPreferenceFragment
int themeIndex = Arrays.asList(themeEntryValues).indexOf(Prefs.getTheme(context));
if (themeIndex == -1) themeIndex = 0;
- String imagePath = Prefs.getBackgroundImagePath(context);
+ String imagePath = Prefs.getBackgroundImagePath(context, DcHelper.getContext(context).getAccountId());
String backgroundString;
if(imagePath.isEmpty()){
backgroundString = context.getString(R.string.def);
diff --git a/src/org/thoughtcrime/securesms/preferences/ChatBackgroundActivity.java b/src/org/thoughtcrime/securesms/preferences/ChatBackgroundActivity.java
index b0aa6fa50..dea68ce2d 100644
--- a/src/org/thoughtcrime/securesms/preferences/ChatBackgroundActivity.java
+++ b/src/org/thoughtcrime/securesms/preferences/ChatBackgroundActivity.java
@@ -7,7 +7,6 @@ import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import androidx.appcompat.app.ActionBar;
import android.view.Display;
@@ -24,8 +23,9 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.connect.DcHelper;
+import org.thoughtcrime.securesms.mms.AttachmentManager;
import org.thoughtcrime.securesms.mms.GlideApp;
-import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.Prefs;
@@ -36,9 +36,6 @@ import java.util.concurrent.ExecutionException;
public class ChatBackgroundActivity extends PassphraseRequiredActionBarActivity {
- private final DynamicTheme dynamicTheme = new DynamicTheme();
- private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
-
Button galleryButton;
Button defaultButton;
MenuItem acceptMenuItem;
@@ -48,11 +45,7 @@ public class ChatBackgroundActivity extends PassphraseRequiredActionBarActivity
Uri imageUri;
Boolean imageUpdate = false;
- @Override
- protected void onPreCreate() {
- dynamicTheme.onCreate(this);
- dynamicLanguage.onCreate(this);
- }
+ private int accountId;
@Override
protected void onCreate(Bundle savedInstanceState, boolean ready) {
@@ -61,11 +54,12 @@ public class ChatBackgroundActivity extends PassphraseRequiredActionBarActivity
defaultButton = findViewById(R.id.set_default_button);
galleryButton = findViewById(R.id.from_gallery_button);
preview = findViewById(R.id.preview);
+ accountId = DcHelper.getContext(getApplicationContext()).getAccountId();
defaultButton.setOnClickListener(new DefaultClickListener());
galleryButton.setOnClickListener(new GalleryClickListener());
- String backgroundImagePath = Prefs.getBackgroundImagePath(this);
+ String backgroundImagePath = Prefs.getBackgroundImagePath(this, accountId);
if(backgroundImagePath.isEmpty()){
setDefaultLayoutBackgroundImage();
}else {
@@ -81,13 +75,6 @@ public class ChatBackgroundActivity extends PassphraseRequiredActionBarActivity
}
- @Override
- protected void onResume() {
- super.onResume();
- dynamicTheme.onResume(this);
- dynamicLanguage.onResume(this);
- }
-
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuInflater inflater = this.getMenuInflater();
@@ -110,14 +97,14 @@ public class ChatBackgroundActivity extends PassphraseRequiredActionBarActivity
Thread thread = new Thread() {
@Override
public void run() {
- String destination = context.getFilesDir().getAbsolutePath() + "/background";
- Prefs.setBackgroundImagePath(context, destination);
+ String destination = context.getFilesDir().getAbsolutePath() + "/background."+ accountId;
+ Prefs.setBackgroundImagePath(context, accountId, destination);
scaleAndSaveImage(context, destination);
}
};
thread.start();
} else {
- Prefs.setBackgroundImagePath(context, "");
+ Prefs.setBackgroundImagePath(context, accountId, "");
}
}
finish();
@@ -137,7 +124,7 @@ public class ChatBackgroundActivity extends PassphraseRequiredActionBarActivity
Point size = new Point();
display.getSize(size);
// resize so that the larger side fits the screen accurately
- int largerSide = (size.x > size.y ? size.x : size.y);
+ int largerSide = Math.max(size.x, size.y);
Bitmap scaledBitmap = GlideApp.with(context)
.asBitmap()
.load(imageUri)
@@ -150,15 +137,15 @@ public class ChatBackgroundActivity extends PassphraseRequiredActionBarActivity
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 85, outStream);
} catch (InterruptedException e) {
e.printStackTrace();
- Prefs.setBackgroundImagePath(context, "");
+ Prefs.setBackgroundImagePath(context, accountId, "");
showBackgroundSaveError();
} catch (ExecutionException e) {
e.printStackTrace();
- Prefs.setBackgroundImagePath(context, "");
+ Prefs.setBackgroundImagePath(context, accountId, "");
showBackgroundSaveError();
} catch (FileNotFoundException e) {
e.printStackTrace();
- Prefs.setBackgroundImagePath(context, "");
+ Prefs.setBackgroundImagePath(context, accountId, "");
showBackgroundSaveError();
}
}
@@ -169,7 +156,7 @@ public class ChatBackgroundActivity extends PassphraseRequiredActionBarActivity
}
private void setDefaultLayoutBackgroundImage() {
- if(dynamicTheme.isDarkTheme(this)) {
+ if(DynamicTheme.isDarkTheme(this)) {
Drawable image = getResources().getDrawable(R.drawable.background_hd_dark);
preview.setImageDrawable(image);
}
@@ -222,17 +209,7 @@ public class ChatBackgroundActivity extends PassphraseRequiredActionBarActivity
private class GalleryClickListener implements View.OnClickListener {
@Override
public void onClick(View view) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
- intent.addCategory(Intent.CATEGORY_OPENABLE);
- intent.setType("image/*");
- startActivityForResult(intent, ApplicationPreferencesActivity.REQUEST_CODE_SET_BACKGROUND);
- } else {
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
- intent.addCategory(Intent.CATEGORY_OPENABLE);
- intent.setType("image/*");
- startActivityForResult(intent, ApplicationPreferencesActivity.REQUEST_CODE_SET_BACKGROUND);
- }
+ AttachmentManager.selectImage(ChatBackgroundActivity.this, ApplicationPreferencesActivity.REQUEST_CODE_SET_BACKGROUND);
}
}
}
diff --git a/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java
index d4c536a46..967b937a2 100644
--- a/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java
+++ b/src/org/thoughtcrime/securesms/preferences/ChatsPreferenceFragment.java
@@ -21,7 +21,7 @@ import androidx.preference.Preference;
import com.b44t.messenger.DcContext;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
-import org.thoughtcrime.securesms.BlockedAndShareContactsActivity;
+import org.thoughtcrime.securesms.BlockedContactsActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.permissions.Permissions;
@@ -185,8 +185,7 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment {
private class BlockedContactsClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
- Intent intent = new Intent(getActivity(), BlockedAndShareContactsActivity.class);
- intent.putExtra(BlockedAndShareContactsActivity.SHOW_ONLY_BLOCKED_EXTRA, true);
+ Intent intent = new Intent(getActivity(), BlockedContactsActivity.class);
startActivity(intent);
return true;
}
@@ -202,7 +201,7 @@ public class ChatsPreferenceFragment extends ListSummaryPreferenceFragment {
}
private class AutodelChangeListener implements Preference.OnPreferenceChangeListener {
- private String coreKey;
+ private final String coreKey;
AutodelChangeListener(String coreKey) {
this.coreKey = coreKey;
diff --git a/src/org/thoughtcrime/securesms/preferences/ListSummaryPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/ListSummaryPreferenceFragment.java
index 879bf6fbf..6972127a6 100644
--- a/src/org/thoughtcrime/securesms/preferences/ListSummaryPreferenceFragment.java
+++ b/src/org/thoughtcrime/securesms/preferences/ListSummaryPreferenceFragment.java
@@ -98,12 +98,19 @@ public abstract class ListSummaryPreferenceFragment extends CorrectedPreferenceF
pref.setSummary(pref.getEntry());
}
- protected ProgressDialog progressDialog = null;
- protected int progressWhat = 0;
- protected String imexDir = "";
- protected boolean imexUserAborted = false;
protected void startImex(int what)
{
+ String path = DcHelper.getImexDir().getAbsolutePath();
+ startImex(what, path, path);
+ }
+
+ protected ProgressDialog progressDialog = null;
+ protected int progressWhat = 0;
+ protected String pathAsDisplayedToUser = "";
+ protected boolean imexUserAborted = false;
+ protected void startImex(int what, String imexPath, String pathAsDisplayedToUser)
+ {
+ this.pathAsDisplayedToUser = pathAsDisplayedToUser;
imexUserAborted = false;
notificationController = GenericForegroundService.startForegroundTask(getContext(), getString(R.string.export_backup_desktop));
if( progressDialog!=null ) {
@@ -123,10 +130,9 @@ public abstract class ListSummaryPreferenceFragment extends CorrectedPreferenceF
});
progressDialog.show();
- imexDir = DcHelper.getImexDir().getAbsolutePath();
DcHelper.getAccounts(getActivity()).stopIo();
DcHelper.getEventCenter(getActivity()).captureNextError();
- dcContext.imex(progressWhat, imexDir);
+ dcContext.imex(progressWhat, imexPath);
}
@Override
@@ -163,13 +169,13 @@ public abstract class ListSummaryPreferenceFragment extends CorrectedPreferenceF
notificationController = null;
String msg = "";
if (progressWhat==DcContext.DC_IMEX_EXPORT_BACKUP) {
- msg = context.getString(R.string.pref_backup_written_to_x, imexDir);
+ msg = context.getString(R.string.pref_backup_written_to_x, pathAsDisplayedToUser);
}
else if (progressWhat==DcContext.DC_IMEX_EXPORT_SELF_KEYS) {
- msg = context.getString(R.string.pref_managekeys_secret_keys_exported_to_x, imexDir);
+ msg = context.getString(R.string.pref_managekeys_secret_keys_exported_to_x, pathAsDisplayedToUser);
}
else if (progressWhat==DcContext.DC_IMEX_IMPORT_SELF_KEYS) {
- msg = context.getString(R.string.pref_managekeys_secret_keys_imported_from_x, imexDir);
+ msg = context.getString(R.string.pref_managekeys_secret_keys_imported_from_x, pathAsDisplayedToUser);
}
new AlertDialog.Builder(context)
.setMessage(msg)
diff --git a/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java
index 37bbdf630..0d11d4e35 100644
--- a/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java
+++ b/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java
@@ -12,6 +12,7 @@ import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import androidx.annotation.Nullable;
+import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import androidx.preference.CheckBoxPreference;
import androidx.preference.ListPreference;
@@ -21,14 +22,13 @@ import android.text.TextUtils;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.connect.KeepAliveService;
+import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.util.Prefs;
import static android.app.Activity.RESULT_OK;
public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragment {
- @SuppressWarnings("unused")
- private static final String TAG = NotificationsPreferenceFragment.class.getSimpleName();
private static final int REQUEST_CODE_NOTIFICATION_SELECTED = 1;
private CheckBoxPreference ignoreBattery;
@@ -198,8 +198,11 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
}
public static CharSequence getSummary(Context context) {
- boolean notificationsEnabled = Prefs.isNotificationsEnabled(context);
- String ret = context.getString(notificationsEnabled ? R.string.on : R.string.off);
- return ret;
+ NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || notificationManager.areNotificationsEnabled()) {
+ return context.getString(Prefs.isNotificationsEnabled(context) ? R.string.on : R.string.off);
+ } else {
+ return context.getString(R.string.disabled_in_system_settings);
+ }
}
}
diff --git a/src/org/thoughtcrime/securesms/providers/SingleUseBlobProvider.java b/src/org/thoughtcrime/securesms/providers/SingleUseBlobProvider.java
index 4bad3b2d4..ca61637dc 100644
--- a/src/org/thoughtcrime/securesms/providers/SingleUseBlobProvider.java
+++ b/src/org/thoughtcrime/securesms/providers/SingleUseBlobProvider.java
@@ -14,9 +14,6 @@ import java.util.Map;
public class SingleUseBlobProvider {
- @SuppressWarnings("unused")
- private static final String TAG = SingleUseBlobProvider.class.getSimpleName();
-
public static final String AUTHORITY = "org.thoughtcrime.securesms";
public static final String PATH = "memory/*/#";
private static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/memory");
diff --git a/src/org/thoughtcrime/securesms/qr/BackupTransferActivity.java b/src/org/thoughtcrime/securesms/qr/BackupTransferActivity.java
index d68f51529..fadf4b5fc 100644
--- a/src/org/thoughtcrime/securesms/qr/BackupTransferActivity.java
+++ b/src/org/thoughtcrime/securesms/qr/BackupTransferActivity.java
@@ -24,8 +24,6 @@ import org.thoughtcrime.securesms.WebViewActivity;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.service.GenericForegroundService;
import org.thoughtcrime.securesms.service.NotificationController;
-import org.thoughtcrime.securesms.util.DynamicLanguage;
-import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.Util;
public class BackupTransferActivity extends BaseActionBarActivity {
@@ -54,9 +52,6 @@ public class BackupTransferActivity extends BaseActionBarActivity {
private TransferMode transferMode = TransferMode.RECEIVER_SCAN_QR;
private TransferState transferState = TransferState.TRANSFER_UNKNOWN;
- private final DynamicTheme dynamicTheme = new DynamicTheme();
- private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
-
NotificationController notificationController;
private boolean notificationControllerClosed = false;
public boolean warnAboutCopiedQrCodeOnAbort = false;
@@ -64,8 +59,6 @@ public class BackupTransferActivity extends BaseActionBarActivity {
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
- dynamicTheme.onCreate(this);
- dynamicLanguage.onCreate(this);
transferMode = TransferMode.fromInt(getIntent().getIntExtra(TRANSFER_MODE, TransferMode.INVALID.getInt()));
if (transferMode == TransferMode.INVALID) {
@@ -104,13 +97,6 @@ public class BackupTransferActivity extends BaseActionBarActivity {
DcHelper.getAccounts(this).startIo();
}
- @Override
- protected void onResume() {
- super.onResume();
- dynamicTheme.onResume(this);
- dynamicLanguage.onResume(this);
- }
-
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.clear();
@@ -220,7 +206,7 @@ public class BackupTransferActivity extends BaseActionBarActivity {
new Thread(() -> {
try {
// depending on the android version, getting the SSID requires none, all or one of
- // ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE, ACCESS_NETWORK_STATE and maybe even more.
+ // ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION, NEARBY_WIFI_DEVICES, ACCESS_WIFI_STATE, ACCESS_NETWORK_STATE and maybe even more.
final WifiManager wifiManager = (WifiManager)activity.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
if (wifiManager.isWifiEnabled()) {
final WifiInfo info = wifiManager.getConnectionInfo();
diff --git a/src/org/thoughtcrime/securesms/qr/QrActivity.java b/src/org/thoughtcrime/securesms/qr/QrActivity.java
index f3fe4427d..2ddb5181e 100644
--- a/src/org/thoughtcrime/securesms/qr/QrActivity.java
+++ b/src/org/thoughtcrime/securesms/qr/QrActivity.java
@@ -32,9 +32,7 @@ import org.thoughtcrime.securesms.BaseActionBarActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.mms.AttachmentManager;
import org.thoughtcrime.securesms.permissions.Permissions;
-import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
-import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
@@ -43,9 +41,6 @@ import java.io.InputStream;
public class QrActivity extends BaseActionBarActivity {
- private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
- private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
-
private final static String TAG = QrActivity.class.getSimpleName();
private final static int REQUEST_CODE_IMAGE = 46243;
private final static int TAB_SHOW = 0;
@@ -55,11 +50,15 @@ public class QrActivity extends BaseActionBarActivity {
private ViewPager viewPager;
private QrShowFragment qrShowFragment;
+ @Override
+ protected void onPreCreate() {
+ dynamicTheme = new DynamicNoActionBarTheme();
+ super.onPreCreate();
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- dynamicTheme.onCreate(this);
- dynamicLanguage.onCreate(this);
setContentView(R.layout.activity_qr);
tabLayout = ViewUtil.findById(this, R.id.tab_layout);
@@ -119,14 +118,6 @@ public class QrActivity extends BaseActionBarActivity {
return super.onPrepareOptionsMenu(menu);
}
- @Override
- protected void onResume() {
- super.onResume();
-
- dynamicTheme.onResume(this);
- dynamicLanguage.onResume(this);
- }
-
@Override
protected void onPause() {
super.onPause();
@@ -216,7 +207,7 @@ public class QrActivity extends BaseActionBarActivity {
private class ProfilePagerAdapter extends FragmentStatePagerAdapter {
- private QrActivity activity;
+ private final QrActivity activity;
ProfilePagerAdapter(QrActivity activity, FragmentManager fragmentManager) {
super(fragmentManager, FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
diff --git a/src/org/thoughtcrime/securesms/qr/QrCodeHandler.java b/src/org/thoughtcrime/securesms/qr/QrCodeHandler.java
index 68183089c..8e2b909f0 100644
--- a/src/org/thoughtcrime/securesms/qr/QrCodeHandler.java
+++ b/src/org/thoughtcrime/securesms/qr/QrCodeHandler.java
@@ -1,13 +1,11 @@
package org.thoughtcrime.securesms.qr;
import android.app.Activity;
-import android.content.DialogInterface;
import android.content.Intent;
-import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
-import android.text.Html;
+
import android.widget.Toast;
import com.b44t.messenger.DcContext;
@@ -23,8 +21,8 @@ import org.thoughtcrime.securesms.util.Util;
public class QrCodeHandler {
- private Activity activity;
- private DcContext dcContext;
+ private final Activity activity;
+ private final DcContext dcContext;
public QrCodeHandler(Activity activity) {
this.activity = activity;
diff --git a/src/org/thoughtcrime/securesms/qr/QrScanFragment.java b/src/org/thoughtcrime/securesms/qr/QrScanFragment.java
index 6dfc3138f..76233bd65 100644
--- a/src/org/thoughtcrime/securesms/qr/QrScanFragment.java
+++ b/src/org/thoughtcrime/securesms/qr/QrScanFragment.java
@@ -95,7 +95,7 @@ public class QrScanFragment extends Fragment {
}
public class MyCaptureManager extends CaptureManager {
- private Activity myActivity;
+ private final Activity myActivity;
public MyCaptureManager(Activity activity, DecoratedBarcodeView barcodeView) {
super(activity, barcodeView);
diff --git a/src/org/thoughtcrime/securesms/qr/QrShowFragment.java b/src/org/thoughtcrime/securesms/qr/QrShowFragment.java
index 803ce9098..c89590274 100644
--- a/src/org/thoughtcrime/securesms/qr/QrShowFragment.java
+++ b/src/org/thoughtcrime/securesms/qr/QrShowFragment.java
@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.qr;
-import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
diff --git a/src/org/thoughtcrime/securesms/qr/RegistrationQrActivity.java b/src/org/thoughtcrime/securesms/qr/RegistrationQrActivity.java
index f2aadc60d..1187d100b 100644
--- a/src/org/thoughtcrime/securesms/qr/RegistrationQrActivity.java
+++ b/src/org/thoughtcrime/securesms/qr/RegistrationQrActivity.java
@@ -16,16 +16,11 @@ import com.journeyapps.barcodescanner.CompoundBarcodeView;
import org.thoughtcrime.securesms.BaseActionBarActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.permissions.Permissions;
-import org.thoughtcrime.securesms.util.DynamicLanguage;
-import org.thoughtcrime.securesms.util.DynamicTheme;
public class RegistrationQrActivity extends BaseActionBarActivity {
public static final String ADD_AS_SECOND_DEVICE_EXTRA = "add_as_second_device";
- private final DynamicTheme dynamicTheme = new DynamicTheme();
- private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
-
private CaptureManager capture;
private CompoundBarcodeView barcodeScannerView;
@@ -33,8 +28,6 @@ public class RegistrationQrActivity extends BaseActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- dynamicTheme.onCreate(this);
- dynamicLanguage.onCreate(this);
boolean addAsAnotherDevice = getIntent().getBooleanExtra(ADD_AS_SECOND_DEVICE_EXTRA, false);
if (addAsAnotherDevice) {
@@ -103,8 +96,6 @@ public class RegistrationQrActivity extends BaseActionBarActivity {
@Override
protected void onResume() {
super.onResume();
- dynamicTheme.onResume(this);
- dynamicLanguage.onResume(this);
if (capture != null) {
capture.onResume();
}
@@ -138,4 +129,4 @@ public class RegistrationQrActivity extends BaseActionBarActivity {
public boolean onKeyDown(int keyCode, KeyEvent event) {
return barcodeScannerView.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
}
-}
\ No newline at end of file
+}
diff --git a/src/org/thoughtcrime/securesms/reactions/ReactionRecipientItem.java b/src/org/thoughtcrime/securesms/reactions/ReactionRecipientItem.java
new file mode 100644
index 000000000..3bac30608
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/reactions/ReactionRecipientItem.java
@@ -0,0 +1,77 @@
+package org.thoughtcrime.securesms.reactions;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import com.b44t.messenger.DcContact;
+
+import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.components.AvatarImageView;
+import org.thoughtcrime.securesms.connect.DcHelper;
+import org.thoughtcrime.securesms.mms.GlideRequests;
+import org.thoughtcrime.securesms.recipients.Recipient;
+import org.thoughtcrime.securesms.util.ViewUtil;
+
+public class ReactionRecipientItem extends LinearLayout {
+
+ private AvatarImageView contactPhotoImage;
+ private View addrContainer;
+ private TextView addrView;
+ private TextView nameView;
+ private TextView reactionView;
+
+ private int contactId;
+
+ public ReactionRecipientItem(Context context) {
+ super(context);
+ }
+
+ public ReactionRecipientItem(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ this.contactPhotoImage = findViewById(R.id.contact_photo_image);
+ this.addrContainer = findViewById(R.id.addr_container);
+ this.addrView = findViewById(R.id.addr);
+ this.nameView = findViewById(R.id.name);
+ this.reactionView = findViewById(R.id.reaction);
+
+ ViewUtil.setTextViewGravityStart(this.nameView, getContext());
+ }
+
+ public void bind(@NonNull GlideRequests glideRequests, int contactId, String reaction) {
+ this.contactId = contactId;
+ DcContact dcContact = DcHelper.getContext(getContext()).getContact(contactId);
+ Recipient recipient = new Recipient(getContext(), dcContact);
+ this.contactPhotoImage.setAvatar(glideRequests, recipient, false);
+ this.reactionView.setText(reaction);
+ setText(dcContact.getDisplayName(), dcContact.getAddr());
+ }
+
+ public void unbind(GlideRequests glideRequests) {
+ contactPhotoImage.clear(glideRequests);
+ }
+
+ private void setText(String name, String addr) {
+ this.nameView.setText(name==null? "#" : name);
+
+ if(addr != null) {
+ this.addrView.setText(addr);
+ this.addrContainer.setVisibility(View.VISIBLE);
+ } else {
+ this.addrContainer.setVisibility(View.GONE);
+ }
+ }
+
+ public int getContactId() {
+ return contactId;
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/reactions/ReactionRecipientsAdapter.java b/src/org/thoughtcrime/securesms/reactions/ReactionRecipientsAdapter.java
new file mode 100644
index 000000000..d86cf473e
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/reactions/ReactionRecipientsAdapter.java
@@ -0,0 +1,102 @@
+package org.thoughtcrime.securesms.reactions;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+
+import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.mms.GlideRequests;
+import org.thoughtcrime.securesms.util.Pair;
+
+import java.util.ArrayList;
+
+public class ReactionRecipientsAdapter extends RecyclerView.Adapter
+{
+ private @NonNull ArrayList> contactsReactions = new ArrayList<>();
+ private final LayoutInflater layoutInflater;
+ private final ItemClickListener clickListener;
+ private final GlideRequests glideRequests;
+
+ @Override
+ public int getItemCount() {
+ return contactsReactions.size();
+ }
+
+ public abstract static class ViewHolder extends RecyclerView.ViewHolder {
+
+ public ViewHolder(View itemView) {
+ super(itemView);
+ }
+
+ public abstract void bind(@NonNull GlideRequests glideRequests, int contactId, String reaction);
+ public abstract void unbind(@NonNull GlideRequests glideRequests);
+ }
+
+ public static class ReactionViewHolder extends ViewHolder {
+
+ ReactionViewHolder(@NonNull final View itemView,
+ @Nullable final ItemClickListener clickListener) {
+ super(itemView);
+ itemView.setOnClickListener(view -> {
+ if (clickListener != null) {
+ clickListener.onItemClick(getView());
+ }
+ });
+ }
+
+ public ReactionRecipientItem getView() {
+ return (ReactionRecipientItem) itemView;
+ }
+
+ public void bind(@NonNull GlideRequests glideRequests, int contactId, String reaction) {
+ getView().bind(glideRequests, contactId, reaction);
+ }
+
+ @Override
+ public void unbind(@NonNull GlideRequests glideRequests) {
+ getView().unbind(glideRequests);
+ }
+ }
+
+ public ReactionRecipientsAdapter(@NonNull Context context,
+ @NonNull GlideRequests glideRequests,
+ @Nullable ItemClickListener clickListener)
+ {
+ super();
+ this.layoutInflater = LayoutInflater.from(context);
+ this.glideRequests = glideRequests;
+ this.clickListener = clickListener;
+ }
+
+ @NonNull
+ @Override
+ public ReactionRecipientsAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new ReactionViewHolder(layoutInflater.inflate(R.layout.reaction_recipient_item, parent, false), clickListener);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
+
+ Pair pair = contactsReactions.get(i);
+ Integer contactId = pair.first();
+ String reaction = pair.second();
+
+ ViewHolder holder = (ViewHolder) viewHolder;
+ holder.unbind(glideRequests);
+ holder.bind(glideRequests, contactId, reaction);
+ }
+
+ public interface ItemClickListener {
+ void onItemClick(ReactionRecipientItem item);
+ }
+
+ public void changeData(ArrayList> contactsReactions) {
+ this.contactsReactions = contactsReactions==null? new ArrayList<>() : contactsReactions;
+ notifyDataSetChanged();
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/reactions/ReactionsConversationView.java b/src/org/thoughtcrime/securesms/reactions/ReactionsConversationView.java
new file mode 100644
index 000000000..ac6f84d8b
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/reactions/ReactionsConversationView.java
@@ -0,0 +1,124 @@
+package org.thoughtcrime.securesms.reactions;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
+
+import com.b44t.messenger.rpc.Reaction;
+
+import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
+import org.thoughtcrime.securesms.util.ViewUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ReactionsConversationView extends LinearLayout {
+
+ // Normally 6dp, but we have 1dp left+right margin on the pills themselves
+ private static final int OUTER_MARGIN = ViewUtil.dpToPx(5);
+
+ private final List reactions = new ArrayList<>();
+ private boolean isIncoming;
+
+ public ReactionsConversationView(Context context) {
+ super(context);
+ init(null);
+ }
+
+ public ReactionsConversationView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ init(attrs);
+ }
+
+ private void init(@Nullable AttributeSet attrs) {
+ if (attrs != null) {
+ TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.ReactionsConversationView, 0, 0);
+ isIncoming = typedArray.getInt(R.styleable.ReactionsConversationView_reaction_type, 0) == 2;
+ }
+ }
+
+ public void clear() {
+ this.reactions.clear();
+ removeAllViews();
+ }
+
+ public void setReactions(List reactions) {
+ if (reactions.equals(this.reactions)) {
+ return;
+ }
+
+ clear();
+ this.reactions.addAll(reactions);
+
+ for (Reaction reaction : buildShortenedReactionsList(this.reactions)) {
+ View pill = buildPill(getContext(), this, reaction);
+ addView(pill);
+ }
+
+ if (isIncoming) {
+ ViewUtil.setLeftMargin(this, OUTER_MARGIN);
+ } else {
+ ViewUtil.setRightMargin(this, OUTER_MARGIN);
+ }
+ }
+
+ private static @NonNull List buildShortenedReactionsList(@NonNull List reactions) {
+ if (reactions.size() > 3) {
+ List shortened = new ArrayList<>(3);
+ shortened.add(reactions.get(0));
+ shortened.add(reactions.get(1));
+ int count = 0;
+ boolean isFromSelf = false;
+ for (int index = 2; index < reactions.size(); index++) {
+ count += reactions.get(index).getCount();
+ isFromSelf = isFromSelf || reactions.get(index).isFromSelf();
+ }
+ shortened.add(new Reaction(null, count, isFromSelf));
+
+ return shortened;
+ } else {
+ return reactions;
+ }
+ }
+
+ private static View buildPill(@NonNull Context context, @NonNull ViewGroup parent, @NonNull Reaction reaction) {
+ View root = LayoutInflater.from(context).inflate(R.layout.reactions_pill, parent, false);
+ EmojiTextView emojiView = root.findViewById(R.id.reactions_pill_emoji);
+ TextView countView = root.findViewById(R.id.reactions_pill_count);
+ View spacer = root.findViewById(R.id.reactions_pill_spacer);
+
+ if (reaction.getEmoji() != null) {
+ emojiView.setText(reaction.getEmoji());
+
+ if (reaction.getCount() > 1) {
+ countView.setText(String.valueOf(reaction.getCount()));
+ } else {
+ countView.setVisibility(GONE);
+ spacer.setVisibility(GONE);
+ }
+ } else {
+ emojiView.setVisibility(GONE);
+ spacer.setVisibility(GONE);
+ countView.setText("+" + reaction.getCount());
+ }
+
+ if (reaction.isFromSelf()) {
+ root.setBackground(ContextCompat.getDrawable(context, R.drawable.reaction_pill_background_selected));
+ countView.setTextColor(ContextCompat.getColor(context, R.color.reaction_pill_text_color_selected));
+ } else {
+ root.setBackground(ContextCompat.getDrawable(context, R.drawable.reaction_pill_background));
+ }
+
+ return root;
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/reactions/ReactionsDetailsFragment.java b/src/org/thoughtcrime/securesms/reactions/ReactionsDetailsFragment.java
new file mode 100644
index 000000000..fa35d1448
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/reactions/ReactionsDetailsFragment.java
@@ -0,0 +1,125 @@
+package org.thoughtcrime.securesms.reactions;
+
+import android.app.Dialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.DialogFragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.b44t.messenger.DcContact;
+import com.b44t.messenger.DcContext;
+import com.b44t.messenger.DcEvent;
+import com.b44t.messenger.rpc.RpcException;
+
+import org.thoughtcrime.securesms.ProfileActivity;
+import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.connect.DcEventCenter;
+import org.thoughtcrime.securesms.connect.DcHelper;
+import org.thoughtcrime.securesms.mms.GlideApp;
+import org.thoughtcrime.securesms.util.Pair;
+import org.thoughtcrime.securesms.util.ViewUtil;
+
+import java.util.ArrayList;
+import java.util.Map;
+
+public class ReactionsDetailsFragment extends DialogFragment implements DcEventCenter.DcEventDelegate {
+ private static final String TAG = ReactionsDetailsFragment.class.getSimpleName();
+
+ private RecyclerView recyclerView;
+ private ReactionRecipientsAdapter adapter;
+ private final int msgId;
+
+ public ReactionsDetailsFragment(int msgId) {
+ super();
+ this.msgId = msgId;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ adapter = new ReactionRecipientsAdapter(requireActivity(), GlideApp.with(requireActivity()), new ListClickListener());
+
+ LayoutInflater inflater = requireActivity().getLayoutInflater();
+ View view = inflater.inflate(R.layout.reactions_details_fragment, null);
+ recyclerView = ViewUtil.findById(view, R.id.recycler_view);
+ recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+
+ recyclerView.setAdapter(adapter);
+
+ refreshData();
+
+ DcEventCenter eventCenter = DcHelper.getEventCenter(requireContext());
+ eventCenter.addObserver(DcContext.DC_EVENT_REACTIONS_CHANGED, this);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity())
+ .setTitle(R.string.reactions)
+ .setNegativeButton(R.string.ok, null);
+ return builder.setView(view).create();
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.i(TAG, "onDestroy()");
+ super.onDestroy();
+ DcHelper.getEventCenter(requireActivity()).removeObservers(this);
+ }
+
+ @Override
+ public void handleEvent(@NonNull DcEvent event) {
+ if (event.getId() == DcContext.DC_EVENT_REACTIONS_CHANGED) {
+ if (event.getData2Int() == msgId) {
+ refreshData();
+ }
+ }
+ }
+
+ private void refreshData() {
+ if (recyclerView == null) return;
+
+ int accId = DcHelper.getContext(requireActivity()).getAccountId();
+ try {
+ Map reactionsByContact = DcHelper.getRpc(requireActivity()).getMsgReactions(accId, msgId).getReactionsByContact();
+ ArrayList> contactsReactions = new ArrayList<>();
+ String[] selfReactions = reactionsByContact.remove(DcContact.DC_CONTACT_ID_SELF);
+ for (Integer contact: reactionsByContact.keySet()) {
+ for (String reaction: reactionsByContact.get(contact)) {
+ contactsReactions.add(new Pair<>(contact, reaction));
+ }
+ }
+ if (selfReactions != null) {
+ for (String reaction: selfReactions) {
+ contactsReactions.add(new Pair<>(DcContact.DC_CONTACT_ID_SELF, reaction));
+ }
+ }
+ adapter.changeData(contactsReactions);
+ } catch (RpcException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void openConversation(int contactId) {
+ Intent intent = new Intent(getContext(), ProfileActivity.class);
+ intent.putExtra(ProfileActivity.CONTACT_ID_EXTRA, contactId);
+ requireContext().startActivity(intent);
+ }
+
+ private class ListClickListener implements ReactionRecipientsAdapter.ItemClickListener {
+
+ @Override
+ public void onItemClick(ReactionRecipientItem item) {
+ int contactId = item.getContactId();
+ if (contactId != DcContact.DC_CONTACT_ID_SELF) {
+ ReactionsDetailsFragment.this.dismiss();
+ openConversation(contactId);
+ }
+ }
+ }
+
+}
diff --git a/src/org/thoughtcrime/securesms/recipients/Recipient.java b/src/org/thoughtcrime/securesms/recipients/Recipient.java
index 91fa99b57..9225e79dd 100644
--- a/src/org/thoughtcrime/securesms/recipients/Recipient.java
+++ b/src/org/thoughtcrime/securesms/recipients/Recipient.java
@@ -55,15 +55,15 @@ public class Recipient {
private final @NonNull Address address;
- private @Nullable String customLabel;
+ private final @Nullable String customLabel;
private @Nullable Uri systemContactPhoto;
- private Uri contactUri;
+ private final Uri contactUri;
- private @Nullable String profileName;
+ private final @Nullable String profileName;
private @Nullable String profileAvatar;
- private @Nullable DcChat dcChat;
+ private final @Nullable DcChat dcChat;
private @Nullable DcContact dcContact;
public static @NonNull Recipient fromChat(@NonNull Context context, int dcMsgId) {
@@ -121,7 +121,7 @@ public class Recipient {
int chatId = dcChat.getId();
this.address = Address.fromChat(chatId);
if (!dcChat.isMultiUser()) {
- DcContext dcContext = DcHelper.getContext(context);
+ DcContext dcContext = DcHelper.getAccounts(context).getAccount(dcChat.getAccountId());
int[] contacts = dcContext.getChatContacts(chatId);
if( contacts.length>=1 ) {
this.dcContact = dcContext.getContact(contacts[0]);
@@ -171,7 +171,7 @@ public class Recipient {
public @NonNull List loadParticipants(Context context) {
List participants = new ArrayList<>();
if (dcChat!=null) {
- DcContext dcContext = DcHelper.getContext(context);
+ DcContext dcContext = DcHelper.getAccounts(context).getAccount(dcChat.getAccountId());
int[] contactIds = dcContext.getChatContacts(dcChat.getId());
for (int contactId : contactIds) {
participants.add(new Recipient(context, dcContext.getContact(contactId)));
@@ -287,7 +287,7 @@ public class Recipient {
public DcChat getChat()
{
- return dcChat!=null? dcChat : new DcChat(0);
+ return dcChat!=null? dcChat : new DcChat(0, 0);
}
@Override
diff --git a/src/org/thoughtcrime/securesms/scribbles/ScribbleActivity.java b/src/org/thoughtcrime/securesms/scribbles/ScribbleActivity.java
index e3817957c..de0af535c 100644
--- a/src/org/thoughtcrime/securesms/scribbles/ScribbleActivity.java
+++ b/src/org/thoughtcrime/securesms/scribbles/ScribbleActivity.java
@@ -4,12 +4,19 @@ import android.os.Bundle;
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity;
import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
public class ScribbleActivity extends PassphraseRequiredActionBarActivity {
public static final int SCRIBBLE_REQUEST_CODE = 31424;
public static final String CROP_AVATAR = "crop_avatar";
ImageEditorFragment imageEditorFragment;
+ @Override
+ protected void onPreCreate() {
+ dynamicTheme = new DynamicNoActionBarTheme();
+ super.onPreCreate();
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState, boolean ready) {
setContentView(R.layout.scribble_activity);
diff --git a/src/org/thoughtcrime/securesms/scribbles/StickerSelectFragment.java b/src/org/thoughtcrime/securesms/scribbles/StickerSelectFragment.java
index 8c8562753..22f01de54 100644
--- a/src/org/thoughtcrime/securesms/scribbles/StickerSelectFragment.java
+++ b/src/org/thoughtcrime/securesms/scribbles/StickerSelectFragment.java
@@ -138,7 +138,7 @@ public class StickerSelectFragment extends Fragment implements LoaderManager.Loa
class StickerViewHolder extends RecyclerView.ViewHolder {
private String fileName;
- private ImageView image;
+ private final ImageView image;
StickerViewHolder(View itemView) {
super(itemView);
diff --git a/src/org/thoughtcrime/securesms/scribbles/UriGlideRenderer.java b/src/org/thoughtcrime/securesms/scribbles/UriGlideRenderer.java
index bde217d17..487d4b451 100644
--- a/src/org/thoughtcrime/securesms/scribbles/UriGlideRenderer.java
+++ b/src/org/thoughtcrime/securesms/scribbles/UriGlideRenderer.java
@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.scribbles;
-import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
diff --git a/src/org/thoughtcrime/securesms/scribbles/widget/ColorPaletteAdapter.java b/src/org/thoughtcrime/securesms/scribbles/widget/ColorPaletteAdapter.java
index ffcb9bdf0..020bb2afa 100644
--- a/src/org/thoughtcrime/securesms/scribbles/widget/ColorPaletteAdapter.java
+++ b/src/org/thoughtcrime/securesms/scribbles/widget/ColorPaletteAdapter.java
@@ -55,7 +55,7 @@ public class ColorPaletteAdapter extends RecyclerView.Adapter':
+ case '?':
+ case '\\':
+ case '|':
+ case 0x7F:
+ return false;
+ default:
+ return true;
+ }
+ }
}
diff --git a/src/org/thoughtcrime/securesms/util/JsonUtils.java b/src/org/thoughtcrime/securesms/util/JsonUtils.java
index e868566de..e21b04ba3 100644
--- a/src/org/thoughtcrime/securesms/util/JsonUtils.java
+++ b/src/org/thoughtcrime/securesms/util/JsonUtils.java
@@ -9,7 +9,6 @@ import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
-import java.io.InputStreamReader;
import java.io.Reader;
public class JsonUtils {
diff --git a/src/org/thoughtcrime/securesms/util/Prefs.java b/src/org/thoughtcrime/securesms/util/Prefs.java
index cef5f5e68..cb254e328 100644
--- a/src/org/thoughtcrime/securesms/util/Prefs.java
+++ b/src/org/thoughtcrime/securesms/util/Prefs.java
@@ -46,6 +46,7 @@ public class Prefs {
private static final String ENTER_SENDS_PREF = "pref_enter_sends";
private static final String PROMPTED_DOZE_MSG_ID_PREF = "pref_prompted_doze_msg_id";
public static final String DOZE_ASKED_DIRECTLY = "pref_doze_asked_directly";
+ public static final String ASKED_FOR_NOTIFICATION_PERMISSION= "pref_asked_for_notification_permission";
private static final String IN_THREAD_NOTIFICATION_PREF = "pref_key_inthread_notifications";
public static final String MESSAGE_BODY_TEXT_SIZE_PREF = "pref_message_body_text_size";
@@ -53,12 +54,11 @@ public class Prefs {
public static final String NOTIFICATION_PRIORITY_PREF = "pref_notification_priority";
public static final String SYSTEM_EMOJI_PREF = "pref_system_emoji";
+ public static final String BUILTIN_CAMERA_PREF = "pref_builtin_camera";
public static final String DIRECT_CAPTURE_CAMERA_ID = "pref_direct_capture_camera_id";
private static final String PROFILE_AVATAR_ID_PREF = "pref_profile_avatar_id";
public static final String INCOGNITO_KEYBORAD_PREF = "pref_incognito_keyboard";
- public static final String SCREEN_LOCK = "pref_android_screen_lock";
-
private static final String PREF_CONTACT_PHOTO_IDENTIFIERS = "pref_contact_photo_identifiers";
private static final String MAP_CENTER_LATITUDE = "pref_map_center_latitude";
@@ -76,14 +76,6 @@ public class Prefs {
public static VibrateState fromId(int id) { return values()[id]; }
}
- public static boolean isScreenLockEnabled(@NonNull Context context) {
- return getBooleanPreference(context, SCREEN_LOCK, false);
- }
-
- public static void setScreenLockEnabled(@NonNull Context context, boolean value) {
- setBooleanPreference(context, SCREEN_LOCK, value);
- }
-
public static void setDatabaseEncryptedSecret(@NonNull Context context, @NonNull String secret, int accountId) {
setStringPreference(context, DATABASE_ENCRYPTED_SECRET + accountId, secret);
}
@@ -271,29 +263,33 @@ public class Prefs {
// map
- public static void setMapZoom(Context context, int chatId, double zoom) {
- setLongPreference(context, MAP_ZOOM+chatId, Double.doubleToRawLongBits(zoom));
+ public static void setMapZoom(Context context, int accountId, int chatId, double zoom) {
+ setLongPreference(context, MAP_ZOOM+accountId+"."+chatId, Double.doubleToRawLongBits(zoom));
}
-
- public static double getMapZoom(Context context, int chatId) {
- return Double.longBitsToDouble(0);
+ public static double getMapZoom(Context context, int accountId, int chatId) {
+ long zoom = getLongPreference(context, MAP_ZOOM+accountId+"."+chatId, Double.doubleToLongBits(MINIMUM_ZOOM));
+ return Double.longBitsToDouble(zoom);
}
// misc.
- public static String getBackgroundImagePath(Context context) {
- return getStringPreference(context, BACKGROUND_PREF, "");
+ public static String getBackgroundImagePath(Context context, int accountId) {
+ return getStringPreference(context, BACKGROUND_PREF+accountId, "");
}
- public static void setBackgroundImagePath(Context context, String path) {
- setStringPreference(context, BACKGROUND_PREF, path);
+ public static void setBackgroundImagePath(Context context, int accountId, String path) {
+ setStringPreference(context, BACKGROUND_PREF+accountId, path);
}
public static boolean isSystemEmojiPreferred(Context context) {
return getBooleanPreference(context, SYSTEM_EMOJI_PREF, false);
}
+ public static boolean isBuiltInCameraPreferred(Context context) {
+ return getBooleanPreference(context, BUILTIN_CAMERA_PREF, false);
+ }
+
public static boolean getAlwaysLoadRemoteContent(Context context) {
return getBooleanPreference(context, Prefs.ALWAYS_LOAD_REMOTE_CONTENT,
Prefs.ALWAYS_LOAD_REMOTE_CONTENT_DEFAULT);
diff --git a/src/org/thoughtcrime/securesms/util/RelayUtil.java b/src/org/thoughtcrime/securesms/util/RelayUtil.java
index 04903c7c0..08a62637d 100644
--- a/src/org/thoughtcrime/securesms/util/RelayUtil.java
+++ b/src/org/thoughtcrime/securesms/util/RelayUtil.java
@@ -14,6 +14,7 @@ public class RelayUtil {
private static final String FORWARDED_MESSAGE_IDS = "forwarded_message_ids";
private static final String SHARED_URIS = "shared_uris";
private static final String IS_SHARING = "is_sharing";
+ private static final String SHARED_TITLE = "shared_title";
private static final String DIRECT_SHARING_CHAT_ID = "direct_sharing_chat_id";
public static boolean isRelayingMessageContent(Activity activity) {
@@ -79,6 +80,14 @@ public class RelayUtil {
}
}
+ public static String getSharedTitle(Activity activity) {
+ try {
+ return activity.getIntent().getStringExtra(SHARED_TITLE);
+ } catch (NullPointerException npe) {
+ return null;
+ }
+ }
+
public static void resetRelayingMessageContent(Activity activity) {
try {
@@ -123,6 +132,10 @@ public class RelayUtil {
composeIntent.putExtra(IS_SHARING, true);
}
+ public static void setSharedTitle(Intent composeIntent, String text) {
+ composeIntent.putExtra(SHARED_TITLE, text);
+ }
+
public static void setDirectSharing(Intent composeIntent, int chatId) {
composeIntent.putExtra(DIRECT_SHARING_CHAT_ID, chatId);
}
diff --git a/src/org/thoughtcrime/securesms/util/ResUtil.java b/src/org/thoughtcrime/securesms/util/ResUtil.java
index 08a28cef8..b23f28863 100644
--- a/src/org/thoughtcrime/securesms/util/ResUtil.java
+++ b/src/org/thoughtcrime/securesms/util/ResUtil.java
@@ -22,9 +22,7 @@ import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
-import androidx.annotation.ArrayRes;
import androidx.annotation.AttrRes;
-import androidx.annotation.DrawableRes;
import androidx.core.content.ContextCompat;
import android.util.TypedValue;
diff --git a/src/org/thoughtcrime/securesms/util/ScreenLockUtil.java b/src/org/thoughtcrime/securesms/util/ScreenLockUtil.java
index 89ad8b559..f24888880 100644
--- a/src/org/thoughtcrime/securesms/util/ScreenLockUtil.java
+++ b/src/org/thoughtcrime/securesms/util/ScreenLockUtil.java
@@ -5,8 +5,6 @@ import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
-import org.thoughtcrime.securesms.R;
-
public class ScreenLockUtil {
public static final int REQUEST_CODE_CONFIRM_CREDENTIALS = 1001;
diff --git a/src/org/thoughtcrime/securesms/util/SelectedRecipientsAdapter.java b/src/org/thoughtcrime/securesms/util/SelectedRecipientsAdapter.java
index 48fa5a517..1a95c83c4 100644
--- a/src/org/thoughtcrime/securesms/util/SelectedRecipientsAdapter.java
+++ b/src/org/thoughtcrime/securesms/util/SelectedRecipientsAdapter.java
@@ -23,7 +23,6 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.guava.Optional;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -32,11 +31,11 @@ import java.util.Set;
import static com.b44t.messenger.DcContact.DC_CONTACT_ID_SELF;
public class SelectedRecipientsAdapter extends BaseAdapter {
- @NonNull private Context context;
- @Nullable private OnRecipientDeletedListener onRecipientDeletedListener;
- @NonNull private List recipients;
- @NonNull private final DcContext dcContext;
- @NonNull private final GlideRequests glideRequests;
+ @NonNull private final Context context;
+ @Nullable private OnRecipientDeletedListener onRecipientDeletedListener;
+ @NonNull private final List recipients;
+ @NonNull private final DcContext dcContext;
+ @NonNull private final GlideRequests glideRequests;
public SelectedRecipientsAdapter(@NonNull Context context,
@NonNull GlideRequests glideRequests,
diff --git a/src/org/thoughtcrime/securesms/util/SendRelayedMessageUtil.java b/src/org/thoughtcrime/securesms/util/SendRelayedMessageUtil.java
index a6d733c9b..ec4820103 100644
--- a/src/org/thoughtcrime/securesms/util/SendRelayedMessageUtil.java
+++ b/src/org/thoughtcrime/securesms/util/SendRelayedMessageUtil.java
@@ -18,7 +18,6 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
-import static android.app.Activity.RESULT_OK;
import static org.thoughtcrime.securesms.util.RelayUtil.getForwardedMessageIDs;
import static org.thoughtcrime.securesms.util.RelayUtil.getSharedText;
import static org.thoughtcrime.securesms.util.RelayUtil.getSharedUris;
diff --git a/src/org/thoughtcrime/securesms/util/ServiceUtil.java b/src/org/thoughtcrime/securesms/util/ServiceUtil.java
index 7b416589d..6d9356c01 100644
--- a/src/org/thoughtcrime/securesms/util/ServiceUtil.java
+++ b/src/org/thoughtcrime/securesms/util/ServiceUtil.java
@@ -1,12 +1,7 @@
package org.thoughtcrime.securesms.util;
import android.app.Activity;
-import android.app.AlarmManager;
-import android.app.NotificationManager;
import android.content.Context;
-import android.media.AudioManager;
-import android.net.ConnectivityManager;
-import android.os.PowerManager;
import android.os.Vibrator;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
diff --git a/src/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java b/src/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java
index 57efc019c..592154e2b 100644
--- a/src/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java
+++ b/src/org/thoughtcrime/securesms/util/StickyHeaderDecoration.java
@@ -4,7 +4,6 @@ import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Rect;
-import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
@@ -28,7 +27,7 @@ public class StickyHeaderDecoration extends RecyclerView.ItemDecoration {
private final Map headerCache;
private final StickyHeaderAdapter adapter;
private final boolean renderInline;
- private boolean sticky;
+ private final boolean sticky;
private int screenOrientation;
/**
@@ -218,4 +217,4 @@ public class StickyHeaderDecoration extends RecyclerView.ItemDecoration {
*/
void onBindHeaderViewHolder(T viewHolder, int position);
}
-}
\ No newline at end of file
+}
diff --git a/src/org/thoughtcrime/securesms/util/ThreadUtil.java b/src/org/thoughtcrime/securesms/util/ThreadUtil.java
index df29c2730..ee21bcdfc 100644
--- a/src/org/thoughtcrime/securesms/util/ThreadUtil.java
+++ b/src/org/thoughtcrime/securesms/util/ThreadUtil.java
@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.util;
-import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
diff --git a/src/org/thoughtcrime/securesms/util/Util.java b/src/org/thoughtcrime/securesms/util/Util.java
index 45d90b0ba..463e452d8 100644
--- a/src/org/thoughtcrime/securesms/util/Util.java
+++ b/src/org/thoughtcrime/securesms/util/Util.java
@@ -20,7 +20,6 @@ import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.ClipData;
import android.content.ClipDescription;
-import android.content.ClipboardManager;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
@@ -34,10 +33,12 @@ import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.StyleSpan;
import android.util.Log;
+import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.ComposeText;
@@ -52,12 +53,13 @@ import java.security.SecureRandom;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Objects;
import java.util.concurrent.CountDownLatch;
public class Util {
private static final String TAG = Util.class.getSimpleName();
- public static Handler handler = new Handler(Looper.getMainLooper());
+ public static final Handler handler = new Handler(Looper.getMainLooper());
public static String join(Collection list, String delimiter) {
StringBuilder result = new StringBuilder();
@@ -245,7 +247,7 @@ public class Util {
}
public static boolean equals(@Nullable Object a, @Nullable Object b) {
- return a == b || (a != null && a.equals(b));
+ return Objects.equals(a, b);
}
public static int hashCode(@Nullable Object... objects) {
@@ -269,18 +271,6 @@ public class Util {
}
public static void writeTextToClipboard(@NonNull Context context, @NonNull String text) {
- int sdk = android.os.Build.VERSION.SDK_INT;
- if (sdk < android.os.Build.VERSION_CODES.HONEYCOMB) {
- @SuppressWarnings("deprecation") android.text.ClipboardManager clipboard =
- (android.text.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
- clipboard.setText(text);
- } else {
- copyToClipboardSdk11(context, text);
- }
- }
-
- @TargetApi(android.os.Build.VERSION_CODES.HONEYCOMB)
- private static void copyToClipboardSdk11(Context context, String text) {
android.content.ClipboardManager clipboard =
(android.content.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText(context.getString(R.string.app_name), text);
@@ -345,4 +335,28 @@ public class Util {
public static int rgbToArgbColor(int rgb) {
return Color.argb(0xFF, Color.red(rgb), Color.green(rgb), Color.blue(rgb));
}
+
+ private static long lastClickTime = 0;
+ public static boolean isClickedRecently() {
+ long now = System.currentTimeMillis();
+ if (now - lastClickTime < 500) {
+ Log.i(TAG, "tap discarded");
+ return true;
+ }
+ lastClickTime = now;
+ return false;
+ }
+
+ private static AccessibilityManager accessibilityManager;
+ public static boolean isTouchExplorationEnabled(Context context) {
+ try {
+ if (accessibilityManager == null) {
+ Context applicationContext = context.getApplicationContext();
+ accessibilityManager = ((AccessibilityManager) applicationContext.getSystemService(Context.ACCESSIBILITY_SERVICE));
+ }
+ return accessibilityManager.isTouchExplorationEnabled();
+ } catch (Exception e) {
+ return false;
+ }
+ }
}
diff --git a/src/org/thoughtcrime/securesms/util/ViewUtil.java b/src/org/thoughtcrime/securesms/util/ViewUtil.java
index 22383668d..7c032a5e6 100644
--- a/src/org/thoughtcrime/securesms/util/ViewUtil.java
+++ b/src/org/thoughtcrime/securesms/util/ViewUtil.java
@@ -254,6 +254,16 @@ public class ViewUtil {
view.requestLayout();
}
+ public static void setRightMargin(@NonNull View view, int margin) {
+ if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
+ ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).rightMargin = margin;
+ } else {
+ ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).leftMargin = margin;
+ }
+ view.forceLayout();
+ view.requestLayout();
+ }
+
public static void setTopMargin(@NonNull View view, int margin) {
((ViewGroup.MarginLayoutParams) view.getLayoutParams()).topMargin = margin;
view.requestLayout();
diff --git a/src/org/thoughtcrime/securesms/util/guava/Optional.java b/src/org/thoughtcrime/securesms/util/guava/Optional.java
index bde8b04a5..185e1f07e 100644
--- a/src/org/thoughtcrime/securesms/util/guava/Optional.java
+++ b/src/org/thoughtcrime/securesms/util/guava/Optional.java
@@ -19,7 +19,6 @@ package org.thoughtcrime.securesms.util.guava;
import static org.thoughtcrime.securesms.util.guava.Preconditions.checkNotNull;
import java.io.Serializable;
-import java.util.Iterator;
import java.util.Set;
diff --git a/src/org/thoughtcrime/securesms/util/views/ConversationAdaptiveActionsToolbar.java b/src/org/thoughtcrime/securesms/util/views/ConversationAdaptiveActionsToolbar.java
index 336e31a1b..f61d39bf3 100644
--- a/src/org/thoughtcrime/securesms/util/views/ConversationAdaptiveActionsToolbar.java
+++ b/src/org/thoughtcrime/securesms/util/views/ConversationAdaptiveActionsToolbar.java
@@ -29,7 +29,7 @@ public class ConversationAdaptiveActionsToolbar extends Toolbar {
private static final int ID_NEVER_SHOW_AS_ACTION_4 = R.id.menu_resend;
private static final int ID_ALWAYS_SHOW_AS_ACTION = R.id.menu_context_forward;
- private int maxShown;
+ private final int maxShown;
public ConversationAdaptiveActionsToolbar(@NonNull Context context) {
this(context, null);
diff --git a/src/org/thoughtcrime/securesms/video/exo/AttachmentDataSourceFactory.java b/src/org/thoughtcrime/securesms/video/exo/AttachmentDataSourceFactory.java
index 3225f8428..1638db7d4 100644
--- a/src/org/thoughtcrime/securesms/video/exo/AttachmentDataSourceFactory.java
+++ b/src/org/thoughtcrime/securesms/video/exo/AttachmentDataSourceFactory.java
@@ -1,13 +1,10 @@
package org.thoughtcrime.securesms.video.exo;
-import android.content.Context;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
-import com.google.android.exoplayer2.upstream.TransferListener;
public class AttachmentDataSourceFactory implements DataSource.Factory {
diff --git a/src/org/thoughtcrime/securesms/video/recode/MP4Builder.java b/src/org/thoughtcrime/securesms/video/recode/MP4Builder.java
index 385f4c5fb..b5502928a 100644
--- a/src/org/thoughtcrime/securesms/video/recode/MP4Builder.java
+++ b/src/org/thoughtcrime/securesms/video/recode/MP4Builder.java
@@ -51,7 +51,7 @@ public class MP4Builder {
private long dataOffset = 0;
private long writedSinceLastMdat = 0;
private boolean writeNewMdat = true;
- private HashMap