mirror of
https://github.com/ArcaneChat/android.git
synced 2026-07-03 14:05:24 +02:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 302663055c | |||
| aafa3e13fe | |||
| d9cb575e68 | |||
| cff35c5380 |
@@ -1,6 +1,6 @@
|
||||
# Delta Chat Android Changelog
|
||||
|
||||
## 2.51.0
|
||||
## v2.50.0
|
||||
2026-05
|
||||
|
||||
* Better incoming call system integration
|
||||
@@ -34,16 +34,11 @@
|
||||
* Resend the last 10 messages to new broadcast channel member
|
||||
* Enable PQC (Post-Quantum Cryptography) support. We do not generate PQC keys yet, this step is needed for forward compatibility
|
||||
* Improve avatar quality
|
||||
* Add new webxdc.isAppSender and webxdc.isBroadcast APIs for mini-apps
|
||||
* Fix: avoid invalid empty "~" notifications when some peer is streaming location
|
||||
* Fix: Improve detection of stickers
|
||||
* Fix text direction issues for RTL languages in "Show full message" view
|
||||
* Fix: Reconnect when removing a relay
|
||||
* Fix: Location streaming now works correctly with multiple accounts
|
||||
* Fix debouncing in chatlist and search
|
||||
* Fix sharing contact across profiles
|
||||
* Fix: Reset scroll location after switching account
|
||||
* Update to core 2.51.0
|
||||
* Update to core 2.50.0
|
||||
|
||||
## v2.49.0
|
||||
2026-04
|
||||
|
||||
+2
-2
@@ -34,8 +34,8 @@ android {
|
||||
useLibrary 'org.apache.http.legacy'
|
||||
|
||||
defaultConfig {
|
||||
versionCode 30000744
|
||||
versionName "2.51.0"
|
||||
versionCode 30000743
|
||||
versionName "2.50.0"
|
||||
|
||||
applicationId "chat.delta.lite"
|
||||
multiDexEnabled true
|
||||
|
||||
+1
-1
Submodule jni/deltachat-core-rust updated: 66254b9c8c...ad50c27fab
@@ -29,19 +29,19 @@
|
||||
<li><a href="#сколько-участников-может-быть-в-одной-группе">Сколько участников может быть в одной группе?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#channels">Каналы</a>
|
||||
<li><a href="#channels">Channels</a>
|
||||
<ul>
|
||||
<li><a href="#подписка-на-канал">Подписка на канал</a></li>
|
||||
<li><a href="#создание-канала">Создание канала</a></li>
|
||||
<li><a href="#какое-максимальное-количество-подписчиков-может-быть-у-канала">Какое максимальное количество подписчиков может быть у канала?</a></li>
|
||||
<li><a href="#subscribe-to-a-channel">Subscribe to a channel</a></li>
|
||||
<li><a href="#create-a-channel">Create a channel</a></li>
|
||||
<li><a href="#how-many-subscribers-can-a-channel-have">How many subscribers can a channel have?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#calls">Звонки</a>
|
||||
<li><a href="#calls">Calls</a>
|
||||
<ul>
|
||||
<li><a href="#как-сделать-звонок">Как сделать звонок</a></li>
|
||||
<li><a href="#принять-или-отклонить-вызов">Принять или отклонить вызов</a></li>
|
||||
<li><a href="#во-время-звонка">Во время звонка</a></li>
|
||||
<li><a href="#пропущенные-вызовы-и-уведомления">Пропущенные вызовы и уведомления</a></li>
|
||||
<li><a href="#place-a-call">Place a call</a></li>
|
||||
<li><a href="#accept-or-reject-a-call">Accept or reject a call</a></li>
|
||||
<li><a href="#during-a-call">During a call</a></li>
|
||||
<li><a href="#missed-calls-and-notifications">Missed calls and notifications</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#webxdc">Встроенные приложения чата</a>
|
||||
@@ -640,191 +640,191 @@
|
||||
<h2 id="channels">
|
||||
|
||||
|
||||
Каналы <a href="#channels" class="anchor"></a>
|
||||
Channels <a href="#channels" class="anchor"></a>
|
||||
|
||||
|
||||
</h2>
|
||||
|
||||
<p>Каналы представляют собой инструмент типа “один-ко-многим” для трансляции сообщений.</p>
|
||||
<p>Channels are a one-to-many tool for broadcasting messages.</p>
|
||||
|
||||
<h3 id="подписка-на-канал">
|
||||
<h3 id="subscribe-to-a-channel">
|
||||
|
||||
|
||||
Подписка на канал <a href="#подписка-на-канал" class="anchor"></a>
|
||||
Subscribe to a channel <a href="#subscribe-to-a-channel" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>Отсканируйте <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> <strong>QR-код</strong>
|
||||
или нажмите на ссылку-приглашение, которую вы получили от владельца канала.</li>
|
||||
<li>Scan the <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> <strong>QR code</strong>
|
||||
or tap the <strong>invite link</strong> you got from the channel owner.</li>
|
||||
</ul>
|
||||
|
||||
<p>Всё готово!
|
||||
Сначала вы получите несколько сообщений из истории канала,
|
||||
а затем — все новые сообщения, поступающие в него.</p>
|
||||
<p>That’s all!
|
||||
You will receive a few of the messages from the channel history
|
||||
and, from that point on, all new messages from the channel.</p>
|
||||
|
||||
<p><strong>Не беспокойтесь,</strong> если это произойдет не сразу.
|
||||
Как только владелец канала выйдет в сеть, ваш запрос на вступление будет обработан.</p>
|
||||
<p><strong>Don’t worry,</strong> if that does not happen immediately.
|
||||
Once the channel owner comes online, your join request will be processed.</p>
|
||||
|
||||
<p>Как и весь Delta Chat, каналы являются приватными и децентрализованными,
|
||||
поэтому возможность публичного поиска каналов отсутствует.</p>
|
||||
<p>As all of Delta Chat, also Channels are private and decentralized,
|
||||
there is no public discovery.</p>
|
||||
|
||||
<p>Другие подписчики канала не увидят факта вашей подписки и не смогут отправлять вам сообщения.
|
||||
Однако, владелец канала сможет отправить вам сообщение.
|
||||
Также он будет видеть, что вы прочитали сообщение, если только вы не отключите подтверждение о прочтении.</p>
|
||||
<p>Other channel subscribers will not see that you subscribed and cannot message you.
|
||||
The channel owner, however, can message you.
|
||||
They will also see that you read a message unless you have read receipts disabled.</p>
|
||||
|
||||
<p>Если вы не хотите использовать свой основной профиль,
|
||||
вы можете создать <a href="#multiple-accounts">специальный профиль</a> для подписки на канал.</p>
|
||||
<p>If you do not want to share your main profile,
|
||||
you can also create a <a href="#multiple-accounts">dedicated profile</a> for joining a channel.</p>
|
||||
|
||||
<h3 id="создание-канала">
|
||||
<h3 id="create-a-channel">
|
||||
|
||||
|
||||
Создание канала <a href="#создание-канала" class="anchor"></a>
|
||||
Create a channel <a href="#create-a-channel" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>Нажмите <strong>Новый чат</strong> и выберите <strong>Новый канал</strong>.</p>
|
||||
<p>Tap <strong>New Chat</strong> and choose <strong>New Channel</strong>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Введите <strong>название</strong>, по желанию добавьте <strong>изображение</strong> и <strong>описание</strong>, а затем нажмите кнопку <strong>Создать</strong>.</p>
|
||||
<p>Enter a <strong>name</strong>, optionally set an <strong>image</strong> and <strong>description</strong>, and hit the <strong>Create</strong> button.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Теперь вы можете отправлять сообщения и управлять ими в обычном режиме.</p>
|
||||
<p>You can now send and manage messages as usual.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>В профиле канала вы можете <strong>поделиться QR-кодом или ссылкой-приглашением с другими пользователями</strong>.</p>
|
||||
<p>From the channel’s profile, <strong>share the QR code or invite link with others</strong>.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Подписчики будут получать ваши сообщения,
|
||||
но не смогут отправлять сообщения в вашем канале.
|
||||
При подписке они получат <strong>несколько последних сообщений из истории канала</strong>.</p>
|
||||
<p>Subscribers will receive your messages,
|
||||
but they cannot send messages in your channel.
|
||||
When subscribing, they will receive <strong>a few of the latest messages of the channel history</strong>.</p>
|
||||
|
||||
<p>Рядом с каждым сообщением вы можете увидеть <strong>количество просмотров</strong>.
|
||||
Обратите внимание, что учитываются только те подписчики, у которых включены уведомления о прочтении,
|
||||
поэтому реальное количество просмотров может быть больше.</p>
|
||||
<p>You can see the <strong>view count</strong> beside each message.
|
||||
Note that this only counts subscribers who have read receipts enabled,
|
||||
so the real view count may be larger.</p>
|
||||
|
||||
<h3 id="какое-максимальное-количество-подписчиков-может-быть-у-канала">
|
||||
<h3 id="how-many-subscribers-can-a-channel-have">
|
||||
|
||||
|
||||
Какое максимальное количество подписчиков может быть у канала? <a href="#какое-максимальное-количество-подписчиков-может-быть-у-канала" class="anchor"></a>
|
||||
How many subscribers can a channel have? <a href="#how-many-subscribers-can-a-channel-have" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Каналы предназначены для гораздо более широкой аудитории, чем <a href="#groups">группы</a>.</p>
|
||||
<p>Channels are designed for much larger audiences than <a href="#groups">groups</a>.</p>
|
||||
|
||||
<p>Практический предел зависит от используемого <a href="#relays">релея</a>,
|
||||
поэтому не существует единого фиксированного значения, применимого во всех случаях.</p>
|
||||
<p>The practical limit depends on the used <a href="#relays">relay</a>,
|
||||
so there is no single fixed number that applies everywhere.</p>
|
||||
|
||||
<p>Для крайне крупных каналов с десятками тысяч подписчиков,
|
||||
мы рекомендуем использовать <a href="#multiple-accounts">специальный профиль</a> для управления каналом,
|
||||
а также предварительно проверить пригодность релея.</p>
|
||||
<p>For really large channels with several tens of thousands of subscribers,
|
||||
we recommend using a <a href="#multiple-accounts">dedicated profile</a> for the channel
|
||||
and checking whether the relay is suitable.</p>
|
||||
|
||||
<p>Не стоит опасаться: архитектура Delta Chat позволяет использовать любой релей (relay-agnostic),
|
||||
поэтому вы можете легко изменить его в любой момент -
|
||||
ваши текущие подписчики этого даже не заметят.
|
||||
В этом случае достаточно будет обновить ссылку-приглашение, которую вы передаёте новым пользователям.</p>
|
||||
<p>But don’t be too hesitant: Delta Chat is designed to be relay-agnostic,
|
||||
so you can change your relay at any point easily -
|
||||
your existing subscribers will not even notice.
|
||||
You only have to update the invite link you share with new subscribers in that case.</p>
|
||||
|
||||
<h2 id="calls">
|
||||
|
||||
|
||||
Звонки <a href="#calls" class="anchor"></a>
|
||||
Calls <a href="#calls" class="anchor"></a>
|
||||
|
||||
|
||||
</h2>
|
||||
|
||||
<p>Delta Chat поддерживает <strong>аудио-</strong> и <strong>видеозвонки</strong> в режиме “один-на-один”.</p>
|
||||
<p>Delta Chat supports one-to-one <strong>audio calls</strong> and <strong>video calls</strong>.</p>
|
||||
|
||||
<p>Звонки работают на ПК, Ubuntu Touch, iOS и Android версии 8 и новее.</p>
|
||||
<p>Calls are supported on Desktop, Ubuntu Touch, iOS and Android 8 and newer.</p>
|
||||
|
||||
<h3 id="как-сделать-звонок">
|
||||
<h3 id="place-a-call">
|
||||
|
||||
|
||||
Как сделать звонок <a href="#как-сделать-звонок" class="anchor"></a>
|
||||
Place a call <a href="#place-a-call" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>В чате “один-на-один” нажмите на 📞 <strong>значок вызова</strong>.</p>
|
||||
<p>In a one-to-one chat, tap the 📞 <strong>call icon</strong>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Откроется небольшое меню
|
||||
в котором вы сможете выбрать вид связи <strong>аудио-</strong> или <strong>видеозвонок</strong>.</p>
|
||||
<p>This opens a small menu
|
||||
where you can choose whether to place an <strong>Audio Call</strong> or a <strong>Video Call</strong>.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="принять-или-отклонить-вызов">
|
||||
<h3 id="accept-or-reject-a-call">
|
||||
|
||||
|
||||
Принять или отклонить вызов <a href="#принять-или-отклонить-вызов" class="anchor"></a>
|
||||
Accept or reject a call <a href="#accept-or-reject-a-call" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>При входящем звонке,
|
||||
Delta Chat показывает <strong>экран входящего вызова</strong> или уведомление.</p>
|
||||
<p>When someone calls you,
|
||||
Delta Chat shows an <strong>incoming call screen</strong> or notification.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Нажмите <strong>Принять</strong> чтобы ответить
|
||||
или <strong>Отклонить</strong> чтобы сбросить звонок.</p>
|
||||
<p>Tap <strong>Accept</strong> to answer
|
||||
or <strong>Decline</strong> to reject the call.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="во-время-звонка">
|
||||
<h3 id="during-a-call">
|
||||
|
||||
|
||||
Во время звонка <a href="#во-время-звонка" class="anchor"></a>
|
||||
During a call <a href="#during-a-call" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>Вы можете <strong>отключить</strong> звук микрофона.</p>
|
||||
<p>You can <strong>mute</strong> your microphone.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Вы можете <strong>включить или выключить камеру</strong>.</p>
|
||||
<p>You can <strong>enable or disable your camera</strong>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>На мобильных устройствах можно <strong>переключаться между фронтальной и основной камерами</strong>.</p>
|
||||
<p>On mobile, you can <strong>switch between front and back cameras</strong>.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>В зависимости от устройства вы можете выбрать источник аудиовыхода или использовать режим “картинка в картинке”.
|
||||
В приложении для ПК звонок осуществляется в отдельном окне,
|
||||
что позволяет продолжать работу в основном окне Delta Chat в обычном режиме.</p>
|
||||
<p>Depending on the device, you can also select the audio output or use picture-in-picture.
|
||||
On desktop, the call is using a dedicated window
|
||||
and you can continue using the main Delta Chat window as usual.</p>
|
||||
|
||||
<h3 id="пропущенные-вызовы-и-уведомления">
|
||||
<h3 id="missed-calls-and-notifications">
|
||||
|
||||
|
||||
Пропущенные вызовы и уведомления <a href="#пропущенные-вызовы-и-уведомления" class="anchor"></a>
|
||||
Missed calls and notifications <a href="#missed-calls-and-notifications" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>Если вы не ответите на звонок, не услышите сигнал или ваше устройство будет недоступно,
|
||||
вызов отобразится как <strong>пропущенный</strong>.</p>
|
||||
<p>If you do not answer, do not hear the ringing, or do not have your device at hand,
|
||||
the call appears as a <strong>missed call</strong>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Только ваши подтвержденные контакты</strong> могут заставить ваше устройство звонить.
|
||||
Запросы на добавление в контакты будут приходить как обычно, но вызова не будет.</p>
|
||||
<p><strong>Only your accepted contacts</strong> can make your device ring.
|
||||
Contact requests will appear as usual and will not ring.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>В разделе <strong>Настройки → Уведомления → Звонки</strong>,
|
||||
вы можете полностью отключить специальный экран входящего вызова.
|
||||
Если вы это сделаете, никакие уведомления о звонках не будут вас беспокоить;
|
||||
при этом вы всё равно сможете принять звонок, нажав на иконку сообщения о входящем звонке в соответствующем чате.</p>
|
||||
<p>At <strong>Settings → Notifications → Calls</strong>,
|
||||
you can disable the special call ringing screen completely.
|
||||
If you do so, you will not be disturbed by any ringing notification,
|
||||
you can still pick up the call by tapping the incoming call message bubble in its chat.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1624,20 +1624,20 @@ Delta Chat вместо этого использует реализацию Ope
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Используемым <a href="#relays">релеям</a> необходимо знать ваш IP-адрес,
|
||||
а в некоторых случаях — данные устройств ваших контактов, если вы совершаете <a href="#calls">вызов</a>
|
||||
или совместно используете <a href="#webxdc">приложения</a>.</p>
|
||||
<p>The used <a href="#relays">relays</a> need to know your IP Address,
|
||||
as well as sometimes your contact’s devices if you have a <a href="#calls">call</a>
|
||||
or use <a href="#webxdc">apps</a> together.</p>
|
||||
|
||||
<p>IP-адреса необходимы для обеспечения связи и эффективной работы.
|
||||
Delta Chat не сохраняет их и не раскрывает третьим лицам.
|
||||
Обратите внимание, что IP-адрес
|
||||
— это не тот же адрес, который вы указываете службе доставки,
|
||||
он, как правило, менее точен и зачастую позволяет определить лишь город или регион.</p>
|
||||
<p>IP Addresses are needed for connectivity and efficiency.
|
||||
Delta Chat neither persists nor exposes them.
|
||||
Note that IP Addresses
|
||||
are not like an address you give to a delivery service,
|
||||
but typically less precise, often defining city or region only.</p>
|
||||
|
||||
<p>Если вы считаете свой IP-адрес зоной риска,
|
||||
мы рекомендуем использовать VPN для всей системы.
|
||||
Настройка VPN для отдельных приложений оставляет уязвимости в общей защите устройства.
|
||||
Например, нажатие на ссылку может раскрыть ваш IP-адрес неизвестным сторонам, что представляет собой гораздо больший риск.</p>
|
||||
<p>If you see your IP Address as a risk,
|
||||
we recommend to use a VPN for the whole system.
|
||||
Per-app options leave gaps across your system.
|
||||
For example, tapping a link can expose IP Addresses to unknown parties, which is by far the larger risk.</p>
|
||||
|
||||
<h3 id="sealedsender">
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
package chat.delta.rpc.types;
|
||||
|
||||
public class WebxdcMessageInfo {
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public String orientation;
|
||||
/** if the Webxdc represents a document, then this is the name of the document */
|
||||
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
|
||||
public String document;
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.connect.DirectShareUtil;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.SendRelayedMessageUtil;
|
||||
import org.thoughtcrime.securesms.util.ShareUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.task.SnackbarAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.views.ProgressDialog;
|
||||
@@ -487,6 +488,10 @@ public abstract class BaseConversationListFragment extends Fragment implements A
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
if (isRelayingMessageContent(getActivity())) {
|
||||
if (ShareUtil.getSharedContactId(getActivity()) != 0) {
|
||||
return false; // no sharing of a contact to multiple recipients at the same time, we can
|
||||
// reconsider when that becomes a real-world need
|
||||
}
|
||||
Context context = getContext();
|
||||
if (context != null) {
|
||||
fab.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_send_sms_white_24dp));
|
||||
|
||||
@@ -879,10 +879,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
private void handleSharing() {
|
||||
ArrayList<Uri> uriList = ShareUtil.getSharedUris(this);
|
||||
int sharedContactId = ShareUtil.getSharedContactId(this);
|
||||
if (uriList.size() > 1) {
|
||||
askSendingFiles(uriList, () -> SendRelayedMessageUtil.immediatelyRelay(this, chatId));
|
||||
} else {
|
||||
if (ShareUtil.getSharedHtml(this) != null
|
||||
if (sharedContactId != 0) {
|
||||
addAttachmentContactInfo(sharedContactId);
|
||||
} else if (ShareUtil.getSharedHtml(this) != null
|
||||
|| ShareUtil.getSharedSubject(this) != null
|
||||
|| ("sticker".equals(ShareUtil.getSharedType(this)) && !uriList.isEmpty())) {
|
||||
SendRelayedMessageUtil.immediatelyRelay(this, chatId);
|
||||
|
||||
@@ -727,7 +727,6 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
|
||||
refreshAvatar();
|
||||
refreshUnreadIndicator();
|
||||
refreshTitle();
|
||||
conversationListFragment.resetScrollPosition();
|
||||
conversationListFragment.loadChatlistAsync();
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,6 @@ public class ConversationListFragment extends BaseConversationListFragment
|
||||
private Timer reloadTimer;
|
||||
private boolean chatlistJustLoaded;
|
||||
private boolean reloadTimerInstantly;
|
||||
private boolean resetScrollPosition;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
@@ -241,9 +240,9 @@ public class ConversationListFragment extends BaseConversationListFragment
|
||||
|
||||
public void loadChatlistAsync() {
|
||||
synchronized (loadChatlistLock) {
|
||||
needsAnotherLoad = true;
|
||||
if (inLoadChatlist) {
|
||||
Log.i(TAG, "chatlist loading debounced");
|
||||
needsAnotherLoad = true;
|
||||
return;
|
||||
}
|
||||
inLoadChatlist = true;
|
||||
@@ -252,9 +251,6 @@ public class ConversationListFragment extends BaseConversationListFragment
|
||||
Util.runOnAnyBackgroundThread(
|
||||
() -> {
|
||||
while (true) {
|
||||
Log.i(TAG, "executing debounced chatlist loading");
|
||||
loadChatlist();
|
||||
|
||||
synchronized (loadChatlistLock) {
|
||||
if (!needsAnotherLoad) {
|
||||
inLoadChatlist = false;
|
||||
@@ -263,6 +259,8 @@ public class ConversationListFragment extends BaseConversationListFragment
|
||||
needsAnotherLoad = false;
|
||||
}
|
||||
|
||||
Log.i(TAG, "executing debounced chatlist loading");
|
||||
loadChatlist();
|
||||
Util.sleep(100);
|
||||
}
|
||||
});
|
||||
@@ -304,11 +302,6 @@ public class ConversationListFragment extends BaseConversationListFragment
|
||||
}
|
||||
|
||||
((ConversationListAdapter) list.getAdapter()).changeData(chatlist);
|
||||
|
||||
if (resetScrollPosition) {
|
||||
list.scrollToPosition(0);
|
||||
resetScrollPosition = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -365,8 +358,4 @@ public class ConversationListFragment extends BaseConversationListFragment
|
||||
loadChatlistAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public void resetScrollPosition() {
|
||||
resetScrollPosition = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
@@ -26,11 +25,8 @@ import com.b44t.messenger.DcContact;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcEvent;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import org.thoughtcrime.securesms.connect.DcEventCenter;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.Prefs;
|
||||
import org.thoughtcrime.securesms.util.ShareUtil;
|
||||
@@ -40,8 +36,6 @@ import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
public class ProfileActivity extends PassphraseRequiredActionBarActivity
|
||||
implements DcEventCenter.DcEventDelegate {
|
||||
|
||||
private static final String TAG = "ProfileActivity";
|
||||
|
||||
public static final String CHAT_ID_EXTRA = "chat_id";
|
||||
public static final String CONTACT_ID_EXTRA = "contact_id";
|
||||
|
||||
@@ -404,18 +398,7 @@ public class ProfileActivity extends PassphraseRequiredActionBarActivity
|
||||
Intent composeIntent = new Intent();
|
||||
DcContact dcContact = dcContext.getContact(contactId);
|
||||
if (dcContact.isKeyContact()) {
|
||||
try {
|
||||
byte[] vcard =
|
||||
rpc.makeVcard(rpc.getSelectedAccountId(), Collections.singletonList(contactId))
|
||||
.getBytes();
|
||||
Uri vcardUri =
|
||||
PersistentBlobProvider.getInstance().create(this, vcard, "text/vcard", "contact.vcf");
|
||||
ArrayList<Uri> uris = new ArrayList<>();
|
||||
uris.add(vcardUri);
|
||||
ShareUtil.setSharedUris(composeIntent, uris);
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "Failed to create vCard for sharing contactId=" + contactId, e);
|
||||
}
|
||||
ShareUtil.setSharedContactId(composeIntent, contactId);
|
||||
} else {
|
||||
ShareUtil.setSharedText(composeIntent, dcContact.getAddr());
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ import androidx.core.content.pm.ShortcutManagerCompat;
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
import chat.delta.rpc.Rpc;
|
||||
import chat.delta.rpc.RpcException;
|
||||
import chat.delta.rpc.types.WebxdcMessageInfo;
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcEvent;
|
||||
@@ -75,8 +74,6 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
private String baseURL;
|
||||
private String sourceCodeUrl = "";
|
||||
private String selfAddr;
|
||||
private boolean isAppSender;
|
||||
private boolean isBroadcast;
|
||||
private int sendUpdateMaxSize;
|
||||
private int sendUpdateInterval;
|
||||
private boolean internetAccess = false;
|
||||
@@ -223,32 +220,21 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
// (a random-id would also work, but would need maintenance and does not add benefits as we
|
||||
// regard the file-part interceptRequest() only,
|
||||
// also a random-id is not that useful for debugging)
|
||||
this.baseURL = "https://acc" + accountId + "-msg" + appMessageId + ".localhost";
|
||||
this.baseURL = "https://acc" + dcContext.getAccountId() + "-msg" + appMessageId + ".localhost";
|
||||
|
||||
WebxdcMessageInfo info;
|
||||
try {
|
||||
info = rpc.getWebxdcInfo(accountId, appMessageId);
|
||||
|
||||
if ("landscape".equals(info.orientation)) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||
} else {
|
||||
// enter fullscreen mode if necessary,
|
||||
// this is needed here because if the app is opened while already in landscape mode,
|
||||
// onConfigurationChanged() is not triggered
|
||||
setScreenMode(getResources().getConfiguration());
|
||||
}
|
||||
|
||||
internetAccess = info.internetAccess;
|
||||
selfAddr = info.selfAddr;
|
||||
isAppSender = info.isAppSender;
|
||||
isBroadcast = info.isBroadcast;
|
||||
sendUpdateMaxSize = info.sendUpdateMaxSize;
|
||||
sendUpdateInterval = info.sendUpdateInterval;
|
||||
} catch (RpcException e) { // unexpected error, log it and finish
|
||||
Log.e(TAG, "RPC Error", e);
|
||||
finish();
|
||||
return;
|
||||
final JSONObject info = this.dcAppMsg.getWebxdcInfo();
|
||||
internetAccess = JsonUtils.optBoolean(info, "internet_access");
|
||||
if ("landscape".equals(JsonUtils.optString(info, "orientation"))) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||
} else {
|
||||
// enter fullscreen mode if necessary,
|
||||
// this is needed here because if the app is opened while already in landscape mode,
|
||||
// onConfigurationChanged() is not triggered
|
||||
setScreenMode(getResources().getConfiguration());
|
||||
}
|
||||
selfAddr = info.optString("self_addr");
|
||||
sendUpdateMaxSize = info.optInt("send_update_max_size");
|
||||
sendUpdateInterval = info.optInt("send_update_interval");
|
||||
|
||||
toggleFakeProxy(!internetAccess);
|
||||
|
||||
@@ -501,22 +487,22 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
this.dcContext.getMsg(event.getData2Int()); // msg changed, reload data from db
|
||||
Util.runOnAnyBackgroundThread(
|
||||
() -> {
|
||||
try {
|
||||
final WebxdcMessageInfo info =
|
||||
rpc.getWebxdcInfo(dcContext.getAccountId(), dcAppMsg.getId());
|
||||
final DcChat chat = dcContext.getChat(dcAppMsg.getChatId());
|
||||
Util.runOnMain(() -> updateTitleAndMenu(info, chat));
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "RPC Error", e);
|
||||
}
|
||||
final JSONObject info = dcAppMsg.getWebxdcInfo();
|
||||
final DcChat chat = dcContext.getChat(dcAppMsg.getChatId());
|
||||
Util.runOnMain(
|
||||
() -> {
|
||||
updateTitleAndMenu(info, chat);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTitleAndMenu(WebxdcMessageInfo info, DcChat chat) {
|
||||
final String docName = TextUtils.isEmpty(info.document) ? info.name : info.document;
|
||||
getSupportActionBar().setTitle(docName + " – " + chat.getName());
|
||||
String currSourceCodeUrl = info.sourceCodeUrl != null ? info.sourceCodeUrl : "";
|
||||
private void updateTitleAndMenu(JSONObject info, DcChat chat) {
|
||||
final String docName = JsonUtils.optString(info, "document");
|
||||
final String xdcName = JsonUtils.optString(info, "name");
|
||||
final String currSourceCodeUrl = JsonUtils.optString(info, "source_code_url");
|
||||
getSupportActionBar()
|
||||
.setTitle((docName.isEmpty() ? xdcName : docName) + " – " + chat.getName());
|
||||
if (!sourceCodeUrl.equals(currSourceCodeUrl)) {
|
||||
sourceCodeUrl = currSourceCodeUrl;
|
||||
invalidateOptionsMenu();
|
||||
@@ -536,20 +522,19 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
Context context = activity.getApplicationContext();
|
||||
try {
|
||||
DcContext dcContext = DcHelper.getContext(context);
|
||||
Rpc rpc = DcHelper.getRpc(context);
|
||||
int accountId = dcContext.getAccountId();
|
||||
DcMsg msg = dcContext.getMsg(msgId);
|
||||
WebxdcMessageInfo info = rpc.getWebxdcInfo(accountId, msgId);
|
||||
final JSONObject info = msg.getWebxdcInfo();
|
||||
|
||||
final String docName = TextUtils.isEmpty(info.document) ? info.name : info.document;
|
||||
byte[] blob = msg.getWebxdcBlob(info.icon);
|
||||
final String docName = JsonUtils.optString(info, "document");
|
||||
final String xdcName = JsonUtils.optString(info, "name");
|
||||
byte[] blob = msg.getWebxdcBlob(JsonUtils.optString(info, "icon"));
|
||||
ByteArrayInputStream is = new ByteArrayInputStream(blob);
|
||||
BitmapDrawable drawable = (BitmapDrawable) Drawable.createFromStream(is, "icon");
|
||||
Bitmap bitmap = drawable.getBitmap();
|
||||
|
||||
ShortcutInfoCompat shortcutInfoCompat =
|
||||
new ShortcutInfoCompat.Builder(context, "xdc-" + accountId + "-" + msgId)
|
||||
.setShortLabel(docName)
|
||||
new ShortcutInfoCompat.Builder(context, "xdc-" + dcContext.getAccountId() + "-" + msgId)
|
||||
.setShortLabel(docName.isEmpty() ? xdcName : docName)
|
||||
.setIcon(
|
||||
IconCompat.createWithBitmap(
|
||||
bitmap)) // createWithAdaptiveBitmap() removes decorations but cuts out a too
|
||||
@@ -646,16 +631,6 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
return WebxdcActivity.this.dcContext.getName();
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean isAppSender() {
|
||||
return WebxdcActivity.this.isAppSender;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public boolean isBroadcast() {
|
||||
return WebxdcActivity.this.isBroadcast;
|
||||
}
|
||||
|
||||
/**
|
||||
* @noinspection unused
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.thoughtcrime.securesms.geolocation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public final class ActiveLocationChats {
|
||||
|
||||
private static final String PREFS_NAME = "location_streaming";
|
||||
private static final String KEY_ACTIVE = "active_chat_ids";
|
||||
|
||||
private ActiveLocationChats() {}
|
||||
|
||||
private static SharedPreferences prefs(Context context) {
|
||||
return context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a chat. Uses commit() to guarantee the write reaches disk before the process can die to
|
||||
* preserve the superset invariant.
|
||||
*/
|
||||
static void add(Context context, int chatId) {
|
||||
Set<String> current = new HashSet<>(getAll(context));
|
||||
current.add(String.valueOf(chatId));
|
||||
prefs(context).edit().putStringSet(KEY_ACTIVE, current).commit();
|
||||
}
|
||||
|
||||
public static void remove(Context context, int chatId) {
|
||||
Set<String> current = new HashSet<>(getAll(context));
|
||||
current.remove(String.valueOf(chatId));
|
||||
prefs(context).edit().putStringSet(KEY_ACTIVE, current).apply();
|
||||
}
|
||||
|
||||
static void clear(Context context) {
|
||||
prefs(context).edit().remove(KEY_ACTIVE).apply();
|
||||
}
|
||||
|
||||
static Set<Integer> getAllIds(Context context) {
|
||||
Set<Integer> ids = new HashSet<>();
|
||||
for (String s : getAll(context)) {
|
||||
try {
|
||||
ids.add(Integer.parseInt(s));
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
private static Set<String> getAll(Context context) {
|
||||
return prefs(context).getStringSet(KEY_ACTIVE, new HashSet<>());
|
||||
}
|
||||
}
|
||||
+25
-58
@@ -17,15 +17,13 @@ import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.ServiceCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import chat.delta.rpc.Rpc;
|
||||
import chat.delta.rpc.RpcException;
|
||||
import org.thoughtcrime.securesms.ConversationListActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
|
||||
public class LocationStreamingService extends Service {
|
||||
|
||||
private static final String TAG = "LocationStreamingSvc";
|
||||
private static final String TAG = "LocationStreamingService";
|
||||
private static final String ACTION_STOP = "org.thoughtcrime.securesms.geolocation.STOP_STREAMING";
|
||||
private static final int NOTIFICATION_ID = 8801;
|
||||
private static final String CHANNEL_ID = "location_streaming";
|
||||
@@ -38,63 +36,38 @@ public class LocationStreamingService extends Service {
|
||||
// static API
|
||||
|
||||
/** Register a chat for location updates, then ensure the service is running. */
|
||||
public static void startSharing(Context context, int accountId, int chatId, int durationSeconds) {
|
||||
try {
|
||||
DcHelper.getRpc(context).sendLocationsToChat(accountId, chatId, durationSeconds);
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "Failed to start location streaming", e);
|
||||
return;
|
||||
}
|
||||
public static void startSharing(Context context, int chatId, int durationSeconds) {
|
||||
ActiveLocationChats.add(context, chatId);
|
||||
DcHelper.getContext(context).sendLocationsToChat(chatId, durationSeconds);
|
||||
ContextCompat.startForegroundService(
|
||||
context, new Intent(context, LocationStreamingService.class));
|
||||
}
|
||||
|
||||
/** Unregister a chat. If no chats remain, stop the service. */
|
||||
public static void stopSharing(Context context, int accountId, int chatId) {
|
||||
try {
|
||||
DcHelper.getRpc(context).sendLocationsToChat(accountId, chatId, 0);
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "Failed to stop location streaming", e);
|
||||
}
|
||||
if (!isAnySharingActive(context)) {
|
||||
public static void stopSharing(Context context, int chatId) {
|
||||
ActiveLocationChats.remove(context, chatId);
|
||||
DcHelper.getContext(context).sendLocationsToChat(chatId, 0);
|
||||
if (!DcHelper.getContext(context).isSendingLocationsToChat(0)) {
|
||||
context.stopService(new Intent(context, LocationStreamingService.class));
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureRunning(Context context) {
|
||||
if (!hasLocationPermission(context)) {
|
||||
try {
|
||||
DcHelper.getRpc(context).stopSendingLocations();
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "Failed to stop location streaming", e);
|
||||
for (int chatId : ActiveLocationChats.getAllIds(context)) {
|
||||
DcHelper.getContext(context).sendLocationsToChat(chatId, 0);
|
||||
}
|
||||
ActiveLocationChats.clear(context);
|
||||
return;
|
||||
}
|
||||
if (isAnySharingActive(context)) {
|
||||
ContextCompat.startForegroundService(
|
||||
context, new Intent(context, LocationStreamingService.class));
|
||||
}
|
||||
ContextCompat.startForegroundService(
|
||||
context, new Intent(context, LocationStreamingService.class));
|
||||
}
|
||||
|
||||
public static boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
private static boolean isAnySharingActive(Context context) {
|
||||
try {
|
||||
Rpc rpc = DcHelper.getRpc(context);
|
||||
for (int accountId : rpc.getAllAccountIds()) {
|
||||
if (rpc.isSendingLocations(accountId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "Failed to check location streaming state", e);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// lifecycle
|
||||
|
||||
@Override
|
||||
@@ -113,7 +86,7 @@ public class LocationStreamingService extends Service {
|
||||
@Override
|
||||
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
|
||||
if (intent != null && ACTION_STOP.equals(intent.getAction())) {
|
||||
stopAllSharing(this);
|
||||
stopAllSharing();
|
||||
stopSelf();
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
@@ -127,12 +100,11 @@ public class LocationStreamingService extends Service {
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
private static void stopAllSharing(Context context) {
|
||||
try {
|
||||
DcHelper.getRpc(context).stopSendingLocations();
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "Failed to stop sending locations", e);
|
||||
private void stopAllSharing() {
|
||||
for (int chatId : ActiveLocationChats.getAllIds(this)) {
|
||||
DcHelper.getContext(this).sendLocationsToChat(chatId, 0);
|
||||
}
|
||||
ActiveLocationChats.clear(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -178,21 +150,16 @@ public class LocationStreamingService extends Service {
|
||||
private void publishAndWrite(Location location) {
|
||||
LocationData.getInstance().post(location);
|
||||
|
||||
boolean keepGoing;
|
||||
try {
|
||||
keepGoing =
|
||||
DcHelper.getRpc(this)
|
||||
.setLocation(
|
||||
(float) location.getLatitude(),
|
||||
(float) location.getLongitude(),
|
||||
location.getAccuracy());
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "Failed to set location", e);
|
||||
return;
|
||||
}
|
||||
boolean keepGoing =
|
||||
DcHelper.getContext(this)
|
||||
.setLocation(
|
||||
(float) location.getLatitude(),
|
||||
(float) location.getLongitude(),
|
||||
location.getAccuracy());
|
||||
Log.d(TAG, "keepGoing: " + keepGoing);
|
||||
|
||||
if (!keepGoing) {
|
||||
stopAllSharing();
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import chat.delta.rpc.Rpc;
|
||||
import chat.delta.rpc.RpcException;
|
||||
import chat.delta.util.ListenableFuture;
|
||||
import chat.delta.util.SettableFuture;
|
||||
@@ -64,6 +63,7 @@ import org.thoughtcrime.securesms.components.audioplay.AudioPlaybackViewModel;
|
||||
import org.thoughtcrime.securesms.components.audioplay.AudioView;
|
||||
import org.thoughtcrime.securesms.connect.DcHelper;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.geolocation.ActiveLocationChats;
|
||||
import org.thoughtcrime.securesms.geolocation.LocationStreamingService;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
|
||||
@@ -491,29 +491,16 @@ public class AttachmentManager {
|
||||
|
||||
public static void selectLocation(Activity activity, int chatId) {
|
||||
Context appContext = activity.getApplicationContext();
|
||||
Rpc rpc = DcHelper.getRpc(appContext);
|
||||
int accountId = DcHelper.getContext(appContext).getAccountId();
|
||||
|
||||
boolean currentlySharing;
|
||||
try {
|
||||
currentlySharing = rpc.isSendingLocationsToChat(accountId, chatId);
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "Failed to check location streaming state", e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentlySharing) {
|
||||
if (DcHelper.getContext(appContext).isSendingLocationsToChat(chatId)) {
|
||||
if (LocationStreamingService.isRunning()) {
|
||||
LocationStreamingService.stopSharing(appContext, accountId, chatId);
|
||||
LocationStreamingService.stopSharing(appContext, chatId);
|
||||
return;
|
||||
}
|
||||
// Stale — service is dead but chat layer still thinks it's sharing.
|
||||
// Clean up and fall through to the fresh start flow.
|
||||
try {
|
||||
rpc.sendLocationsToChat(accountId, chatId, 0);
|
||||
} catch (RpcException e) {
|
||||
Log.e(TAG, "Failed to stop stale location streaming", e);
|
||||
}
|
||||
// Clean up this chat and fall through to the fresh start flow.
|
||||
ActiveLocationChats.remove(appContext, chatId);
|
||||
DcHelper.getContext(appContext).sendLocationsToChat(chatId, 0);
|
||||
}
|
||||
|
||||
Permissions.with(activity)
|
||||
@@ -527,8 +514,7 @@ public class AttachmentManager {
|
||||
ShareLocationDialog.show(
|
||||
activity,
|
||||
durationInSeconds ->
|
||||
LocationStreamingService.startSharing(
|
||||
appContext, accountId, chatId, durationInSeconds));
|
||||
LocationStreamingService.startSharing(appContext, chatId, durationInSeconds));
|
||||
})
|
||||
.request(
|
||||
android.Manifest.permission.ACCESS_FINE_LOCATION,
|
||||
|
||||
+1
-1
@@ -167,7 +167,7 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
|
||||
notificationsEnabled.setChecked(!dcContext.isMuted());
|
||||
notificationsEnabled.setSummary(getSummary(getContext(), false));
|
||||
mentionNotifEnabled.setChecked(dcContext.isMentionsEnabled());
|
||||
notifyCalls.setChecked(!"2".equals(dcContext.getConfig("who_can_call_me")));
|
||||
notifyCalls.setChecked(dcContext.getConfig("who_can_call_me") != "2");
|
||||
|
||||
// set without altering "unset" state of the preference
|
||||
reliableService.setOnPreferenceChangeListener(null);
|
||||
|
||||
@@ -70,12 +70,11 @@ public class EditRelayActivity extends BaseActionBarActivity
|
||||
|
||||
private Group advancedGroup;
|
||||
private ImageView advancedIcon;
|
||||
private View imapFolderLayout;
|
||||
private boolean imapFolderHasValue = false;
|
||||
private ProgressDialog progressDialog;
|
||||
private boolean cancelled = false;
|
||||
|
||||
private View imapFolderLayout;
|
||||
private boolean showImapFolder = false;
|
||||
|
||||
Spinner imapSecurity;
|
||||
Spinner smtpSecurity;
|
||||
Spinner certCheck;
|
||||
@@ -202,8 +201,8 @@ public class EditRelayActivity extends BaseActionBarActivity
|
||||
if (config.imapPort != null) imapPortInput.setText(config.imapPort.toString());
|
||||
expandAdvanced = expandAdvanced || config.imapPort != null;
|
||||
|
||||
showImapFolder = !TextUtils.isEmpty(config.imapFolder);
|
||||
if (showImapFolder) {
|
||||
imapFolderHasValue = !TextUtils.isEmpty(config.imapFolder);
|
||||
if (imapFolderHasValue) {
|
||||
imapFolderInput.setText(config.imapFolder);
|
||||
expandAdvanced = true;
|
||||
}
|
||||
@@ -408,7 +407,7 @@ public class EditRelayActivity extends BaseActionBarActivity
|
||||
advancedIcon.setRotation(45);
|
||||
} else {
|
||||
advancedGroup.setVisibility(View.VISIBLE);
|
||||
imapFolderLayout.setVisibility(showImapFolder ? View.VISIBLE : View.GONE);
|
||||
imapFolderLayout.setVisibility(imapFolderHasValue ? View.VISIBLE : View.GONE);
|
||||
advancedIcon.setRotation(0);
|
||||
}
|
||||
}
|
||||
@@ -511,7 +510,7 @@ public class EditRelayActivity extends BaseActionBarActivity
|
||||
param.password = getParam(R.id.password_text, false);
|
||||
param.imapServer = getParam(R.id.imap_server_text, true);
|
||||
param.imapPort = Util.objectToInt(getParam(R.id.imap_port_text, true));
|
||||
param.imapFolder = getParam(R.id.imap_folder_text, true);
|
||||
param.imapFolder = getParam(R.id.imap_folder_text, false);
|
||||
param.imapSecurity = socketSecurityFromInt(imapSecurity.getSelectedItemPosition());
|
||||
param.imapUser = getParam(R.id.imap_login_text, false);
|
||||
param.smtpServer = getParam(R.id.smtp_server_text, true);
|
||||
|
||||
@@ -22,7 +22,6 @@ class SearchViewModel extends ViewModel {
|
||||
private final Context appContext;
|
||||
private final DcContext dcContext;
|
||||
private boolean forwarding = false;
|
||||
private final Object bgSearchLock = new Object();
|
||||
private boolean inBgSearch;
|
||||
private boolean needsAnotherBgSearch;
|
||||
|
||||
@@ -46,29 +45,27 @@ class SearchViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
public void updateQuery() {
|
||||
synchronized (bgSearchLock) {
|
||||
if (inBgSearch) {
|
||||
Log.i(TAG, "... search call debounced");
|
||||
needsAnotherBgSearch = true;
|
||||
return;
|
||||
}
|
||||
if (inBgSearch) {
|
||||
needsAnotherBgSearch = true;
|
||||
Log.i(TAG, "... search call debounced");
|
||||
} else {
|
||||
inBgSearch = true;
|
||||
}
|
||||
Util.runOnBackground(
|
||||
() -> {
|
||||
while (true) {
|
||||
Log.i(TAG, "... executing debounced search call");
|
||||
queryAndCallback(lastQuery, searchResult::postValue);
|
||||
synchronized (bgSearchLock) {
|
||||
if (!needsAnotherBgSearch) {
|
||||
inBgSearch = false;
|
||||
return;
|
||||
}
|
||||
needsAnotherBgSearch = false;
|
||||
}
|
||||
Util.runOnBackground(
|
||||
() -> {
|
||||
Util.sleep(100);
|
||||
}
|
||||
});
|
||||
needsAnotherBgSearch = false;
|
||||
queryAndCallback(lastQuery, searchResult::postValue);
|
||||
|
||||
while (needsAnotherBgSearch) {
|
||||
Util.sleep(100);
|
||||
needsAnotherBgSearch = false;
|
||||
Log.i(TAG, "... executing debounced search call");
|
||||
queryAndCallback(lastQuery, searchResult::postValue);
|
||||
}
|
||||
|
||||
inBgSearch = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void queryAndCallback(@NonNull String query, @NonNull SearchViewModel.Callback callback) {
|
||||
|
||||
@@ -15,6 +15,7 @@ public class ShareUtil {
|
||||
private static final String FORWARDED_MESSAGE_ACCID = "forwarded_message_accid";
|
||||
private static final String FORWARDED_MESSAGE_IDS = "forwarded_message_ids";
|
||||
private static final String SHARED_URIS = "shared_uris";
|
||||
private static final String SHARED_CONTACT_ID = "shared_contact_id";
|
||||
private static final String IS_SHARING = "is_sharing";
|
||||
private static final String IS_FROM_WEBXDC = "is_from_webxdc";
|
||||
private static final String SHARED_TITLE = "shared_title";
|
||||
@@ -111,6 +112,15 @@ public class ShareUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static int getSharedContactId(Activity activity) {
|
||||
try {
|
||||
return activity.getIntent().getIntExtra(SHARED_CONTACT_ID, 0);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getSharedText(Activity activity) {
|
||||
try {
|
||||
return activity.getIntent().getStringExtra(TEXT_EXTRA);
|
||||
@@ -132,6 +142,7 @@ public class ShareUtil {
|
||||
activity.getIntent().removeExtra(FORWARDED_MESSAGE_ACCID);
|
||||
activity.getIntent().removeExtra(FORWARDED_MESSAGE_IDS);
|
||||
activity.getIntent().removeExtra(SHARED_URIS);
|
||||
activity.getIntent().removeExtra(SHARED_CONTACT_ID);
|
||||
activity.getIntent().removeExtra(IS_SHARING);
|
||||
activity.getIntent().removeExtra(DIRECT_SHARING_CHAT_ID);
|
||||
activity.getIntent().removeExtra(TEXT_EXTRA);
|
||||
@@ -156,6 +167,9 @@ public class ShareUtil {
|
||||
if (!getSharedUris(currentActivity).isEmpty()) {
|
||||
newActivityIntent.putParcelableArrayListExtra(SHARED_URIS, getSharedUris(currentActivity));
|
||||
}
|
||||
if (getSharedContactId(currentActivity) != 0) {
|
||||
newActivityIntent.putExtra(SHARED_CONTACT_ID, getSharedContactId(currentActivity));
|
||||
}
|
||||
if (getSharedText(currentActivity) != null) {
|
||||
newActivityIntent.putExtra(TEXT_EXTRA, getSharedText(currentActivity));
|
||||
}
|
||||
@@ -206,6 +220,11 @@ public class ShareUtil {
|
||||
composeIntent.putExtra(IS_SHARING, true);
|
||||
}
|
||||
|
||||
public static void setSharedContactId(Intent composeIntent, int contactId) {
|
||||
composeIntent.putExtra(SHARED_CONTACT_ID, contactId);
|
||||
composeIntent.putExtra(IS_SHARING, true);
|
||||
}
|
||||
|
||||
public static void setSharedTitle(Intent composeIntent, String text) {
|
||||
composeIntent.putExtra(SHARED_TITLE, text);
|
||||
}
|
||||
|
||||
@@ -224,6 +224,7 @@
|
||||
android:id="@+id/imap_folder"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="58dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline_root_end"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline_root_start"
|
||||
app:layout_constraintTop_toBottomOf="@id/imap_port">
|
||||
@@ -233,7 +234,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:enabled="false"
|
||||
android:hint="IMAP folder" />
|
||||
android:hint="@string/login_imap_folder" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
|
||||
@@ -70,10 +70,6 @@ window.webxdc = (() => {
|
||||
|
||||
selfName: InternalJSApi.selfName(),
|
||||
|
||||
isAppSender: InternalJSApi.isAppSender(),
|
||||
|
||||
isBroadcast: InternalJSApi.isBroadcast(),
|
||||
|
||||
joinRealtimeChannel: () => {
|
||||
realtimeChannel = createRealtimeChannel();
|
||||
InternalJSApi.sendRealtimeAdvertisement();
|
||||
|
||||
@@ -944,7 +944,7 @@
|
||||
<string name="welcome_desktop">Sveiki atvykę į Delta Chat</string>
|
||||
<string name="global_menu_preferences_language_desktop">Pasirinkti kalbą...</string>
|
||||
<string name="global_menu_file_desktop">Failas</string>
|
||||
<string name="global_menu_file_quit_desktop">Išjungti</string>
|
||||
<string name="global_menu_file_quit_desktop">Išeiti</string>
|
||||
<string name="global_menu_edit_desktop">Taisyti</string>
|
||||
<string name="global_menu_edit_undo_desktop">Atšaukti</string>
|
||||
<string name="global_menu_edit_redo_desktop">Grąžinti</string>
|
||||
@@ -956,8 +956,6 @@
|
||||
<string name="global_menu_view_developer_desktop">Plėtotojas</string>
|
||||
<string name="global_menu_view_developer_tools_desktop">Plėtotojo įrankiai</string>
|
||||
<string name="global_menu_help_desktop">Žinynas</string>
|
||||
<string name="delta_chat_homepage">Delta Chat internetinė svetainė</string>
|
||||
<string name="contribute">Talkinti</string>
|
||||
<string name="global_menu_help_report_desktop">Pranešti apie klaidą</string>
|
||||
<string name="global_menu_help_about_desktop">Apie Delta Chat</string>
|
||||
<string name="global_menu_file_open_desktop">Atverti Delta Chat</string>
|
||||
@@ -992,9 +990,6 @@
|
||||
<string name="a11y_message_context_menu_btn_label">Veiksmai su žinute</string>
|
||||
<string name="a11y_background_preview_label">Fono peržiūra</string>
|
||||
<string name="a11y_disappearing_messages_activated">Išnykstančios žinutės aktyvuotos</string>
|
||||
<!-- The placeholder will be replaced by a link, eg. "Open https://delta.chat". Used e.g. in for accessibility. -->
|
||||
<string name="open_link">Atverti %1$s</string>
|
||||
|
||||
<string name="a11y_connectivity_hint">Bakstelėkite du kartus norėdami pamatyti išsamesnę informaciją apie jungiamumą.</string>
|
||||
<string name="login_error_no_internet_connection">Nėra interneto ryšio, nepavyko prisijungti.</string>
|
||||
<string name="share_account_not_configured">Paskyra nėra sukonfigūruota.</string>
|
||||
|
||||
@@ -438,7 +438,6 @@
|
||||
<string name="call_answered_elsewhere">Er is opgenomen op een ander apparaat</string>
|
||||
<string name="call_requires_camera_permission">Cameratoegang is vereist om te kunnen videobellen</string>
|
||||
<string name="call_requires_mic_permission">Microfoontoegang is vereist om te kunnen bellen</string>
|
||||
<string name="call_requires_connection">De oproep kan niet worden gestart. Zorg dat je verbonden bent met het internet en probeer het opnieuw.</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">Bellen met %1$s</string>
|
||||
<!-- Use e.g. in a notifications during ringing. Placeholder is relaced by the name of the contact being called. -->
|
||||
@@ -673,7 +672,6 @@
|
||||
<string name="welcome_chat_over_email">Veilige, gedecentraliseerde gesprekken</string>
|
||||
<string name="scan_invitation_code">Uitnodigingscode scannen</string>
|
||||
<string name="login_title">Inloggen</string>
|
||||
<string name="login_advanced_hint">Deze inlogmethode is alleen voor ervaren gebruikers.\n\nGebruik geen adres dat je reeds in een andere app gebruikt.</string>
|
||||
<string name="login_inbox">Postvak in</string>
|
||||
<string name="login_imap_login">IMAP-inlognaam</string>
|
||||
<string name="login_imap_server">IMAP-server</string>
|
||||
@@ -708,7 +706,6 @@
|
||||
<string name="used_for_sending">Wordt gebruikt voor versturen</string>
|
||||
<string name="hide_from_contacts">Verbergen op contactpersonenlijst</string>
|
||||
<string name="hidden_from_contacts">Verborgen op contactpersonenlijst</string>
|
||||
<string name="enforce_e2ee">Versleuteling afdwingen op alle relays</string>
|
||||
<!-- Hint for the list of relays -->
|
||||
<string name="transport_list_hint">Berichten worden op alle relays ontvangen.\n\n⚠️ Als je hier iets wijzigt, doe dat dan alléén als ál je apparaten gebruikmaken van versie 2.47.0, anders kunnen er berichten ontbreken.</string>
|
||||
<!-- shown if a QR code was scanned that can be used as a relay -->
|
||||
|
||||
@@ -462,7 +462,6 @@
|
||||
<string name="call_answered_elsewhere">Звонок отвечен на другом устройстве</string>
|
||||
<string name="call_requires_camera_permission">Для видеозвонков требуется разрешение на использование камеры</string>
|
||||
<string name="call_requires_mic_permission">Для совершения звонков требуется разрешение на использование микрофона</string>
|
||||
<string name="call_requires_connection">Не удалось начать вызов. Убедитесь, что ваше устройство подключено к интернету, и повторите попытку.</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">Звонок от %1$s</string>
|
||||
<!-- Use e.g. in a notifications during ringing. Placeholder is relaced by the name of the contact being called. -->
|
||||
@@ -488,9 +487,9 @@
|
||||
<!-- First placeholder gets replaced by the number of messages to forward. Second placeholder gets replaced by the name of the destination chat -->
|
||||
<plurals name="ask_forward_messages">
|
||||
<item quantity="one">Переслать сообщение пользователю %2$s?</item>
|
||||
<item quantity="few">Переслать %1$d сообщения пользователю %2$s?</item>
|
||||
<item quantity="many">Переслать %1$d сообщений пользователю %2$s?</item>
|
||||
<item quantity="other">Переслать %1$d сообщений пользователю %2$s?</item>
|
||||
<item quantity="few">Переслать %1$d сообщения пользователям %2$s?</item>
|
||||
<item quantity="many">Переслать %1$d сообщения пользователям %2$s?</item>
|
||||
<item quantity="other">Переслать %1$d сообщения пользователям %2$s?</item>
|
||||
</plurals>
|
||||
<!-- deprecated, use ask_forward_messages -->
|
||||
<string name="ask_forward">Переслать сообщения %1$s?</string>
|
||||
@@ -709,7 +708,6 @@
|
||||
<string name="welcome_chat_over_email">Безопасный децентрализованный чат</string>
|
||||
<string name="scan_invitation_code">Сканировать код приглашения</string>
|
||||
<string name="login_title">Войти</string>
|
||||
<string name="login_advanced_hint">Этот способ входа предназначен для опытных пользователей.\n\nНе используйте адрес, который вы уже используете в другом приложении.</string>
|
||||
<string name="login_inbox">Входящие</string>
|
||||
<string name="login_imap_login">Имя пользователя IMAP</string>
|
||||
<string name="login_imap_server">Сервер IMAP</string>
|
||||
@@ -744,7 +742,6 @@
|
||||
<string name="used_for_sending">Используется для отправки</string>
|
||||
<string name="hide_from_contacts">Скрыть из контактов</string>
|
||||
<string name="hidden_from_contacts">Скрыт из контактов</string>
|
||||
<string name="enforce_e2ee">Принудительное шифрование для всех релеев</string>
|
||||
<!-- Hint for the list of relays -->
|
||||
<string name="transport_list_hint">Сообщения поступают на все релеи.\n\n⚠️ Если вы измените какие-либо настройки в этом разделе, убедитесь, что на всех ваших устройствах установлена версия Delta Chat не ниже 2.47.0. В противном случае старые версии могут не получать сообщения.</string>
|
||||
<!-- shown if a QR code was scanned that can be used as a relay -->
|
||||
|
||||
@@ -430,7 +430,6 @@
|
||||
<string name="call_answered_elsewhere">Thirrje që ka marrë përgjigje në pajisje tjetër</string>
|
||||
<string name="call_requires_camera_permission">Për thirrje me video janë të domosdoshme leje mbi kamerën</string>
|
||||
<string name="call_requires_mic_permission">Leja mbi mikrofonin është e domosdoshme për thirrje</string>
|
||||
<string name="call_requires_connection">S’niset dot thirrja. Sigurohuni se keni lidhje internet dhe riprovoni.</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">Thirrje me %1$s</string>
|
||||
<!-- Use e.g. in a notifications during ringing. Placeholder is relaced by the name of the contact being called. -->
|
||||
@@ -665,7 +664,6 @@
|
||||
<string name="welcome_chat_over_email">Fjalosje e Decentralizuar, e Parrezik</string>
|
||||
<string name="scan_invitation_code">Skanoni Kod Ftese</string>
|
||||
<string name="login_title">Hyni</string>
|
||||
<string name="login_advanced_hint">Kjo hyrje është për përdorues të thelluar.\n\nMos përdorni një adresë që e përdorni në një aplikacion tjetër.</string>
|
||||
<string name="login_inbox">Të marrë</string>
|
||||
<string name="login_imap_login">Emër Hyrjeje në IMAP</string>
|
||||
<string name="login_imap_server">Shërbyes IMAP</string>
|
||||
@@ -700,7 +698,6 @@
|
||||
<string name="used_for_sending">E përdorur për dërgim</string>
|
||||
<string name="hide_from_contacts">Fshihe prej Kontaktesh</string>
|
||||
<string name="hidden_from_contacts">Fshehur prej kontakteve</string>
|
||||
<string name="enforce_e2ee">Detyro Fshehtëzim për Krejt Reletë</string>
|
||||
<!-- Hint for the list of relays -->
|
||||
<string name="transport_list_hint">Mesazhet merren në të tëra reletë.\n\n⚠️ Nëse ndryshoni diçka këtu, sigurohuni se krejt pajisjet tuaja xhirojnë të paktën versionin 2.47.0. Përndryshe, pajisje më të vjetra mund të humbin mesazhe.</string>
|
||||
<!-- shown if a QR code was scanned that can be used as a relay -->
|
||||
@@ -844,7 +841,6 @@
|
||||
<string name="pref_show_emails_all">Krejt</string>
|
||||
<string name="pref_experimental_features">Veçori Eksperimentale</string>
|
||||
<string name="pref_experimental_features_explain">Këto veçori mund të jenë të paqëndrueshme dhe mund të ndryshohen, ose hiqen</string>
|
||||
<string name="pref_on_demand_location_streaming">Dhënie Vendndodhjeje</string>
|
||||
<string name="pref_background_default">Figurë parazgjedhje</string>
|
||||
<string name="pref_background_default_color">Ngjyrë parazgjedhje</string>
|
||||
<string name="pref_background_custom_image">Figurë vetjake</string>
|
||||
|
||||
@@ -426,7 +426,6 @@
|
||||
<string name="call_answered_elsewhere">通话已在其他设备上接听</string>
|
||||
<string name="call_requires_camera_permission">视频通话需要摄像头权限</string>
|
||||
<string name="call_requires_mic_permission">通话需要麦克风权限</string>
|
||||
<string name="call_requires_connection">无法开始通话。请确保您的设备已连接到互联网,然后重试。</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">与 %1$s 的通话</string>
|
||||
<!-- Use e.g. in a notifications during ringing. Placeholder is relaced by the name of the contact being called. -->
|
||||
@@ -655,7 +654,6 @@
|
||||
<string name="welcome_chat_over_email">安全的去中心化聊天</string>
|
||||
<string name="scan_invitation_code">扫描邀请码</string>
|
||||
<string name="login_title">登录</string>
|
||||
<string name="login_advanced_hint">此登录适用于高级用户。\n\n请勿使用您在其他应用中已使用的地址。</string>
|
||||
<string name="login_inbox">收件箱</string>
|
||||
<string name="login_imap_login">IMAP 登录名</string>
|
||||
<string name="login_imap_server">IMAP 服务器</string>
|
||||
@@ -690,7 +688,6 @@
|
||||
<string name="used_for_sending">用于发送</string>
|
||||
<string name="hide_from_contacts">对联系人隐藏</string>
|
||||
<string name="hidden_from_contacts">已对联系人隐藏</string>
|
||||
<string name="enforce_e2ee">对所有中继强制加密</string>
|
||||
<!-- Hint for the list of relays -->
|
||||
<string name="transport_list_hint">在所有中继上均已收到消息。\n\n⚠️ 如果您在此处进行任何更改,请确保所有设备至少运行 2.47.0 版本。否则,较旧的设备可能会错过消息。</string>
|
||||
<!-- shown if a QR code was scanned that can be used as a relay -->
|
||||
|
||||
@@ -834,6 +834,7 @@
|
||||
<string name="pref_background_btn_gallery">Select From Gallery</string>
|
||||
<!-- deprecated -->
|
||||
<string name="pref_imap_folder_warn_disable_defaults">If you change this option, make sure, your server and your other clients are configured accordingly.\n\nOtherwise things may not work at all.</string>
|
||||
<string name="login_imap_folder">IMAP folder</string>
|
||||
<!-- No need to be literal here, you can also use "Use Multiple Devices", "Support Multiple Devices" or other fitting terms. However, it should fit to the wording or your language at https://delta.chat/help -->
|
||||
<string name="pref_multidevice">Multi-Device Mode</string>
|
||||
<string name="pref_multidevice_explain">Synchronize your messages with your other devices. Automatically enabled on adding a second device</string>
|
||||
|
||||
Reference in New Issue
Block a user