Compare commits

..

115 Commits

Author SHA1 Message Date
adbenitez ccedf31b96 update core 2025-03-03 17:16:15 +01:00
adbenitez 31f89cec02 Merge remote-tracking branch 'upstream/main' 2025-03-03 15:49:30 +01:00
adb 17a182625d Merge pull request #3644 from deltachat/prep-1.54.3
prepare 1.54.3
2025-03-03 15:31:06 +01:00
adbenitez 625d0ed66c update changelog and version number 2025-03-03 13:58:33 +01:00
adb 134fbc7188 Merge pull request #3643 from deltachat/update-core-and-stuff-3/3/25
Update core to 1.156.2
2025-03-03 13:57:14 +01:00
adbenitez 5d26387aa3 update translations 2025-03-03 13:36:33 +01:00
adbenitez 4a3070bb54 update deltachat-core-rust to 'chore(release): prepare for 1.156.2' of 'v1.156.2' 2025-03-03 13:35:14 +01:00
B. Petersen ec4c96dd92 clarify chat deletion impact 2025-03-02 23:03:24 +01:00
adbenitez f0fb8e62f8 show trash/delete icon when selecting a message 2025-03-02 02:09:32 +01:00
adbenitez 823e6b6e1f update build.gradle 2025-03-02 01:58:59 +01:00
adbenitez 49a59fbebe allow to access map 2025-03-02 01:58:13 +01:00
adbenitez 3424d60ea4 Merge remote-tracking branch 'upstream/main' 2025-03-02 01:49:54 +01:00
adbenitez 1ead71c87a update core 2025-03-02 01:49:27 +01:00
adb 75c111cb0b Merge pull request #3641 from deltachat/adb/tweak-release-instructions
update amazon instructions
2025-02-28 20:25:04 +01:00
adbenitez 1c9b51f5dc update amazon instructions 2025-02-28 20:01:45 +01:00
B. Petersen 76307706f1 update translations 2025-02-28 16:43:22 +01:00
adbenitez 4db770b1f8 Merge remote-tracking branch 'upstream/main' 2025-02-28 15:38:30 +01:00
adb 7dd3176a06 Merge pull request #3637 from deltachat/prep-1.54.2
prepare 1.54.2
2025-02-28 15:36:55 +01:00
adbenitez 16037e7693 update version number and changelog 2025-02-28 14:35:59 +01:00
adbenitez 6f5756c331 Merge remote-tracking branch 'upstream/main' 2025-02-28 14:12:59 +01:00
adb 13c45f37e7 Merge pull request #3636 from deltachat/update-core-28/2/25
update core to  1.156.1
2025-02-28 14:00:49 +01:00
adbenitez c45b5fe9e4 update deltachat-core-rust to 'chore(release): prepare for 1.156.1' of 'v1.156.1' 2025-02-28 13:54:03 +01:00
adbenitez 0a9d042747 fix colors 2025-02-28 02:32:10 +01:00
adbenitez 6cbbb9608e Merge remote-tracking branch 'upstream/adb/allow-to-edit-and-delete-for-all' 2025-02-28 00:10:20 +01:00
adbenitez f8766820db Merge remote-tracking branch 'upstream/main' 2025-02-27 23:46:10 +01:00
adbenitez a2847ea45e allow to edit messages 2025-02-27 23:10:16 +01:00
adbenitez 841e5ee30a show "delete for everyone" in MediaPreviewActivity 2025-02-27 22:55:44 +01:00
adbenitez 624f797888 allow to "delete for everyone" outgoing messages 2025-02-27 22:30:31 +01:00
adbenitez a640c81c73 add bindings for new "delete for everyone" API 2025-02-27 22:29:58 +01:00
adb 247ddfcf1a Merge pull request #3635 from deltachat/adb/update-some-strings-usage
avoid using some deprecated strings
2025-02-27 22:29:25 +01:00
adbenitez d09ad5748d deprecate cannot_delete_contacts_in_use 2025-02-27 22:28:48 +01:00
B. Petersen 30ddf2b03e clarify deletion impact 2025-02-27 22:24:54 +01:00
adbenitez edb2c963c6 avoid using some deprecated strings 2025-02-27 22:24:23 +01:00
adbenitez cf2f6bd2ec Merge remote-tracking branch 'upstream/main' 2025-02-27 20:51:22 +01:00
adb 8cc8ee404d Merge pull request #3632 from deltachat/prep-1.54.1
prepare 1.54.1
2025-02-27 20:31:20 +01:00
adbenitez 180b278a95 bump version 2025-02-27 19:29:26 +01:00
adbenitez e91671b958 update changelog 2025-02-27 19:28:32 +01:00
adb 255a96d393 Merge pull request #3631 from deltachat/adb/issue-3627
don't allow editing for now
2025-02-27 18:54:43 +01:00
adbenitez dfed8aec00 Merge remote-tracking branch 'upstream/main' 2025-02-27 18:53:58 +01:00
adbenitez a498d58c2a don't allow editing for now 2025-02-27 18:48:09 +01:00
B. Petersen 276c0785fa update local help 2025-02-27 18:38:45 +01:00
B. Petersen 6a4efb3358 update translations 2025-02-27 18:38:45 +01:00
B. Petersen 120007d87a add a warning when scanning a backup QR code from a too new delta chat version 2025-02-27 18:30:05 +01:00
adb ea99f0cd48 Merge pull request #3621 from deltachat/adb/cleanup-ContactSelectionListAdapter
cleanup ContactSelectionListAdapter
2025-02-27 18:26:43 +01:00
adb 2bb63878b1 Merge pull request #3601 from deltachat/adb/allow-to-edit-msg
allow to edit message
2025-02-27 18:25:27 +01:00
adbenitez c5d7ec0946 don't allow to edit html messages 2025-02-27 17:41:53 +01:00
adbenitez da7a3fc50a tweak menu 2025-02-27 17:41:53 +01:00
adbenitez 9c36749619 remove unnecessary icon for reply menu item 2025-02-27 17:41:53 +01:00
adbenitez cb3fd74af9 move delete-messages to the end and make it red 2025-02-27 17:41:53 +01:00
adbenitez 7b11c5dd79 re-organize menu and add pencil icon 2025-02-27 17:41:53 +01:00
adbenitez e3c108c773 use new strings 2025-02-27 17:41:53 +01:00
adbenitez 6621046d6d cancel editing when user swipe-to-reply a message 2025-02-27 17:41:53 +01:00
adbenitez ed23c93526 fix: don't discard draft if not in editing mode 2025-02-27 17:41:53 +01:00
adbenitez 19cd640dc8 tweak code style 2025-02-27 17:41:53 +01:00
adbenitez f1fe3e0807 allow to edit any normal message with text, not only "text viewtype" 2025-02-27 17:41:53 +01:00
adbenitez fce62453d1 show "Edit Message" instead of "Me" in edition mode 2025-02-27 17:41:53 +01:00
adbenitez 441f943190 add "edited" label 2025-02-27 17:41:53 +01:00
adbenitez fb759e12f0 fix bindings 2025-02-27 17:41:53 +01:00
adbenitez d1737cb69f allow to edit outgoing text messages 2025-02-27 17:41:51 +01:00
adbenitez c94f9371fe add C bindings 2025-02-27 17:32:56 +01:00
B. Petersen 2e2aeed127 update deltachat-core-rust to 'chore(release): prepare for 1.156.0' of 'v1.156.0' 2025-02-27 17:25:10 +01:00
adbenitez 6e764a6f2f Merge remote-tracking branch 'upstream/main' 2025-02-27 00:10:57 +01:00
adb d8cdc21880 Merge pull request #3624 from deltachat/adb/cleanup-AdvancedPreferenceFragment
cleanup AdvancedPreferenceFragment
2025-02-27 00:04:07 +01:00
adb 139dafab7a Merge pull request #3625 from deltachat/adb/cleanup-ChatsPreferenceFragment
cleanup ChatsPreferenceFragment
2025-02-27 00:02:56 +01:00
adbenitez ef8ac7ae04 Merge remote-tracking branch 'upstream/main' 2025-02-27 00:02:20 +01:00
adb 185fd5d730 Merge pull request #3623 from deltachat/adb/code-cleanup3
fix some more linter warnings
2025-02-26 23:14:04 +01:00
adb c09be6c35b Merge pull request #3626 from deltachat/adb/cleanup-NotificationsPreferenceFragment
cleanup NotificationsPreferenceFragment
2025-02-26 23:13:13 +01:00
adbenitez 2a385c6768 cleanup NotificationsPreferenceFragment 2025-02-26 22:27:25 +01:00
adbenitez 93a114cff4 cleanup ChatsPreferenceFragment 2025-02-26 22:26:02 +01:00
adbenitez 49dab181aa cleanup AdvancedPreferenceFragment 2025-02-26 22:24:14 +01:00
adbenitez 3ad9ffd94f fix some linter warnings 2025-02-26 22:21:27 +01:00
adbenitez 53f1c73e52 cleanup ContactSelectionListAdapter 2025-02-26 20:17:10 +01:00
adbenitez 641c3da629 Merge remote-tracking branch 'upstream/main' 2025-02-26 19:57:23 +01:00
adb f8dc6bb3db Merge pull request #3618 from deltachat/adb/code-cleanup-26/2/25
fix some linter warnings
2025-02-26 19:54:55 +01:00
adbenitez b0d842b3c4 Merge remote-tracking branch 'upstream/main' 2025-02-26 19:54:43 +01:00
adbenitez 9cc4422f9e Merge remote-tracking branch 'upstream/main' 2025-02-26 19:54:22 +01:00
adb 9b9a546e69 Merge pull request #3619 from deltachat/adb/remove-RepeatableImageKey
remove unused class RepeatableImageKey
2025-02-26 19:37:43 +01:00
adb f29f8bf5f9 Merge pull request #3617 from deltachat/adb/cleanup-ImageDivet
cleanup ImageDivet
2025-02-26 19:37:21 +01:00
adbenitez 3b7fd24fcb delete ImageDivet 2025-02-26 17:12:57 +01:00
adbenitez acdbba6351 cleanup ImageDivet 2025-02-26 17:12:57 +01:00
B. Petersen f45fd60ccd update translations 2025-02-26 16:57:01 +01:00
adbenitez dabe68fbf8 remove unused class RepeatableImageKey 2025-02-26 16:54:19 +01:00
adbenitez 7193b4d64e fix some linter warnings 2025-02-26 16:53:46 +01:00
B. Petersen b5000e2fb9 add second device: warn about 3rd parties
this PR adds a hint about making sure no 3rd party
can see the "Add Second Device" QR code.

the dialog and the hint are already used on all OS,
so that is a low hanging fruit -
(compared to other ideas of changing flow direction etc,
which we might go for at some point;
however, that would be lots of effort on all UI,
is tricky UX wise, will come with new bugs and other cornercases,
and are also not bullet save against careless users,
"hey, scan my QR code ... no, from 'Add Second Device, Next, Next ...'" :)
2025-02-26 16:49:04 +01:00
B. Petersen bf4087792d add strings 'delete for me/everyone' 2025-02-26 13:00:12 +01:00
B. Petersen 25164af440 tweak wording 2025-02-26 13:00:12 +01:00
B. Petersen 9579bd5bc8 reword message deletion string 2025-02-26 13:00:12 +01:00
adb f2ee08a7f1 Merge pull request #3616 from deltachat/adb/cleanup-AvatarImageView
cleanup AvatarImageView
2025-02-25 23:48:25 +01:00
adb 1da80e9264 Merge pull request #3615 from deltachat/adb/fix-warnings-AccountSelectionListFragment
fix linter warnings in AccountSelectionListFragment
2025-02-25 23:48:07 +01:00
adbenitez f501c41d16 cleanup AvatarImageView 2025-02-25 23:18:17 +01:00
adbenitez ec7e3fc91a remove unused import 2025-02-25 22:22:04 +01:00
adbenitez 5e91902a26 fix linter warnings in AccountSelectionListFragment 2025-02-25 22:19:20 +01:00
adb 523b62cfeb Merge pull request #3614 from deltachat/adb/update-gson
update gson lib that was pinned for android4 compat
2025-02-25 21:01:37 +01:00
adb af1cbd09ce Merge pull request #3613 from deltachat/adb/optimize-imports
optimize imports
2025-02-25 20:54:45 +01:00
adbenitez 9b3dc7d434 update gson lib that was pinned for android4 compat 2025-02-25 18:57:48 +01:00
adbenitez 4547f771f5 optimize imports 2025-02-25 18:43:23 +01:00
adb bb0ae0772e Merge pull request #3612 from deltachat/adb/non-final-resource-ids
fix warning about non-final resource IDs
2025-02-25 18:22:09 +01:00
adb bd3ab20981 Merge pull request #3611 from deltachat/adb/cleanup-AccountSelectionListAdapter
cleanup AccountSelectionListAdapter
2025-02-25 17:54:53 +01:00
adbenitez 3dc216ca7f fix warning about non-final resource IDs 2025-02-25 17:46:09 +01:00
adb 5095892847 Merge pull request #3610 from deltachat/adb/remove-android4-deps
remove some android4 depenedencies
2025-02-25 17:40:05 +01:00
adbenitez 25d0f4d017 cleanup AccountSelectionListAdapter 2025-02-25 17:10:08 +01:00
adbenitez 3306c3e8d1 remove some android4 depenedencies 2025-02-25 16:53:49 +01:00
adbenitez fa0c59d01c add force encryption toggle 2025-02-24 23:07:50 +01:00
adbenitez 94291bd0ab Merge remote-tracking branch 'upstream/main' 2025-02-24 17:24:36 +01:00
B. Petersen 415785b59d update translations 2025-02-22 20:07:52 +01:00
B. Petersen c0e45abd3d remove 'server' part from asking about message deletion 2025-02-22 20:00:52 +01:00
adb de96a4dc6e Merge pull request #3603 from deltachat/adb/update-forward-icon
update forward icon
2025-02-20 23:47:06 +01:00
B. Petersen 38ed95003f removed unused setFile()
the corresponding cffi function will be removed soon,
see https://github.com/deltachat/deltachat-core-rust/pull/6558
2025-02-20 23:28:43 +01:00
adbenitez ca174e82a2 update forward icon 2025-02-20 22:05:32 +01:00
B. Petersen 7b833b43d7 remove deprecated strings 2025-02-20 12:05:42 +01:00
B. Petersen f8cee59e28 use new 'edit message' strings 2025-02-20 12:05:42 +01:00
B. Petersen 2f85654567 update translations 2025-02-20 12:05:42 +01:00
adb b0ab464ef8 Merge pull request #3600 from deltachat/adb/use-rpc-for-add-account
use JSON-RPC for addAccount
2025-02-19 17:50:11 +01:00
adbenitez c1a49332d0 use JSON-RPC for addAccount 2025-02-19 16:33:31 +01:00
adbenitez a69ff20ac4 point to Lemmy community for changelogs 2025-02-17 15:49:37 +01:00
314 changed files with 2264 additions and 2242 deletions
+20
View File
@@ -1,5 +1,25 @@
# ArcaneChat Android Changelog
## v1.54.3
2025-03
* allow to add any chat to the home screen
* update "forward message" icon and organize the messages actions bar
* do not allow non-members to change ephemeral timer settings of groups
* properly display padlock when the message is not sent over the network
* sync message deletion to other devices
* sync chat deletion across devices
* Show sender in "Saved Messages"
* allow scanning multiple QR-invitation codes without needing to wait for completion to scan the next one
* when reactions are seen in one device, remove notification from your other devices
* don't disturb with notification when someone leave a group
* detect incompatible profiles from newer app version when importing them
* prepare the app for receiving edited messages
* prepare the app for receiving message deletion requests
* do some small bug fixes
* update translations
* update to core 1.156.2
## v1.54.0
2025-02
+1 -1
View File
@@ -114,7 +114,7 @@ This may take some days.
on <https://developer.amazon.com/dashboard>:
12. a) for "Delta Chat", select "Add upcoming version" on the left
12. a) click "App List", 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" and "Step 3" pages, hit "Next"
+4 -5
View File
@@ -33,8 +33,8 @@ android {
useLibrary 'org.apache.http.legacy'
defaultConfig {
versionCode 30000711
versionName "1.54.1"
versionCode 30000714
versionName "1.54.4"
applicationId "chat.delta.lite"
multiDexEnabled true
@@ -233,7 +233,7 @@ dependencies {
implementation 'com.google.zxing:core:3.3.0' // fixed version to support SDK<24
implementation ('com.journeyapps:zxing-android-embedded:4.3.0') { transitive = false } // QR Code scanner
implementation 'com.fasterxml.jackson.core:jackson-databind:2.11.1' // used as JSON library
implementation 'com.google.code.gson:gson:2.9.1' // used as JSON library. Don't upgrade to 2.10.1: https://github.com/deltachat/deltachat-android/pull/2610
implementation 'com.google.code.gson:gson:2.12.1' // used as JSON library.
implementation "me.leolin:ShortcutBadger:1.1.16" // display messagecount on the home screen icon.
implementation 'com.jpardogo.materialtabstrip:library:1.0.9' // used in the emoji selector for the tab selection.
implementation 'com.github.chrisbanes:PhotoView:2.1.3' // does the zooming on photos / media
@@ -252,11 +252,10 @@ dependencies {
}
implementation 'com.annimon:stream:1.1.8' // brings future java streams api to SDK Version < 24
implementation 'com.getkeepsafe.relinker:relinker:1.4.4' // needed to avoid safe-content-resolver-v14 trying to fetch older non-existing version
// Replacement for ContentResolver
// that protects against the Surreptitious Sharing attack.
// <https://github.com/cketti/SafeContentResolver>
implementation 'de.cketti.safecontentresolver:safe-content-resolver-v14:1.0.0'
implementation 'de.cketti.safecontentresolver:safe-content-resolver-v21:1.0.0'
gplayImplementation('com.google.firebase:firebase-messaging:24.1.0') { // for PUSH notifications
exclude group: 'com.google.firebase', module: 'firebase-core'
-1
View File
@@ -1,6 +1,5 @@
android.defaults.buildfeatures.buildconfig=true
android.enableJetifier=true
android.nonFinalResIds=false
android.nonTransitiveRClass=false
android.useAndroidX=true
org.gradle.jvmargs=-Xmx4608m
+23 -16
View File
@@ -231,12 +231,6 @@ JNIEXPORT jboolean Java_com_b44t_messenger_DcAccounts_backgroundFetch(JNIEnv *en
}
JNIEXPORT jint Java_com_b44t_messenger_DcAccounts_addAccount(JNIEnv *env, jobject obj)
{
return dc_accounts_add_account(get_dc_accounts(env, obj));
}
JNIEXPORT jint Java_com_b44t_messenger_DcAccounts_migrateAccount(JNIEnv *env, jobject obj, jstring dbfile)
{
CHAR_REF(dbfile);
@@ -659,6 +653,14 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcContext_getMsgInfo(JNIEnv *env, jobj
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_sendEditRequest(JNIEnv *env, jobject obj, jint msg_id, jstring text)
{
CHAR_REF(text);
dc_send_edit_request(get_dc_context(env, obj), msg_id, textPtr);
CHAR_UNREF(text);
}
JNIEXPORT jstring Java_com_b44t_messenger_DcContext_getMsgHtml(JNIEnv *env, jobject obj, jint msg_id)
{
char* temp = dc_get_msg_html(get_dc_context(env, obj), msg_id);
@@ -683,6 +685,15 @@ JNIEXPORT void Java_com_b44t_messenger_DcContext_deleteMsgs(JNIEnv *env, jobject
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_sendDeleteRequest(JNIEnv *env, jobject obj, jintArray msg_ids)
{
int msg_ids_cnt = 0;
uint32_t* msg_ids_ptr = jintArray2uint32Pointer(env, msg_ids, &msg_ids_cnt);
dc_send_delete_request(get_dc_context(env, obj), msg_ids_ptr, msg_ids_cnt);
free(msg_ids_ptr);
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_forwardMsgs(JNIEnv *env, jobject obj, jintArray msg_ids, jint chat_id)
{
int msg_ids_cnt = 0;
@@ -1649,16 +1660,6 @@ JNIEXPORT void Java_com_b44t_messenger_DcMsg_setHtml(JNIEnv *env, jobject obj, j
}
JNIEXPORT void Java_com_b44t_messenger_DcMsg_setFile(JNIEnv *env, jobject obj, jstring file, jstring filemime)
{
CHAR_REF(file);
CHAR_REF(filemime);
dc_msg_set_file(get_dc_msg(env, obj), filePtr, filemimePtr);
CHAR_UNREF(filemime);
CHAR_UNREF(file);
}
JNIEXPORT void Java_com_b44t_messenger_DcMsg_forceSticker(JNIEnv *env, jobject obj)
{
dc_msg_force_sticker(get_dc_msg(env, obj));
@@ -1674,6 +1675,12 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcMsg_getPOILocation(JNIEnv *env, jobj
}
JNIEXPORT jboolean Java_com_b44t_messenger_DcMsg_isEdited(JNIEnv *env, jobject obj)
{
return dc_msg_is_edited(get_dc_msg(env, obj));
}
JNIEXPORT void Java_com_b44t_messenger_DcMsg_setFileAndDeduplicate(JNIEnv *env, jobject obj, jstring file, jstring name, jstring filemime)
{
CHAR_REF(file);
@@ -1,5 +1,11 @@
package com.b44t.messenger;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
@@ -23,12 +29,6 @@ import org.thoughtcrime.securesms.util.AccessibilityUtil;
import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.Util;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
public class TestUtils {
private static int createdAccountId = 0;
private static boolean resetEnterSends = false;
@@ -1,5 +1,14 @@
package com.b44t.messenger.uibenchmarks;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.Espresso.pressBack;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.replaceText;
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import android.util.Log;
import androidx.test.espresso.contrib.RecyclerViewActions;
@@ -17,15 +26,6 @@ import org.junit.runner.RunWith;
import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.R;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.Espresso.pressBack;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.replaceText;
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
@Ignore("This is not a test, but a benchmark. Remove the @Ignore to run it.")
@RunWith(AndroidJUnit4.class)
@LargeTest
@@ -1,5 +1,16 @@
package com.b44t.messenger.uitests.offline;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.Espresso.pressBack;
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.withHint;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
@@ -25,17 +36,6 @@ import org.thoughtcrime.securesms.connect.DcHelper;
import java.io.File;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.Espresso.pressBack;
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.withHint;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@RunWith(AndroidJUnit4.class)
@LargeTest
@@ -1,6 +1,15 @@
package com.b44t.messenger.uitests.online;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.replaceText;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import android.text.TextUtils;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
@@ -17,15 +26,6 @@ import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.WelcomeActivity;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.replaceText;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class OnboardingTest {
@@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.notifications;
import android.content.Context;
import androidx.annotation.Nullable;
/*
+1 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<html lang="cs"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<li><a href="#co-je-delta-chat">Co je Delta Chat?</a>
<ul>
<li><a href="#jak-najít-lidi-k-hovoru">Jak najít lidi k hovoru?</a></li>
+1 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<html lang="de"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<li><a href="#was-ist-delta-chat">Was ist Delta Chat?</a>
<ul>
<li><a href="#wie-finde-ich-leute-mit-denen-ich-chatten-kann">Wie finde ich Leute, mit denen ich chatten kann?</a></li>
+1 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<li><a href="#what-is-delta-chat">What is Delta Chat?</a>
<ul>
<li><a href="#how-can-i-find-people-to-chat-with">How can I find people to chat with?</a></li>
+1 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<html lang="es"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<li><a href="#qué-es-delta-chat">¿Qué es Delta Chat?</a>
<ul>
<li><a href="#cómo-puedo-encontrar-personas-con-las-que-chatear">¿Cómo puedo encontrar personas con las que chatear?</a></li>
+1 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<html lang="fr"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<li><a href="#quest-ce-que-delta-chat-">Quest-ce que Delta Chat ?</a>
<ul>
<li><a href="#comment-puis-je-trouver-des-gens-avec-qui-discuter-">Comment puis-je trouver des gens avec qui discuter ?</a></li>
+1 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<html lang="id"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<li><a href="#apa-itu-delta-chat">Apa itu Delta Chat?</a>
<ul>
<li><a href="#bagaimana-saya-bisa-menemukan-orang-untuk-diajak-ngobrol">Bagaimana Saya bisa menemukan orang untuk diajak ngobrol?</a></li>
+15 -15
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<html lang="it"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<li><a href="#cosè-delta-chat">Cos’è Delta Chat?</a>
<ul>
<li><a href="#come-posso-trovare-persone-con-cui-messaggiare">Come posso trovare persone con cui messaggiare?</a></li>
@@ -874,14 +874,13 @@ se lintero messaggio è crittografato e firmato correttamente.
Ad esempio, le “Firme distaccate” non sono considerate sicure.</p>
<p>OpenPGP non è insicuro di per sé.
I problemi di sicurezza di OpenPGP più discussi pubblicamente
in realtà derivano da una cattiva usabilità o da cattive implementazioni di strumenti o app (o entrambi).
La maggior parte dei problemi di sicurezza di OpenPGP discussi pubblicamentederivano in realtà da una cattiva usabilità o da cattive implementazioni di strumenti o app (o entrambi).
È particolarmente importante distinguere tra OpenPGP, lo standard di crittografia IETF,
e GnuPG (GPG), uno strumento da riga di comando che implementa OpenPGP.
Molte critiche pubbliche di OpenPGP in realtà discutono di GnuPG che Delta Chat non ha mai utilizzato.
Delta Chat utilizza piuttosto limplementazione OpenPGP Rust <a href="https://github.com/rpgp/rpgp">rPGP</a>,
Delta Chat utilizza piuttosto limplementazione Rust di OpenPGP <a href="https://github.com/rpgp/rpgp">rPGP</a>,
disponibile come <a href="https://crates.io/crates/pgp">un pacchetto “pgp” indipendente</a>,
e <a href="https://delta.chat/assets/blog/2019-first-security-review.pdf">controllato sulla sicurezza nel 2019</a>.</p>
e <a href="#security-audits">verificato per la sicurezza nel 2019 e nel 2024</a>.</p>
<p>Puntiamo, insieme ad altri implementatori di OpenPGP,
per migliorare ulteriormente le caratteristiche di sicurezza implementando il
@@ -1190,24 +1189,25 @@ Per altri programmi, dovresti essere in grado di trovare una soluzione online.</
</h3>
<p>Yes, multiple times.
The Delta Chat project continuously undergoes independent security audits and analysis,
from most recent to older:</p>
<p>Sì, più volte.
Il progetto Delta Chat è sottoposto costantemente a verifiche e analisi di sicurezza indipendenti,
dal più recente al più vecchio:</p>
<ul>
<li>
<p>2024 December, an <a href="https://github.com/rpgp/docs/blob/main/audits/NGI%20Core%20rPGP%20penetration%20test%20report%202024%201.0.pdf">NLNET-commissioned Evaluation of
rPGP</a> by <a href="https://www.radicallyopensecurity.com/">Radically Open Security</a> took place.
<p>Dicembre 2024, un <a href="https://github.com/rpgp/docs/blob/main/audits/NGI%20Core%20rPGP%20penetration%20test%20report%202024%201.0.pdf">Valutazione commissionata da NLNET di
rPGP</a> di <a href="https://www.radicallyopensecurity.com/">Radically Open Security</a> ha preso parte.
rPGP serves as the end-to-end encyption <a href="https://openpgp.org">OpenPGP</a> engine of Delta Chat.
Two advisories were released related to the findings of this audit:</p>
Sono stati rilasciati due avvisi relativi ai risultati di questa verifica:</p>
<ul>
<li><a href="https://github.com/rpgp/rpgp/security/advisories/GHSA-9rmp-2568-59rv">“Panics on Malformed Untrusted Input</a> CVE-2024-53856</li>
<li><a href="https://github.com/rpgp/rpgp/security/advisories/GHSA-4grw-m28r-q285">“Potential Resource Exhaustion when handling Untrusted Messages</a> CVE-2024-53857</li>
<li><a href="https://github.com/rpgp/rpgp/security/advisories/GHSA-9rmp-2568-59rv">“Panico per Input non Attendibili e Malformati</a> CVE-2024-53856</li>
<li><a href="https://github.com/rpgp/rpgp/security/advisories/GHSA-4grw-m28r-q285">“Potenziale Esaurimento delle Risorse durante la gestione di Messaggi non Attendibili</a> CVE-2024-53857</li>
</ul>
<p>The issues outlined in these advisories have been fixed and are part of Delta Chat
releases on all appstores since December 2024.</p>
<p>I problemi descritti in questi avvisi sono stati risolti e fanno
parte delle versioni di Delta Chat
su tutti gli appstore da Dicembre 2024.</p>
</li>
<li>
<p>Marzo 2024, abbiamo ricevuto unanalisi approfondita della sicurezza dalla Crittografia Applicata
+1 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<html lang="nl"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<li><a href="#wat-is-delta-chat">Wat is Delta Chat?</a>
<ul>
<li><a href="#hoe-kan-ik-gesprekspartners-vinden">Hoe kan ik gesprekspartners vinden?</a></li>
+12 -17
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<html lang="pl"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<li><a href="#czym-jest-delta-chat">Czym jest Delta Chat?</a>
<ul>
<li><a href="#jak-mogę-znaleźć-ludzi-z-którymi-mogę-czatować">Jak mogę znaleźć ludzi, z którymi mogę czatować?</a></li>
@@ -669,7 +669,7 @@ nadal będziesz mógł pisać, ale nie będziesz już powiadamiany o żadnych no
<p>Tak, Delta Chat korzysta z bezpiecznego podzbioru OpenPGP i wyświetla wskaźnik bezpieczeństwa kłódki na wiadomości tylko wtedy, gdy cała wiadomość jest prawidłowo zaszyfrowana i podpisana. Na przykład „Odłączone podpisy” nie są traktowane jako bezpieczne.</p>
<p>OpenPGP samo w sobie nie jest niebezpieczne. Większość publicznie omawianych problemów związanych z bezpieczeństwem OpenPGP tak naprawdę wynika ze złej użyteczności lub złej implementacji narzędzi, lub aplikacji (lub obu). Szczególnie ważne jest rozróżnienie pomiędzy OpenPGP, standardem szyfrowania IETF, a GnuPG (GPG), narzędziem wiersza poleceń implementującym OpenPGP. Wiele publicznych komentarzy krytycznych na temat OpenPGP tak naprawdę omawia GnuPG, którego Delta Chat nigdy nie używał. Delta Chat korzysta raczej z implementacji OpenPGP Rust <a href="https://github.com/rpgp/rpgp">rPGP</a>, dostępnej jako <a href="https://crates.io/crates/pgp">niezależny pakiet „pgp”</a> i poddanej <a href="https://delta.chat/assets/blog/2019-first-security-review.pdf">audytowi bezpieczeństwa w 2019 roku</a>.</p>
<p>OpenPGP samo w sobie nie jest niebezpieczne. Większość publicznie omawianych problemów związanych z bezpieczeństwem OpenPGP tak naprawdę wynika ze złej użyteczności lub złej implementacji narzędzi, lub aplikacji (lub obu). Szczególnie ważne jest rozróżnienie pomiędzy OpenPGP, standardem szyfrowania IETF, a GnuPG (GPG), narzędziem wiersza poleceń implementującym OpenPGP. Wiele publicznych komentarzy krytycznych na temat OpenPGP tak naprawdę omawia GnuPG, którego Delta Chat nigdy nie używał. Delta Chat korzysta raczej z implementacji OpenPGP Rust <a href="https://github.com/rpgp/rpgp">rPGP</a>, dostępnej jako <a href="https://crates.io/crates/pgp">niezależny pakiet „pgp”</a> i poddanej <a href="#security-audits">audytowi bezpieczeństwa w 2019 i 2024 roku</a>.</p>
<p>Naszym celem, wraz z innymi wdrażającymi OpenPGP, jest dalsza poprawa parametrów bezpieczeństwa poprzez wdrożenie <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/">nowego IETF OpenPGP Crypto-Refresh</a>, który na szczęście został przyjęty latem 2023 roku.</p>
@@ -868,25 +868,20 @@ W przypadku innych programów można znaleźć rozwiązanie online.</p>
</h3>
<p>Yes, multiple times.
The Delta Chat project continuously undergoes independent security audits and analysis,
from most recent to older:</p>
<p>Tak, wielokrotnie.
Projekt Delta Chat stale przechodzi niezależne audyty bezpieczeństwa i analizy,
od najnowszych do najstarszych:</p>
<p>W grudniu 2024 r. <a href="https://github.com/rpgp/docs/blob/main/audits/NGI%20Core%20rPGP%20penetration%20test%20report%202024%201.0.pdf">NLNET</a> wykonała ocenę rPGP zleconą przez <a href="https://www.radicallyopensecurity.com/">Radically Open Security</a>. rPGP służy jako kompleksowy silnik szyfrowania <a href="https://openpgp.org">OpenPGP</a> w Delta Chat. Wydano dwa ostrzeżenia związane z wynikami tego audytu:</p>
<ul>
<li>
<p>2024 December, an <a href="https://github.com/rpgp/docs/blob/main/audits/NGI%20Core%20rPGP%20penetration%20test%20report%202024%201.0.pdf">NLNET-commissioned Evaluation of
rPGP</a> by <a href="https://www.radicallyopensecurity.com/">Radically Open Security</a> took place.
rPGP serves as the end-to-end encyption <a href="https://openpgp.org">OpenPGP</a> engine of Delta Chat.
Two advisories were released related to the findings of this audit:</p>
<li><a href="https://github.com/rpgp/rpgp/security/advisories/GHSA-9rmp-2568-59rv">„Paniki przy nieprawidłowym wprowadzaniu niezaufanych danych wejściowych”</a> CVE-2024-53856</li>
<li><a href="https://github.com/rpgp/rpgp/security/advisories/GHSA-4grw-m28r-q285">„Potencjalne wyczerpanie zasobów podczas obsługi niezaufanych wiadomości”</a> CVE-2024-53857</li>
</ul>
<ul>
<li><a href="https://github.com/rpgp/rpgp/security/advisories/GHSA-9rmp-2568-59rv">“Panics on Malformed Untrusted Input”</a> CVE-2024-53856</li>
<li><a href="https://github.com/rpgp/rpgp/security/advisories/GHSA-4grw-m28r-q285">“Potential Resource Exhaustion when handling Untrusted Messages”</a> CVE-2024-53857</li>
</ul>
<p>Problemy opisane w tych ostrzeżeniach zostały naprawione i są częścią wydań Delta Chat we wszystkich sklepach z aplikacjami od grudnia 2024 r.</p>
<p>The issues outlined in these advisories have been fixed and are part of Delta Chat
releases on all appstores since December 2024.</p>
</li>
<ul>
<li>
<p>W marcu 2024 r. otrzymaliśmy dogłębną analizę bezpieczeństwa od grupy badawczej ds. kryptografii stosowanej w ETH Zuerich i zajęliśmy się wszystkimi poruszonymi kwestiami. Więcej szczegółowych informacji można znaleźć na naszym blogu na temat <a href="https://delta.chat/en/2024-03-25-crypto-analysis-securejoin">szyfrowania typu End-to-End z gwarancją Hardening</a> oraz opublikowanym później artykule badawczym <a href="https://eprint.iacr.org/2024/918.pdf">Cryptographic Analysis of Delta Chat</a>.</p>
</li>
+1 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<html lang="pt"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<li><a href="#o-que-é-o-delta-chat">O que é o Delta Chat</a>
<ul>
<li><a href="#como-encontro-as-pessoas-para-conversar">Como encontro as pessoas para conversar?</a></li>
+2 -2
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<html lang="ru"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<li><a href="#что-такое-delta-chat">Что такое Delta Chat?</a>
<ul>
<li><a href="#как-мне-найти-людей-для-общения">Как мне найти людей для общения?</a></li>
@@ -891,7 +891,7 @@ Autocrypt использует ограниченное и <a href="#openpgp-sec
Многие публичные критические замечания по OpenPGP фактически касаются GnuPG, который Delta Chat никогда не использовал.
Delta Chat вместо этого использует реализацию OpenPGP на Rust <a href="https://github.com/rpgp/rpgp">rPGP</a>,
доступную как <a href="https://crates.io/crates/pgp">независимый пакет “pgp”</a>,
и <a href="https://delta.chat/assets/blog/2019-first-security-review.pdf">проверенную на безопасность в 2019 году</a>.</p>
и <a href="#security-audits">проверенную на безопасность в 2019 и 2024 годах</a>.</p>
<p>Вместе с другими разработчиками OpenPGP мы стремимся,
повысить характеристики безопасности путём реализации
+1 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<html lang="sk"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<li><a href="#čo-je-to-delta-chat">Čo je to Delta Chat?</a>
<ul>
<li><a href="#ako-nájdem-ľudí-s-ktorými-môžem-chatovať">Ako nájdem ľudí s ktorými môžem chatovať?</a></li>
+1 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<html lang="sq"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<li><a href="#çështë-delta-chat-i">Ç’është Delta Chat-i?</a>
<ul>
<li><a href="#si-mund-të-gjej-persona-për-të-biseduar-me-ta">Si mund të gjej persona për të biseduar me ta?</a></li>
+14 -17
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<html lang="uk"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<li><a href="#що-таке-delta-chat">Що таке Delta Chat?</a>
<ul>
<li><a href="#як-мені-знайти-людей-для-спілкування">Як мені знайти людей для спілкування?</a></li>
@@ -676,7 +676,10 @@ Autocrypt використовує обмежену і <a href="#openpgp-secure"
<p>Так, Delta Chat використовує безпечну підмножину OpenPGP і відображає індикатор безпеки у вигляді замка в повідомленні лише тоді, коли все повідомлення належним чином зашифровано і підписано. Наприклад, “Відокремлені підписи” не вважаються безпечними.</p>
<p>OpenPGP не є небезпечним сам по собі. Більшість публічно обговорюваних проблем безпеки OpenPGP насправді виникають через погане використання або погану реалізацію інструментів або програм (або і те, і інше). Особливо важливо розрізняти OpenPGP, стандарт шифрування IETF, і GnuPG (GPG), інструментом командного рядка, що реалізує OpenPGP. Багато публічних критиків OpenPGP насправді обговорюють GnuPG, який Delta Chat ніколи не використовував. Натомість Delta Chat використовує реалізацію OpenPGP на Rust <a href="https://github.com/rpgp/rpgp">rPGP</a>, доступну як <a href="https://crates.io/crates/pgp">незалежний пакет “pgp”</a>, і <a href="https://delta.chat/assets/blog/2019-first-security-review.pdf">пройшов аудит безпеки у 2019 році</a>.</p>
<p>OpenPGP сам по собі не є небезпечним. Більшість публічно обговорюваних проблем безпеки OpenPGP насправді виникають через недостатню зручність використання або погану реалізацію інструментів чи програм (або обох).
Особливо важливо розрізняти OpenPGP, стандарт шифрування IETF, і GnuPG (GPG), інструмент командного рядка, що реалізує OpenPGP.
Багато публічних критиків OpenPGP насправді обговорюють GnuPG, який Delta Chat ніколи не використовував.
Delta Chat скоріше використовує реалізацію OpenPGP Rust <a href="https://github.com/rpgp/rpgp">rPGP</a>, доступну як <a href="https://crates.io/crates/pgp">незалежний пакет «pgp»</a> і <a href="#security-audits">перевірку безпеки в 2019 і 2024 роках</a>.</p>
<p>Ми прагнемо, разом з іншими розробниками OpenPGP, подальше покращення характеристик безпеки шляхом впровадження <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/">нового IETF OpenPGP Crypto-Refresh</a> який, на щастя, був прийнятий влітку 2023 року.</p>
@@ -872,25 +875,19 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
</h3>
<p>Yes, multiple times.
The Delta Chat project continuously undergoes independent security audits and analysis,
from most recent to older:</p>
<p>Так, багаторазово. Проект Delta Chat постійно проходить незалежні перевірки та аналіз безпеки, а саме, від останніх і закінчуючи старішими:</p>
<ul>
<li>
<p>2024 December, an <a href="https://github.com/rpgp/docs/blob/main/audits/NGI%20Core%20rPGP%20penetration%20test%20report%202024%201.0.pdf">NLNET-commissioned Evaluation of
rPGP</a> by <a href="https://www.radicallyopensecurity.com/">Radically Open Security</a> took place.
rPGP serves as the end-to-end encyption <a href="https://openpgp.org">OpenPGP</a> engine of Delta Chat.
Two advisories were released related to the findings of this audit:</p>
<ul>
<li><a href="https://github.com/rpgp/rpgp/security/advisories/GHSA-9rmp-2568-59rv">“Panics on Malformed Untrusted Input”</a> CVE-2024-53856</li>
<li><a href="https://github.com/rpgp/rpgp/security/advisories/GHSA-4grw-m28r-q285">“Potential Resource Exhaustion when handling Untrusted Messages”</a> CVE-2024-53857</li>
</ul>
<p>The issues outlined in these advisories have been fixed and are part of Delta Chat
releases on all appstores since December 2024.</p>
<p>2024 грудня відбулася <a href="https://github.com/rpgp/docs/blob/main/audits/NGI%20Core%20rPGP%20penetration%20test%20report%202024%201.0.pdf">оцінка rPGP на замовлення NLNET</a> від <a href="https://www.radicallyopensecurity.com/">Radically Open Security</a>. rPGP слугує рушієм наскрізного шифрування <a href="https://openpgp.org">OpenPGP</a> для Delta Chat. За результатами цього аудиту було випущено дві рекомендації:</p>
</li>
<li><a href="https://github.com/rpgp/rpgp/security/advisories/GHSA-9rmp-2568-59rv">“Паніка через неправильне ненадійне введення”</a> CVE-2024-53856</li>
<li><a href="https://github.com/rpgp/rpgp/security/advisories/GHSA-4grw-m28r-q285">“Потенційне виснаження ресурсів під час обробки ненадійних повідомлень”</a> CVE-2024-53857</li>
</ul>
<p>Проблеми, описані в цих рекомендаціях, виправлено, і вони є частиною випусків Delta Chat у всіх магазинах додатків з грудня 2024 року.</p>
<ul>
<li>
<p>2024 березня ми отримали глибокий аналіз безпеки від дослідницької групи Applied Cryptography дослідницької групи з прикладної криптографії в ETH Zuerich і вирішили всі порушені питання. Більш детальну інформацію можна знайти в нашому блозі в статті <a href="https://delta.chat/en/2024-03-25-crypto-analysis-securejoin">Hardening Guaranteed End-to-end encryption</a>, а також в дослідженні <a href="https://eprint.iacr.org/2024/918.pdf">Cryptographic Analysis of Delta Chat</a>, опублікованому пізніше.</p>
</li>
+10 -10
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<html lang="zh_CN"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<li><a href="#什么是-delta-chat">什么是 Delta Chat</a>
<ul>
<li><a href="#如何找到可以聊天的人">如何找到可以聊天的人?</a></li>
@@ -835,15 +835,15 @@ Autocrypt 使用 OpenPGP 标准的有限且<a href="#openpgp-secure">安全子
,前提是整个消息都已正确加密和签名。
例如,“分离签名”不被视为安全。</p>
<p>OpenPGP 本身并不不安全。
大多数公开讨论的 OpenPGP 安全问题
实际上都源于工具或应用的糟糕可用性或糟糕实现(或两者兼而有之)。
区分 OpenPGPIETF 加密标准)
GnuPG (GPG)(实现 OpenPGP 的命令行工具)尤为重要。
许多对 OpenPGP 的公开批评实际上是在讨论 GnuPG,而 Delta Chat 从未使用过 GnuPG。
Delta Chat 而是使用 OpenPGP Rust 实现 <a href="https://github.com/rpgp/rpgp">rPGP</a>
作为 <a href="https://crates.io/crates/pgp">独立的“pgp”软件包</a> 提供,
并且 <a href="https://delta.chat/assets/blog/2019-first-security-review.pdf">在 2019 年进行了安全审计</a></p>
<p>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 <a href="https://github.com/rpgp/rpgp">rPGP</a>,
available as <a href="https://crates.io/crates/pgp">an independent “pgp” package</a>,
and <a href="#security-audits">security-audited in 2019 and 2024</a>.</p>
<p>我们的目标是与其他 OpenPGP 实现者一起,
通过实施 <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/">新的 IETF OpenPGP Crypto-Refresh</a>
@@ -35,7 +35,6 @@ public class DcAccounts {
public native void setPushDeviceToken (String token);
public native boolean backgroundFetch (int timeoutSeconds);
public native int addAccount ();
public native int migrateAccount (String dbfile);
public native boolean removeAccount (int accountId);
public native int[] getAll ();
@@ -51,8 +51,8 @@ public class DcContext {
public final static int DC_QR_FPR_MISMATCH = 220;
public final static int DC_QR_FPR_WITHOUT_ADDR = 230;
public final static int DC_QR_ACCOUNT = 250;
public final static int DC_QR_BACKUP = 251;
public final static int DC_QR_BACKUP2 = 252;
public final static int DC_QR_BACKUP_TOO_NEW = 255;
public final static int DC_QR_WEBRTC = 260;
public final static int DC_QR_PROXY = 271;
public final static int DC_QR_ADDR = 320;
@@ -88,7 +88,7 @@ public class DcContext {
private static final String CONFIG_ACCOUNT_ENABLED = "ui.enabled";
private static final String CONFIG_MUTE_MENTIONS_IF_MUTED = "ui.mute_mentions_if_muted";
// when using DcAccounts, use DcAccounts.addAccount() instead
// when using DcAccounts, use Rpc.addAccount() instead
public DcContext(String osName, String dbfile) {
contextCPtr = createContextCPtr(osName, dbfile);
}
@@ -137,7 +137,8 @@ public class DcContext {
public void setConfigInt (String key, int value) { setConfig(key, Integer.toString(value)); }
public native boolean setConfigFromQr (String qr);
public native String getConfig (String key);
public int getConfigInt (String key) { try{return Integer.parseInt(getConfig(key));} catch(Exception e) {} return 0; }
public int getConfigInt (String key) { return getConfigInt(key, 0); }
public int getConfigInt (String key, int defValue) { try{return Integer.parseInt(getConfig(key));} catch(Exception e) {} return defValue; }
public native String getInfo ();
public native int getConnectivity ();
public native String getConnectivityHtml ();
@@ -187,12 +188,14 @@ public class DcContext {
public native void blockChat (int chat_id);
public native void acceptChat (int chat_id);
public DcMsg getMsg (int msg_id) { return new DcMsg(getMsgCPtr(msg_id)); }
public native void sendEditRequest (int msg_id, String text);
public native String getMsgInfo (int id);
public native String getMsgHtml (int msg_id);
public native void downloadFullMsg (int msg_id);
public native int getFreshMsgCount (int chat_id);
public native int estimateDeletionCount(boolean from_server, long seconds);
public native void deleteMsgs (int msg_ids[]);
public native void sendDeleteRequest (int msg_ids[]);
public native void forwardMsgs (int msg_ids[], int chat_id);
public native void saveMsgs (int msg_ids[]);
public native boolean resendMsgs (int msg_ids[]);
+1 -1
View File
@@ -159,7 +159,6 @@ public class DcMsg {
public native void setText (String text);
public native void setSubject (String text);
public native void setHtml (String text);
public native void setFile (String file, String filemime);
public native void forceSticker ();
public native void setFileAndDeduplicate(String file, String name, String filemime);
public native void setDimension (int width, int height);
@@ -171,6 +170,7 @@ public class DcMsg {
public native String getError ();
public native String getOverrideSenderName();
public native int getSenderColor();
public native boolean isEdited ();
public String getSenderName(DcContact dcContact) {
String overrideName = getOverrideSenderName();
@@ -125,6 +125,10 @@ public class Rpc {
return getResult("get_account_file_size", accountId).getAsInt();
}
public int addAccount() throws RpcException {
return getResult("add_account").getAsInt();
}
private static class Request {
private final String jsonrpc = "2.0";
public final String method;
@@ -26,6 +26,7 @@ import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcEvent;
import com.b44t.messenger.DcEventEmitter;
import com.b44t.messenger.rpc.Rpc;
import com.b44t.messenger.rpc.RpcException;
import org.thoughtcrime.securesms.connect.AccountManager;
import org.thoughtcrime.securesms.connect.DcEventCenter;
@@ -126,7 +127,11 @@ public class ApplicationContext extends MultiDexApplication {
}
}
if (allAccounts.length == 0) {
dcAccounts.addAccount();
try {
rpc.addAccount();
} catch (RpcException e) {
e.printStackTrace();
}
}
dcContext = dcAccounts.getSelectedAccount();
notificationCenter = new NotificationCenter(this);
@@ -44,10 +44,10 @@ import org.thoughtcrime.securesms.preferences.PrivacyPreferenceFragment;
import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment;
import org.thoughtcrime.securesms.preferences.NotificationsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.widgets.ProfilePreference;
import org.thoughtcrime.securesms.qr.BackupTransferActivity;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.IntentUtils;
import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.qr.BackupTransferActivity;
import org.thoughtcrime.securesms.util.ScreenLockUtil;
/**
@@ -1,9 +1,7 @@
package org.thoughtcrime.securesms;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewConfiguration;
@@ -18,9 +18,8 @@ import com.b44t.messenger.rpc.Rpc;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.HashSet;
import java.util.Set;
@@ -15,7 +15,6 @@ import android.graphics.Bitmap;
import android.graphics.Color;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -410,14 +409,28 @@ public abstract class BaseConversationListFragment extends Fragment implements A
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_select_all: handleSelectAllThreads(); return true;
case R.id.menu_delete_selected: handleDeleteAllSelected(); return true;
case R.id.menu_pin_selected: handlePinAllSelected(); return true;
case R.id.menu_archive_selected: handleArchiveAllSelected(); return true;
case R.id.menu_mute_selected: handleMuteAllSelected(); return true;
case R.id.menu_marknoticed_selected: handleMarknoticedSelected(); return true;
case R.id.menu_add_to_home_screen: handleAddToHomeScreen(); return true;
int itemId = item.getItemId();
if (itemId == R.id.menu_select_all) {
handleSelectAllThreads();
return true;
} else if (itemId == R.id.menu_delete_selected) {
handleDeleteAllSelected();
return true;
} else if (itemId == R.id.menu_pin_selected) {
handlePinAllSelected();
return true;
} else if (itemId == R.id.menu_archive_selected) {
handleArchiveAllSelected();
return true;
} else if (itemId == R.id.menu_mute_selected) {
handleMuteAllSelected();
return true;
} else if (itemId == R.id.menu_marknoticed_selected) {
handleMarknoticedSelected();
return true;
} else if (itemId == R.id.menu_add_to_home_screen) {
handleAddToHomeScreen();
return true;
}
return false;
@@ -1,20 +1,20 @@
package org.thoughtcrime.securesms;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcEvent;
@@ -57,11 +57,10 @@ public class ContactMultiSelectionActivity extends ContactSelectionActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.menu_add_members:
saveSelection();
finish();
return true;
if (item.getItemId() == R.id.menu_add_members) {
saveSelection();
finish();
return true;
}
return false;
@@ -26,7 +26,6 @@ import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.util.SparseIntArray;
@@ -149,16 +148,16 @@ public class ContactSelectionListFragment extends Fragment
@Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.menu_select_all:
handleSelectAll();
return true;
case R.id.menu_view_profile:
handleViewProfile();
return true;
case R.id.menu_delete_selected:
handleDeleteSelected();
return true;
int itemId = menuItem.getItemId();
if (itemId == R.id.menu_select_all) {
handleSelectAll();
return true;
} else if (itemId == R.id.menu_view_profile) {
handleViewProfile();
return true;
} else if (itemId == R.id.menu_delete_selected) {
handleDeleteSelected();
return true;
}
return false;
}
@@ -213,16 +212,9 @@ public class ContactSelectionListFragment extends Fragment
ContactSelectionListAdapter adapter = getContactSelectionListAdapter();
final SparseIntArray actionModeSelection = adapter.getActionModeSelection().clone();
new Thread(() -> {
boolean failed = false;
for (int index = 0; index < actionModeSelection.size(); index++) {
int contactId = actionModeSelection.valueAt(index);
boolean currentFailed = !dcContext.deleteContact(contactId);
failed = currentFailed || failed;
}
if (failed) {
Util.runOnMain(()-> {
Toast.makeText(getActivity(), R.string.cannot_delete_contacts_in_use, Toast.LENGTH_LONG).show();
});
dcContext.deleteContact(contactId);
}
}).start();
adapter.resetActionModeSelection();
@@ -195,6 +195,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private boolean isDefaultSms = true;
private boolean isSecurityInitialized = false;
private boolean successfulForwardingAttempt = false;
private boolean isEditing = false;
@Override
protected void onCreate(Bundle state, boolean ready) {
@@ -502,17 +503,37 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.menu_leave: handleLeaveGroup(); return true;
case R.id.menu_archive_chat: handleArchiveChat(); return true;
case R.id.menu_clear_chat: fragment.handleClearChat(); return true;
case R.id.menu_delete_chat: handleDeleteChat(); return true;
case R.id.menu_mute_notifications: handleMuteNotifications(); return true;
case R.id.menu_show_map: WebxdcActivity.openMaps(this, chatId); return true;
case R.id.menu_search_up: handleMenuSearchNext(false); return true;
case R.id.menu_search_down: handleMenuSearchNext(true); return true;
case android.R.id.home: handleReturnToConversationList(); return true;
case R.id.menu_ephemeral_messages: handleEphemeralMessages(); return true;
int itemId = item.getItemId();
if (itemId == R.id.menu_leave) {
handleLeaveGroup();
return true;
} else if (itemId == R.id.menu_archive_chat) {
handleArchiveChat();
return true;
} else if (itemId == R.id.menu_clear_chat) {
fragment.handleClearChat();
return true;
} else if (itemId == R.id.menu_delete_chat) {
handleDeleteChat();
return true;
} else if (itemId == R.id.menu_mute_notifications) {
handleMuteNotifications();
return true;
} else if (itemId == R.id.menu_show_map) {
WebxdcActivity.openMaps(this, chatId);
return true;
} else if (itemId == R.id.menu_search_up) {
handleMenuSearchNext(false);
return true;
} else if (itemId == R.id.menu_search_down) {
handleMenuSearchNext(true);
return true;
} else if (itemId == android.R.id.home) {
handleReturnToConversationList();
return true;
} else if (itemId == R.id.menu_ephemeral_messages) {
handleEphemeralMessages();
return true;
}
return false;
@@ -708,6 +729,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
* @return
*/
private ListenableFuture<Boolean> initializeDraft() {
isEditing = false;
final SettableFuture<Boolean> future = new SettableFuture<>();
DcMsg draft = dcContext.getDraft(chatId);
final String sharedText = RelayUtil.getSharedText(this);
@@ -993,6 +1015,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
DcMsg msg = null;
Optional<QuoteModel> quote = inputPanel.getQuote();
Integer recompress = 0;
boolean editing = isEditing;
// for a quick ui feedback, we clear the related controls immediately on sending messages.
// for drafts, however, we do not change the controls, the activity may be resumed.
@@ -1001,6 +1024,18 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
inputPanel.clearQuote();
}
if (editing) {
int msgId = quote.get().getQuotedMsg().getId();
Util.runOnAnyBackgroundThread(() -> {
if (action == ACTION_SEND_OUT) {
dcContext.sendEditRequest(msgId, body);
} else {
dcContext.setDraft(chatId, null);
}
});
return future;
}
if(slideDeck!=null) {
if (action==ACTION_SEND_OUT) {
@@ -1139,7 +1174,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
return;
}
if (composeText.getText().length() == 0 && !attachmentManager.isAttachmentPresent()) {
if (!isEditing && composeText.getText().length() == 0 && !attachmentManager.isAttachmentPresent()) {
buttonToggle.display(attachButton);
quickAttachmentToggle.show();
} else {
@@ -1257,9 +1292,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
}
@Override
public void onQuoteDismissed() {
if (isEditing) composeText.setText("");
isEditing = false;
}
// media selected by the system keyboard
@Override
public void onMediaSelected(@NonNull Uri uri, String contentType) {
if (isEditing) return;
if (MediaUtil.isImageType(contentType)) {
sendSticker(uri, contentType);
} else if (MediaUtil.isVideoType(contentType)) {
@@ -1391,6 +1433,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public void handleReplyMessage(DcMsg msg) {
if (isEditing) composeText.setText("");
isEditing = false;
// If you modify these lines you may also want to modify ConversationItem.setQuote():
Recipient author = new Recipient(this, dcContext.getContact(msg.getFromId()));
@@ -1406,11 +1450,32 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
msg.getTimestamp(),
author,
text,
slideDeck);
slideDeck,
false);
inputPanel.clickOnComposeInput();
}
@Override
public void handleEditMessage(DcMsg msg) {
isEditing = true;
Recipient author = new Recipient(this, dcContext.getContact(msg.getFromId()));
SlideDeck slideDeck = new SlideDeck();
String text = msg.getSummarytext(500);
inputPanel.setQuote(GlideApp.with(this),
msg,
msg.getTimestamp(),
author,
text,
slideDeck,
true);
setDraftText(msg.getText());
inputPanel.clickOnComposeInput();
}
@Override
public void onAttachmentChanged() {
handleSecurityChange(isSecureText, isDefaultSms);
@@ -16,17 +16,19 @@
*/
package org.thoughtcrime.securesms;
import android.content.Context;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import static org.thoughtcrime.securesms.ConversationItem.PULSE_HIGHLIGHT_MILLIS;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcMsg;
@@ -49,8 +51,6 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static org.thoughtcrime.securesms.ConversationItem.PULSE_HIGHLIGHT_MILLIS;
/**
* A DC adapter for a conversation thread. Ultimately
* used by ConversationActivity to display a conversation
@@ -63,8 +63,8 @@ import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.reactions.AddReactionView;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.reactions.ReactionsDetailsFragment;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.AccessibilityUtil;
import org.thoughtcrime.securesms.util.Debouncer;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
@@ -324,7 +324,8 @@ public class ConversationFragment extends MessageSelectorFragment
if (messageRecords.size() > 1) {
menu.findItem(R.id.menu_context_details).setVisible(false);
menu.findItem(R.id.menu_context_share).setVisible(false);
menu.findItem(R.id.menu_context_reply).setVisible(false);
//menu.findItem(R.id.menu_context_reply).setVisible(false);
menu.findItem(R.id.menu_context_edit).setVisible(false);
menu.findItem(R.id.menu_context_reply_privately).setVisible(false);
menu.findItem(R.id.menu_add_to_home_screen).setVisible(false);
} else {
@@ -333,7 +334,9 @@ public class ConversationFragment extends MessageSelectorFragment
menu.findItem(R.id.menu_context_details).setVisible(true);
menu.findItem(R.id.menu_context_share).setVisible(messageRecord.hasFile());
boolean canReply = canReplyToMsg(messageRecord);
menu.findItem(R.id.menu_context_reply).setVisible(chat.canSend() && canReply);
//menu.findItem(R.id.menu_context_reply).setVisible(chat.canSend() && canReply);
boolean canEdit = canEditMsg(messageRecord);
menu.findItem(R.id.menu_context_edit).setVisible(chat.canSend() && canEdit);
boolean showReplyPrivately = !dcContext.isCommunity() && chat.isMultiUser() && !messageRecord.isOutgoing() && canReply;
menu.findItem(R.id.menu_context_reply_privately).setVisible(showReplyPrivately);
menu.findItem(R.id.menu_add_to_home_screen).setVisible(messageRecord.getType() == DcMsg.DC_MSG_WEBXDC);
@@ -377,6 +380,10 @@ public class ConversationFragment extends MessageSelectorFragment
return canReply;
}
static boolean canEditMsg(DcMsg dcMsg) {
return dcMsg.isOutgoing() && !dcMsg.isInfo() && dcMsg.getType() != DcMsg.DC_MSG_VIDEOCHAT_INVITATION && !dcMsg.hasHtml() && !dcMsg.getText().isEmpty();
}
public void handleClearChat() {
handleDeleteMessages((int) chatId, getListAdapter().getMessageIds());
}
@@ -477,6 +484,16 @@ public class ConversationFragment extends MessageSelectorFragment
listener.handleReplyMessage(message);
}
@SuppressLint("RestrictedApi")
private void handleEditMessage(final DcMsg message) {
if (getActivity() != null) {
//noinspection ConstantConditions
((AppCompatActivity) getActivity()).getSupportActionBar().collapseActionView();
}
listener.handleEditMessage(message);
}
private void handleReplyMessagePrivately(final DcMsg msg) {
if (getActivity() != null) {
@@ -607,6 +624,7 @@ public class ConversationFragment extends MessageSelectorFragment
public interface ConversationFragmentListener {
void handleReplyMessage(DcMsg messageRecord);
void handleEditMessage(DcMsg messageRecord);
}
private class ConversationScrollListener extends OnScrollListener {
@@ -926,6 +944,7 @@ public class ConversationFragment extends MessageSelectorFragment
statusBarColor = window.getStatusBarColor();
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
Util.redMenuItem(menu, R.id.menu_context_delete_message);
setCorrectMenuVisibility(menu);
ConversationAdaptiveActionsToolbar.adjustMenuActions(menu, 10, requireActivity().getWindow().getDecorView().getMeasuredWidth());
return true;
@@ -950,43 +969,47 @@ public class ConversationFragment extends MessageSelectorFragment
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
hideAddReactionView();
switch(item.getItemId()) {
case R.id.menu_context_copy:
handleCopyMessage(getListAdapter().getSelectedItems());
actionMode.finish();
return true;
case R.id.menu_context_delete_message:
handleDeleteMessages((int) chatId, getListAdapter().getSelectedItems());
return true;
case R.id.menu_context_share:
DcHelper.openForViewOrShare(getContext(), getSelectedMessageRecord(getListAdapter().getSelectedItems()).getId(), Intent.ACTION_SEND);
return true;
case R.id.menu_context_details:
handleDisplayDetails(getSelectedMessageRecord(getListAdapter().getSelectedItems()));
actionMode.finish();
return true;
case R.id.menu_context_forward:
handleForwardMessage(getListAdapter().getSelectedItems());
actionMode.finish();
return true;
case R.id.menu_add_to_home_screen:
WebxdcActivity.addToHomeScreen(getActivity(), getSelectedMessageRecord(getListAdapter().getSelectedItems()).getId());
actionMode.finish();
return true;
case R.id.menu_context_save_attachment:
handleSaveAttachment(getListAdapter().getSelectedItems());
return true;
case R.id.menu_context_reply:
handleReplyMessage(getSelectedMessageRecord(getListAdapter().getSelectedItems()));
actionMode.finish();
return true;
case R.id.menu_context_reply_privately:
handleReplyMessagePrivately(getSelectedMessageRecord(getListAdapter().getSelectedItems()));
return true;
case R.id.menu_resend:
handleResendMessage(getListAdapter().getSelectedItems());
return true;
}
int itemId = item.getItemId();
if (itemId == R.id.menu_context_copy) {
handleCopyMessage(getListAdapter().getSelectedItems());
actionMode.finish();
return true;
} else if (itemId == R.id.menu_context_delete_message) {
handleDeleteMessages((int) chatId, getListAdapter().getSelectedItems());
return true;
} else if (itemId == R.id.menu_context_share) {
DcHelper.openForViewOrShare(getContext(), getSelectedMessageRecord(getListAdapter().getSelectedItems()).getId(), Intent.ACTION_SEND);
return true;
} else if (itemId == R.id.menu_context_details) {
handleDisplayDetails(getSelectedMessageRecord(getListAdapter().getSelectedItems()));
actionMode.finish();
return true;
} else if (itemId == R.id.menu_context_forward) {
handleForwardMessage(getListAdapter().getSelectedItems());
actionMode.finish();
return true;
} else if (itemId == R.id.menu_add_to_home_screen) {
WebxdcActivity.addToHomeScreen(getActivity(), getSelectedMessageRecord(getListAdapter().getSelectedItems()).getId());
actionMode.finish();
return true;
} else if (itemId == R.id.menu_context_save_attachment) {
handleSaveAttachment(getListAdapter().getSelectedItems());
return true;
// } else if (itemId == R.id.menu_context_reply) {
// handleReplyMessage(getSelectedMessageRecord(getListAdapter().getSelectedItems()));
// actionMode.finish();
// return true;
} else if (itemId == R.id.menu_context_edit) {
handleEditMessage(getSelectedMessageRecord(getListAdapter().getSelectedItems()));
actionMode.finish();
return true;
} else if (itemId == R.id.menu_context_reply_privately) {
handleReplyMessagePrivately(getSelectedMessageRecord(getListAdapter().getSelectedItems()));
return true;
} else if (itemId == R.id.menu_resend) {
handleResendMessage(getListAdapter().getSelectedItems());
return true;
}
return false;
}
@@ -720,7 +720,8 @@ public class ConversationItem extends BaseConversationItem
author,
quoteTxt,
slideDeck,
current.getType() == DcMsg.DC_MSG_STICKER);
current.getType() == DcMsg.DC_MSG_STICKER,
false);
quoteView.setVisibility(View.VISIBLE);
quoteView.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
import android.os.Vibrator;
import android.view.MotionEvent;
import android.view.View;
@@ -20,8 +20,8 @@ import static org.thoughtcrime.securesms.ConversationActivity.CHAT_ID_EXTRA;
import static org.thoughtcrime.securesms.ConversationActivity.STARTING_POSITION_EXTRA;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_ADDRESS;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_PROXY_ENABLED;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SERVER_FLAGS;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_PROXY_URL;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SERVER_FLAGS;
import static org.thoughtcrime.securesms.util.RelayUtil.acquireRelayMessageContent;
import static org.thoughtcrime.securesms.util.RelayUtil.getDirectSharingChatId;
import static org.thoughtcrime.securesms.util.RelayUtil.getSharedTitle;
@@ -123,7 +123,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
// Util.copy(inputStream, new FileOutputStream(outputFile));
// msg.setFile(outputFile, "image/jpeg");
msg.setText(getString(R.string.update_1_50_android, "https://raw.githubusercontent.com/ArcaneChat/android/refs/heads/main/CHANGELOG.md"));
msg.setText(getString(R.string.update_1_50_android, "https://lemmy.ml/c/ArcaneChat"));
}
dcContext.addDeviceMsg(deviceMsgLabel, msg);
@@ -434,31 +434,31 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.menu_invite_friends:
shareInvite();
return true;
case R.id.menu_settings:
startActivity(new Intent(this, ApplicationPreferencesActivity.class));
return true;
case R.id.menu_qr:
new IntentIntegrator(this).setCaptureActivity(QrActivity.class).initiateScan();
return true;
case R.id.menu_global_map:
WebxdcActivity.openMaps(this, 0);
return true;
case R.id.menu_proxy_settings:
startActivity(new Intent(this, ProxySettingsActivity.class));
return true;
case android.R.id.home:
onBackPressed();
return true;
case R.id.menu_all_media:
startActivity(new Intent(this, ProfileActivity.class));
return true;
case R.id.menu_public_bots:
handleShowBot("botsindex@arcanechat.me", "https://i.delta.chat/#67889B0362BEDBFEE05ACD92C1D737FA632A9582&a=botsindex%40arcanechat.me&n=Public%20Bots&i=336MTEz38EH-RJxM9OKWygYK&s=TpVVGK6C4KrJmRG0bwHLalXt");
return true;
int itemId = item.getItemId();
if (itemId == R.id.menu_invite_friends) {
shareInvite();
return true;
} else if (itemId == R.id.menu_settings) {
startActivity(new Intent(this, ApplicationPreferencesActivity.class));
return true;
} else if (itemId == R.id.menu_qr) {
new IntentIntegrator(this).setCaptureActivity(QrActivity.class).initiateScan();
return true;
} else if (itemId == R.id.menu_global_map) {
WebxdcActivity.openMaps(this, 0);
return true;
} else if (itemId == R.id.menu_proxy_settings) {
startActivity(new Intent(this, ProxySettingsActivity.class));
return true;
} else if (itemId == android.R.id.home) {
onBackPressed();
return true;
} else if (itemId == R.id.menu_all_media) {
startActivity(new Intent(this, ProfileActivity.class));
return true;
} else if (itemId == R.id.menu_public_bots) {
handleShowBot("botsindex@arcanechat.me", "https://i.delta.chat/#67889B0362BEDBFEE05ACD92C1D737FA632A9582&a=botsindex%40arcanechat.me&n=Public%20Bots&i=336MTEz38EH-RJxM9OKWygYK&s=TpVVGK6C4KrJmRG0bwHLalXt");
return true;
}
return false;
@@ -1,5 +1,11 @@
package org.thoughtcrime.securesms;
import static org.thoughtcrime.securesms.ConversationActivity.CHAT_ID_EXTRA;
import static org.thoughtcrime.securesms.ConversationActivity.FROM_ARCHIVED_CHATS_EXTRA;
import static org.thoughtcrime.securesms.util.RelayUtil.acquireRelayMessageContent;
import static org.thoughtcrime.securesms.util.RelayUtil.isRelayingMessageContent;
import static org.thoughtcrime.securesms.util.RelayUtil.isSharing;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
@@ -10,12 +16,6 @@ import com.b44t.messenger.DcChat;
import org.thoughtcrime.securesms.connect.DcHelper;
import static org.thoughtcrime.securesms.ConversationActivity.CHAT_ID_EXTRA;
import static org.thoughtcrime.securesms.ConversationActivity.FROM_ARCHIVED_CHATS_EXTRA;
import static org.thoughtcrime.securesms.util.RelayUtil.acquireRelayMessageContent;
import static org.thoughtcrime.securesms.util.RelayUtil.isRelayingMessageContent;
import static org.thoughtcrime.securesms.util.RelayUtil.isSharing;
public class ConversationListArchiveActivity extends PassphraseRequiredActionBarActivity
implements ConversationListFragment.ConversationSelectedListener
{
@@ -60,14 +60,13 @@ public class ConversationListArchiveActivity extends PassphraseRequiredActionBar
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
case R.id.mark_as_read:
DcHelper.getContext(this).marknoticedChat(DcChat.DC_CHAT_ID_ARCHIVED_LINK);
return true;
int itemId = item.getItemId();
if (itemId == android.R.id.home) {
onBackPressed();
return true;
} else if (itemId == R.id.mark_as_read) {
DcHelper.getContext(this).marknoticedChat(DcChat.DC_CHAT_ID_ARCHIVED_LINK);
return true;
}
return false;
@@ -2,13 +2,12 @@ package org.thoughtcrime.securesms;
import android.content.Context;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import android.util.AttributeSet;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.b44t.messenger.DcLot;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
@@ -4,11 +4,11 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
import android.util.AttributeSet;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcMsg;
@@ -1,16 +1,15 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContact;
import com.b44t.messenger.DcContext;
@@ -11,7 +11,6 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -101,13 +100,12 @@ public class CreateProfileActivity extends BaseActionBarActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
case R.id.menu_create_profile:
updateProfile();
break;
int itemId = item.getItemId();
if (itemId == android.R.id.home) {
onBackPressed();
return true;
} else if (itemId == R.id.menu_create_profile) {
updateProfile();
}
return false;
@@ -105,17 +105,16 @@ public class FullMsgActivity extends WebViewActivity
@Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_export_image:
// TODO: extract image from "data:" link or download URL
return true;
case R.id.action_copy_link:
Util.writeTextToClipboard(this, imageUrl);
Toast.makeText(this, getString(R.string.copied_to_clipboard), Toast.LENGTH_SHORT).show();
return true;
default:
return super.onContextItemSelected(item);
int itemId = item.getItemId();
if (itemId == R.id.action_export_image) {
// TODO: extract image from "data:" link or download URL
return true;
} else if (itemId == R.id.action_copy_link) {
Util.writeTextToClipboard(this, imageUrl);
Toast.makeText(this, getString(R.string.copied_to_clipboard), Toast.LENGTH_SHORT).show();
return true;
}
return super.onContextItemSelected(item);
}
private static void loadHtmlAsync(final WeakReference<FullMsgActivity> activityReference) {
@@ -157,40 +156,39 @@ public class FullMsgActivity extends WebViewActivity
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.load_remote_content:
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setTitle(R.string.load_remote_content)
.setMessage(R.string.load_remote_content_ask);
if (item.getItemId() == R.id.load_remote_content) {
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setTitle(R.string.load_remote_content)
.setMessage(R.string.load_remote_content_ask);
// we are using the buttons "[Always] [Never][Once]" in that order.
// 1. Checkmarks before [Always] and [Never] show the current state.
// 2. [Once] is also shown in always-mode and disables always-mode if selected
// (there was the idea to hide [Once] in always mode, but that looks more like a bug in the end)
// (maybe a usual Always-Checkbox and "[Cancel][OK]" buttons are an alternative, however, a [Once]
// would be required as well - probably as the leftmost button which is not that usable in
// not-always-mode where the dialog is used more often. Or [Ok] would mean "Once" as well as "Change checkbox setting",
// which is also a bit weird. Anyway, let's give the three buttons a try :)
final String checkmark = DynamicTheme.getCheckmarkEmoji(this) + " ";
String alwaysCheckmark = "";
String onceCheckmark = "";
String neverCheckmark = "";
if (!blockLoadingRemote && Prefs.getAlwaysLoadRemoteContent(this)) {
alwaysCheckmark = checkmark;
} else if (loadRemoteContent) {
onceCheckmark = checkmark;
} else {
neverCheckmark = checkmark;
}
// we are using the buttons "[Always] [Never][Once]" in that order.
// 1. Checkmarks before [Always] and [Never] show the current state.
// 2. [Once] is also shown in always-mode and disables always-mode if selected
// (there was the idea to hide [Once] in always mode, but that looks more like a bug in the end)
// (maybe a usual Always-Checkbox and "[Cancel][OK]" buttons are an alternative, however, a [Once]
// would be required as well - probably as the leftmost button which is not that usable in
// not-always-mode where the dialog is used more often. Or [Ok] would mean "Once" as well as "Change checkbox setting",
// which is also a bit weird. Anyway, let's give the three buttons a try :)
final String checkmark = DynamicTheme.getCheckmarkEmoji(this) + " ";
String alwaysCheckmark = "";
String onceCheckmark = "";
String neverCheckmark = "";
if (!blockLoadingRemote && Prefs.getAlwaysLoadRemoteContent(this)) {
alwaysCheckmark = checkmark;
} else if (loadRemoteContent) {
onceCheckmark = checkmark;
} else {
neverCheckmark = checkmark;
}
if (!blockLoadingRemote) {
builder.setNeutralButton(alwaysCheckmark + getString(R.string.always), (dialog, which) -> onChangeLoadRemoteContent(LoadRemoteContent.ALWAYS));
}
builder.setNegativeButton(neverCheckmark + getString(blockLoadingRemote ? R.string.no : R.string.never), (dialog, which) -> onChangeLoadRemoteContent(LoadRemoteContent.NEVER));
builder.setPositiveButton(onceCheckmark + getString(R.string.once), (dialog, which) -> onChangeLoadRemoteContent(LoadRemoteContent.ONCE));
if (!blockLoadingRemote) {
builder.setNeutralButton(alwaysCheckmark + getString(R.string.always), (dialog, which) -> onChangeLoadRemoteContent(LoadRemoteContent.ALWAYS));
}
builder.setNegativeButton(neverCheckmark + getString(blockLoadingRemote ? R.string.no : R.string.never), (dialog, which) -> onChangeLoadRemoteContent(LoadRemoteContent.NEVER));
builder.setPositiveButton(onceCheckmark + getString(R.string.once), (dialog, which) -> onChangeLoadRemoteContent(LoadRemoteContent.ONCE));
builder.show();
return true;
builder.show();
return true;
}
return false;
}
@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
@@ -23,7 +24,7 @@ import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContact;
import com.b44t.messenger.DcContext;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import org.thoughtcrime.securesms.components.AvatarSelector;
@@ -74,7 +75,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
setContentView(R.layout.group_create_activity);
verified = false;
broadcast = getIntent().getBooleanExtra(CREATE_BROADCAST, false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_close_white_24dp);
groupChatId = getIntent().getIntExtra(EDIT_GROUP_CHAT_ID, 0);
@@ -207,22 +208,22 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
case R.id.menu_create_group:
String groupName = getGroupName();
if (showGroupNameEmptyToast(groupName)) return true;
int itemId = item.getItemId();
if (itemId == android.R.id.home) {
finish();
return true;
} else if (itemId == R.id.menu_create_group) {
String groupName = getGroupName();
if (showGroupNameEmptyToast(groupName)) return true;
if (groupChatId!=0) {
updateGroup(groupName);
} else {
verified = !broadcast && allMembersVerified();
createGroup(groupName);
}
if (groupChatId != 0) {
updateGroup(groupName);
} else {
verified = !broadcast && allMembersVerified();
createGroup(groupName);
}
return true;
return true;
}
return false;
@@ -354,11 +355,14 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
.diskCacheStrategy(DiskCacheStrategy.NONE)
.centerCrop()
.override(AVATAR_SIZE, AVATAR_SIZE)
.into(new SimpleTarget<Bitmap>() {
.into(new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
setAvatar(output, resource);
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {}
});
}
@@ -4,7 +4,6 @@ import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_PROXY_ENABLED;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_PROXY_URL;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -16,7 +15,6 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.text.util.Linkify;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
@@ -158,14 +156,14 @@ public class InstantOnboardingActivity extends BaseActionBarActivity implements
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case android.R.id.home:
int itemId = item.getItemId();
if (itemId == android.R.id.home) {
getOnBackPressedDispatcher().onBackPressed();
return true;
case R.id.menu_proxy_settings:
} else if (itemId == R.id.menu_proxy_settings) {
startActivity(new Intent(this, ProxySettingsActivity.class));
return true;
case R.id.menu_view_log:
} else if (itemId == R.id.menu_view_log) {
startActivity(new Intent(this, LogViewActivity.class));
return true;
}
@@ -57,22 +57,22 @@ public class LocalHelpActivity extends WebViewActivity
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.log_scroll_up:
webView.scrollTo(0, 0);
return true;
case R.id.learn_more:
openOnlineUrl("https://arcanechat.me");
return true;
case R.id.privacy_policy:
openOnlineUrl("https://arcanechat.me/privacy.html");
return true;
case R.id.contribute:
openOnlineUrl("https://arcanechat.me/#contribute");
return true;
case R.id.report_issue:
openOnlineUrl("https://github.com/ArcaneChat/android/issues");
return true;
int itemId = item.getItemId();
if (itemId == R.id.log_scroll_up) {
webView.scrollTo(0, 0);
return true;
} else if (itemId == R.id.learn_more) {
openOnlineUrl("https://arcanechat.me");
return true;
} else if (itemId == R.id.privacy_policy) {
openOnlineUrl("https://arcanechat.me/privacy.html");
return true;
} else if (itemId == R.id.contribute) {
openOnlineUrl("https://arcanechat.me/#contribute");
return true;
} else if (itemId == R.id.report_issue) {
openOnlineUrl("https://github.com/ArcaneChat/android/issues");
return true;
}
return false;
}
@@ -54,42 +54,42 @@ public class LogViewActivity extends BaseActionBarActivity {
super.onOptionsItemSelected(item);
Float newSize;
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
case R.id.save_log:
Permissions.with(this)
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.alwaysGrantOnSdk30()
.ifNecessary()
.onAllGranted(() -> {
File outputDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
boolean success = logViewFragment.saveLogFile(outputDir) != null;
new AlertDialog.Builder(this)
.setMessage(success? R.string.pref_saved_log : R.string.pref_save_log_failed)
.setPositiveButton(android.R.string.ok, null)
.show();
})
.execute();
return true;
case R.id.share_log:
shareLog();
return true;
case R.id.log_zoom_in:
newSize = logViewFragment.getLogTextSize() + 2.0f;
logViewFragment.setLogTextSize(newSize);
return false;
case R.id.log_zoom_out:
newSize = logViewFragment.getLogTextSize() - 2.0f;
logViewFragment.setLogTextSize(newSize);
return false;
case R.id.log_scroll_down:
logViewFragment.scrollDownLog();
return false;
case R.id.log_scroll_up:
logViewFragment.scrollUpLog();
return false;
int itemId = item.getItemId();
if (itemId == android.R.id.home) {
finish();
return true;
} else if (itemId == R.id.save_log) {
Permissions.with(this)
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.alwaysGrantOnSdk30()
.ifNecessary()
.onAllGranted(() -> {
File outputDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
boolean success = logViewFragment.saveLogFile(outputDir) != null;
new AlertDialog.Builder(this)
.setMessage(success ? R.string.pref_saved_log : R.string.pref_save_log_failed)
.setPositiveButton(android.R.string.ok, null)
.show();
})
.execute();
return true;
} else if (itemId == R.id.share_log) {
shareLog();
return true;
} else if (itemId == R.id.log_zoom_in) {
newSize = logViewFragment.getLogTextSize() + 2.0f;
logViewFragment.setLogTextSize(newSize);
return false;
} else if (itemId == R.id.log_zoom_out) {
newSize = logViewFragment.getLogTextSize() - 2.0f;
logViewFragment.setLogTextSize(newSize);
return false;
} else if (itemId == R.id.log_scroll_down) {
logViewFragment.scrollDownLog();
return false;
} else if (itemId == R.id.log_scroll_up) {
logViewFragment.scrollUpLog();
return false;
}
return false;
@@ -18,14 +18,12 @@
package org.thoughtcrime.securesms;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.PowerManager;
import android.text.TextUtils;
@@ -16,9 +16,9 @@
*/
package org.thoughtcrime.securesms;
import android.app.Activity;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@@ -344,28 +344,31 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
DcMsg dcMsg = dcContext.getMsg(mediaItem.msgId);
DcChat dcChat = dcContext.getChat(dcMsg.getChatId());
String text = getResources().getQuantityString(
dcChat.isDeviceTalk()? R.plurals.ask_delete_messages_simple : R.plurals.ask_delete_messages,
1, 1);
String text = getResources().getQuantityString(R.plurals.ask_delete_messages, 1, 1);
int positiveBtnLabel = dcChat.isSelfTalk() ? R.string.delete : R.string.delete_for_me;
final int[] messageIds = new int[]{mediaItem.msgId};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(text);
builder.setCancelable(true);
builder.setPositiveButton(R.string.delete, (dialogInterface, which) -> {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
dcContext.deleteMsgs(new int[]{mediaItem.msgId});
return null;
}
}.execute();
builder.setNeutralButton(android.R.string.cancel, null);
builder.setPositiveButton(positiveBtnLabel, (dialogInterface, which) -> {
Util.runOnAnyBackgroundThread(() -> dcContext.deleteMsgs(messageIds));
finish();
});
builder.setNegativeButton(android.R.string.cancel, null);
AlertDialog dialog = builder.show();
Util.redPositiveButton(dialog);
if(dcChat.canSend() && !dcChat.isSelfTalk() && dcMsg.isOutgoing()) {
builder.setNegativeButton(R.string.delete_for_everyone, (d, which) -> {
Util.runOnAnyBackgroundThread(() -> dcContext.sendDeleteRequest(messageIds));
finish();
});
AlertDialog dialog = builder.show();
Util.redButton(dialog, AlertDialog.BUTTON_NEGATIVE);
Util.redPositiveButton(dialog);
} else {
AlertDialog dialog = builder.show();
Util.redPositiveButton(dialog);
}
}
@Override
@@ -395,14 +398,28 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.media_preview__edit: editAvatar(); return true;
case R.id.media_preview__overview: showOverview(); return true;
case R.id.media_preview__share: share(); return true;
case R.id.save: saveToDisk(); return true;
case R.id.delete: deleteMedia(); return true;
case R.id.show_in_chat: showInChat(); return true;
case android.R.id.home: finish(); return true;
int itemId = item.getItemId();
if (itemId == R.id.media_preview__edit) {
editAvatar();
return true;
} else if (itemId == R.id.media_preview__overview) {
showOverview();
return true;
} else if (itemId == R.id.media_preview__share) {
share();
return true;
} else if (itemId == R.id.save) {
saveToDisk();
return true;
} else if (itemId == R.id.delete) {
deleteMedia();
return true;
} else if (itemId == R.id.show_in_chat) {
showInChat();
return true;
} else if (itemId == android.R.id.home) {
finish();
return true;
}
return false;
@@ -61,22 +61,44 @@ public abstract class MessageSelectorFragment
}
protected void handleDeleteMessages(int chatId, final int[] messageIds) {
DcChat dcChat = DcHelper.getContext(getActivity()).getChat(chatId);
DcChat dcChat = dcContext.getChat(chatId);
boolean canDeleteForAll = true;
if (dcChat.canSend() && !dcChat.isSelfTalk()) {
for(int msgId : messageIds) {
DcMsg msg = dcContext.getMsg(msgId);
if (!msg.isOutgoing()) {
canDeleteForAll = false;
break;
}
}
} else {
canDeleteForAll = false;
}
String text = getActivity().getResources().getQuantityString(
dcChat.isDeviceTalk()? R.plurals.ask_delete_messages_simple : R.plurals.ask_delete_messages,
messageIds.length, messageIds.length);
String text = requireActivity().getResources().getQuantityString(R.plurals.ask_delete_messages, messageIds.length, messageIds.length);
int positiveBtnLabel = dcChat.isSelfTalk() ? R.string.delete : R.string.delete_for_me;
AlertDialog dialog = new AlertDialog.Builder(getActivity())
.setMessage(text)
.setCancelable(true)
.setPositiveButton(R.string.delete, (d, which) -> {
dcContext.deleteMsgs(messageIds);
if (actionMode != null) actionMode.finish();
})
.setNegativeButton(android.R.string.cancel, null)
.show();
Util.redPositiveButton(dialog);
AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity())
.setMessage(text)
.setCancelable(true)
.setNeutralButton(android.R.string.cancel, null)
.setPositiveButton(positiveBtnLabel, (d, which) -> {
dcContext.deleteMsgs(messageIds);
if (actionMode != null) actionMode.finish();
});
if(canDeleteForAll) {
builder.setNegativeButton(R.string.delete_for_everyone, (d, which) -> {
dcContext.sendDeleteRequest(messageIds);
if (actionMode != null) actionMode.finish();
});
AlertDialog dialog = builder.show();
Util.redButton(dialog, AlertDialog.BUTTON_NEGATIVE);
Util.redPositiveButton(dialog);
} else {
AlertDialog dialog = builder.show();
Util.redPositiveButton(dialog);
}
}
protected void handleSaveAttachment(final Set<DcMsg> messageRecords) {
@@ -1,6 +1,7 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
@@ -16,12 +16,15 @@
*/
package org.thoughtcrime.securesms;
import static org.thoughtcrime.securesms.ConversationActivity.CHAT_ID_EXTRA;
import static org.thoughtcrime.securesms.ConversationActivity.TEXT_EXTRA;
import static org.thoughtcrime.securesms.util.RelayUtil.acquireRelayMessageContent;
import static org.thoughtcrime.securesms.util.RelayUtil.isRelayingMessageContent;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
@@ -36,11 +39,6 @@ import org.thoughtcrime.securesms.qr.QrActivity;
import org.thoughtcrime.securesms.qr.QrCodeHandler;
import org.thoughtcrime.securesms.util.MailtoUtil;
import static org.thoughtcrime.securesms.ConversationActivity.CHAT_ID_EXTRA;
import static org.thoughtcrime.securesms.ConversationActivity.TEXT_EXTRA;
import static org.thoughtcrime.securesms.util.RelayUtil.acquireRelayMessageContent;
import static org.thoughtcrime.securesms.util.RelayUtil.isRelayingMessageContent;
/**
* Activity container for starting a new conversation.
*
@@ -416,35 +416,27 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case android.R.id.home:
backPressed = true;
finish();
return true;
case R.id.menu_mute_notifications:
onNotifyOnOff();
break;
case R.id.menu_sound:
onSoundSettings();
break;
case R.id.menu_vibrate:
onVibrateSettings();
break;
case R.id.edit_name:
onEditName();
break;
case R.id.share:
onShare();
break;
case R.id.show_encr_info:
onEncrInfo();
break;
case R.id.block_contact:
onBlockContact();
break;
case R.id.menu_clone:
onClone();
break;
int itemId = item.getItemId();
if (itemId == android.R.id.home) {
backPressed = true;
finish();
return true;
} else if (itemId == R.id.menu_mute_notifications) {
onNotifyOnOff();
} else if (itemId == R.id.menu_sound) {
onSoundSettings();
} else if (itemId == R.id.menu_vibrate) {
onVibrateSettings();
} else if (itemId == R.id.edit_name) {
onEditName();
} else if (itemId == R.id.share) {
onShare();
} else if (itemId == R.id.show_encr_info) {
onEncrInfo();
} else if (itemId == R.id.block_contact) {
onBlockContact();
} else if (itemId == R.id.menu_clone) {
onClone();
}
return false;
@@ -453,10 +445,8 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity
@Override
public boolean onContextItemSelected(MenuItem item) {
super.onContextItemSelected(item);
switch (item.getItemId()) {
case R.id.copy_addr_to_clipboard:
onCopyAddrToClipboard();
break;
if (item.getItemId() == R.id.copy_addr_to_clipboard) {
onCopyAddrToClipboard();
}
return false;
}
@@ -1,12 +1,13 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import androidx.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.b44t.messenger.DcMsg;
import com.codewaves.stickyheadergrid.StickyHeaderGridAdapter;
@@ -5,7 +5,6 @@ import static com.b44t.messenger.DcChat.DC_CHAT_NO_CHAT;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -249,35 +248,35 @@ public class ProfileDocumentsFragment
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.details:
handleDisplayDetails(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
mode.finish();
return true;
case R.id.delete:
handleDeleteMessages(chatId, getListAdapter().getSelectedMedia());
mode.finish();
return true;
case R.id.share:
handleShare(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
return true;
case R.id.menu_add_to_home_screen:
WebxdcActivity.addToHomeScreen(getActivity(), getSelectedMessageRecord(getListAdapter().getSelectedMedia()).getId());
mode.finish();
return true;
case R.id.show_in_chat:
handleShowInChat(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
return true;
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;
int itemId = menuItem.getItemId();
if (itemId == R.id.details) {
handleDisplayDetails(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
mode.finish();
return true;
} else if (itemId == R.id.delete) {
handleDeleteMessages(chatId, getListAdapter().getSelectedMedia());
mode.finish();
return true;
} else if (itemId == R.id.share) {
handleShare(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
return true;
} else if (itemId == R.id.menu_add_to_home_screen) {
WebxdcActivity.addToHomeScreen(getActivity(), getSelectedMessageRecord(getListAdapter().getSelectedMedia()).getId());
mode.finish();
return true;
} else if (itemId == R.id.show_in_chat) {
handleShowInChat(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
return true;
} else if (itemId == R.id.save) {
handleSaveAttachment(getListAdapter().getSelectedMedia());
return true;
} else if (itemId == R.id.menu_resend) {
handleResendMessage(getListAdapter().getSelectedMedia());
return true;
} else if (itemId == R.id.menu_select_all) {
getListAdapter().selectAll();
updateActionModeBar();
return true;
}
return false;
}
@@ -1,12 +1,13 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import androidx.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.b44t.messenger.DcMsg;
import com.codewaves.stickyheadergrid.StickyHeaderGridAdapter;
@@ -5,14 +5,7 @@ import static com.b44t.messenger.DcChat.DC_CHAT_NO_CHAT;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@@ -21,6 +14,13 @@ import android.view.ViewGroup;
import android.view.Window;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.RecyclerView;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcEvent;
import com.b44t.messenger.DcMsg;
@@ -233,31 +233,31 @@ public class ProfileGalleryFragment
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.details:
handleDisplayDetails(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
mode.finish();
return true;
case R.id.delete:
handleDeleteMessages(chatId, getListAdapter().getSelectedMedia());
mode.finish();
return true;
case R.id.share:
handleShare(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
return true;
case R.id.show_in_chat:
handleShowInChat(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
return true;
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;
int itemId = menuItem.getItemId();
if (itemId == R.id.details) {
handleDisplayDetails(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
mode.finish();
return true;
} else if (itemId == R.id.delete) {
handleDeleteMessages(chatId, getListAdapter().getSelectedMedia());
mode.finish();
return true;
} else if (itemId == R.id.share) {
handleShare(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
return true;
} else if (itemId == R.id.show_in_chat) {
handleShowInChat(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
return true;
} else if (itemId == R.id.save) {
handleSaveAttachment(getListAdapter().getSelectedMedia());
return true;
} else if (itemId == R.id.menu_resend) {
handleResendMessage(getListAdapter().getSelectedMedia());
return true;
} else if (itemId == R.id.menu_select_all) {
getListAdapter().selectAll();
updateActionModeBar();
return true;
}
return false;
}
@@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.contacts.ContactSelectionListItem;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.ResUtil;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration.StickyHeaderAdapter;
import java.util.ArrayList;
@@ -330,7 +331,8 @@ public class ProfileSettingsAdapter extends RecyclerView.Adapter
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.def_accent, 0));
int color = ResUtil.getColor(context, R.attr.colorAccent);
itemData.add(new ItemData(ItemData.CATEGORY_INFO, INFO_SEND_MESSAGE_BUTTON, context.getString(R.string.send_message), color, 0));
}
itemDataStatusText = dcContact.getStatus();
@@ -4,7 +4,6 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -281,29 +280,28 @@ public class ProfileSettingsFragment extends Fragment
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.delete:
final Collection<Integer> toDelIds = adapter.getSelectedMembers();
StringBuilder readableToDelList = new StringBuilder();
for (Integer toDelId : toDelIds) {
if(readableToDelList.length()>0) {
readableToDelList.append(", ");
}
readableToDelList.append(dcContext.getContact(toDelId).getDisplayName());
if (menuItem.getItemId() == R.id.delete) {
final Collection<Integer> toDelIds = adapter.getSelectedMembers();
StringBuilder readableToDelList = new StringBuilder();
for (Integer toDelId : toDelIds) {
if (readableToDelList.length() > 0) {
readableToDelList.append(", ");
}
DcChat dcChat = dcContext.getChat(chatId);
AlertDialog dialog = new AlertDialog.Builder(getContext())
.setPositiveButton(R.string.remove_desktop, (d, which) -> {
for (Integer toDelId : toDelIds) {
dcContext.removeContactFromChat(chatId, toDelId);
}
mode.finish();
})
.setNegativeButton(android.R.string.cancel, null)
.setMessage(getString(dcChat.isBroadcast()? R.string.ask_remove_from_broadcast : R.string.ask_remove_members, readableToDelList))
.show();
Util.redPositiveButton(dialog);
return true;
readableToDelList.append(dcContext.getContact(toDelId).getDisplayName());
}
DcChat dcChat = dcContext.getChat(chatId);
AlertDialog dialog = new AlertDialog.Builder(getContext())
.setPositiveButton(R.string.remove_desktop, (d, which) -> {
for (Integer toDelId : toDelIds) {
dcContext.removeContactFromChat(chatId, toDelId);
}
mode.finish();
})
.setNegativeButton(android.R.string.cancel, null)
.setMessage(getString(dcChat.isBroadcast() ? R.string.ask_remove_from_broadcast : R.string.ask_remove_members, readableToDelList))
.show();
Util.redPositiveButton(dialog);
return true;
}
return false;
}
@@ -31,9 +31,8 @@ public class ProfileSettingsItem extends LinearLayout {
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));
labelView.setTextColor(labelColor);
} else {
labelView.setTextColor(ResUtil.getColor(getContext(), R.attr.emoji_text_color));
}
@@ -6,13 +6,13 @@ import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_MAIL_PORT;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_MAIL_SECURITY;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_MAIL_SERVER;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_MAIL_USER;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_PROXY_ENABLED;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SEND_PASSWORD;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SEND_PORT;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SEND_SECURITY;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SEND_SERVER;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SEND_USER;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_SERVER_FLAGS;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_PROXY_ENABLED;
import static org.thoughtcrime.securesms.connect.DcHelper.getContext;
import static org.thoughtcrime.securesms.service.IPCAddAccountsService.ACCOUNT_DATA;
@@ -1,5 +1,7 @@
package org.thoughtcrime.securesms;
import static org.thoughtcrime.securesms.util.MediaUtil.getMimeType;
import android.app.Activity;
import android.database.Cursor;
import android.net.Uri;
@@ -14,8 +16,6 @@ import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import static org.thoughtcrime.securesms.util.MediaUtil.getMimeType;
import de.cketti.safecontentresolver.SafeContentResolver;
public class ResolveMediaTask extends AsyncTask<Uri, Void, Uri> {
@@ -34,7 +34,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.pm.ShortcutManagerCompat;
import com.b44t.messenger.DcContext;
@@ -1,6 +1,7 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
@@ -1,6 +1,9 @@
package org.thoughtcrime.securesms;
import static org.thoughtcrime.securesms.TransportOption.Type;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -9,8 +12,6 @@ import org.thoughtcrime.securesms.util.guava.Optional;
import java.util.LinkedList;
import java.util.List;
import static org.thoughtcrime.securesms.TransportOption.Type;
public class TransportOptions {
private final List<OnTransportChangedListener> listeners = new LinkedList<>();
private final Context context;
@@ -1,8 +1,6 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import androidx.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -10,6 +8,8 @@ import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.List;
@@ -1,12 +1,13 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.ListPopupWindow;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.ListPopupWindow;
import java.util.LinkedList;
import java.util.List;
@@ -1,9 +1,5 @@
package org.thoughtcrime.securesms;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
@@ -20,10 +16,10 @@ import android.widget.Toast;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.SearchView;
import androidx.webkit.ProxyConfig;
import androidx.webkit.ProxyController;
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;
import androidx.webkit.ProxyController;
import androidx.webkit.ProxyConfig;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.IntentUtils;
@@ -254,24 +250,24 @@ public class WebViewActivity extends PassphraseRequiredActionBarActivity
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
case R.id.menu_search_up:
if (lastQuery.isEmpty()) {
webView.scrollTo(0, 0);
} else {
webView.findNext(false);
}
return true;
case R.id.menu_search_down:
if (lastQuery.isEmpty()) {
webView.scrollTo(0, 1000000000);
} else {
webView.findNext(true);
}
return true;
int itemId = item.getItemId();
if (itemId == android.R.id.home) {
finish();
return true;
} else if (itemId == R.id.menu_search_up) {
if (lastQuery.isEmpty()) {
webView.scrollTo(0, 0);
} else {
webView.findNext(false);
}
return true;
} else if (itemId == R.id.menu_search_down) {
if (lastQuery.isEmpty()) {
webView.scrollTo(0, 1000000000);
} else {
webView.findNext(true);
}
return true;
}
return false;
}
@@ -289,16 +289,16 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.menu_add_to_home_screen:
addToHomeScreen(this, dcAppMsg.getId());
return true;
case R.id.source_code:
IntentUtils.showInBrowser(this, sourceCodeUrl);
return true;
case R.id.show_in_chat:
showInChat();
return true;
int itemId = item.getItemId();
if (itemId == R.id.menu_add_to_home_screen) {
addToHomeScreen(this, dcAppMsg.getId());
return true;
} else if (itemId == R.id.source_code) {
IntentUtils.showInBrowser(this, sourceCodeUrl);
return true;
} else if (itemId == R.id.show_in_chat) {
showInChat();
return true;
}
return false;
}
@@ -5,8 +5,6 @@ import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -15,7 +13,6 @@ import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -352,7 +349,6 @@ public class WelcomeActivity extends BaseActionBarActivity implements DcEventCen
}
DcLot qrParsed = dcContext.checkQr(qrRaw);
switch (qrParsed.getState()) {
case DcContext.DC_QR_BACKUP:
case DcContext.DC_QR_BACKUP2:
final String finalQrRaw = qrRaw;
new AlertDialog.Builder(this)
@@ -364,6 +360,14 @@ public class WelcomeActivity extends BaseActionBarActivity implements DcEventCen
.show();
break;
case DcContext.DC_QR_BACKUP_TOO_NEW:
new AlertDialog.Builder(this)
.setTitle(R.string.multidevice_receiver_title)
.setMessage(R.string.multidevice_receiver_needs_update)
.setPositiveButton(R.string.ok, null)
.show();
break;
default:
new AlertDialog.Builder(this)
.setMessage(R.string.qraccount_qr_code_cannot_be_used)
@@ -16,10 +16,9 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.mms.GlideRequests;
public class AccountSelectionListAdapter extends RecyclerView.Adapter
public class AccountSelectionListAdapter extends RecyclerView.Adapter<AccountSelectionListAdapter.AccountViewHolder>
{
private final @NonNull AccountSelectionListFragment fragment;
private final @NonNull Context context;
private final @NonNull DcAccounts accounts;
private @NonNull int[] accountList = new int[0];
private int selectedAccountId;
@@ -32,18 +31,7 @@ public class AccountSelectionListAdapter extends RecyclerView.Adapter
return accountList.length;
}
public abstract static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
}
public abstract void bind(@NonNull GlideRequests glideRequests, int accountId, DcContext dcContext, boolean selected, AccountSelectionListFragment fragment);
public abstract void unbind(@NonNull GlideRequests glideRequests);
}
public static class AccountViewHolder extends ViewHolder {
public static class AccountViewHolder extends RecyclerView.ViewHolder {
AccountViewHolder(@NonNull final View itemView,
@Nullable final ItemClickListener clickListener) {
super(itemView);
@@ -62,7 +50,6 @@ public class AccountSelectionListAdapter extends RecyclerView.Adapter
getView().bind(glideRequests, accountId, dcContext, selected, fragment);
}
@Override
public void unbind(@NonNull GlideRequests glideRequests) {
getView().unbind(glideRequests);
}
@@ -73,8 +60,8 @@ public class AccountSelectionListAdapter extends RecyclerView.Adapter
@Nullable ItemClickListener clickListener)
{
super();
Context context = fragment.requireActivity();
this.fragment = fragment;
this.context = fragment.getActivity();
this.accounts = DcHelper.getAccounts(context);
this.li = LayoutInflater.from(context);
this.glideRequests = glideRequests;
@@ -83,16 +70,15 @@ public class AccountSelectionListAdapter extends RecyclerView.Adapter
@NonNull
@Override
public AccountSelectionListAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
public AccountViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new AccountViewHolder(li.inflate(R.layout.account_selection_list_item, parent, false), clickListener);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
public void onBindViewHolder(@NonNull AccountViewHolder holder, int i) {
int id = accountList[i];
DcContext dcContext = accounts.getAccount(id);
ViewHolder holder = (ViewHolder) viewHolder;
holder.unbind(glideRequests);
holder.bind(glideRequests, id, dcContext, id == selectedAccountId, fragment);
}
@@ -1,10 +1,14 @@
package org.thoughtcrime.securesms.accounts;
import static com.b44t.messenger.DcContact.DC_CONTACT_ID_ADD_ACCOUNT;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_PRIVATE_TAG;
import android.app.Activity;
import android.app.Dialog;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuItem;
@@ -37,26 +41,12 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import static com.b44t.messenger.DcContact.DC_CONTACT_ID_ADD_ACCOUNT;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_PRIVATE_TAG;
public class AccountSelectionListFragment extends DialogFragment implements DcEventCenter.DcEventDelegate
{
private static final String TAG = AccountSelectionListFragment.class.getSimpleName();
private RecyclerView recyclerView;
private AccountSelectionListAdapter adapter;
@Override
public void onActivityCreated(Bundle icicle) {
super.onActivityCreated(icicle);
adapter = new AccountSelectionListAdapter(this, GlideApp.with(getActivity()), new ListClickListener());
recyclerView.setAdapter(adapter);
refreshData();
DcEventCenter eventCenter = DcHelper.getEventCenter(requireActivity());
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_CONNECTIVITY_CHANGED, this);
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_INCOMING_MSG, this);
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_MSGS_NOTICED, this);
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
@@ -72,6 +62,14 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv
recyclerView = ViewUtil.findById(view, R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
adapter = new AccountSelectionListAdapter(this, GlideApp.with(getActivity()), new ListClickListener());
recyclerView.setAdapter(adapter);
refreshData();
DcEventCenter eventCenter = DcHelper.getEventCenter(requireActivity());
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_CONNECTIVITY_CHANGED, this);
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_INCOMING_MSG, this);
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_MSGS_NOTICED, this);
return builder.setView(view).create();
}
@@ -102,13 +100,13 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
public void onCreateContextMenu(@NonNull ContextMenu menu, @NonNull View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
requireActivity().getMenuInflater().inflate(R.menu.account_item_context, menu);
AccountSelectionListItem listItem = (AccountSelectionListItem) v;
int accountId = listItem.getAccountId();
DcAccounts dcAccounts = DcHelper.getAccounts(getActivity());
DcAccounts dcAccounts = DcHelper.getAccounts(requireActivity());
Util.redMenuItem(menu, R.id.delete);
@@ -129,16 +127,13 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv
}
private void onContextItemSelected(MenuItem item, int accountId) {
switch (item.getItemId()) {
case R.id.delete:
int itemId = item.getItemId();
if (itemId == R.id.delete) {
onDeleteAccount(accountId);
break;
case R.id.menu_mute_notifications:
} else if (itemId == R.id.menu_mute_notifications) {
onToggleMute(accountId);
break;
case R.id.menu_set_tag:
} else if (itemId == R.id.menu_set_tag) {
onSetTag(accountId);
break;
}
}
@@ -185,7 +180,7 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv
if (TextUtils.isEmpty(name)) {
name = contact.getAddr();
}
Recipient recipient = new Recipient(getContext(), contact, name);
Recipient recipient = new Recipient(requireContext(), contact, name);
avatar.setAvatar(GlideApp.with(activity), recipient, false);
nameView.setText(name);
addrView.setText(contact.getAddr());
@@ -196,7 +191,7 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv
sizeView.setText(Util.getPrettyFileSize(sizeBytes));
});
} catch (RpcException e) {
e.printStackTrace();
Log.e(TAG, "Error calling rpc.getAccountFileSize()", e);
}
});
description.setText(activity.getString(R.string.delete_account_explain_with_name, name));
@@ -226,7 +221,7 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv
}
private void onToggleMute(int accountId) {
DcAccounts dcAccounts = DcHelper.getAccounts(getActivity());
DcAccounts dcAccounts = DcHelper.getAccounts(requireActivity());
DcContext dcContext = dcAccounts.getAccount(accountId);
dcContext.setMuted(!dcContext.isMuted());
recyclerView.getAdapter().notifyDataSetChanged();
@@ -236,7 +231,7 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv
@Override
public void onItemClick(AccountSelectionListItem contact) {
Activity activity = getActivity();
Activity activity = requireActivity();
AccountSelectionListFragment.this.dismiss();
int accountId = contact.getAccountId();
if (accountId == DC_CONTACT_ID_ADD_ACCOUNT) {
@@ -3,15 +3,17 @@ package org.thoughtcrime.securesms.animation;
import android.animation.Animator;
import androidx.annotation.NonNull;
public abstract class AnimationCompleteListener implements Animator.AnimatorListener {
@Override
public final void onAnimationStart(Animator animation) {}
public final void onAnimationStart(@NonNull Animator animation) {}
@Override
public abstract void onAnimationEnd(Animator animation);
public abstract void onAnimationEnd(@NonNull Animator animation);
@Override
public final void onAnimationCancel(Animator animation) {}
public final void onAnimationCancel(@NonNull Animator animation) {}
@Override
public final void onAnimationRepeat(Animator animation) {}
public final void onAnimationRepeat(@NonNull Animator animation) {}
}
@@ -33,7 +33,7 @@ public class UriAttachment extends Attachment {
@Override
public boolean equals(Object other) {
return other != null && other instanceof UriAttachment && ((UriAttachment) other).dataUri.equals(this.dataUri);
return other instanceof UriAttachment && ((UriAttachment) other).dataUri.equals(this.dataUri);
}
@Override
@@ -3,10 +3,11 @@ package org.thoughtcrime.securesms.audio;
import android.content.Context;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import androidx.annotation.NonNull;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.NonNull;
import com.b44t.messenger.util.concurrent.ListenableFuture;
import com.b44t.messenger.util.concurrent.SettableFuture;
@@ -45,7 +46,7 @@ public class AudioRecorder {
throw new AssertionError("We can only record once at a time.");
}
ParcelFileDescriptor fds[] = ParcelFileDescriptor.createPipe();
ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
captureUri = blobProvider.create(context, new ParcelFileDescriptor.AutoCloseInputStream(fds[0]),
MediaUtil.AUDIO_AAC, "voice.aac", null);
@@ -337,7 +337,7 @@ public class AudioSlidePlayer {
}
@Override
public void handleMessage(Message msg) {
public void handleMessage(@NonNull Message msg) {
AudioSlidePlayer player = playerReference.get();
if (player == null || player.mediaPlayer == null || !isPlayerActive(player.mediaPlayer)) {
@@ -1,12 +1,11 @@
package org.thoughtcrime.securesms.components;
import android.animation.Animator;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.loader.app.LoaderManager;
@@ -89,10 +88,11 @@ public class AttachmentTypeSelector extends PopupWindow {
this.webxdcButton.setOnClickListener(new PropagatingClickListener(ADD_WEBXDC));
this.recentRail.setListener(new RecentPhotoSelectedListener());
if (!Prefs.isLocationStreamingEnabled(context)) {
// disable location streaming button for now
//if (!Prefs.isLocationStreamingEnabled(context)) {
this.locationButton.setVisibility(View.GONE);
ViewUtil.findById(layout, R.id.location_button_label).setVisibility(View.GONE);
}
//}
if (!DcHelper.isWebrtcConfigOk(DcHelper.getContext(context))) {
this.videoChatButton.setVisibility(View.GONE);
@@ -188,13 +188,6 @@ public class AttachmentTypeSelector extends PopupWindow {
animator.start();
}
private void animateWindowInTranslate(@NonNull View contentView) {
Animation animation = new TranslateAnimation(0, 0, contentView.getHeight(), 0);
animation.setDuration(ANIMATION_DURATION);
getContentView().startAnimation(animation);
}
private void animateWindowOutCircular(@Nullable View anchor, @NonNull View contentView) {
Pair<Integer, Integer> coordinates = getClickOrigin(anchor, contentView);
Animator animator = ViewAnimationUtils.createCircularReveal(getContentView(),
@@ -206,20 +199,20 @@ public class AttachmentTypeSelector extends PopupWindow {
animator.setDuration(ANIMATION_DURATION);
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
public void onAnimationStart(@NonNull Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
public void onAnimationEnd(@NonNull Animator animation) {
AttachmentTypeSelector.super.dismiss();
}
@Override
public void onAnimationCancel(Animator animation) {
public void onAnimationCancel(@NonNull Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
public void onAnimationRepeat(@NonNull Animator animation) {
}
});
@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.components;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.PorterDuff;
@@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
@@ -11,7 +11,6 @@ import android.util.AttributeSet;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.thoughtcrime.securesms.ProfileActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto;
import org.thoughtcrime.securesms.mms.GlideRequests;
@@ -30,11 +29,6 @@ public class AvatarImageView extends AppCompatImageView {
public AvatarImageView(Context context, AttributeSet attrs) {
super(context, attrs);
setScaleType(ScaleType.CENTER_CROP);
if (attrs != null) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.AvatarImageView, 0, 0);
typedArray.recycle();
}
}
@Override
@@ -52,7 +46,7 @@ public class AvatarImageView extends AppCompatImageView {
.circleCrop()
.into(this);
if(quickContactEnabled) {
setAvatarClickHandler(recipient, quickContactEnabled);
setAvatarClickHandler(recipient);
}
} else {
setImageDrawable(new GeneratedContactPhoto("+").asDrawable(getContext(), ThemeUtil.getDummyContactColor(getContext())));
@@ -64,8 +58,8 @@ public class AvatarImageView extends AppCompatImageView {
glideRequests.clear(this);
}
private void setAvatarClickHandler(final Recipient recipient, boolean quickContactEnabled) {
if (!recipient.isMultiUserRecipient() && quickContactEnabled) {
private void setAvatarClickHandler(final Recipient recipient) {
if (!recipient.isMultiUserRecipient()) {
super.setOnClickListener(v -> {
if(recipient.getAddress().isDcContact()) {
Intent intent = new Intent(getContext(), ProfileActivity.class);
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.components;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.view.Gravity;
@@ -32,10 +33,6 @@ public class AvatarSelector extends PopupWindow {
private final @NonNull LoaderManager loaderManager;
private final @NonNull RecentPhotoViewRail recentRail;
private final @NonNull ImageView imageButton;
private final @NonNull ImageView cameraButton;
private final @NonNull ImageView removeButton;
private final @NonNull ImageView closeButton;
private @Nullable AttachmentClickedListener listener;
@@ -48,25 +45,25 @@ public class AvatarSelector extends PopupWindow {
this.listener = listener;
this.loaderManager = loaderManager;
this.recentRail = ViewUtil.findById(layout, R.id.recent_photos);
this.imageButton = ViewUtil.findById(layout, R.id.gallery_button);
this.cameraButton = ViewUtil.findById(layout, R.id.camera_button);
this.closeButton = ViewUtil.findById(layout, R.id.close_button);
this.removeButton = ViewUtil.findById(layout, R.id.remove_button);
ImageView imageButton = ViewUtil.findById(layout, R.id.gallery_button);
ImageView cameraButton = ViewUtil.findById(layout, R.id.camera_button);
ImageView closeButton = ViewUtil.findById(layout, R.id.close_button);
ImageView removeButton = ViewUtil.findById(layout, R.id.remove_button);
this.imageButton.setOnClickListener(new PropagatingClickListener(ADD_GALLERY));
this.cameraButton.setOnClickListener(new PropagatingClickListener(TAKE_PHOTO));
this.closeButton.setOnClickListener(new CloseClickListener());
this.removeButton.setOnClickListener(new PropagatingClickListener(REMOVE_PHOTO));
imageButton.setOnClickListener(new PropagatingClickListener(ADD_GALLERY));
cameraButton.setOnClickListener(new PropagatingClickListener(TAKE_PHOTO));
closeButton.setOnClickListener(new CloseClickListener());
removeButton.setOnClickListener(new PropagatingClickListener(REMOVE_PHOTO));
this.recentRail.setListener(new RecentPhotoSelectedListener());
if (!includeClear) {
this.removeButton.setVisibility(View.GONE);
removeButton.setVisibility(View.GONE);
ViewUtil.findById(layout, R.id.remove_button_label).setVisibility(View.GONE);
}
setContentView(layout);
setWidth(LinearLayout.LayoutParams.MATCH_PARENT);
setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);
setBackgroundDrawable(new BitmapDrawable());
setBackgroundDrawable(new BitmapDrawable(context.getResources(), (Bitmap) null));
setAnimationStyle(0);
setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
setFocusable(true);
@@ -88,7 +85,7 @@ public class AvatarSelector extends PopupWindow {
getContentView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
getContentView().getViewTreeObserver().removeGlobalOnLayoutListener(this);
getContentView().getViewTreeObserver().removeOnGlobalLayoutListener(this);
animateWindowInTranslate(getContentView());
}
@@ -167,8 +164,8 @@ public class AvatarSelector extends PopupWindow {
}
public interface AttachmentClickedListener {
public void onClick(int type);
public void onQuickAttachment(Uri uri);
void onClick(int type);
void onQuickAttachment(Uri uri);
}
}
@@ -58,10 +58,6 @@ public class AvatarView extends ConstraintLayout {
avatarImage.setOnClickListener(listener);
}
public void setAvatarLongClickListener(OnLongClickListener listener) {
avatarImage.setOnLongClickListener(listener);
}
public void setSeenRecently(boolean enabled) {
seenRecentlyIndicator.setVisibility(enabled? View.VISIBLE : View.GONE);
}
@@ -26,9 +26,9 @@ public class CircleColorImageView extends AppCompatImageView {
int circleColor = Color.WHITE;
if (attrs != null) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircleColorImageView, 0, 0);
circleColor = typedArray.getColor(R.styleable.CircleColorImageView_circleColor, Color.WHITE);
typedArray.recycle();
try (TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircleColorImageView, 0, 0)) {
circleColor = typedArray.getColor(R.styleable.CircleColorImageView_circleColor, Color.WHITE);
}
}
Drawable circle = context.getResources().getDrawable(R.drawable.circle_tintable);
@@ -115,7 +115,7 @@ public class ComposeText extends AppCompatEditText {
}
@Override
public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
public InputConnection onCreateInputConnection(@NonNull EditorInfo editorInfo) {
InputConnection inputConnection = super.onCreateInputConnection(editorInfo);
if(Prefs.isEnterSendsEnabled(getContext())) {
@@ -143,7 +143,6 @@ public class ComposeText extends AppCompatEditText {
}
}
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB_MR2)
private static class CommitContentListener implements InputConnectionCompat.OnCommitContentListener {
private static final String TAG = CommitContentListener.class.getName();
@@ -155,7 +154,7 @@ public class ComposeText extends AppCompatEditText {
}
@Override
public boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags, Bundle opts) {
public boolean onCommitContent(@NonNull InputContentInfoCompat inputContentInfo, int flags, Bundle opts) {
if (BuildCompat.isAtLeastNMR1() && (flags & InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
try {
inputContentInfo.requestPermission();
@@ -45,12 +45,9 @@ public class ContactFilterToolbar extends Toolbar {
searchText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
this.clearToggle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
searchText.setText("");
displayTogglingView(null);
}
this.clearToggle.setOnClickListener(v -> {
searchText.setText("");
displayTogglingView(null);
});
this.searchText.addTextChangedListener(new TextWatcher() {
@@ -115,19 +112,16 @@ public class ContactFilterToolbar extends Toolbar {
private void expandTapArea(final View container, final View child) {
final int padding = getResources().getDimensionPixelSize(R.dimen.contact_selection_actions_tap_area);
container.post(new Runnable() {
@Override
public void run() {
Rect rect = new Rect();
child.getHitRect(rect);
container.post(() -> {
Rect rect = new Rect();
child.getHitRect(rect);
rect.top -= padding;
rect.left -= padding;
rect.right += padding;
rect.bottom += padding;
rect.top -= padding;
rect.left -= padding;
rect.right += padding;
rect.bottom += padding;
container.setTouchDelegate(new TouchDelegate(rect, child));
}
container.setTouchDelegate(new TouchDelegate(rect, child));
});
}
@@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.util.DateUtils;
public class ConversationItemFooter extends LinearLayout {
private TextView dateView;
private TextView editedView;
private ImageView bookmarkIndicatorView;
private ImageView secureIndicatorView;
private ImageView locationIndicatorView;
@@ -45,16 +46,17 @@ public class ConversationItemFooter extends LinearLayout {
inflate(getContext(), R.layout.conversation_item_footer, this);
dateView = findViewById(R.id.footer_date);
editedView = findViewById(R.id.footer_edited);
bookmarkIndicatorView = findViewById(R.id.footer_bookmark_indicator);
secureIndicatorView = findViewById(R.id.footer_secure_indicator);
locationIndicatorView = findViewById(R.id.footer_location_indicator);
deliveryStatusView = new DeliveryStatusView(findViewById(R.id.delivery_indicator));
if (attrs != null) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.ConversationItemFooter, 0, 0);
textColor = typedArray.getInt(R.styleable.ConversationItemFooter_footer_text_color, getResources().getColor(R.color.core_white));
setTextColor(textColor);
typedArray.recycle();
try (TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.ConversationItemFooter, 0, 0)) {
textColor = typedArray.getInt(R.styleable.ConversationItemFooter_footer_text_color, getResources().getColor(R.color.core_white));
setTextColor(textColor);
}
}
}
@@ -62,6 +64,7 @@ public class ConversationItemFooter extends LinearLayout {
presentDate(messageRecord);
boolean bookmark = messageRecord.getOriginalMsgId() != 0 || messageRecord.getSavedMsgId() != 0;
bookmarkIndicatorView.setVisibility(bookmark ? View.VISIBLE : View.GONE);
editedView.setVisibility(messageRecord.isEdited() ? View.VISIBLE : View.GONE);
secureIndicatorView.setVisibility(messageRecord.isSecure() ? View.VISIBLE : View.GONE);
locationIndicatorView.setVisibility(messageRecord.hasLocation() ? View.VISIBLE : View.GONE);
presentDeliveryStatus(messageRecord);
@@ -69,6 +72,7 @@ public class ConversationItemFooter extends LinearLayout {
private void setTextColor(int color) {
dateView.setTextColor(color);
editedView.setTextColor(color);
bookmarkIndicatorView.setColorFilter(color);
secureIndicatorView.setColorFilter(color);
locationIndicatorView.setColorFilter(color);
@@ -30,8 +30,6 @@ import java.util.concurrent.ExecutionException;
public class ConversationItemThumbnail extends FrameLayout {
private static final String TAG = ConversationItemThumbnail.class.getSimpleName();
private static final Paint LIGHT_THEME_OUTLINE_PAINT = new Paint();
private static final Paint DARK_THEME_OUTLINE_PAINT = new Paint();
public static final double IMAGE_ASPECT_RATIO = 1.0;
@@ -62,20 +60,20 @@ public class ConversationItemThumbnail extends FrameLayout {
public ConversationItemThumbnail(Context context) {
super(context);
init(null);
init();
}
public ConversationItemThumbnail(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
init();
}
public ConversationItemThumbnail(final Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
init();
}
private void init(@Nullable AttributeSet attrs) {
private void init() {
inflate(getContext(), R.layout.conversation_item_thumbnail, this);
this.thumbnail = findViewById(R.id.conversation_thumbnail_image);
@@ -119,7 +117,7 @@ public class ConversationItemThumbnail extends FrameLayout {
@SuppressWarnings("SuspiciousNameCombination")
@Override
protected void dispatchDraw(Canvas canvas) {
protected void dispatchDraw(@NonNull Canvas canvas) {
super.dispatchDraw(canvas);
@@ -39,14 +39,12 @@ public class CustomDefaultPreference extends DialogPreference {
super(context, attrs);
int[] attributeNames = new int[]{android.R.attr.inputType, R.attr.custom_pref_toggle};
TypedArray attributes = context.obtainStyledAttributes(attrs, attributeNames);
this.inputType = attributes.getInt(0, 0);
this.customPreference = getKey();
this.customToggle = attributes.getString(1);
this.validator = new CustomDefaultPreferenceDialogFragmentCompat.NullValidator();
attributes.recycle();
try (TypedArray attributes = context.obtainStyledAttributes(attrs, attributeNames)) {
this.inputType = attributes.getInt(0, 0);
this.customPreference = getKey();
this.customToggle = attributes.getString(1);
this.validator = new CustomDefaultPreferenceDialogFragmentCompat.NullValidator();
}
setPersistent(false);
setDialogLayoutResource(R.layout.custom_default_preference_dialog);
@@ -130,6 +128,7 @@ public class CustomDefaultPreference extends DialogPreference {
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle instanceState) {
Dialog dialog = super.onCreateDialog(instanceState);
@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import androidx.annotation.AttrRes;
@@ -19,9 +18,6 @@ import org.thoughtcrime.securesms.util.guava.Optional;
public class DocumentView extends FrameLayout {
private static final String TAG = DocumentView.class.getSimpleName();
private final @NonNull View container;
private final @NonNull TextView fileName;
private final @NonNull TextView fileSize;
@@ -39,7 +35,6 @@ public class DocumentView extends FrameLayout {
super(context, attrs, defStyleAttr);
inflate(context, R.layout.document_view, this);
this.container = findViewById(R.id.document_container);
this.fileName = findViewById(R.id.file_name);
this.fileSize = findViewById(R.id.file_size);
}
@@ -4,7 +4,7 @@ import android.content.Context;
import android.graphics.Typeface;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.core.view.ViewCompat;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
@@ -14,6 +14,7 @@ import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.util.AttributeSet;
import android.view.View;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -22,8 +23,6 @@ import org.thoughtcrime.securesms.util.spans.CenterAlignedRelativeSizeSpan;
public class FromTextView extends AppCompatTextView {
private static final String TAG = FromTextView.class.getSimpleName();
public FromTextView(Context context) {
super(context);
}
@@ -59,7 +58,7 @@ public class FromTextView extends AppCompatTextView {
profileName.setSpan(new TypefaceSpan("sans-serif-light"), 0, profileName.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
profileName.setSpan(new ForegroundColorSpan(ResUtil.getColor(getContext(), R.attr.conversation_list_item_subject_color)), 0, profileName.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL){
if (this.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
builder.append(profileName);
builder.append(fromSpan);
} else {
@@ -1,8 +1,7 @@
package org.thoughtcrime.securesms.components;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import android.util.AttributeSet;
import android.view.animation.AlphaAnimation;
@@ -21,7 +20,6 @@ public class HidingLinearLayout extends LinearLayout {
super(context, attrs);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public HidingLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@@ -1,109 +0,0 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
import org.thoughtcrime.securesms.R;
public class ImageDivet extends ImageView {
private static final float CORNER_OFFSET = 12F;
private static final String[] POSITIONS = new String[] {"bottom_right"};
private Drawable drawable;
private int drawableIntrinsicWidth;
private int drawableIntrinsicHeight;
private int position;
private float density;
public ImageDivet(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize(attrs);
}
public ImageDivet(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(attrs);
}
public ImageDivet(Context context) {
super(context);
initialize(null);
}
private void initialize(AttributeSet attrs) {
if (attrs != null) {
position = attrs.getAttributeListValue(null, "position", POSITIONS, -1);
}
density = getContext().getResources().getDisplayMetrics().density;
setDrawable();
}
private void setDrawable() {
int attributes[] = new int[] {R.attr.lower_right_divet};
TypedArray drawables = getContext().obtainStyledAttributes(attributes);
switch (position) {
case 0:
drawable = drawables.getDrawable(0);
break;
}
drawableIntrinsicWidth = drawable.getIntrinsicWidth();
drawableIntrinsicHeight = drawable.getIntrinsicHeight();
drawables.recycle();
}
@Override
public void onDraw(Canvas c) {
super.onDraw(c);
c.save();
computeBounds(c);
drawable.draw(c);
c.restore();
}
public void setPosition(int position) {
this.position = position;
setDrawable();
invalidate();
}
public int getPosition() {
return position;
}
public float getCloseOffset() {
return CORNER_OFFSET * density;
}
public ImageView asImageView() {
return this;
}
public float getFarOffset() {
return getCloseOffset() + drawableIntrinsicHeight;
}
private void computeBounds(Canvas c) {
final int right = getWidth();
final int bottom = getHeight();
switch (position) {
case 0:
drawable.setBounds(
right - drawableIntrinsicWidth,
bottom - drawableIntrinsicHeight,
right,
bottom);
break;
}
}
}
@@ -120,9 +120,10 @@ public class InputPanel extends ConstraintLayout
long id,
@NonNull Recipient author,
@NonNull CharSequence body,
@NonNull SlideDeck attachments)
@NonNull SlideDeck attachments,
@NonNull boolean isEdit)
{
this.quoteView.setQuote(glideRequests, msg, author, body, attachments, false);
this.quoteView.setQuote(glideRequests, msg, author, body, attachments, false, isEdit);
int originalHeight = this.quoteView.getVisibility() == VISIBLE ? this.quoteView.getMeasuredHeight()
: 0;
@@ -141,6 +142,7 @@ public class InputPanel extends ConstraintLayout
public void clearQuoteWithoutAnimation() {
quoteView.dismiss();
if (listener != null) listener.onQuoteDismissed();
}
public void clearQuote() {
@@ -150,8 +152,9 @@ public class InputPanel extends ConstraintLayout
quoteAnimator = createHeightAnimator(quoteView, quoteView.getMeasuredHeight(), 0, new AnimationCompleteListener() {
@Override
public void onAnimationEnd(Animator animation) {
public void onAnimationEnd(@NonNull Animator animation) {
quoteView.dismiss();
if (listener != null) listener.onQuoteDismissed();
}
});
@@ -183,7 +186,7 @@ public class InputPanel extends ConstraintLayout
if (quoteView.getVisibility() == View.VISIBLE && quoteView.getBody() != null) {
return Optional.of(new QuoteModel(
quoteView.getDcContact(), quoteView.getBody().toString(),
false, quoteView.getAttachments(), quoteView.getOriginalMsg()
quoteView.getAttachments(), quoteView.getOriginalMsg()
));
} else {
return Optional.absent();
@@ -329,6 +332,7 @@ public class InputPanel extends ConstraintLayout
void onRecorderCanceled();
void onRecorderPermissionRequired();
void onEmojiToggle();
void onQuoteDismissed();
}
private static class SlideToCancel {
@@ -19,7 +19,6 @@ package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.util.Log;
import android.util.DisplayMetrics;
@@ -27,8 +26,8 @@ import android.view.Surface;
import android.view.View;
import android.view.WindowInsets;
import androidx.annotation.RequiresApi;
import androidx.appcompat.widget.LinearLayoutCompat;
import androidx.preference.PreferenceManager;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.ServiceUtil;
@@ -58,7 +57,6 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
private final int defaultCustomKeyboardSize;
private final int minCustomKeyboardTopMarginPortrait;
private final int minCustomKeyboardTopMarginLandscape;
private final int minCustomKeyboardTopMarginLandscapeBubble;
private final int statusBarHeight;
private int viewInset;
@@ -82,7 +80,6 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
defaultCustomKeyboardSize = getResources().getDimensionPixelSize(R.dimen.default_custom_keyboard_size);
minCustomKeyboardTopMarginPortrait = getResources().getDimensionPixelSize(R.dimen.min_custom_keyboard_top_margin_portrait);
minCustomKeyboardTopMarginLandscape = getResources().getDimensionPixelSize(R.dimen.min_custom_keyboard_top_margin_portrait);
minCustomKeyboardTopMarginLandscapeBubble = getResources().getDimensionPixelSize(R.dimen.min_custom_keyboard_top_margin_landscape_bubble);
statusBarHeight = ViewUtil.getStatusBarHeight(this);
viewInset = getViewInset();
}

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