mirror of
https://github.com/ArcaneChat/android.git
synced 2026-07-03 14:05:24 +02:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 923e2a43a4 | |||
| f29f94f138 | |||
| 3a35374a03 | |||
| fe0793481c | |||
| 3c2a672799 | |||
| 2476d17394 | |||
| 5ae021bde3 | |||
| 9f46c242fc | |||
| cf36254c1a | |||
| 0d94bf2006 | |||
| 4757150aae | |||
| 0d76ab930f | |||
| 5515e2fe3c | |||
| 508f320b4c | |||
| 176e6c94b2 | |||
| deb650970c | |||
| 36fc2261f9 | |||
| f11c6baaf6 | |||
| 6add250258 | |||
| d15efbdb44 | |||
| 2422f9110c | |||
| b6162c2044 | |||
| 75245cfa02 | |||
| 0d19f058fe | |||
| 911e695187 | |||
| b8c8969162 | |||
| 0610520064 | |||
| 5ae60af73f | |||
| dd6f1015dc | |||
| 74ea10ebef | |||
| 877f991131 | |||
| e556878b2f | |||
| 99a1fee994 | |||
| dfd323a89f | |||
| 5a086ff022 | |||
| bedaa2c287 | |||
| 50037d5232 | |||
| 6756b04da9 | |||
| b12ecf66c5 | |||
| d05403db02 | |||
| 2d8344a61f | |||
| 44afda55e5 | |||
| bfa6eef604 | |||
| 6327b30350 | |||
| f92cac4d55 | |||
| 0204fca27d |
@@ -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 =
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user