mirror of
https://github.com/ArcaneChat/android.git
synced 2026-07-03 14:05:24 +02:00
Compare commits
83 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 | |||
| eed844469b | |||
| 59ceae46a0 | |||
| 693188dcff | |||
| 025ff086c8 | |||
| a802bfcb61 | |||
| 1f18bfe2dd | |||
| dc1bc77925 | |||
| ac6383d79a | |||
| 59fec8462c | |||
| d64baa294f | |||
| e7e14af158 | |||
| e094e96669 | |||
| fa56e8ca0d | |||
| 3d5abdcd79 | |||
| 92c4e41b59 | |||
| bbfd0c31da | |||
| d5f627be50 | |||
| f0ca58d53f | |||
| 56f5655693 | |||
| 310a7f99c8 | |||
| d0f7fca8c9 | |||
| 80de08f980 | |||
| 80a0ff0098 | |||
| 9c55de17a3 | |||
| 8c1bc8e70e | |||
| f0760d6695 | |||
| 6d038e9d7f | |||
| 46be278bf5 | |||
| 1577b90047 | |||
| 515a84b161 | |||
| d757ef59c0 | |||
| d0a584d15d | |||
| f8ec6d2da2 | |||
| c3b7e06b86 | |||
| d5edcea5d0 | |||
| 8a3d29fca3 | |||
| 6156ee0534 |
@@ -25,7 +25,7 @@ jobs:
|
||||
- uses: nttld/setup-ndk@ed92fe6cadad69be94a966a7ee3271275e62f779 # v1.6.0
|
||||
id: setup-ndk
|
||||
with:
|
||||
ndk-version: r27
|
||||
ndk-version: "r29"
|
||||
|
||||
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
- uses: nttld/setup-ndk@ed92fe6cadad69be94a966a7ee3271275e62f779 # v1.6.0
|
||||
id: setup-ndk
|
||||
with:
|
||||
ndk-version: r27
|
||||
ndk-version: "r29"
|
||||
|
||||
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
|
||||
with:
|
||||
@@ -77,12 +77,14 @@ jobs:
|
||||
|
||||
- name: Upload APK
|
||||
id: upload
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: app-preview.apk
|
||||
path: 'build/outputs/apk/foss/debug/*.apk'
|
||||
|
||||
- name: Add artifact links to PR
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
ARTIFACT_URL: ${{ steps.upload.outputs.artifact-url }}
|
||||
|
||||
+20
-1
@@ -1,5 +1,24 @@
|
||||
# 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
|
||||
|
||||
* Use message style notifications for longer message previews
|
||||
* Remove notification after audio playback ends
|
||||
* Fix: do not allow blocked contacts to use our invite links
|
||||
* Fix sending mini-app that was used/prepared before sending
|
||||
* Some more small fixes and updated translations
|
||||
* Update to core 2.53.0
|
||||
|
||||
## v2.52.0
|
||||
2026-06
|
||||
|
||||
@@ -7,7 +26,7 @@
|
||||
* Fix: Incorrect total time when attaching audio files as draft
|
||||
* Fix: Audio files in draft showing total time from wrong file
|
||||
* Fix: Update the channel title after joining if the QR code had an outdated title
|
||||
* Voice recording will be automatically saved as draft when interrupted
|
||||
* Voice recording will be automatically saved as draft when interrupted
|
||||
* Update to core 2.52.0
|
||||
|
||||
## v2.51.0
|
||||
|
||||
+2
-2
@@ -34,8 +34,8 @@ android {
|
||||
useLibrary 'org.apache.http.legacy'
|
||||
|
||||
defaultConfig {
|
||||
versionCode 30000745
|
||||
versionName "2.52.0"
|
||||
versionCode 30000746
|
||||
versionName "2.53.0"
|
||||
|
||||
applicationId "chat.delta.lite"
|
||||
multiDexEnabled true
|
||||
|
||||
+1
-1
Submodule jni/deltachat-core-rust updated: 4ecf597ba3...9435042594
@@ -84,7 +84,7 @@ public class DcAccounts {
|
||||
public boolean isAllChatmail() {
|
||||
for (int accountId : getAll()) {
|
||||
DcContext dcContext = getAccount(accountId);
|
||||
if (!dcContext.isChatmail()) {
|
||||
if (dcContext.getConfigInt("is_chatmail") == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,10 +362,6 @@ public class DcContext {
|
||||
return displayname;
|
||||
}
|
||||
|
||||
public boolean isChatmail() {
|
||||
return getConfigInt("is_chatmail") == 1;
|
||||
}
|
||||
|
||||
public boolean isMuted() {
|
||||
return getConfigInt("is_muted") == 1;
|
||||
}
|
||||
|
||||
@@ -302,7 +302,10 @@ public class ApplicationContext extends MultiDexApplication {
|
||||
Log.i(
|
||||
"DeltaChat",
|
||||
"++++++++++++++++++ NetworkCallback.onAvailable() #" + debugOnAvailableCount++);
|
||||
getDcAccounts().maybeNetwork();
|
||||
// onBlockedStatusChanged is only available on API 29+
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
getDcAccounts().maybeNetwork();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -310,8 +313,13 @@ public class ApplicationContext extends MultiDexApplication {
|
||||
@NonNull android.net.Network network, boolean blocked) {
|
||||
Log.i(
|
||||
"DeltaChat",
|
||||
"++++++++++++++++++ NetworkCallback.onBlockedStatusChanged() #"
|
||||
"++++++++++++++++++ NetworkCallback.onBlockedStatusChanged("
|
||||
+ blocked
|
||||
+ ") #"
|
||||
+ debugOnBlockedStatusChangedCount++);
|
||||
if (!blocked) {
|
||||
getDcAccounts().maybeNetwork();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -260,12 +260,11 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
|
||||
final String addr = extraEmail[0];
|
||||
int contactId = dcContext.lookupContactIdByAddr(addr);
|
||||
|
||||
if (contactId == 0) {
|
||||
contactId = dcContext.createContact(null, addr);
|
||||
if (contactId != 0 || dcContext.getConfigInt(DcHelper.CONFIG_FORCE_ENCRYPTION) == 0) {
|
||||
if (contactId == 0) contactId = dcContext.createContact(null, addr);
|
||||
chatId = dcContext.createChatByContactId(contactId);
|
||||
accId = dcContext.getAccountId();
|
||||
}
|
||||
|
||||
chatId = dcContext.createChatByContactId(contactId);
|
||||
accId = dcContext.getAccountId();
|
||||
}
|
||||
Intent composeIntent;
|
||||
if (accId != -1 && chatId > 0) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -51,7 +51,6 @@ import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import org.json.JSONObject;
|
||||
import org.thoughtcrime.securesms.connect.AccountManager;
|
||||
import org.thoughtcrime.securesms.connect.DcEventCenter;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.util.IntentUtils;
|
||||
@@ -62,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";
|
||||
@@ -72,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 = "";
|
||||
@@ -113,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) {
|
||||
@@ -121,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);
|
||||
@@ -140,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);
|
||||
@@ -150,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)
|
||||
@@ -201,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());
|
||||
@@ -220,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
|
||||
@@ -516,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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,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);
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -264,7 +264,10 @@ public class AudioPlaybackViewModel extends ViewModel {
|
||||
updateCurrentState(false);
|
||||
} else if (player.getPlaybackState() == Player.STATE_ENDED
|
||||
&& !player.hasNextMediaItem()) {
|
||||
mediaController.setPlayWhenReady(false);
|
||||
mediaController.stop();
|
||||
mediaController.clearMediaItems();
|
||||
stopUpdateProgress();
|
||||
playbackState.setValue(AudioPlaybackState.idle());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public class DcContactsLoader extends AsyncLoader<DcContactsLoader.Ret> {
|
||||
if (query == null && addScanQRLink) {
|
||||
additional_items = Util.appendInt(additional_items, DcContact.DC_CONTACT_ID_QR_INVITE);
|
||||
}
|
||||
if (addCreateContactLink && !dcContext.isChatmail()) {
|
||||
if (addCreateContactLink && dcContext.getConfigInt(DcHelper.CONFIG_FORCE_ENCRYPTION) == 0) {
|
||||
additional_items =
|
||||
Util.appendInt(additional_items, DcContact.DC_CONTACT_ID_NEW_CLASSIC_CONTACT);
|
||||
}
|
||||
@@ -54,7 +54,7 @@ public class DcContactsLoader extends AsyncLoader<DcContactsLoader.Ret> {
|
||||
additional_items = Util.appendInt(additional_items, DcContact.DC_CONTACT_ID_NEW_GROUP);
|
||||
additional_items = Util.appendInt(additional_items, DcContact.DC_CONTACT_ID_NEW_BROADCAST);
|
||||
|
||||
if (!dcContext.isChatmail()) {
|
||||
if (dcContext.getConfigInt(DcHelper.CONFIG_FORCE_ENCRYPTION) == 0) {
|
||||
additional_items =
|
||||
Util.appendInt(additional_items, DcContact.DC_CONTACT_ID_NEW_UNENCRYPTED_GROUP);
|
||||
}
|
||||
|
||||
@@ -528,16 +528,20 @@ public class DcHelper {
|
||||
.show();
|
||||
}
|
||||
|
||||
public static void showInvalidUnencryptedDialog(Context context) {
|
||||
new AlertDialog.Builder(context)
|
||||
public static AlertDialog.Builder prepareInvalidUnencryptedDialog(
|
||||
Context context, AlertDialog.Builder builder) {
|
||||
return builder
|
||||
.setMessage(context.getString(R.string.invalid_unencrypted_explanation))
|
||||
.setNeutralButton(R.string.learn_more, (d, w) -> openHelp(context, "#howtoe2ee"))
|
||||
.setNegativeButton(
|
||||
R.string.qrscan_title,
|
||||
(d, w) -> context.startActivity(new Intent(context, QrActivity.class)))
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.setCancelable(true)
|
||||
.show();
|
||||
.setCancelable(true);
|
||||
}
|
||||
|
||||
public static void showInvalidUnencryptedDialog(Context context) {
|
||||
prepareInvalidUnencryptedDialog(context, new AlertDialog.Builder(context)).show();
|
||||
}
|
||||
|
||||
public static void openHelp(Context context, String section) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.RingtoneManager;
|
||||
@@ -23,13 +24,18 @@ import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.core.app.Person;
|
||||
import androidx.core.app.RemoteInput;
|
||||
import androidx.core.app.TaskStackBuilder;
|
||||
import androidx.core.content.pm.ShortcutInfoCompat;
|
||||
import androidx.core.content.pm.ShortcutManagerCompat;
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcContact;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.HashMap;
|
||||
@@ -42,6 +48,7 @@ import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.ConversationActivity;
|
||||
import org.thoughtcrime.securesms.ConversationListActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.ShareActivity;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference;
|
||||
@@ -61,8 +68,18 @@ public class NotificationCenter {
|
||||
private volatile long lastAudibleNotification = 0;
|
||||
private static final long MIN_AUDIBLE_PERIOD_MILLIS = TimeUnit.SECONDS.toMillis(2);
|
||||
|
||||
// Map<accountId, Map<chatId, lines>, contains the last lines of each chat for each account
|
||||
private final HashMap<Integer, HashMap<Integer, LinkedHashMap<Integer, String>>> inboxes =
|
||||
private static class NotifData {
|
||||
final Person sender;
|
||||
final String text;
|
||||
|
||||
NotifData(Person sender, String text) {
|
||||
this.sender = sender;
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
|
||||
// notification history of each chat for each account
|
||||
private final HashMap<Integer, HashMap<Integer, LinkedHashMap<Integer, NotifData>>> inboxes =
|
||||
new HashMap<>();
|
||||
|
||||
public NotificationCenter(Context context) {
|
||||
@@ -410,31 +427,42 @@ public class NotificationCenter {
|
||||
DcMsg dcMsg = dcContext.getMsg(msgId);
|
||||
NotificationPrivacyPreference privacy = Prefs.getNotificationPrivacy(context);
|
||||
|
||||
String shortLine =
|
||||
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)
|
||||
: context.getString(R.string.notify_new_message);
|
||||
String shortLine = text;
|
||||
if (dcChat.isMultiUser() && privacy.isDisplayContact()) {
|
||||
shortLine =
|
||||
dcMsg.getSenderName(dcContext.getContact(dcMsg.getFromId())) + ": " + shortLine;
|
||||
shortLine = senderName + ": " + text;
|
||||
}
|
||||
String tickerLine = shortLine;
|
||||
if (!dcChat.isMultiUser() && privacy.isDisplayContact()) {
|
||||
tickerLine =
|
||||
dcMsg.getSenderName(dcContext.getContact(dcMsg.getFromId())) + ": " + tickerLine;
|
||||
|
||||
if (dcMsg.getOverrideSenderName() != null) {
|
||||
// There is an "overridden" display name on the message, so, we need to prepend the
|
||||
// display name to the message,
|
||||
// i.e. set the shortLine to be the same as the tickerLine.
|
||||
shortLine = tickerLine;
|
||||
}
|
||||
NotifData notifData =
|
||||
new NotifData(
|
||||
new Person.Builder()
|
||||
.setName(senderName)
|
||||
.setIcon(getAvatarIcon(sender))
|
||||
.setBot(sender.isBot())
|
||||
.setKey(personId)
|
||||
.build(),
|
||||
text);
|
||||
|
||||
String tickerLine = text;
|
||||
if (privacy.isDisplayContact()) {
|
||||
tickerLine = senderName + ": " + text;
|
||||
}
|
||||
|
||||
DcMsg quotedMsg = dcMsg.getQuotedMsg();
|
||||
boolean isMention = dcChat.isMultiUser() && quotedMsg != null && quotedMsg.isOutgoing();
|
||||
|
||||
maybeAddNotification(accountId, dcChat, msgId, shortLine, tickerLine, true, isMention);
|
||||
maybeAddNotification(accountId, dcChat, msgId, notifData, tickerLine, true, isMention);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -450,16 +478,27 @@ public class NotificationCenter {
|
||||
// just do nothing.
|
||||
}
|
||||
|
||||
DcContact sender = dcContext.getContact(contactId);
|
||||
String shortLine =
|
||||
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());
|
||||
|
||||
NotifData notifData =
|
||||
new NotifData(
|
||||
new Person.Builder()
|
||||
.setName(contact.getDisplayName())
|
||||
.setIcon(getAvatarIcon(contact))
|
||||
.setBot(contact.isBot())
|
||||
.setKey(accountId + "-" + contactId)
|
||||
.build(),
|
||||
text);
|
||||
|
||||
maybeAddNotification(
|
||||
accountId, dcChat, msgId, shortLine, shortLine, false, dcChat.isMultiUser());
|
||||
accountId, dcChat, msgId, notifData, text, false, dcChat.isMultiUser());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -473,6 +512,7 @@ public class NotificationCenter {
|
||||
|
||||
DcContext dcContext = context.getDcAccounts().getAccount(accountId);
|
||||
DcMsg dcMsg = dcContext.getMsg(msgId);
|
||||
DcChat dcChat = dcContext.getChat(dcMsg.getChatId());
|
||||
DcMsg parentMsg;
|
||||
if (dcMsg.getType() == DcMsg.DC_MSG_WEBXDC) {
|
||||
parentMsg = dcMsg;
|
||||
@@ -486,10 +526,34 @@ public class NotificationCenter {
|
||||
|
||||
JSONObject info = parentMsg.getWebxdcInfo();
|
||||
final String name = JsonUtils.optString(info, "name");
|
||||
String shortLine = name.isEmpty() ? text : (name + ": " + text);
|
||||
DcChat dcChat = dcContext.getChat(dcMsg.getChatId());
|
||||
String tickerLine = name.isEmpty() ? text : (name + ": " + text);
|
||||
|
||||
NotifData notifData;
|
||||
if (dcChat.isMultiUser()) {
|
||||
byte[] blob = parentMsg.getWebxdcBlob(JsonUtils.optString(info, "icon"));
|
||||
notifData =
|
||||
new NotifData(
|
||||
new Person.Builder()
|
||||
.setName(name)
|
||||
.setIcon(getAvatarIcon(blob))
|
||||
.setKey(accountId + "-webxdc-" + msgId)
|
||||
.build(),
|
||||
text);
|
||||
} else {
|
||||
DcContact sender = dcContext.getContact(contactId);
|
||||
notifData =
|
||||
new NotifData(
|
||||
new Person.Builder()
|
||||
.setName(sender.getDisplayName())
|
||||
.setIcon(getAvatarIcon(sender))
|
||||
.setBot(sender.isBot())
|
||||
.setKey(accountId + "-" + contactId)
|
||||
.build(),
|
||||
tickerLine);
|
||||
}
|
||||
|
||||
maybeAddNotification(
|
||||
accountId, dcChat, msgId, shortLine, shortLine, false, dcChat.isMultiUser());
|
||||
accountId, dcChat, msgId, notifData, tickerLine, false, dcChat.isMultiUser());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -498,7 +562,7 @@ public class NotificationCenter {
|
||||
int accountId,
|
||||
DcChat dcChat,
|
||||
int msgId,
|
||||
String shortLine,
|
||||
NotifData notifData,
|
||||
String tickerLine,
|
||||
boolean playInChatSound,
|
||||
boolean isMention) {
|
||||
@@ -538,20 +602,23 @@ public class NotificationCenter {
|
||||
// the user may eg. have chosen a different sound
|
||||
String notificationChannel = getNotificationChannel(notificationManager, chatData, dcChat);
|
||||
|
||||
LinkedHashMap<Integer, String> messagesForInbox = null;
|
||||
if (privacy.isDisplayContact() && privacy.isDisplayMessage()) {
|
||||
LinkedHashMap<Integer, NotifData> messagesForInbox = null;
|
||||
if (privacy.isDisplayContact()) {
|
||||
synchronized (inboxes) {
|
||||
HashMap<Integer, LinkedHashMap<Integer, String>> accountInbox = inboxes.get(accountId);
|
||||
HashMap<Integer, LinkedHashMap<Integer, NotifData>> accountInbox = inboxes.get(accountId);
|
||||
if (accountInbox == null) {
|
||||
accountInbox = new HashMap<>();
|
||||
inboxes.put(accountId, accountInbox);
|
||||
}
|
||||
LinkedHashMap<Integer, String> messages = accountInbox.get(chatId);
|
||||
LinkedHashMap<Integer, NotifData> messages = accountInbox.get(chatId);
|
||||
if (messages == null) {
|
||||
messages = new LinkedHashMap<>();
|
||||
accountInbox.put(chatId, messages);
|
||||
}
|
||||
messages.put(msgId, shortLine);
|
||||
if (!privacy.isDisplayMessage()) {
|
||||
messages.clear();
|
||||
}
|
||||
messages.put(msgId, notifData);
|
||||
messagesForInbox = new LinkedHashMap<>(messages);
|
||||
}
|
||||
}
|
||||
@@ -564,7 +631,7 @@ public class NotificationCenter {
|
||||
dcContext,
|
||||
dcChat,
|
||||
notificationChannel,
|
||||
shortLine,
|
||||
notifData.text,
|
||||
tickerLine,
|
||||
signal,
|
||||
messagesForInbox,
|
||||
@@ -583,7 +650,7 @@ public class NotificationCenter {
|
||||
String contentText,
|
||||
String ticker,
|
||||
boolean signal,
|
||||
LinkedHashMap<Integer, String> messagesForInbox,
|
||||
LinkedHashMap<Integer, NotifData> messagesForInbox,
|
||||
int messageCount,
|
||||
boolean includeSummary) {
|
||||
try {
|
||||
@@ -658,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 =
|
||||
@@ -694,14 +761,42 @@ public class NotificationCenter {
|
||||
}
|
||||
}
|
||||
|
||||
// Create inbox style (gets visible if the notification is expanded)
|
||||
if (privacy.isDisplayContact() && privacy.isDisplayMessage() && messagesForInbox != null) {
|
||||
// Create messaging style
|
||||
if (privacy.isDisplayContact() && messagesForInbox != null) {
|
||||
try {
|
||||
NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
|
||||
for (String line : messagesForInbox.values()) {
|
||||
inboxStyle.addLine(line);
|
||||
Intent viewChatIntent = new Intent(context, ShareActivity.class);
|
||||
viewChatIntent.setAction(Intent.ACTION_SEND);
|
||||
viewChatIntent.putExtra(ShareActivity.EXTRA_ACC_ID, dcContext.getAccountId());
|
||||
viewChatIntent.putExtra(ShareActivity.EXTRA_CHAT_ID, dcChat.getId());
|
||||
|
||||
ShortcutInfoCompat shortcut =
|
||||
new ShortcutInfoCompat.Builder(
|
||||
context, "chat-" + dcContext.getAccountId() + "-" + dcChat.getId())
|
||||
.setShortLabel(dcChat.getName())
|
||||
.setLongLived(true)
|
||||
.setIntent(viewChatIntent)
|
||||
.build();
|
||||
ShortcutManagerCompat.pushDynamicShortcut(context, shortcut);
|
||||
builder.setShortcutInfo(shortcut);
|
||||
|
||||
DcContact selfContact = dcContext.getContact(DcContact.DC_CONTACT_ID_SELF);
|
||||
Person self =
|
||||
new Person.Builder()
|
||||
.setName(selfContact.getDisplayName())
|
||||
.setIcon(getAvatarIcon(selfContact))
|
||||
.setKey(accountId + "-" + selfContact.getId())
|
||||
.build();
|
||||
NotificationCompat.MessagingStyle style = new NotificationCompat.MessagingStyle(self);
|
||||
if (dcChat.isMultiUser()) {
|
||||
style.setGroupConversation(true);
|
||||
style.setConversationTitle(dcChat.getName());
|
||||
}
|
||||
builder.setStyle(inboxStyle);
|
||||
for (Map.Entry<Integer, NotifData> msgEntry : messagesForInbox.entrySet()) {
|
||||
long timestamp_ms = dcContext.getMsg(msgEntry.getKey()).getSortTimestamp() * 1000;
|
||||
NotifData notifData = msgEntry.getValue();
|
||||
style.addMessage(notifData.text, timestamp_ms, notifData.sender);
|
||||
}
|
||||
builder.setStyle(style);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
@@ -748,7 +843,7 @@ public class NotificationCenter {
|
||||
|
||||
@WorkerThread
|
||||
private void rebuildNotification(
|
||||
int accountId, int chatId, LinkedHashMap<Integer, String> messages) {
|
||||
int accountId, int chatId, LinkedHashMap<Integer, NotifData> messages) {
|
||||
try {
|
||||
DcContext dcContext = ApplicationContext.getDcAccounts().getAccount(accountId);
|
||||
DcChat dcChat = dcContext.getChat(chatId);
|
||||
@@ -766,9 +861,9 @@ public class NotificationCenter {
|
||||
// Get the latest message ID (last entry in LinkedHashMap)
|
||||
Integer latestMsgId = null;
|
||||
String lastLine = null;
|
||||
for (Map.Entry<Integer, String> entry : messages.entrySet()) {
|
||||
for (Map.Entry<Integer, NotifData> entry : messages.entrySet()) {
|
||||
latestMsgId = entry.getKey();
|
||||
lastLine = entry.getValue();
|
||||
lastLine = entry.getValue().text;
|
||||
}
|
||||
if (latestMsgId == null || lastLine == null) {
|
||||
return;
|
||||
@@ -797,8 +892,35 @@ public class NotificationCenter {
|
||||
}
|
||||
}
|
||||
|
||||
public Bitmap getAvatar(DcChat dcChat) {
|
||||
Recipient recipient = new Recipient(context, dcChat);
|
||||
private static @Nullable IconCompat getAvatarIcon(byte[] blob) {
|
||||
if (blob == null) {
|
||||
return null;
|
||||
}
|
||||
ByteArrayInputStream is = new ByteArrayInputStream(blob);
|
||||
BitmapDrawable drawable = (BitmapDrawable) Drawable.createFromStream(is, "icon");
|
||||
Bitmap bitmap = drawable.getBitmap();
|
||||
return IconCompat.createWithBitmap(bitmap);
|
||||
}
|
||||
|
||||
private @Nullable IconCompat getAvatarIcon(DcChat dcChat) {
|
||||
Bitmap avatar = getAvatar(dcChat);
|
||||
return avatar != null ? IconCompat.createWithBitmap(avatar) : null;
|
||||
}
|
||||
|
||||
private @Nullable IconCompat getAvatarIcon(DcContact dcContact) {
|
||||
Bitmap avatar = getAvatar(dcContact);
|
||||
return avatar != null ? IconCompat.createWithBitmap(avatar) : null;
|
||||
}
|
||||
|
||||
private @Nullable Bitmap getAvatar(DcChat dcChat) {
|
||||
return getAvatar(new Recipient(context, dcChat));
|
||||
}
|
||||
|
||||
private @Nullable Bitmap getAvatar(DcContact dcContact) {
|
||||
return getAvatar(new Recipient(context, dcContact));
|
||||
}
|
||||
|
||||
private @Nullable Bitmap getAvatar(Recipient recipient) {
|
||||
try {
|
||||
Drawable drawable;
|
||||
ContactPhoto contactPhoto = recipient.getContactPhoto(context);
|
||||
@@ -837,12 +959,12 @@ public class NotificationCenter {
|
||||
public void removeNotification(int accountId, int chatId, int msgId) {
|
||||
boolean shouldCancelNotification = false;
|
||||
boolean removeSummary = false;
|
||||
LinkedHashMap<Integer, String> remainingMessages = null;
|
||||
LinkedHashMap<Integer, NotifData> remainingMessages = null;
|
||||
|
||||
synchronized (inboxes) {
|
||||
HashMap<Integer, LinkedHashMap<Integer, String>> accountInbox = inboxes.get(accountId);
|
||||
HashMap<Integer, LinkedHashMap<Integer, NotifData>> accountInbox = inboxes.get(accountId);
|
||||
if (accountInbox != null) {
|
||||
LinkedHashMap<Integer, String> messages = accountInbox.get(chatId);
|
||||
LinkedHashMap<Integer, NotifData> messages = accountInbox.get(chatId);
|
||||
if (messages != null) {
|
||||
messages.remove(msgId);
|
||||
|
||||
@@ -875,7 +997,7 @@ public class NotificationCenter {
|
||||
public void removeNotifications(int accountId, int chatId) {
|
||||
boolean removeSummary;
|
||||
synchronized (inboxes) {
|
||||
HashMap<Integer, LinkedHashMap<Integer, String>> accountInbox = inboxes.get(accountId);
|
||||
HashMap<Integer, LinkedHashMap<Integer, NotifData>> accountInbox = inboxes.get(accountId);
|
||||
if (accountInbox == null) {
|
||||
accountInbox = new HashMap<>();
|
||||
}
|
||||
@@ -901,7 +1023,7 @@ public class NotificationCenter {
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
String tag = String.valueOf(accountId);
|
||||
synchronized (inboxes) {
|
||||
HashMap<Integer, LinkedHashMap<Integer, String>> accountInbox = inboxes.get(accountId);
|
||||
HashMap<Integer, LinkedHashMap<Integer, NotifData>> accountInbox = inboxes.get(accountId);
|
||||
notificationManager.cancel(tag, ID_MSG_SUMMARY);
|
||||
if (accountInbox != null) {
|
||||
for (Integer chatId : accountInbox.keySet()) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.google.android.material.tabs.TabLayoutMediator;
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
@@ -120,10 +121,12 @@ public class QrActivity extends BaseActionBarActivity implements View.OnClickLis
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
DcContext dcContext = DcHelper.getContext(this);
|
||||
|
||||
menu.clear();
|
||||
getMenuInflater().inflate(R.menu.qr_show, menu);
|
||||
menu.findItem(R.id.new_classic_contact)
|
||||
.setVisible(!scanRelay && !DcHelper.getContext(this).isChatmail());
|
||||
.setVisible(!scanRelay && dcContext.getConfigInt(DcHelper.CONFIG_FORCE_ENCRYPTION) == 0);
|
||||
|
||||
Util.redMenuItem(menu, R.id.withdraw);
|
||||
if (tabLayout.getSelectedTabPosition() == TAB_SCAN) {
|
||||
|
||||
@@ -314,6 +314,12 @@ public class QrCodeHandler {
|
||||
|
||||
private void showFingerprintOrQrSuccess(
|
||||
AlertDialog.Builder builder, DcLot qrParsed, String name) {
|
||||
if (qrParsed.getState() == DcContext.DC_QR_ADDR
|
||||
&& dcContext.getConfigInt(DcHelper.CONFIG_FORCE_ENCRYPTION) == 1) {
|
||||
DcHelper.prepareInvalidUnencryptedDialog(activity, builder);
|
||||
return;
|
||||
}
|
||||
|
||||
@StringRes
|
||||
int resId =
|
||||
qrParsed.getState() == DcContext.DC_QR_ADDR
|
||||
|
||||
@@ -57,22 +57,26 @@ public class LongClickCopySpan extends ClickableSpan {
|
||||
DcContext dcContext = DcHelper.getContext(activity);
|
||||
|
||||
int contactId = dcContext.lookupContactIdByAddr(addr);
|
||||
if (contactId == 0 && dcContext.mayBeValidAddr(addr)) {
|
||||
contactId = dcContext.createContact(null, addr);
|
||||
}
|
||||
DcContact contact = dcContext.getContact(contactId);
|
||||
if (contact.getId() != 0
|
||||
DcContact contact = (contactId != 0) ? dcContext.getContact(contactId) : null;
|
||||
if (contact != null
|
||||
&& !contact.isBlocked()
|
||||
&& dcContext.getChatIdByContactId(contact.getId()) != 0) {
|
||||
openChat(activity, contact);
|
||||
} else if (contact == null
|
||||
&& dcContext.getConfigInt(DcHelper.CONFIG_FORCE_ENCRYPTION) == 1) {
|
||||
DcHelper.showInvalidUnencryptedDialog(activity);
|
||||
} else {
|
||||
String name = contact != null ? contact.getDisplayName() : addr;
|
||||
new AlertDialog.Builder(activity)
|
||||
.setMessage(
|
||||
activity.getString(R.string.ask_start_chat_with, contact.getDisplayName()))
|
||||
.setMessage(activity.getString(R.string.ask_start_chat_with, name))
|
||||
.setPositiveButton(
|
||||
android.R.string.ok,
|
||||
(dialog, which) -> {
|
||||
openChat(activity, contact);
|
||||
openChat(
|
||||
activity,
|
||||
contact == null
|
||||
? dcContext.getContact(dcContext.createContact(null, addr))
|
||||
: contact);
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -272,7 +272,7 @@
|
||||
<!-- consider keeping the term "channel" as in WhatsApp or Telegram -->
|
||||
<string name="channels">Canals</string>
|
||||
<!-- consider keeping the term "channel" as in WhatsApp or Telegram -->
|
||||
<string name="new_channel">Nou Canal</string>
|
||||
<string name="new_channel">Canal nou</string>
|
||||
<!-- consider keeping the term "channel" as in WhatsApp or Telegram -->
|
||||
<string name="channel_name">Nom del Canal</string>
|
||||
<!-- The number of times a message is shown in channels. Use what other messengers are using here, smth. as "N-times seen" or "1 Aufruf" (german) work as well, where "1 impression" or "1 hit" may sound too formal and may be more marketing slang. -->
|
||||
|
||||
@@ -673,7 +673,7 @@
|
||||
<string name="welcome_chat_over_email">Sicherer dezentraler Chat</string>
|
||||
<string name="scan_invitation_code">Einladungscode scannen</string>
|
||||
<string name="login_title">Login</string>
|
||||
<string name="login_advanced_hint">Für fortgeschrittene Benutzer:\n\nVerwende keine Adresse, die du bereits in einer anderen App nutzt.</string>
|
||||
<string name="login_advanced_hint">Für fortgeschrittene Benutzer.\n\nVerwende keine Adresse, die du bereits in einer anderen App nutzt.</string>
|
||||
<string name="login_inbox">Posteingang</string>
|
||||
<string name="login_imap_login">IMAP-Anmeldename</string>
|
||||
<string name="login_imap_server">IMAP-Server</string>
|
||||
|
||||
+397
-187
File diff suppressed because it is too large
Load Diff
@@ -450,6 +450,7 @@
|
||||
<string name="call_answered_elsewhere">Chiamata risposta su un altro dispositivo</string>
|
||||
<string name="call_requires_camera_permission">Per le videochiamate è necessaria l\'autorizzazione per l\'utilizzo della fotocamera.</string>
|
||||
<string name="call_requires_mic_permission">Per le chiamate è necessaria l\'autorizzazione per l\'utilizzo del microfono.</string>
|
||||
<string name="call_requires_connection">Impossibile avviare la chiamata. Assicurati che il tuo dispositivo disponga di una connessione a Internet e riprova.</string>
|
||||
<!-- Used e.g. in a notifications to describe an ongoing call. "Call" is a noun here, in the meaning of "This is the call with ..." where the placeholder is replaced by the name of the contact. -->
|
||||
<string name="call_with">Chiama con %1$s</string>
|
||||
<!-- Use e.g. in a notifications during ringing. Placeholder is relaced by the name of the contact being called. -->
|
||||
@@ -690,6 +691,7 @@
|
||||
<string name="welcome_chat_over_email">Chat Decentralizzata Sicura</string>
|
||||
<string name="scan_invitation_code">Scansiona Codice Invito</string>
|
||||
<string name="login_title">Accedi</string>
|
||||
<string name="login_advanced_hint">Questo login è riservato agli utenti avanzati.\n\nNon utilizzare un indirizzo che stai già usando in un\'altra app.</string>
|
||||
<string name="login_inbox">In Arrivo</string>
|
||||
<string name="login_imap_login">Nome Accesso IMAP</string>
|
||||
<string name="login_imap_server">Server IMAP</string>
|
||||
@@ -724,6 +726,7 @@
|
||||
<string name="used_for_sending">Utilizzato per l\'invio</string>
|
||||
<string name="hide_from_contacts">Nascondi dai Contatti</string>
|
||||
<string name="hidden_from_contacts">Nascosto dai contatti</string>
|
||||
<string name="enforce_e2ee">Applica Crittografia a Tutti i Ripetitori</string>
|
||||
<!-- Hint for the list of relays -->
|
||||
<string name="transport_list_hint">I messaggi vengono ricevuti su tutti i ripetitori.\n\n⚠️ Se modifichi qualcosa qui, assicurati che tutti i tuoi dispositivi abbiano almeno la versione 2.47.0. Altrimenti i dispositivi più vecchi potrebbero non ricevere i messaggi.</string>
|
||||
<!-- shown if a QR code was scanned that can be used as a relay -->
|
||||
|
||||
@@ -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