Compare commits

..

23 Commits

Author SHA1 Message Date
adbenitez 6f5e90be53 update build.gradle 2024-12-21 03:11:47 +01:00
adbenitez 34fa0620ab update full_description 2024-12-21 03:04:53 +01:00
adbenitez fe37af1a3f allow to jump to POI 2024-12-21 02:54:54 +01:00
adbenitez 3459802d67 update core 2024-12-20 19:52:40 +01:00
adbenitez b8bf847941 fix contact selection list 2024-12-20 19:51:59 +01:00
adbenitez 5b6024584a force sending sticker 2024-12-20 17:47:45 +01:00
adbenitez 101a31628d lowercase Bot string for English and Spanish 2024-12-18 19:34:29 +01:00
adbenitez 2a2ce883b4 Merge remote-tracking branch 'upstream/main' 2024-12-18 19:33:04 +01:00
adbenitez 5a0e26bdb5 show last-seen status in contact list 2024-12-18 19:20:34 +01:00
B. Petersen 0c9277dfd0 remove email-address unconditionally from title
the email-address was removed for guaranteed-e2ee chats quite a while ago
(https://github.com/deltachat/deltachat-android/pull/2916) reason was, among others,
that these addresses are often chatmail and therefore random.
(despite expecting otherwise, that was fine for most users).

this PR removes the email-adress unconditionally:

- having the email-address sometimes shown and sometimes not is confusing,
  and easily looks like a bug.
  this has become worse with the added vcard-support
  (before, there were rare non-guaranteed chats in chatmail) -
  resulting in more random addresses being shown

- _always_ protect against over-the-shoulder attacks

- better privacy in screenshots sent around without thinking much before
  (cmp. https://github.com/deltachat/deltachat-ios/pull/2329)

- wrt impersonation attacks:
  the pure email address in the subtitle did never protect against impersonation,
  one could always get sth. trustworthy looking there,
  it is better to check the profile with additional information (eg. other chats) if in doubt

- general cleaner, uncluttered layout

- pave the way of the upcoming multi-addresses

drawback is that sometimes one more tap is needed to access the email-address -
however, as it is _always_ one tap away now,
this can also go easily to the finger memory.
2024-12-18 18:31:43 +01:00
adb 37886cf296 Merge pull request #3506 from deltachat/adb/issue-3502
run SendRelayedMessageUtil.sendMultipleMsgs() in background
2024-12-18 17:19:34 +01:00
adbenitez df5ba2eced call SendRelayedMessageUtil.sendMultipleMsgs() in background 2024-12-18 17:17:46 +01:00
adbenitez 3c2ddb96b3 update full_description 2024-12-18 16:04:30 +01:00
Hocuri e2a825dfb2 fix(ANR): Mark messages as seen asynchronously (#3505)
Mark messages as seen in a background thread instead of on the UI
thread in order to fix #3504.
2024-12-18 15:41:44 +01:00
Hocuri 8d49c7e595 fix: Fetch synchronously in the background if no foreground service is allowed (#3501)
Fix #3500.
2024-12-18 13:19:47 +01:00
B. Petersen 966d5dac24 do not lowercase string 'Bot'
it is regarded as a bug
and esp. weird in translations.

it was regarded to be a bit 'nerdy' that time to write it lowercase,
but it seems we have to get more serious now.
2024-12-17 17:43:21 +01:00
adb 7f684f4d2e Merge pull request #3499 from deltachat/adb/issue-3498
avoid IllegalArgumentException dismissing dialog
2024-12-17 17:07:03 +01:00
adb 1510f7f3a8 Merge pull request #3497 from deltachat/adb/issue-3496
avoid NPE in ConversationListActivity.onNewIntent()
2024-12-17 16:32:45 +01:00
adbenitez 6916becf7f avoid IllegalArgumentException dismissing dialog 2024-12-17 16:13:37 +01:00
adbenitez ad5b496f5c avoid NPE in ConversationListActivity.onNewIntent() 2024-12-17 15:00:25 +01:00
adb e647401db4 Merge pull request #3491 from deltachat/adb/issue-3490
improve emoji pickers
2024-12-17 14:47:43 +01:00
adbenitez 3113f9c3ab optimize onConfigurationChanged() 2024-12-16 17:19:34 +01:00
adbenitez a1e47865c5 improve emoji pickers 2024-12-16 15:27:13 +01:00
20 changed files with 162 additions and 29 deletions
+2 -2
View File
@@ -33,8 +33,8 @@ android {
useLibrary 'org.apache.http.legacy'
defaultConfig {
versionCode 30000700
versionName "1.50.4"
versionCode 30000701
versionName "1.50.5"
applicationId "chat.delta.lite"
multiDexEnabled true
@@ -17,19 +17,21 @@ ArcaneChat is a Delta Chat client and was created with a focus on usability, goo
<b>Main differences with official Delta Chat client:</b>
<ul>
<li>Basic markdown support (bold, italic, strike, etc.)</li>
<li>Support for some markdown styles in text messages (bold, italic, strike, etc.)</li>
<li>Support for displaying Telegram's animated stickers (.tgs files)</li>
<li>Support for SVG images previews</li>
<li>Multiple color themes/skins</li>
<li>It is possible to disable accounts to completely disconnect them to save data/bandwidth</li>
<li>It is possible to disable profiles to completely disconnect them saving data/bandwidth</li>
<li>You can easily see the connection status of all your profiles in the profile switcher</li>
<li>A videochat instance is set by default</li>
<li>Replies to your messages in muted groups trigger notifications</li>
<li>Location streaming feature enabled by default and extra option to share location for 12 hours</li>
<li>Clicking on a message with a POI location, will open the POI on the map</li>
<li>Last-seen status of contacts is shown in your contact list, like in WhatsApp, Telegram, etc.</li>
<li>Videos are played in loop, useful for short GIF videos</li>
<li>Verified icon is shown in the chat list for the "Device Messages" chat</li>
<li>Voice messages have more aggressive compression in "worse quality" mode to save data plan</li>
<li>Automatic download of messages limited to 640KB by default</li>
<li>Account's display name is always shown in the app's title bar instead of the name of the app</li>
<li>Profile's display name is always shown in the app's title bar instead of the name of the app</li>
<li>Your avatar is visible to other users in Mailing Lists</li>
<li>Can be selected as app to open .xdc files</li>
<li>For mini-apps developers: there are some extra features in the WebXDC API, check https://github.com/ArcaneChat/android/#webxdc</li>
+15
View File
@@ -1658,6 +1658,21 @@ JNIEXPORT void Java_com_b44t_messenger_DcMsg_setFile(JNIEnv *env, jobject obj, j
}
JNIEXPORT void Java_com_b44t_messenger_DcMsg_forceSticker(JNIEnv *env, jobject obj)
{
dc_msg_force_sticker(get_dc_msg(env, obj));
}
JNIEXPORT jstring Java_com_b44t_messenger_DcMsg_getPOILocation(JNIEnv *env, jobject obj)
{
char* temp = dc_msg_get_poi_location(get_dc_msg(env, obj));
jstring ret = JSTRING_NEW(temp);
dc_str_unref(temp);
return ret;
}
JNIEXPORT void Java_com_b44t_messenger_DcMsg_setDimension(JNIEnv *env, jobject obj, int width, int height)
{
dc_msg_set_dimension(get_dc_msg(env, obj), width, height);
Binary file not shown.
@@ -160,9 +160,11 @@ public class DcMsg {
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 setDimension (int width, int height);
public native void setDuration (int duration);
public native void setLocation (float latitude, float longitude);
public native String getPOILocation ();
public void setQuote (DcMsg quote) { setQuoteCPtr(quote.msgCPtr); }
public native String getQuotedText ();
public native String getError ();
@@ -45,6 +45,7 @@ import android.text.TextWatcher;
import android.util.Log;
import android.util.Pair;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -53,6 +54,7 @@ import android.view.View.OnFocusChangeListener;
import android.view.View.OnKeyListener;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
@@ -179,7 +181,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private AttachmentTypeSelector attachmentTypeSelector;
private AttachmentManager attachmentManager;
private AudioRecorder audioRecorder;
private Stub<MediaKeyboard> emojiDrawerStub;
private FrameLayout emojiPickerContainer;
private MediaKeyboard emojiPicker;
protected HidingLinearLayout quickAttachmentToggle;
private InputPanel inputPanel;
@@ -319,10 +322,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
super.onConfigurationChanged(newConfig);
composeText.setTransport(sendButton.getSelectedTransport());
if (emojiDrawerStub.resolved() && container.getCurrentInput() == emojiDrawerStub.get()) {
if (emojiPicker != null && container.getCurrentInput() == emojiPicker) {
container.hideAttachedInput(true);
}
emojiPicker = null; // force reloading next time onEmojiToggle() is called
initializeBackground();
}
@@ -360,7 +364,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
for (int i = 0; i < uriCount; i++) {
uriList.add(multipleUris.getItemAt(i).getUri());
}
askSendingFiles(uriList, () -> SendRelayedMessageUtil.sendMultipleMsgs(this, chatId, uriList, null));
askSendingFiles(uriList, () -> {
Util.runOnAnyBackgroundThread(() -> {
SendRelayedMessageUtil.sendMultipleMsgs(this, chatId, uriList, null);
});
});
}
}
}
@@ -806,7 +814,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
sendButton = ViewUtil.findById(this, R.id.send_button);
attachButton = ViewUtil.findById(this, R.id.attach_button);
composeText = ViewUtil.findById(this, R.id.embedded_text_editor);
emojiDrawerStub = ViewUtil.findStubById(this, R.id.emoji_drawer_stub);
emojiPickerContainer = ViewUtil.findById(this, R.id.emoji_picker_container);
composePanel = ViewUtil.findById(this, R.id.bottom_panel);
container = ViewUtil.findById(this, R.id.layout_container);
quickAttachmentToggle = ViewUtil.findById(this, R.id.quick_attachment_toggle);
@@ -1081,7 +1089,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
);
});
doSend = VideoRecoder.prepareVideo(ConversationActivity.this, dcChat.getId(), msg);
Util.runOnMain(() -> progressDialog.dismiss());
Util.runOnMain(() -> {
try {
progressDialog.dismiss();
} catch (final IllegalArgumentException e) {
// The activity is finishing/destroyed, do nothing.
}
});
}
if (doSend) {
@@ -1237,16 +1251,23 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
});
}
private void reloadEmojiPicker() {
emojiPickerContainer.removeAllViews();
emojiPicker = (MediaKeyboard) LayoutInflater.from(this).inflate(R.layout.conversation_activity_emojidrawer_stub, emojiPickerContainer, false);
emojiPickerContainer.addView(emojiPicker);
inputPanel.setMediaKeyboard(emojiPicker);
}
@Override
public void onEmojiToggle() {
if (!emojiDrawerStub.resolved()) {
inputPanel.setMediaKeyboard(emojiDrawerStub.get());
if (emojiPicker == null) {
reloadEmojiPicker();
}
if (container.getCurrentInput() == emojiDrawerStub.get()) {
if (container.getCurrentInput() == emojiPicker) {
container.showSoftkey(composeText);
} else {
container.show(composeText, emojiDrawerStub.get());
container.show(composeText, emojiPicker);
}
}
@@ -1275,6 +1296,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
msg.setQuote(quote.get().getQuotedMsg());
}
msg.setFile(path, null);
msg.forceSticker();
dcContext.sendMsg(chatId, msg);
}
@@ -26,6 +26,7 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
@@ -210,7 +211,7 @@ public class ConversationFragment extends MessageSelectorFragment
public void onResume() {
super.onResume();
dcContext.marknoticedChat((int) chatId);
Util.runOnBackground(() -> dcContext.marknoticedChat((int) chatId));
if (list.getAdapter() != null) {
list.getAdapter().notifyDataSetChanged();
}
@@ -811,6 +812,9 @@ public class ConversationFragment extends MessageSelectorFragment
else if(messageRecord.getInfoType() == DcMsg.DC_INFO_WEBXDC_INFO_MESSAGE) {
WebxdcActivity.openWebxdcActivity(getContext(), messageRecord.getParent(), messageRecord.getWebxdcHref());
}
else if (!TextUtils.isEmpty(messageRecord.getPOILocation()) && messageRecord.getType() == DcMsg.DC_MSG_TEXT && !messageRecord.hasHtml()) {
WebxdcActivity.openMaps(getContext(), getListAdapter().getChat().getId(), "index.html#"+messageRecord.getPOILocation());
}
else {
String self_mail = dcContext.getConfig("configured_mail_user");
if (self_mail != null && !self_mail.isEmpty()
@@ -257,6 +257,10 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
@Override
protected void onNewIntent(Intent intent) {
if (isFinishing()) {
Log.w(TAG, "Activity is finishing, aborting onNewIntent()");
return;
}
super.onNewIntent(intent);
setIntent(intent);
refresh();
@@ -82,8 +82,16 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
private boolean hideActionBar = false;
public static void openMaps(Context context, int chatId) {
openMaps(context, chatId, "");
}
public static void openMaps(Context context, int chatId, String href) {
DcContext dcContext = DcHelper.getContext(context);
int msgId = dcContext.initWebxdcIntegration(chatId);
int msgId = 0;
final int mapsVersion = 2;
if (dcContext.getConfigInt("ui.maps_version") >= mapsVersion) {
msgId = dcContext.initWebxdcIntegration(chatId);
}
if (msgId == 0) {
try {
InputStream inputStream = context.getResources().getAssets().open("webxdc/maps.xdc");
@@ -98,8 +106,9 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
Toast.makeText(context, "Cannot get maps.xdc, see log for details.", Toast.LENGTH_LONG).show();
return;
}
dcContext.setConfigInt("ui.maps_version", mapsVersion);
}
openWebxdcActivity(context, msgId, true, "");
openWebxdcActivity(context, msgId, true, href);
}
public static void openWebxdcActivity(Context context, DcMsg instance) {
@@ -16,6 +16,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.AvatarView;
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.ThemeUtil;
@@ -109,6 +110,19 @@ public class ContactSelectionListItem extends LinearLayout implements RecipientM
this.nameView.setEnabled(true);
this.nameView.setText(name==null? "#" : name);
if (contact != null) {
if (contact.isBot()) {
number = getContext().getString(R.string.bot);
} else if (contact.wasSeenRecently()) {
number = getContext().getString(R.string.online);
} else {
long timestamp = contact.getLastSeen();
if (timestamp != 0) {
number = getContext().getString(R.string.last_seen_at, DateUtils.getExtendedTimeSpanString(getContext(), timestamp));
}
}
}
if(number!=null) {
this.numberView.setText(number == null ? "" : number);
this.labelView.setText(label==null? "" : label);
@@ -21,6 +21,8 @@ import org.thoughtcrime.securesms.util.Util;
public final class FetchForegroundService extends Service {
private static final String TAG = FcmReceiveService.class.getSimpleName();
private static final Object SERVICE_LOCK = new Object();
private static final Object STOP_NOTIFIER = new Object();
private static volatile boolean fetchingSynchronously = false;
private static Intent service;
public static void start(Context context) {
@@ -30,15 +32,40 @@ public final class FetchForegroundService extends Service {
}
GenericForegroundService.createFgNotificationChannel(context);
synchronized (SERVICE_LOCK) {
if (service == null) {
service = new Intent(context, FetchForegroundService.class);
ContextCompat.startForegroundService(context, service);
try {
synchronized (SERVICE_LOCK) {
if (service == null) {
service = new Intent(context, FetchForegroundService.class);
ContextCompat.startForegroundService(context, service);
}
}
} catch (Exception e) {
Log.w(TAG, "Failed to start foreground service: " + e + ", fetching in background.");
// According to the documentation https://firebase.google.com/docs/cloud-messaging/android/receive,
// we need to handle the message within 20s, and the time window may be even shorter than 20s,
// so, use 10s to be safe.
fetchingSynchronously = true;
if (ApplicationContext.dcAccounts.backgroundFetch(10)) {
// The background fetch was successful, but we need to wait until all events were processed.
// After all events were processed, we will get DC_EVENT_ACCOUNTS_BACKGROUND_FETCH_DONE,
// and stop() will be called.
while (fetchingSynchronously) {
try {
// The `wait()` needs to be enclosed in a while loop because there may be
// "spurious wake-ups", i.e. `wait()` may return even though `notifyAll()` wasn't called.
STOP_NOTIFIER.wait();
} catch (InterruptedException ex) { }
}
}
}
}
public static void stop(Context context) {
if (fetchingSynchronously) {
fetchingSynchronously = false;
STOP_NOTIFIER.notifyAll();
}
synchronized (SERVICE_LOCK) {
if (service != null) {
context.stopService(service);
@@ -110,6 +110,7 @@ public class SendRelayedMessageUtil {
message = new DcMsg(dcContext, DcMsg.DC_MSG_TEXT);
} else if ("sticker".equals(type)) {
message = new DcMsg(dcContext, DcMsg.DC_MSG_STICKER);
message.forceSticker();
} else if ("image".equals(type) || MediaUtil.isImageType(mimeType)) {
message = new DcMsg(dcContext, DcMsg.DC_MSG_IMAGE);
} else if ("audio".equals(type) || MediaUtil.isAudioType(mimeType)) {
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.components.emoji.MediaKeyboard
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/emoji_drawer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<androidx.emoji2.emojipicker.EmojiPickerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/emoji_picker"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?emoji_drawer_background"
app:emojiGridColumns="12"/>
</org.thoughtcrime.securesms.components.emoji.MediaKeyboard>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.emoji2.emojipicker.EmojiPickerView
android:id="@+id/emoji_picker"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:emojiGridColumns="10"/>
</RelativeLayout>
@@ -51,12 +51,11 @@
<include layout="@layout/conversation_input_panel"/>
<ViewStub
android:id="@+id/emoji_drawer_stub"
<FrameLayout
android:id="@+id/emoji_picker_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="@+id/emoji_drawer"
android:layout="@layout/conversation_activity_emojidrawer_stub" />
/>
</LinearLayout>
</org.thoughtcrime.securesms.components.InputAwareLayout>
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- WARNING: on modifications also update the layout-land folder. -->
<org.thoughtcrime.securesms.components.emoji.MediaKeyboard
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/emoji_drawer"
+1
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- WARNING: on modifications also update the layout-land folder. -->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
+1 -1
View File
@@ -182,7 +182,7 @@
<string name="video">Video</string>
<string name="documents">Documentos</string>
<string name="contact">Contacto</string>
<string name="bot">Bot</string>
<string name="bot">bot</string>
<string name="camera">Cámara</string>
<!-- As in "start a video recording" or "take a photo"; eg. the description of the "shutter button" in cameras -->
<string name="capture">Capturar</string>
+1 -1
View File
@@ -174,7 +174,7 @@
<string name="video">Video</string>
<string name="documents">Documents</string>
<string name="contact">Contact</string>
<string name="bot">Bot</string>
<string name="bot">bot</string>
<string name="camera">Camera</string>
<!-- As in "start a video recording" or "take a photo"; eg. the description of the "shutter button" in cameras -->
<string name="capture">Capture</string>