Compare commits

...

46 Commits

Author SHA1 Message Date
adbenitez 923e2a43a4 fix WebxdcActivity: restore addMultiAccountObserver
accidentally reverted to addObserver while resolving merge conflict
2026-06-30 04:48:27 +02:00
adbenitez f29f94f138 Merge remote-tracking branch 'upstream/main' 2026-06-29 20:26:34 +02:00
adbenitez 3a35374a03 allow webxdc in "unselected account" mode 2026-06-29 18:30:23 +02:00
adb fe0793481c Merge pull request #4497 from deltachat/adb/handle-chat-and-webxdc-deletion
when a webxdc is deleted close WebxdcActivity
2026-06-29 18:20:30 +02:00
adbenitez 3c2a672799 Merge branch 'adb/handle-chat-and-webxdc-deletion' of https://github.com/deltachat/deltachat-android into adb/handle-chat-and-webxdc-deletion 2026-06-29 17:33:26 +02:00
adbenitez 2476d17394 be backward compatible with existing shortcuts 2026-06-29 17:30:11 +02:00
adb 5ae021bde3 Merge branch 'main' into adb/handle-chat-and-webxdc-deletion 2026-06-29 16:47:02 +02:00
adb 9f46c242fc Merge pull request #4504 from deltachat/adb/issue-4501
cancel search mode when back is pressed
2026-06-29 16:45:01 +02:00
adbenitez cf36254c1a add margin top to subject field 2026-06-29 00:07:10 +02:00
adb 0d94bf2006 Merge branch 'main' into adb/handle-chat-and-webxdc-deletion 2026-06-28 23:25:46 +02:00
adbenitez 4757150aae apply spotless 2026-06-28 23:21:23 +02:00
adbenitez 0d76ab930f update changelog 2026-06-28 23:16:46 +02:00
adbenitez 5515e2fe3c cancel search mode when back is pressed 2026-06-28 23:14:55 +02:00
adb 508f320b4c Merge pull request #165 from ArcaneChat/make-input-panel-round
enhance the InputPanel: make it pill-like with rounder corners
2026-06-28 19:35:54 +02:00
adbenitez 176e6c94b2 apply spotless 2026-06-28 19:34:25 +02:00
adbenitez deb650970c enhance the InputPanel: make it pill-like with rounder corners 2026-06-28 19:30:10 +02:00
adbenitez 36fc2261f9 improve welcome screen 2026-06-27 02:47:08 +02:00
B. Petersen f11c6baaf6 shorten shortcut for 'Mark as Read'
all of WhatsApp/Signal/Telegram use 'Read' (past tense) for the shortcut of 'Mark as Read',
we also give that as an example in the translators hint.

this works, as there is enough context,
eg. the chat is long-tapped or swiped.

beside consistency, more reasons to follow that path:

- LiquidGlass has larger buttons, it looks better to stay short here

- while 'Mark Read' is short enough, it results in translations being to long -
  the shorter source, differing more from 'Mark as Read',
  should encourage translators to stay shorter

- surely UI should and do work also with longer texts, but it looks better to be short and on point

the string is mainly used by iOS, for android it is used in the notification,
where we switch to the longer form, that is very similar to what we had before
(but i would be fine with both)
2026-06-26 22:11:45 +02:00
adbenitez 6add250258 Merge remote-tracking branch 'upstream/main' 2026-06-26 20:29:09 +02:00
adbenitez d15efbdb44 update changelog 2026-06-26 19:05:21 +02:00
adbenitez 2422f9110c when a webxdc is deleted close WebxdcActivity
also if a chat is deleted, close webxdc, and chat instead of showing
weird "ghost chat"
2026-06-26 18:47:12 +02:00
wchen342 b6162c2044 Fix video capture to query camera formats and enforce 16:9 aspect ratio for video calls (#4496) 2026-06-26 17:34:28 +02:00
adb 75245cfa02 Merge pull request #4495 from deltachat/adb/issue-4494
properly handle messages with ~overrideSenderName
2026-06-24 15:45:49 +02:00
adb 0d19f058fe Merge branch 'main' into adb/issue-4494 2026-06-24 14:59:33 +02:00
adb 911e695187 Merge pull request #4493 from deltachat/adb/issue-4492
properly hide all input&drafting elements on in-chat search
2026-06-24 14:59:03 +02:00
adbenitez b8c8969162 fix typo in variable name 2026-06-24 14:51:47 +02:00
adbenitez 0610520064 also synchronize the changes in initializeContactRequest() 2026-06-23 22:57:14 +02:00
adbenitez 5ae60af73f avoid redundant code block 2026-06-23 22:51:28 +02:00
adbenitez dd6f1015dc Merge branch 'adb/issue-4492' of https://github.com/deltachat/deltachat-android into adb/issue-4492 2026-06-23 22:45:19 +02:00
adbenitez 74ea10ebef properly avoid revealing hidden views while in search mode 2026-06-23 22:44:29 +02:00
adbenitez 877f991131 improve code and fix notifyWebxdc and notifyReaction 2026-06-23 21:33:30 +02:00
adb e556878b2f Merge branch 'main' into adb/issue-4494 2026-06-23 20:21:32 +02:00
adb 99a1fee994 Merge branch 'main' into adb/issue-4492 2026-06-23 20:21:12 +02:00
adb dfd323a89f Merge pull request #4491 from deltachat/adb/allow-select-multiple-files
allow to select multiple files
2026-06-23 20:18:45 +02:00
adbenitez 5a086ff022 avoid input_area wrapper 2026-06-23 19:21:15 +02:00
adbenitez bedaa2c287 tweak comment 2026-06-23 18:43:22 +02:00
adbenitez 50037d5232 fix code format 2026-06-23 18:42:05 +02:00
adbenitez 6756b04da9 properly handle messages with ~overrideSenderName 2026-06-23 18:30:21 +02:00
adbenitez b12ecf66c5 tweak CHANGELOG 2026-06-23 16:21:24 +02:00
adb d05403db02 Merge branch 'adb/allow-select-multiple-files' into adb/issue-4492 2026-06-23 16:20:36 +02:00
adb 2d8344a61f Merge branch 'main' into adb/allow-select-multiple-files 2026-06-23 16:19:47 +02:00
wchen342 44afda55e5 Add notification for missed calls (#4485) 2026-06-23 16:12:16 +02:00
adbenitez bfa6eef604 update CHANGELOG 2026-06-22 20:28:26 +02:00
adbenitez 6327b30350 properly hide all input&drafting elements on in-chat search 2026-06-22 20:02:16 +02:00
adbenitez f92cac4d55 update CHANGELOG 2026-06-22 18:57:49 +02:00
adbenitez 0204fca27d allow to select multiple files 2026-06-22 18:37:30 +02:00
27 changed files with 593 additions and 168 deletions
+9
View File
@@ -1,5 +1,14 @@
# Delta Chat Android Changelog
## Unreleased
* Allow to select multiple files for sending
* Add notifications for missed calls
* Video call preview now accurately shows what is sent to remote
* Fix: properly hide draft attachment during in-chat search
* Fix: close mini-apps and chats if they are deleted
* Fix: cancel in-chat search when back is pressed, instead of directly returning to chatlist
## v2.53.0
2026-06
@@ -168,6 +168,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private static final int RECORD_VIDEO = 8;
private static final int PICK_WEBXDC = 9;
private static final Object searchLock = new Object();
private GlideRequests glideRequests;
protected ComposeText composeText;
private AnimatingToggle buttonToggle;
@@ -176,7 +178,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
protected ConversationTitleView titleView;
private ConversationFragment fragment;
private InputAwareLayout container;
private View composePanel;
private ScaleStableImageView backgroundView;
private MessageRequestsBottomView messageRequestBottomView;
private ProgressDialog progressDialog;
@@ -263,6 +264,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
public void handleOnBackPressed() {
if (container.isInputOpen()) {
container.hideCurrentInput(composeText);
} else if (searchMenu != null) {
searchCollapse();
} else {
handleReturnToConversationList();
}
@@ -279,6 +282,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
eventCenter.removeObservers(this);
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_CHAT_MODIFIED, this);
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_CHAT_DELETED, this);
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED, this);
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_CONTACTS_CHANGED, this);
@@ -443,32 +447,19 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
else mediaType = MediaType.IMAGE;
setMedia(singleUri, mediaType);
} else {
final ClipData multipleUris = data.getClipData();
if (multipleUris != null) {
final int uriCount = multipleUris.getItemCount();
if (uriCount > 0) {
ArrayList<Uri> uriList = new ArrayList<>(uriCount);
for (int i = 0; i < uriCount; i++) {
uriList.add(multipleUris.getItemAt(i).getUri());
}
askSendingFiles(
uriList,
() -> {
Util.runOnAnyBackgroundThread(
() -> {
SendRelayedMessageUtil.sendMultipleMsgs(this, chatId, uriList, null);
});
});
}
}
sendMultipleMsgs(data);
}
break;
case PICK_DOCUMENT:
final String docMimeType = MediaUtil.getMimeType(this, data.getData());
final MediaType docMediaType =
MediaUtil.isAudioType(docMimeType) ? MediaType.AUDIO : MediaType.DOCUMENT;
setMedia(data.getData(), docMediaType);
if (data.getData() != null) { // single Uri
final String docMimeType = MediaUtil.getMimeType(this, data.getData());
final MediaType docMediaType =
MediaUtil.isAudioType(docMimeType) ? MediaType.AUDIO : MediaType.DOCUMENT;
setMedia(data.getData(), docMediaType);
} else {
sendMultipleMsgs(data);
}
break;
case PICK_WEBXDC:
@@ -511,6 +502,25 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
}
private void sendMultipleMsgs(Intent data) {
final ClipData multipleUris = data.getClipData();
if (multipleUris != null) {
final int uriCount = multipleUris.getItemCount();
if (uriCount > 0) {
ArrayList<Uri> uriList = new ArrayList<>(uriCount);
for (int i = 0; i < uriCount; i++) {
uriList.add(multipleUris.getItemAt(i).getUri());
}
askSendingFiles(
uriList,
() -> {
Util.runOnAnyBackgroundThread(
() -> SendRelayedMessageUtil.sendMultipleMsgs(this, chatId, uriList, null));
});
}
}
}
@Override
public void startActivity(Intent intent) {
if (intent.getStringExtra(Browser.EXTRA_APPLICATION_ID) != null) {
@@ -1021,7 +1031,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
attachButton = ViewUtil.findById(this, R.id.attach_button);
composeText = ViewUtil.findById(this, R.id.embedded_text_editor);
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);
inputPanel = ViewUtil.findById(this, R.id.bottom_panel);
@@ -1047,7 +1056,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
// apply padding top to avoid drawing behind top bar
ViewUtil.applyWindowInsets(findViewById(R.id.fragment_content), false, true, false, false);
// apply padding to root to avoid collision with system bars
ViewUtil.applyWindowInsets(findViewById(R.id.root_layout), true, false, true, true);
ViewUtil.applyWindowInsets(findViewById(R.id.root_layout), true, false, true, false);
ViewUtil.applyWindowInsets(emojiPickerContainer, false, false, false, true);
container.addOnKeyboardShownListener(this);
container.addOnKeyboardHiddenListener(backgroundView);
@@ -1137,22 +1147,24 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
recipient = new Recipient(this, dcChat);
glideRequests = GlideApp.with(this);
setComposePanelVisibility(true);
setInputPanelVisibility(true);
initializeContactRequest();
}
private void setComposePanelVisibility(boolean isInitialization) {
private void setInputPanelVisibility(boolean isInitialization) {
int inputPanelVisibility;
boolean isAttachmentHidden;
if (dcChat.canSend()) {
composePanel.setVisibility(View.VISIBLE);
attachmentManager.setHidden(false);
inputPanelVisibility = View.VISIBLE;
isAttachmentHidden = false;
inputPanel.setSubjectVisible(!dcChat.isEncrypted());
// FIXME: disabled for now to avoid problems with chat scrolling and keyboard covering input
// bar
// ViewUtil.forceApplyWindowInsets(findViewById(R.id.root_layout), true, false, true, true);
// fragment.handleRemoveBottomInsets();
} else {
composePanel.setVisibility(View.GONE);
attachmentManager.setHidden(true);
inputPanelVisibility = View.GONE;
isAttachmentHidden = true;
hideSoftKeyboard();
inputPanel.setSubjectVisible(false);
// FIXME: disabled for now to avoid problems with chat scrolling and keyboard covering input
@@ -1164,6 +1176,15 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
*/
}
synchronized (searchLock) {
if (searchMenu != null) { // in search mode, don't change visibility directly
beforeSearchInputPanelVisibility = inputPanelVisibility;
beforeSearchAttachmentEditorHidden = isAttachmentHidden;
} else {
inputPanel.setVisibility(inputPanelVisibility);
// attachmentManager.setHidden(isAttachmentHidden);
}
}
}
//////// Helper Methods
@@ -1857,19 +1878,23 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
dcChat = dcContext.getChat(chatId);
titleView.setTitle(glideRequests, dcChat);
initializeSecurity(isSecureText, isDefaultSms);
setComposePanelVisibility(false);
setInputPanelVisibility(false);
initializeContactRequest();
} else if ((eventId == DcContext.DC_EVENT_INCOMING_MSG
|| eventId == DcContext.DC_EVENT_MSG_READ)
&& event.getData1Int() == chatId) {
dcChat = dcContext.getChat(chatId);
titleView.setTitle(glideRequests, dcChat);
} else if (eventId == DcContext.DC_EVENT_CHAT_DELETED && event.getData1Int() == chatId) {
finish();
}
}
// in-chat search
private int beforeSearchComposeVisibility = View.VISIBLE;
private boolean beforeSearchAttachmentEditorHidden;
private int beforeSearchMsgRequestVisibility;
private int beforeSearchInputPanelVisibility;
private Menu searchMenu = null;
private int[] searchResult = {};
@@ -1890,17 +1915,28 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void searchExpand(final Menu menu, final MenuItem searchItem) {
searchMenu = menu;
synchronized (searchLock) {
searchMenu = menu;
beforeSearchComposeVisibility = composePanel.getVisibility();
composePanel.setVisibility(View.GONE);
beforeSearchAttachmentEditorHidden = attachmentManager.isHidden();
beforeSearchMsgRequestVisibility = messageRequestBottomView.getVisibility();
beforeSearchInputPanelVisibility = inputPanel.getVisibility();
// attachmentManager.setHidden(true);
messageRequestBottomView.setVisibility(View.GONE);
inputPanel.setVisibility(View.GONE);
}
ConversationActivity.this.makeSearchMenuVisible(menu, searchItem);
}
private void searchCollapse() {
searchMenu = null;
composePanel.setVisibility(beforeSearchComposeVisibility);
synchronized (searchLock) {
searchMenu = null;
// attachmentManager.setHidden(beforeSearchAttachmentEditorHidden);
messageRequestBottomView.setVisibility(beforeSearchMsgRequestVisibility);
inputPanel.setVisibility(beforeSearchInputPanelVisibility);
}
// trigger onPrepareOptionsMenu() to restore correct menu visibility
invalidateOptionsMenu();
@@ -1961,16 +1997,29 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
public void initializeContactRequest() {
if (!dcChat.isContactRequest()) {
messageRequestBottomView.setVisibility(View.GONE);
synchronized (searchLock) {
if (searchMenu != null) { // in search mode, don't change visibility directly
beforeSearchMsgRequestVisibility = View.GONE;
} else {
messageRequestBottomView.setVisibility(View.GONE);
}
}
return;
}
messageRequestBottomView.setVisibility(View.VISIBLE);
synchronized (searchLock) {
if (searchMenu != null) { // in search mode, don't change visibility directly
beforeSearchMsgRequestVisibility = View.VISIBLE;
} else {
messageRequestBottomView.setVisibility(View.VISIBLE);
}
}
messageRequestBottomView.setAcceptOnClickListener(
v -> {
DcHelper.getContext(context).acceptChat(chatId);
messageRequestBottomView.setVisibility(View.GONE);
composePanel.setVisibility(View.VISIBLE);
inputPanel.setVisibility(View.VISIBLE);
});
if (dcChat.getType() == DcChat.DC_CHAT_TYPE_GROUP) {
@@ -68,6 +68,11 @@ public class WebViewActivity extends PassphraseRequiredActionBarActivity
}
}
@Override
protected boolean allowInLockedMode() {
return true;
}
@Override
protected void onCreate(Bundle state, boolean ready) {
setContentView(R.layout.web_view_activity);
@@ -61,6 +61,7 @@ import org.thoughtcrime.securesms.util.Util;
public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcEventDelegate {
private static final String TAG = "WebxdcActivity";
private static final String EXTRA_ACCOUNT_ID = "accountId";
private static final String EXTRA_CHAT_ID = "chatId";
private static final String EXTRA_APP_MSG_ID = "appMessageId";
private static final String EXTRA_HIDE_ACTION_BAR = "hideActionBar";
private static final String EXTRA_HREF = "href";
@@ -71,6 +72,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
private DcContext dcContext;
private int accountId;
private Rpc rpc;
private int chatId;
private DcMsg dcAppMsg;
private String baseURL;
private String sourceCodeUrl = "";
@@ -112,7 +114,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
}
dcContext.setConfigInt("ui.maps_version", mapsVersion);
}
openWebxdcActivity(context, msgId, true, href);
openWebxdcActivity(context, msgId, chatId, true, href);
}
public static void openWebxdcActivity(Context context, DcMsg instance) {
@@ -120,18 +122,18 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
}
public static void openWebxdcActivity(Context context, @NonNull DcMsg instance, String href) {
openWebxdcActivity(context, instance.getId(), false, href);
openWebxdcActivity(context, instance.getId(), instance.getChatId(), false, href);
}
public static void openWebxdcActivity(
Context context, int msgId, boolean hideActionBar, String href) {
Context context, int msgId, int chatId, boolean hideActionBar, String href) {
if (!Util.isClickedRecently()) {
context.startActivity(getWebxdcIntent(context, msgId, hideActionBar, href));
context.startActivity(getWebxdcIntent(context, msgId, chatId, hideActionBar, href));
}
}
private static Intent getWebxdcIntent(
Context context, int msgId, boolean hideActionBar, String href) {
Context context, int msgId, int chatId, boolean hideActionBar, String href) {
DcContext dcContext = DcHelper.getContext(context);
int accountId = dcContext.getAccountId();
Intent intent = new Intent(context, WebxdcActivity.class);
@@ -139,6 +141,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
// Unique URI per webxdc instance so FLAG_ACTIVITY_NEW_DOCUMENT can identify the document:
intent.setData(Uri.parse("webxdc://" + accountId + "/" + msgId));
intent.putExtra(EXTRA_ACCOUNT_ID, accountId);
intent.putExtra(EXTRA_CHAT_ID, chatId);
intent.putExtra(EXTRA_APP_MSG_ID, msgId);
intent.putExtra(EXTRA_HIDE_ACTION_BAR, hideActionBar);
intent.putExtra(EXTRA_HREF, href);
@@ -149,12 +152,13 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
private static Intent[] getWebxdcIntentWithParentStack(Context context, int msgId) {
DcContext dcContext = DcHelper.getContext(context);
int chatId = dcContext.getMsg(msgId).getChatId();
final Intent chatIntent =
new Intent(context, ConversationActivity.class)
.putExtra(ConversationActivity.CHAT_ID_EXTRA, dcContext.getMsg(msgId).getChatId())
.putExtra(ConversationActivity.CHAT_ID_EXTRA, chatId)
.setAction(Intent.ACTION_VIEW);
final Intent webxdcIntent = getWebxdcIntent(context, msgId, false, "");
final Intent webxdcIntent = getWebxdcIntent(context, msgId, chatId, false, "");
return TaskStackBuilder.create(context)
.addNextIntentWithParentStack(chatIntent)
@@ -200,12 +204,6 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
}
});
DcEventCenter eventCenter =
DcHelper.getEventCenter(WebxdcActivity.this.getApplicationContext());
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_WEBXDC_STATUS_UPDATE, this);
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_MSGS_CHANGED, this);
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_WEBXDC_REALTIME_DATA, this);
int appMessageId = b.getInt(EXTRA_APP_MSG_ID);
accountId = b.getInt(EXTRA_ACCOUNT_ID);
this.dcContext = DcHelper.getContext(getApplicationContext());
@@ -219,6 +217,15 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
finish();
return;
}
chatId = b.getInt(EXTRA_CHAT_ID, dcAppMsg.getChatId());
DcEventCenter eventCenter =
DcHelper.getEventCenter(WebxdcActivity.this.getApplicationContext());
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_WEBXDC_STATUS_UPDATE, this);
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_MSGS_CHANGED, this);
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_MSG_DELETED, this);
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_CHAT_DELETED, this);
eventCenter.addMultiAccountObserver(DcContext.DC_EVENT_WEBXDC_REALTIME_DATA, this);
// `msg_id` in the subdomain makes sure, different apps using same files do not share the same
// cache entry
@@ -515,6 +522,10 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
Log.e(TAG, "RPC Error", e);
}
});
} else if ((eventId == DcContext.DC_EVENT_MSG_DELETED
&& event.getData2Int() == dcAppMsg.getId())
|| (eventId == DcContext.DC_EVENT_CHAT_DELETED && event.getData1Int() == chatId)) {
finish();
}
}
@@ -563,7 +574,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
IconCompat.createWithBitmap(
bitmap)) // createWithAdaptiveBitmap() removes decorations but cuts out a too
// small circle and defamiliarize the icon too much
.setIntent(getWebxdcIntent(context, msgId, false, ""))
.setIntent(getWebxdcIntent(context, msgId, msg.getChatId(), false, ""))
.build();
Toast.makeText(context, R.string.one_moment, Toast.LENGTH_SHORT).show();
@@ -1,10 +1,14 @@
package org.thoughtcrime.securesms;
import android.Manifest;
import android.animation.AnimatorInflater;
import android.animation.AnimatorSet;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -71,6 +75,11 @@ public class WelcomeActivity extends BaseActionBarActivity
.setOnClickListener((v) -> showSignInDialogWithPermission());
findViewById(R.id.backup_button).setOnClickListener((v) -> startImportBackup());
AnimatorSet floating =
(AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.floating_logo);
floating.setTarget(findViewById(R.id.welcome_icon));
floating.start();
registerForEvents();
initializeActionBar();
@@ -120,7 +129,15 @@ public class WelcomeActivity extends BaseActionBarActivity
boolean canGoBack = AccountManager.getInstance().canRollbackAccountCreation(this);
supportActionBar.setDisplayHomeAsUpEnabled(canGoBack);
getSupportActionBar().setTitle(canGoBack ? R.string.add_account : R.string.app_name);
if (canGoBack) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
supportActionBar.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
supportActionBar.setElevation(0);
}
supportActionBar.setTitle(R.string.add_account);
} else {
supportActionBar.hide();
}
}
private void registerForEvents() {
@@ -189,7 +206,12 @@ public class WelcomeActivity extends BaseActionBarActivity
File imexDir = DcHelper.getImexDir();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
AttachmentManager.selectMediaType(
this, "application/x-tar", null, PICK_BACKUP, StorageUtil.getDownloadUri());
this,
"application/x-tar",
null,
PICK_BACKUP,
StorageUtil.getDownloadUri(),
false);
} else {
final String backupFile = dcContext.imexHasBackup(imexDir.getAbsolutePath());
if (backupFile != null) {
@@ -5,7 +5,11 @@ import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationManagerCompat;
import org.thoughtcrime.securesms.ConversationActivity;
import org.thoughtcrime.securesms.R;
@RequiresApi(api = Build.VERSION_CODES.O)
public class CallActionReceiver extends BroadcastReceiver {
@@ -22,6 +26,30 @@ public class CallActionReceiver extends BroadcastReceiver {
CallCoordinator.getInstance(context).declineCall();
} else if (CallActivity.ACTION_HANGUP_CALL.equals(action)) {
CallCoordinator.getInstance(context).hangUp();
} else if (CallActivity.ACTION_CALL_BACK.equals(action)) {
int chatId = intent.getIntExtra(ConversationActivity.CHAT_ID_EXTRA, -1);
int accId = intent.getIntExtra(ConversationActivity.ACCOUNT_ID_EXTRA, -1);
boolean video = intent.getBooleanExtra(CallActivity.EXTRA_STARTS_WITH_VIDEO, false);
if (chatId > 0 && accId > 0) {
NotificationManagerCompat.from(context).cancel(CallCoordinator.NOTIFICATION_ID_MISSED_CALL);
CallCoordinator coordinator = CallCoordinator.getInstance(context);
if (coordinator.hasActiveCall()) {
Toast.makeText(context, R.string.already_in_call, Toast.LENGTH_SHORT).show();
} else {
coordinator.initiateOutgoingCall(accId, chatId, video);
}
}
} else if (CallActivity.ACTION_MESSAGE.equals(action)) {
int chatId = intent.getIntExtra(ConversationActivity.CHAT_ID_EXTRA, -1);
int accId = intent.getIntExtra(ConversationActivity.ACCOUNT_ID_EXTRA, -1);
NotificationManagerCompat.from(context).cancel(CallCoordinator.NOTIFICATION_ID_MISSED_CALL);
if (chatId > 0 && accId > 0) {
Intent convIntent = new Intent(context, ConversationActivity.class);
convIntent.putExtra(ConversationActivity.CHAT_ID_EXTRA, chatId);
convIntent.putExtra(ConversationActivity.ACCOUNT_ID_EXTRA, accId);
convIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(convIntent);
}
}
}
}
@@ -63,6 +63,9 @@ public class CallActivity extends AppCompatActivity {
public static final String ACTION_ANSWER_CALL = BuildConfig.APPLICATION_ID + ".ANSWER_CALL";
public static final String ACTION_DECLINE_CALL = BuildConfig.APPLICATION_ID + ".DECLINE_CALL";
public static final String ACTION_HANGUP_CALL = BuildConfig.APPLICATION_ID + ".HANGUP_CALL";
public static final String ACTION_CALL_BACK = BuildConfig.APPLICATION_ID + ".CALL_BACK";
public static final String ACTION_MESSAGE = BuildConfig.APPLICATION_ID + ".MESSAGE";
public static final String EXTRA_STARTS_WITH_VIDEO = "starts_with_video";
// Views
@@ -57,6 +57,7 @@ import kotlinx.coroutines.Dispatchers;
import kotlinx.coroutines.flow.Flow;
import kotlinx.coroutines.flow.FlowKt;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.ConversationActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.connect.DcEventCenter;
import org.thoughtcrime.securesms.connect.DcHelper;
@@ -70,7 +71,18 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
// Notification channels
private static final String CHANNEL_ID_INCOMING = "voip_incoming_calls";
private static final String CHANNEL_ID_ONGOING = "voip_ongoing_calls";
private static final String CHANNEL_ID_MISSED = "voip_missed_calls";
private static final int NOTIFICATION_ID_CALL = 1001;
static final int NOTIFICATION_ID_MISSED_CALL = 1002;
private static final int PI_ANSWER = 0;
private static final int PI_DECLINE = 1;
private static final int PI_FULLSCREEN = 2;
private static final int PI_HANGUP = 3;
private static final int PI_ONGOING_CONTENT = 4;
private static final int PI_MISSED_CONTENT = 5;
private static final int PI_MISSED_CALLBACK = 6;
private static final int PI_MISSED_MESSAGE = 7;
private static final String CALL_IDENTIFIER_SCHEME = "deltachat:";
@@ -123,6 +135,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
private boolean hasNotifiedBackend = false;
private boolean hasAutoSelectedEarpiece = false;
private boolean pendingMediaCapture = false;
private boolean wasAnsweredLocally = false;
private CallControlScope activeCallControlScope;
private CallViewModel activeCallViewModel;
@@ -169,8 +182,14 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
ongoingChannel.setDescription("Notifications for active DeltaChat calls");
ongoingChannel.setSound(null, null);
NotificationChannel missedChannel =
new NotificationChannel(
CHANNEL_ID_MISSED, "Missed Calls", NotificationManager.IMPORTANCE_HIGH);
missedChannel.setDescription("Notifications for missed DeltaChat calls");
notificationManager.createNotificationChannel(incomingChannel);
notificationManager.createNotificationChannel(ongoingChannel);
notificationManager.createNotificationChannel(missedChannel);
}
private void registerTelecom() {
@@ -441,6 +460,8 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
return;
}
wasAnsweredLocally = true;
if (callService != null) {
callService.stopRingtone();
}
@@ -871,7 +892,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
// This event is problematic because it can trigger in both directions,
// in addition to multiple other scenarios which cannot easily be distinguished
// May cause problems in edge cases
onCallEnded(accId, callId);
onCallEnded(accId, callId, startsWithVideo);
break;
}
});
@@ -1022,7 +1043,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
showOrUpdateOngoingNotification(appContext.getString(R.string.call_with, calleeName));
}
private synchronized void onCallEnded(int accId, int callId) {
private synchronized void onCallEnded(int accId, int callId, boolean startsWithVideo) {
Log.d(TAG, "onCallEnded: accId=" + accId + ", callId=" + callId);
if (!hasActiveCall()) {
@@ -1056,6 +1077,10 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
callService.endCall();
}
if (isIncomingCall && !wasAnsweredLocally) {
showMissedCallNotification(activeAccId, activeChatId, startsWithVideo);
}
// Clear active states
cleanupCall(accId, callId);
}
@@ -1127,6 +1152,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
this.hasNotifiedBackend = false;
this.hasAutoSelectedEarpiece = false;
this.pendingMediaCapture = false;
this.wasAnsweredLocally = false;
mainHandler.removeCallbacks(outgoingRingtoneRunnable);
@@ -1417,7 +1443,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
PendingIntent answerPendingIntent =
PendingIntent.getActivity(
this.appContext,
0,
PI_ANSWER,
answerIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
@@ -1428,7 +1454,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
PendingIntent declinePendingIntent =
PendingIntent.getBroadcast(
this.appContext,
1,
PI_DECLINE,
declineIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
@@ -1439,7 +1465,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
PendingIntent fullScreenPendingIntent =
PendingIntent.getActivity(
this.appContext,
2,
PI_FULLSCREEN,
fullScreenIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
@@ -1490,6 +1516,87 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
notificationManager.notify(NOTIFICATION_ID_CALL, builder.build());
}
private void showMissedCallNotification(int accId, int chatId, boolean wasVideoCall) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (!hasNotificationPermission()) {
Log.w(TAG, "Cannot show missed call notification: no permission");
return;
}
}
DcContext dcContext = ApplicationContext.getDcAccounts().getAccount(accId);
DcChat dcChat = dcContext.getChat(chatId);
String callerName = CallUtil.getNameFromChat(dcChat);
Intent contentAction = new Intent(appContext, ConversationActivity.class);
contentAction.putExtra(ConversationActivity.CHAT_ID_EXTRA, chatId);
contentAction.putExtra(ConversationActivity.ACCOUNT_ID_EXTRA, accId);
contentAction.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent contentIntent =
PendingIntent.getActivity(
appContext,
PI_MISSED_CONTENT,
contentAction,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
Intent callBackAction = new Intent(appContext, CallActionReceiver.class);
callBackAction.setAction(CallActivity.ACTION_CALL_BACK);
callBackAction.putExtra(ConversationActivity.CHAT_ID_EXTRA, chatId);
callBackAction.putExtra(ConversationActivity.ACCOUNT_ID_EXTRA, accId);
callBackAction.putExtra(CallActivity.EXTRA_STARTS_WITH_VIDEO, wasVideoCall);
PendingIntent callBackIntent =
PendingIntent.getBroadcast(
appContext,
PI_MISSED_CALLBACK,
callBackAction,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
Intent messageAction = new Intent(appContext, CallActionReceiver.class);
messageAction.setAction(CallActivity.ACTION_MESSAGE);
messageAction.putExtra(ConversationActivity.CHAT_ID_EXTRA, chatId);
messageAction.putExtra(ConversationActivity.ACCOUNT_ID_EXTRA, accId);
PendingIntent messageIntent =
PendingIntent.getBroadcast(
appContext,
PI_MISSED_MESSAGE,
messageAction,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
String contentText = appContext.getString(R.string.missed_call);
Notification.Builder builder =
new Notification.Builder(appContext, CHANNEL_ID_MISSED)
.setSmallIcon(R.drawable.icon_notification)
.setContentTitle(callerName)
.setContentText(contentText)
.setContentIntent(contentIntent)
.setAutoCancel(true)
.addAction(
new Notification.Action.Builder(
null, appContext.getString(R.string.call_back), callBackIntent)
.build())
.addAction(
new Notification.Action.Builder(
null, appContext.getString(R.string.chat_input_placeholder), messageIntent)
.build());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
builder.setCategory(Notification.CATEGORY_MISSED_CALL);
} else {
builder.setCategory(Notification.CATEGORY_CALL);
}
Icon icon = displayIcon.getValue();
if (icon != null) {
builder.setLargeIcon(icon);
}
notificationManager.notify(NOTIFICATION_ID_MISSED_CALL, builder.build());
}
private Notification buildOngoingCallNotification(
String statusText, String displayName, Icon icon) {
Intent activityIntent = new Intent(this.appContext, CallActivity.class);
@@ -1501,14 +1608,14 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
PendingIntent hangupPendingIntent =
PendingIntent.getBroadcast(
this.appContext,
3,
PI_HANGUP,
hangupIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
PendingIntent contentIntent =
PendingIntent.getActivity(
this.appContext,
4,
PI_ONGOING_CONTENT,
activityIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
@@ -9,10 +9,12 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.content.ContextCompat;
import java.util.List;
import org.thoughtcrime.securesms.EglUtils;
import org.webrtc.AudioSource;
import org.webrtc.AudioTrack;
import org.webrtc.Camera2Enumerator;
import org.webrtc.CameraEnumerationAndroid;
import org.webrtc.CameraVideoCapturer;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;
@@ -22,6 +24,7 @@ import org.webrtc.VideoCapturer;
import org.webrtc.VideoSource;
import org.webrtc.VideoTrack;
@RequiresApi(Build.VERSION_CODES.M)
public class MediaStreamManager {
private static final String TAG = "MediaStreamManager";
@@ -29,9 +32,9 @@ public class MediaStreamManager {
private static final String AUDIO_TRACK_ID = "audio_track";
private static final String VIDEO_TRACK_ID = "video_track";
private static final int VIDEO_WIDTH = 1280;
private static final int VIDEO_HEIGHT = 720;
private static final int VIDEO_FPS = 30;
private static final int TARGET_WIDTH = 1280;
private static final int TARGET_HEIGHT = 720;
private static final int TARGET_FPS = 30;
private final Context context;
private final PeerConnectionFactory peerConnectionFactory;
@@ -42,6 +45,9 @@ public class MediaStreamManager {
private SurfaceTextureHelper surfaceTextureHelper;
private volatile boolean isFrontCamera = true;
private volatile boolean isCapturing = false;
private volatile String currentDeviceName;
private volatile int currentCaptureWidth;
private volatile int currentCaptureHeight;
public interface Callback {
void onMediaStreamReady(MediaStream stream);
@@ -90,7 +96,6 @@ public class MediaStreamManager {
*
* @return true if the camera is capturing, false if it could not be started
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public synchronized boolean startVideoCapture() {
if (isCapturing) {
return true;
@@ -116,9 +121,23 @@ public class MediaStreamManager {
videoCapturer.initialize(surfaceTextureHelper, context, videoSource.getCapturerObserver());
}
videoCapturer.startCapture(VIDEO_WIDTH, VIDEO_HEIGHT, VIDEO_FPS);
int[] captureFormat = selectCaptureFormat(currentDeviceName);
currentCaptureWidth = captureFormat[0];
currentCaptureHeight = captureFormat[1];
videoCapturer.startCapture(currentCaptureWidth, currentCaptureHeight, TARGET_FPS);
videoSource.adaptOutputFormat(TARGET_WIDTH, TARGET_HEIGHT, TARGET_FPS);
isCapturing = true;
Log.d(TAG, "Video capture started");
Log.d(
TAG,
"Video capture started at "
+ currentCaptureWidth
+ "x"
+ currentCaptureHeight
+ ", adapted to "
+ TARGET_WIDTH
+ "x"
+ TARGET_HEIGHT);
return true;
}
@@ -141,6 +160,60 @@ public class MediaStreamManager {
isCapturing = false;
}
private int[] selectCaptureFormat(@Nullable String deviceName) {
if (deviceName == null) {
Log.w(TAG, "Device name is null, using target dimensions");
return new int[] {TARGET_WIDTH, TARGET_HEIGHT};
}
Camera2Enumerator enumerator = new Camera2Enumerator(context);
List<CameraEnumerationAndroid.CaptureFormat> formats =
enumerator.getSupportedFormats(deviceName);
if (formats == null || formats.isEmpty()) {
Log.w(TAG, "No supported formats for " + deviceName);
return new int[] {TARGET_WIDTH, TARGET_HEIGHT};
}
CameraEnumerationAndroid.CaptureFormat best = null;
int bestPixels = Integer.MAX_VALUE;
for (CameraEnumerationAndroid.CaptureFormat f : formats) {
if (f.width >= TARGET_WIDTH && f.height >= TARGET_HEIGHT) {
int pixels = f.width * f.height;
if (pixels < bestPixels) {
bestPixels = pixels;
best = f;
}
}
}
if (best != null) {
Log.d(
TAG, "Selected capture format: " + best.width + "x" + best.height + " for " + deviceName);
return new int[] {best.width, best.height};
}
CameraEnumerationAndroid.CaptureFormat largest = null;
int largestPixels = 0;
for (CameraEnumerationAndroid.CaptureFormat f : formats) {
int pixels = f.width * f.height;
if (pixels > largestPixels) {
largestPixels = pixels;
largest = f;
}
}
if (largest != null) {
Log.w(
TAG,
"Using largest format " + largest.width + "x" + largest.height + " for " + deviceName);
return new int[] {largest.width, largest.height};
}
return new int[] {TARGET_WIDTH, TARGET_HEIGHT};
}
@Nullable
private VideoCapturer createVideoCapturer() {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
@@ -158,6 +231,7 @@ public class MediaStreamManager {
VideoCapturer capturer = enumerator.createCapturer(deviceName, null);
if (capturer != null) {
isFrontCamera = true;
currentDeviceName = deviceName;
return capturer;
}
}
@@ -168,6 +242,7 @@ public class MediaStreamManager {
VideoCapturer capturer = enumerator.createCapturer(deviceName, null);
if (capturer != null) {
isFrontCamera = enumerator.isFrontFacing(deviceName);
currentDeviceName = deviceName;
return capturer;
}
}
@@ -224,6 +299,30 @@ public class MediaStreamManager {
public void onCameraSwitchDone(boolean isFront) {
Log.d(TAG, "switchCamera SUCCESS, isFront=" + isFront);
isFrontCamera = isFront;
currentDeviceName = finalTargetCameraName;
int[] newFormat = selectCaptureFormat(finalTargetCameraName);
if (newFormat[0] != currentCaptureWidth || newFormat[1] != currentCaptureHeight) {
Log.d(
TAG,
"Changing capture format: "
+ currentCaptureWidth
+ "x"
+ currentCaptureHeight
+ " to "
+ newFormat[0]
+ "x"
+ newFormat[1]);
currentCaptureWidth = newFormat[0];
currentCaptureHeight = newFormat[1];
cameraVideoCapturer.changeCaptureFormat(
currentCaptureWidth, currentCaptureHeight, TARGET_FPS);
}
if (videoSource != null) {
videoSource.adaptOutputFormat(TARGET_WIDTH, TARGET_HEIGHT, TARGET_FPS);
}
if (callback != null) callback.onCameraSwitch(isFront);
}
@@ -70,14 +70,13 @@ import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.guava.Optional;
import org.thoughtcrime.securesms.util.views.Stub;
public class AttachmentManager {
private static final String TAG = "AttachmentManager";
private final @NonNull Context context;
private final @NonNull Stub<View> attachmentViewStub;
private final @NonNull View attachmentView;
private final @NonNull AttachmentListener attachmentListener;
private RemovableEditableMediaView removableMediaView;
@@ -98,20 +97,16 @@ public class AttachmentManager {
public AttachmentManager(@NonNull Activity activity, @NonNull AttachmentListener listener) {
this.context = activity;
this.attachmentListener = listener;
this.attachmentViewStub = ViewUtil.findStubById(activity, R.id.attachment_editor_stub);
}
private void inflateStub() {
if (!attachmentViewStub.resolved()) {
View root = attachmentViewStub.get();
this.thumbnail = ViewUtil.findById(root, R.id.attachment_thumbnail);
this.audioView = ViewUtil.findById(root, R.id.attachment_audio);
this.documentView = ViewUtil.findById(root, R.id.attachment_document);
this.webxdcView = ViewUtil.findById(root, R.id.attachment_webxdc);
this.vcardView = ViewUtil.findById(root, R.id.attachment_vcard);
// this.mapView = ViewUtil.findById(root, R.id.attachment_location);
this.removableMediaView = ViewUtil.findById(root, R.id.removable_media_view);
this.attachmentView = ViewUtil.findById(activity, R.id.attachment_editor);
if (this.attachmentView != null) {
this.thumbnail = ViewUtil.findById(attachmentView, R.id.attachment_thumbnail);
this.audioView = ViewUtil.findById(attachmentView, R.id.attachment_audio);
this.documentView = ViewUtil.findById(attachmentView, R.id.attachment_document);
this.webxdcView = ViewUtil.findById(attachmentView, R.id.attachment_webxdc);
this.vcardView = ViewUtil.findById(attachmentView, R.id.attachment_vcard);
// this.mapView = ViewUtil.findById(attachmentView, R.id.attachment_location);
this.removableMediaView = ViewUtil.findById(attachmentView, R.id.removable_media_view);
removableMediaView.addRemoveClickListener(new RemoveButtonListener());
removableMediaView.setEditClickListener(new EditButtonListener());
@@ -120,10 +115,10 @@ public class AttachmentManager {
}
public void clear(@NonNull GlideRequests glideRequests, boolean animate) {
if (attachmentViewStub.resolved()) {
if (this.attachmentView != null) {
if (animate) {
ViewUtil.fadeOut(attachmentViewStub.get(), 200)
ViewUtil.fadeOut(attachmentView, 200)
.addListener(
new ListenableFuture.Listener<Boolean>() {
@Override
@@ -229,7 +224,6 @@ public class AttachmentManager {
final int height,
final int chatId,
AudioPlaybackViewModel playbackViewModel) {
inflateStub();
final SettableFuture<Boolean> result = new SettableFuture<>();
@@ -445,7 +439,7 @@ public class AttachmentManager {
}
public static void selectDocument(Activity activity, int requestCode) {
selectMediaType(activity, "*/*", null, requestCode);
selectMediaType(activity, "*/*", null, requestCode, null, true);
}
public static void selectWebxdc(Activity activity, int requestCode) {
@@ -483,7 +477,7 @@ public class AttachmentManager {
.ifNecessary()
.withPermanentDenialDialog(
activity.getString(R.string.perm_explain_access_to_storage_denied))
.onAllGranted(() -> selectMediaType(activity, "image/*", null, requestCode))
.onAllGranted(() -> selectMediaType(activity, "image/*", null, requestCode, null, false))
.execute();
}
@@ -608,20 +602,6 @@ public class AttachmentManager {
.execute();
}
public static void selectMediaType(
Activity activity, @NonNull String type, @Nullable String[] extraMimeType, int requestCode) {
selectMediaType(activity, type, extraMimeType, requestCode, null, false);
}
public static void selectMediaType(
Activity activity,
@NonNull String type,
@Nullable String[] extraMimeType,
int requestCode,
@Nullable Uri initialUri) {
selectMediaType(activity, type, extraMimeType, requestCode, initialUri, false);
}
public static void selectMediaType(
Activity activity,
@NonNull String type,
@@ -801,6 +781,10 @@ public class AttachmentManager {
updateVisibility();
}
public boolean isHidden() {
return hidden;
}
private void setAttachmentPresent(boolean isPresent) {
this.attachmentPresent = isPresent;
updateVisibility();
@@ -813,9 +797,9 @@ public class AttachmentManager {
} else {
vis = View.GONE;
}
if (vis == View.GONE && !attachmentViewStub.resolved()) {
if (attachmentView == null) {
return;
}
attachmentViewStub.get().setVisibility(vis);
attachmentView.setVisibility(vis);
}
}
@@ -429,6 +429,12 @@ public class NotificationCenter {
DcContact sender = dcContext.getContact(dcMsg.getFromId());
String senderName = dcMsg.getSenderName(sender);
String personId = accountId + "-" + dcMsg.getFromId();
if (dcMsg.getOverrideSenderName() != null) {
// we need to treat the contact as a separate Person with different ID
// otherwise the name will be overwritten by future notifications
personId += "-" + senderName;
}
String text =
privacy.isDisplayMessage()
? dcMsg.getSummarytext(2000)
@@ -444,7 +450,7 @@ public class NotificationCenter {
.setName(senderName)
.setIcon(getAvatarIcon(sender))
.setBot(sender.isBot())
.setKey(accountId + "-" + sender.getId())
.setKey(personId)
.build(),
text);
@@ -472,12 +478,11 @@ public class NotificationCenter {
// just do nothing.
}
DcContact sender = dcContext.getContact(contactId);
String senderName = dcMsg.getSenderName(sender);
DcContact contact = dcContext.getContact(contactId);
String text =
context.getString(
R.string.reaction_by_other,
sender.getDisplayName(),
contact.getDisplayName(),
reaction,
dcMsg.getSummarytext(2000));
DcChat dcChat = dcContext.getChat(dcMsg.getChatId());
@@ -485,10 +490,10 @@ public class NotificationCenter {
NotifData notifData =
new NotifData(
new Person.Builder()
.setName(senderName)
.setIcon(getAvatarIcon(sender))
.setBot(sender.isBot())
.setKey(accountId + "-" + sender.getId())
.setName(contact.getDisplayName())
.setIcon(getAvatarIcon(contact))
.setBot(contact.isBot())
.setKey(accountId + "-" + contactId)
.build(),
text);
@@ -539,10 +544,10 @@ public class NotificationCenter {
notifData =
new NotifData(
new Person.Builder()
.setName(dcMsg.getSenderName(sender))
.setName(sender.getDisplayName())
.setIcon(getAvatarIcon(sender))
.setBot(sender.isBot())
.setKey(dcContext.getAccountId() + "-" + sender.getId())
.setKey(accountId + "-" + contactId)
.build(),
tickerLine);
}
@@ -720,7 +725,7 @@ public class NotificationCenter {
NotificationCompat.Action markAsReadAction =
new NotificationCompat.Action(
R.drawable.check, context.getString(R.string.mark_as_read_short), markReadIntent);
R.drawable.check, context.getString(R.string.mark_as_read), markReadIntent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
NotificationCompat.Action replyAction =
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:propertyName="translationY"
android:valueType="floatType"
android:valueFrom="0dp"
android:valueTo="-8dp"
android:duration="1500"
android:repeatMode="reverse"
android:repeatCount="infinite"
android:interpolator="@android:interpolator/accelerate_decelerate" />
</set>
+13
View File
@@ -0,0 +1,13 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="228dp"
android:height="280dp"
android:viewportWidth="228"
android:viewportHeight="280">
<path
android:pathData="m10.03,234.14c0.3,-0.01 0.6,-0.02 0.9,-0.04 -0.07,-0.49 -0.14,-0.97 -0.2,-1.46 -0.15,-1.34 -0.26,-2.69 -0.25,-4.04 -0.02,-0.86 -0.05,-1.71 -0.07,-2.57 -0.09,-0.06 -0.18,-0.13 -0.27,-0.19 -0.02,-0.02 -0.04,-0.03 -0.07,-0.05zM44.87,232.95c11.71,-8.35 26.86,-14.79 46.21,-15.9 0,0 39.93,-0.27 47.91,-3.53 7.98,-3.26 68.68,-14.69 82.94,-98.43 14.26,-83.74 -1.06,-115.09 -1.06,-115.09 0,0 -21.14,55.68 -81.02,59.81 0,0 -14.5,1.03 -38.82,1.42 -24.32,0.39 -75.77,20.65 -90.55,85.62l-0.22,43.44c2.5,4.22 5.49,8.12 8.91,11.66 3.99,4.11 8.11,8.12 12.79,11.45 2.26,1.65 4.65,3.2 6.51,5.33 1.94,2.34 3.33,5 4.2,7.93 0.71,2.1 1.45,4.2 2.2,6.28z"
android:fillColor="#ffffff" />
<path
android:pathData="m217.97,45.86c-0.3,0.01 -0.6,0.02 -0.9,0.04 0.07,0.49 0.14,0.97 0.2,1.46 0.15,1.34 0.26,2.69 0.25,4.04 0.02,0.86 0.05,1.71 0.07,2.57 0.09,0.06 0.18,0.13 0.27,0.19 0.02,0.02 0.04,0.03 0.07,0.05zM183.13,47.05c-11.71,8.35 -26.86,14.79 -46.21,15.9 0,0 -39.93,0.27 -47.91,3.53 -7.98,3.26 -68.68,14.69 -82.94,98.43 -14.26,83.74 1.06,115.09 1.06,115.09 0,0 21.14,-55.68 81.02,-59.81 0,0 14.5,-1.03 38.82,-1.42 24.32,-0.39 75.77,-20.65 90.55,-85.62l0.22,-43.44c-2.5,-4.22 -5.49,-8.12 -8.91,-11.66 -3.99,-4.11 -8.11,-8.12 -12.79,-11.45 -2.26,-1.65 -4.65,-3.2 -6.51,-5.33 -1.94,-2.34 -3.33,-5 -4.2,-7.93 -0.71,-2.1 -1.45,-4.2 -2.2,-6.28z"
android:fillColor="#ffffff" />
</vector>
+12
View File
@@ -0,0 +1,12 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="490.3dp"
android:height="72.64dp"
android:viewportWidth="490.3"
android:viewportHeight="72.64">
<path
android:pathData="M17.45,58.55L50.75,58.55L49.76,46.94L18.53,46.94ZM33.92,30.83 L42.83,51.26 42.29,55.13 49.85,70.7L67.13,70.7L33.92,3.74 0.81,70.7L17.99,70.7L25.82,54.5 25.1,51.17ZM85.75,29.3L72.35,29.3v41.4h13.41zM97.72,43.34 L103.57,31.73q-1.08,-1.62 -3.15,-2.52 -2.07,-0.9 -4.32,-0.9 -3.42,0 -6.57,2.25 -3.15,2.25 -5.13,6.12 -1.89,3.87 -1.89,8.82l3.24,4.77q0,-2.97 0.81,-5.04 0.81,-2.07 2.34,-3.15 1.53,-1.08 3.51,-1.08 1.8,0 2.97,0.63 1.26,0.63 2.34,1.71zM119.41,50q0,-3.06 1.44,-5.49 1.44,-2.43 3.96,-3.87 2.61,-1.44 5.58,-1.44 2.25,0 4.59,0.72 2.34,0.72 4.32,2.07 2.07,1.35 3.33,3.33L142.63,32q-2.16,-1.53 -5.4,-2.52 -3.24,-1.08 -7.92,-1.08 -6.84,0 -12.33,2.7 -5.4,2.7 -8.55,7.56 -3.06,4.77 -3.06,11.34 0,6.48 3.06,11.34 3.15,4.86 8.55,7.56 5.49,2.7 12.33,2.7 4.68,0 7.92,-0.99 3.24,-1.08 5.4,-2.79L142.63,54.59q-1.26,1.98 -3.24,3.33 -1.89,1.35 -4.23,2.16 -2.25,0.72 -4.77,0.72 -2.97,0 -5.58,-1.35 -2.52,-1.35 -3.96,-3.69 -1.44,-2.43 -1.44,-5.76zM162.97,57.74q0,-1.53 0.72,-2.52 0.72,-1.08 2.16,-1.62 1.44,-0.54 3.87,-0.54 3.15,0 5.94,0.9 2.88,0.81 5.04,2.43v-6.03q-1.08,-1.17 -3.24,-2.25 -2.16,-1.08 -5.22,-1.8 -2.97,-0.72 -6.75,-0.72 -7.74,0 -11.97,3.51 -4.23,3.42 -4.23,9.36 0,4.23 1.98,7.2 2.07,2.97 5.4,4.5 3.42,1.44 7.38,1.44 3.96,0 7.38,-1.35 3.42,-1.44 5.58,-4.14 2.16,-2.79 2.16,-6.75l-1.44,-5.4q0,2.88 -1.26,4.86 -1.26,1.98 -3.24,2.97 -1.98,0.99 -4.32,0.99 -1.62,0 -2.97,-0.54 -1.35,-0.63 -2.16,-1.71 -0.81,-1.17 -0.81,-2.79zM156.94,41.99q0.9,-0.54 2.88,-1.35 1.98,-0.9 4.59,-1.53 2.7,-0.63 5.49,-0.63 1.89,0 3.33,0.36 1.53,0.36 2.52,1.17 0.99,0.81 1.44,1.98 0.54,1.08 0.54,2.61v26.1h13.05L190.77,41.54q0,-4.32 -2.52,-7.29 -2.52,-2.97 -6.84,-4.5 -4.32,-1.62 -9.81,-1.62 -5.85,0 -10.8,1.53 -4.95,1.53 -8.46,3.24zM227.85,45.5v25.2h14.13L241.98,44.06q0,-7.47 -3.51,-11.61 -3.51,-4.14 -11.25,-4.14 -4.59,0 -7.74,1.89 -3.06,1.89 -4.86,5.31L214.62,29.3L200.94,29.3v41.4h13.68L214.62,45.5q0,-2.43 0.9,-4.14 0.9,-1.71 2.52,-2.61 1.62,-0.9 3.78,-0.9 3.24,0 4.59,1.98 1.44,1.98 1.44,5.67zM273.74,71.6q7.92,0 13.41,-3.06 5.58,-3.06 8.82,-9.18l-12.15,-2.97q-1.53,2.7 -4.14,4.05 -2.52,1.35 -6.12,1.35 -3.15,0 -5.31,-1.35 -2.16,-1.44 -3.24,-4.14 -1.08,-2.7 -1.08,-6.48 0.09,-4.14 1.08,-6.84 1.08,-2.79 3.15,-4.14 2.07,-1.35 5.13,-1.35 2.43,0 4.23,1.08 1.8,1.08 2.79,3.06 0.99,1.98 0.99,4.68 0,0.63 -0.36,1.62 -0.27,0.9 -0.72,1.53l3.24,-4.14h-25.56v7.56h37.8q0.18,-0.63 0.18,-1.53 0,-0.99 0,-1.98 0,-6.66 -2.61,-11.34 -2.61,-4.68 -7.56,-7.11 -4.95,-2.52 -12.15,-2.52 -7.2,0 -12.51,2.61 -5.22,2.61 -8.01,7.47 -2.79,4.86 -2.79,11.52 0,6.57 2.88,11.43 2.88,4.86 8.1,7.56 5.31,2.61 12.51,2.61zM318.56,39.2q0,-5.85 2.61,-9.99 2.61,-4.14 6.84,-6.3 4.32,-2.16 9.45,-2.16 4.5,0 7.92,1.17 3.42,1.08 6.12,3.06 2.7,1.89 4.77,4.05L356.26,12.65q-3.78,-2.97 -8.28,-4.68 -4.5,-1.71 -11.43,-1.71 -7.56,0 -13.95,2.34 -6.39,2.34 -10.98,6.75 -4.59,4.41 -7.11,10.44 -2.52,6.03 -2.52,13.41 0,7.38 2.52,13.41 2.52,6.03 7.11,10.44 4.59,4.41 10.98,6.75 6.39,2.34 13.95,2.34 6.93,0 11.43,-1.71 4.5,-1.71 8.28,-4.68L356.26,49.37q-2.07,2.16 -4.77,4.05 -2.7,1.89 -6.12,3.06 -3.42,1.17 -7.92,1.17 -5.13,0 -9.45,-2.16 -4.23,-2.16 -6.84,-6.3 -2.61,-4.23 -2.61,-9.99zM381.01,0.5L367.33,0.5L367.33,70.7h13.68zM394.24,45.5v25.2h13.59L407.83,43.52q0,-5.13 -1.44,-8.64 -1.44,-3.51 -4.59,-5.31 -3.06,-1.8 -8.19,-1.8 -5.13,0 -8.46,2.43 -3.24,2.34 -4.86,6.39 -1.62,3.96 -1.62,8.91h2.34q0,-2.43 0.9,-4.05 0.9,-1.71 2.52,-2.61 1.62,-0.99 3.78,-0.99 3.33,0 4.68,1.98 1.35,1.98 1.35,5.67zM428.97,57.74q0,-1.53 0.72,-2.52 0.72,-1.08 2.16,-1.62 1.44,-0.54 3.87,-0.54 3.15,0 5.94,0.9 2.88,0.81 5.04,2.43v-6.03q-1.08,-1.17 -3.24,-2.25 -2.16,-1.08 -5.22,-1.8 -2.97,-0.72 -6.75,-0.72 -7.74,0 -11.97,3.51 -4.23,3.42 -4.23,9.36 0,4.23 1.98,7.2 2.07,2.97 5.4,4.5 3.42,1.44 7.38,1.44 3.96,0 7.38,-1.35 3.42,-1.44 5.58,-4.14 2.16,-2.79 2.16,-6.75l-1.44,-5.4q0,2.88 -1.26,4.86 -1.26,1.98 -3.24,2.97 -1.98,0.99 -4.32,0.99 -1.62,0 -2.97,-0.54 -1.35,-0.63 -2.16,-1.71 -0.81,-1.17 -0.81,-2.79zM422.94,41.99q0.9,-0.54 2.88,-1.35 1.98,-0.9 4.59,-1.53 2.7,-0.63 5.49,-0.63 1.89,0 3.33,0.36 1.53,0.36 2.52,1.17 0.99,0.81 1.44,1.98 0.54,1.08 0.54,2.61v26.1h13.05L456.78,41.54q0,-4.32 -2.52,-7.29 -2.52,-2.97 -6.84,-4.5 -4.32,-1.62 -9.81,-1.62 -5.85,0 -10.8,1.53 -4.95,1.53 -8.46,3.24zM462.09,29.3L462.09,40.55L489.8,40.55L489.8,29.3ZM469.38,14.9L469.38,70.7L482.43,70.7L482.43,14.9Z"
android:strokeWidth="0.999939"
android:fillColor="#ffffff"
android:strokeColor="#ffffff" />
</vector>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#7b00c8"
android:endColor="#5564da"
android:angle="315" />
</shape>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="?attr/input_panel_bg_color" />
<corners android:radius="25dp" />
</shape>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white" />
<corners android:radius="8dp" />
</shape>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="2px"
android:color="@color/white" />
<corners android:radius="8dp" />
</shape>
@@ -36,20 +36,19 @@
android:layout_height="0dp"
android:layout_weight="1" />
<ViewStub
android:id="@+id/attachment_editor_stub"
android:inflatedId="@+id/attachment_editor"
android:layout="@layout/conversation_activity_attachment_editor_stub"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<org.thoughtcrime.securesms.messagerequests.MessageRequestsBottomView
android:id="@+id/conversation_activity_message_request_bottom_bar"
android:background="?android:attr/windowBackground"
android:background="@drawable/input_panel_rounded_bg"
android:elevation="1dp"
android:outlineProvider="background"
android:visibility="gone"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:paddingLeft="16dp"
android:paddingStart="16dp"
android:paddingRight="16dp"
@@ -5,7 +5,6 @@
android:id="@+id/attachment_editor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/input_panel_bg_color"
android:gravity="center_horizontal"
android:visibility="gone">
@@ -29,9 +28,9 @@
android:contentDescription="@string/menu_add_attachment"
app:thumbnail_radius="@dimen/message_corner_radius"
app:minWidth="100dp"
app:maxWidth="300dp"
app:maxWidth="200dp"
app:minHeight="100dp"
app:maxHeight="300dp" />
app:maxHeight="200dp" />
<org.thoughtcrime.securesms.components.audioplay.AudioView
android:id="@+id/attachment_audio"
@@ -29,9 +29,9 @@
<View
android:id="@+id/bottom_divider"
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_height="0dp"
android:layout_gravity="bottom"
android:background="@drawable/compose_divider_background"
android:background="@android:color/transparent"
android:alpha="1" />
<ImageButton
@@ -8,11 +8,17 @@
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:clickable="true"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:clipChildren="false"
android:clipToPadding="false"
android:background="?attr/input_panel_bg_color">
android:background="@drawable/input_panel_rounded_bg"
android:elevation="1dp"
android:outlineProvider="background">
<org.thoughtcrime.securesms.components.QuoteView
android:id="@+id/quote_view"
@@ -25,6 +31,14 @@
app:message_type="preview"
tools:visibility="visible" />
<include
layout="@layout/conversation_activity_attachment_editor_stub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/quote_view" />
<EditText
android:id="@+id/subject_text"
style="@style/ComposeEditText"
@@ -32,6 +46,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
android:hint="@string/subject"
android:inputType="text"
@@ -39,7 +54,7 @@
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/quote_view"
app:layout_constraintTop_toBottomOf="@id/attachment_editor"
tools:visibility="visible" />
<FrameLayout
@@ -69,11 +84,11 @@
android:id="@+id/emoji_toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_gravity="bottom"
android:minHeight="40dp"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:background="@drawable/touch_highlight_background"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:background="@drawable/circle_touch_highlight_background"
android:contentDescription="@string/menu_toggle_keyboard" />
<org.thoughtcrime.securesms.components.ComposeText
@@ -105,11 +120,11 @@
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:src="?quick_camera_icon"
android:paddingLeft="11dp"
android:paddingRight="11dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:background="@drawable/touch_highlight_background"
android:background="@drawable/circle_touch_highlight_background"
android:contentDescription="@string/camera" />
<org.thoughtcrime.securesms.components.MicrophoneRecorderView
@@ -136,15 +151,14 @@
<org.thoughtcrime.securesms.components.AnimatingToggle
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/input_field_frame_layout"
app:layout_constraintBottom_toBottomOf="parent"
android:id="@+id/button_toggle"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="6dp"
android:background="@drawable/send_button_bg"
android:layout_gravity="center_vertical">
android:layout_gravity="bottom">
<ImageButton
android:id="@+id/attach_button"
@@ -4,7 +4,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_margin="8dp"
android:layout_margin="4dp"
android:src="@drawable/conversation_attachment_edit"
android:contentDescription="@string/global_menu_edit_desktop"
android:visibility="gone" />
+23 -22
View File
@@ -4,10 +4,9 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_welcome_gradient"
tools:context=".WelcomeActivity">
<include layout="@layout/status_bar_background" />
<LinearLayout
android:id="@+id/content_container"
android:layout_width="match_parent"
@@ -16,7 +15,7 @@
android:gravity="center_horizontal">
<androidx.legacy.widget.Space
android:layout_weight="3"
android:layout_weight="4"
android:layout_width="match_parent"
android:layout_height="0dp" />
@@ -24,23 +23,25 @@
android:id="@+id/welcome_icon"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:layout_weight="10"
android:gravity="bottom"
android:src="@drawable/intro1"
android:paddingLeft="32dp"
android:paddingRight="32dp"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/welcome_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="4"
android:gravity="center"
android:padding="8dp"
android:src="@drawable/app_logo"
android:paddingLeft="35dp"
android:paddingRight="35dp"
android:scaleType="fitCenter"
android:importantForAccessibility="no" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:src="@drawable/app_name_logo"
android:paddingLeft="35dp"
android:paddingRight="35dp"
android:scaleType="fitCenter"
android:layout_marginBottom="20dp"
android:text="@string/welcome_chat_over_email"
android:textSize="22sp"
android:textStyle="bold" />
android:importantForAccessibility="no" />
<LinearLayout
android:orientation="vertical"
@@ -50,7 +51,7 @@
android:gravity="center_horizontal">
<Button
style="@style/ButtonPrimary"
style="@style/WhiteButtonPrimary"
android:id="@+id/signup_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -60,7 +61,7 @@
android:text="@string/onboarding_create_instant_account" />
<Button
style="@style/ButtonSecondary"
style="@style/WhiteButtonSecondary"
android:id="@+id/add_as_second_device_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -70,7 +71,7 @@
android:text="@string/multidevice_receiver_title" />
<Button
style="@style/ButtonSecondary"
style="@style/WhiteButtonSecondary"
android:id="@+id/backup_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -82,7 +83,7 @@
</LinearLayout>
<androidx.legacy.widget.Space
android:layout_weight="3"
android:layout_weight="4"
android:layout_width="match_parent"
android:layout_height="0dp" />
+1 -1
View File
@@ -24,7 +24,7 @@
<dimen name="message_bubble_bottom_padding">6dp</dimen>
<dimen name="message_bubble_showmore_padding">8dp</dimen>
<dimen name="transparent_footer_padding">2dp</dimen>
<dimen name="media_bubble_remove_button_size">24dp</dimen>
<dimen name="media_bubble_remove_button_size">28dp</dimen>
<dimen name="media_bubble_edit_button_size">24dp</dimen>
<dimen name="media_bubble_default_dimens">210dp</dimen>
<dimen name="media_bubble_min_width">150dp</dimen>
+2 -1
View File
@@ -73,7 +73,7 @@
<!-- Used as an menu entry or button -->
<string name="mark_as_read">Mark as Read</string>
<!-- Used beside an icon with very few space. Shortest text for "Mark as being read". In english, this could be "Read" (past tense of "to read"), in german, this could be "Gelesen". -->
<string name="mark_as_read_short">Mark Read</string>
<string name="mark_as_read_short">Read</string>
<!-- Used as an menu entry or button -->
<string name="mark_as_unread">Mark as Unread</string>
<!-- Used beside an icon with very few space. Shortest text for "Mark as being unread". In english, this could be "Unread" (past tense of "to unread"), in german, this could be "Ungelesen". -->
@@ -435,6 +435,7 @@
<string name="canceled_call">Canceled call</string>
<string name="missed_call">Missed call</string>
<string name="already_in_call">Already in a call</string>
<string name="call_back">Call back</string>
<string name="call_answered_elsewhere">Call answered on another device</string>
<string name="call_requires_camera_permission">Camera permission is required for video calls</string>
<string name="call_requires_mic_permission">Microphone permission is required for calls</string>
+17
View File
@@ -129,6 +129,23 @@
<item name="android:background">@drawable/button_secondary_background</item>
</style>
<style name="WhiteButtonPrimary">
<item name="drawableTint">@color/def_primary</item>
<item name="android:textColor">@color/def_primary</item>
<item name="android:paddingLeft">8dp</item>
<item name="android:paddingRight">8dp</item>
<item name="android:background">@drawable/white_button_bg</item>
</style>
<style name="WhiteButtonSecondary">
<item name="drawableTint">@color/white</item>
<item name="android:textColor">@color/white</item>
<item name="android:drawablePadding">5dp</item>
<item name="android:paddingLeft">8dp</item>
<item name="android:paddingRight">8dp</item>
<item name="android:background">@drawable/white_button_secondary_background</item>
</style>
<style
name="PreferenceThemeOverlay.Fix"
parent="PreferenceThemeOverlay.v14.Material">