diff --git a/.gitmodules b/.gitmodules
index fcc7b8181..e69de29bb 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +0,0 @@
-[submodule "jni/deltachat-core-rust"]
- path = jni/deltachat-core-rust
- url = https://github.com/adbenitez/deltalab-core
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index e765691ad..2a1f35ec4 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -277,6 +277,11 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
+
+
+
diff --git a/jni/dc_wrapper.c b/jni/dc_wrapper.c
index 5a2f9beb1..36003db47 100644
--- a/jni/dc_wrapper.c
+++ b/jni/dc_wrapper.c
@@ -506,6 +506,15 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcContext_getMsgInfo(JNIEnv *env, jobj
}
+JNIEXPORT jstring Java_com_b44t_messenger_DcContext_getMsgHtml(JNIEnv *env, jobject obj, jint msg_id)
+{
+ char* temp = dc_get_msg_html(get_dc_context(env, obj), msg_id);
+ jstring ret = JSTRING_NEW(temp);
+ dc_str_unref(temp);
+ return ret;
+}
+
+
JNIEXPORT void Java_com_b44t_messenger_DcContext_deleteMsgs(JNIEnv *env, jobject obj, jintArray msg_ids)
{
int msg_ids_cnt = 0;
@@ -1337,6 +1346,12 @@ JNIEXPORT jboolean Java_com_b44t_messenger_DcMsg_isSetupMessage(JNIEnv *env, job
}
+JNIEXPORT jboolean Java_com_b44t_messenger_DcMsg_hasHtml(JNIEnv *env, jobject obj)
+{
+ return dc_msg_has_html(get_dc_msg(env, obj))!=0;
+}
+
+
JNIEXPORT jstring Java_com_b44t_messenger_DcMsg_getSetupCodeBegin(JNIEnv *env, jobject obj)
{
char* temp = dc_msg_get_setupcodebegin(get_dc_msg(env, obj));
diff --git a/jni/deltachat-core-rust b/jni/deltachat-core-rust
deleted file mode 160000
index 724ef8a39..000000000
--- a/jni/deltachat-core-rust
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 724ef8a395551e6ffae852aae4b5aa056ea014d3
diff --git a/res/layout/conversation_item_received.xml b/res/layout/conversation_item_received.xml
index 12eb090f8..0c2677295 100644
--- a/res/layout/conversation_item_received.xml
+++ b/res/layout/conversation_item_received.xml
@@ -151,6 +151,23 @@
app:scaleEmojis="true"
tools:text="Mango pickle lorem ipsum"/>
+
+
-
+ android:layout_height="28dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="10dp"
+ android:layout_marginRight="10dp"
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="10dp"
+ android:paddingLeft="12dp"
+ android:paddingRight="12dp"
+ android:background="@drawable/conversation_item_update_background"
+ android:minWidth="20dp"
+ android:text="@string/show_full_message"
+ android:textAllCaps="false"/>
+
+
diff --git a/res/menu/local_help.xml b/res/menu/local_help.xml
index 66b171b9b..1d2dc82bb 100644
--- a/res/menu/local_help.xml
+++ b/res/menu/local_help.xml
@@ -2,28 +2,6 @@
\ No newline at end of file
+
diff --git a/res/menu/web_view.xml b/res/menu/web_view.xml
new file mode 100644
index 000000000..35a8008b4
--- /dev/null
+++ b/res/menu/web_view.xml
@@ -0,0 +1,27 @@
+
+
+
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 9e75252d3..b992b0b75 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -187,6 +187,7 @@
Rul til toppen
Hjælp
Vælg alle
+ Vælg flere
Udvid
Rediger navn
Opsætning
@@ -600,7 +601,6 @@
Tidsudløb for beskeder sat til 1 dag.
Tidsudløb for beskeder sat til 1 uge.
Tidsudløb for beskeder sat til 4 uger.
-
Chat-beskyttelse slået fra.
Chat-beskyttelse sluttet til.
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 91a5be459..5c2496329 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -39,6 +39,12 @@
Profil
Hauptmenü
Chat starten
+ Ganze Nachricht anzeigen…
+ Bilder nachladen
+
+ Das Nachladen von Bildern kann dazu verwendet werden, Sie zu tracken.\n\nDie Option erlaubt auch das Laden von Schriftarten und anderen Inhalten; wenn sie deaktiviert ist, werden eingebettete oder zwischengespeicherte Bilder dennoch angezeigt.\n\nBilder nachladen?
+ Immer
+ Einmal
Passwort anzeigen
Passwort verstecken
Nicht jetzt
@@ -554,6 +560,8 @@ Einige Anbieter platzieren zusätzliche Informationen im Posteingang, die Sie z.
Sofort
Nach 30 Sekunden
Nach 1 Minute
+ Nach 5 Minuten
+ Nach 30 Minuten
Nach 1 Stunde
Nach 1 Tag
Nach 1 Woche
@@ -603,6 +611,10 @@ Einige Anbieter platzieren zusätzliche Informationen im Posteingang, die Sie z.
Ablaufzeit verschwindender Nachrichten auf 1 Tag festgelegt.
Ablaufzeit verschwindender Nachrichten auf 1 Woche festgelegt.
Ablaufzeit verschwindender Nachrichten auf 4 Wochen festgelegt.
+ Ablaufzeit verschwindender Nachrichten auf %1$s Minuten festgelegt.
+ Ablaufzeit verschwindender Nachrichten auf %1$s Stunden festgelegt.
+ Ablaufzeit verschwindender Nachrichten auf %1$s Tage festgelegt.
+ Ablaufzeit verschwindender Nachrichten auf %1$s Wochen festgelegt.
Chat-Absicherung ausgeschaltet.
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index b33c58ca9..97a6bb17b 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -39,6 +39,12 @@
Perfil
Menú principal
Comienza un chat
+ Mostrar el mensaje completo...
+ Cargar imágenes remotas
+
+ Las imágenes remotas pueden ser usadas para seguirte.\n\nLos ajustes también te permiten cargar fuentes y otros contenidos. Inclusive si están deshabilitadas, quizás puedas ver imágenes embebidas o en caché.\n\n¿Cargar imágenes remotas?
+ Siempre
+ Una vez
Mostrar contraseña
Ocultar contraseña
Ahora no
@@ -187,6 +193,7 @@
Desplazarse al inicio
Ayuda
Seleccionar todo
+ Selecciona más
Expandir
Editar nombre
Ajustes
@@ -600,7 +607,6 @@
Temporizador de borrado de mensaje dispuesto en 1 día
Temporizador de borrado de mensaje dispuesto en 1 semana
Temporizador de borrado de mensaje dispuesto en 4 semanas
-
Protección de chat desactivada
Protección de chat activa
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index a51b6515c..a711bd143 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -601,7 +601,6 @@
زمان سنج پیام های ناپدید شونده برای 1 روز تنظیم شد.
زمان سنج پیام های ناپدید شونده برای 1 هفته تنظیم شد.
زمان سنج پیام های ناپدید شونده برای 4 هفته تنظیم شد.
-
محافظت از گفنگو غیرفعال شد.
محافظت از گفتگو فعال شد.
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 1f031116e..0bc61b10b 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -600,7 +600,6 @@
Temporisation des messages éphémères réglée à 1 journée.
Temporisation des messages éphémères réglée à 1 semaine.
Temporisation des messages éphémères réglée à 4 semaines.
-
Protection de la discussion désactivée.
Protection de la discussion activée.
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 86637f901..b17a13386 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -39,6 +39,12 @@
Perfil
Menú principal
Inciar chat
+ Mostrar mensaxe completa...
+ Cargar imaxes remotas
+
+ As imaxes remotas poden ser utilizadas para rastrexarte.\n\nO axuste tamén permite cargar tipografías e outro contido. Incluso estando desactivado, é posible ver as imaxes gardadas na caché.\n\nCargar imaxes remotas?
+ Sempre
+ Unha vez
Mostrar contrasinal
Agochar contrasinal
Agora non
@@ -187,6 +193,7 @@
Moverse arriba
Axuda
Escoller todo
+ Elexir máis
Despregar
Editar nome
Axustes
@@ -600,7 +607,6 @@
As mensaxes desaparecerán após 1 día.
As mensaxes desaparecerán após 1 semana.
As mensaxes desaparecerán após 4 semanas.
-
Protección do chat desactivada.
Protección do chat activada.
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 3527570d6..3a3c1144a 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -71,23 +71,23 @@
Bulan lalu
- - %dmenit
+ - %d menit
- - %djam
+ - %d jam
- %d obrolan
- - %dkontak
+ - %d kontak
- - %dpesan
+ - %d pesan
- - %danggota
+ - %d anggota
Saya
@@ -561,6 +561,7 @@
Pengaturan kode QR membutuhkan koneksi internet. Mohon nyalakan koneksi ke jaringan sebelum mulai proses.
Tidak ada koneksi internet, tidak bisa menjalankan kode pengaturan QR.
Buat alamat email baru di \"%1$s\" dan login di sana?
+ Buat alamat email baru di\"%1$s\" dan masuk ke sana?\n\nAkun yang sudah ada tidak akan di hapus. Gunakankan item \"Ganti akun\" untuk mengganti akun anda
Login berhasil—alamat surel Anda %1$s\n\nKalau mau, masukkan satu nama dan gambar avatar yang dapat dilihat oleh orang yang ngobrol dengan Anda.
Kode QR yang dipindai tidak dapat digunakan untuk mengatur akun baru.
Kode QR yang dipindai adalah untuk mengatur akun baru. Anda dapat memindai kode QR saat mengatur instalasi Delta Chat baru.
@@ -666,6 +667,7 @@
Status pengiriman: Kesalahan
+ Enkripsi padlock
Status pengiriman: Mengirim
Status pengiriman: Draft
Status pengiriman: Terkirim
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 97c1f0e49..014041c6f 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -39,6 +39,12 @@
Profilo
Menu principale
Inizia una chat
+ Mostra messaggio completo...
+ Carica immagini remote
+
+ Le immagini remote possono essere utilizzate per tracciarti.\n\nL\'impostazione consente anche di caricare caratteri e altri contenuti. Anche se è disabilitato, potresti comunque vedere immagini incorporate o memorizzate nella cache.\n\nCaricare immagini remote?
+ Sempre
+ Una volta
Mostra password
Nascondi password
Non ora
@@ -187,6 +193,7 @@
Scorri in cima
Aiuto
Seleziona tutto
+ Seleziona di più
Espandi
Modifica nome
Impostazioni
@@ -600,7 +607,6 @@
Il timer per la scomparsa dei messaggi è impostato su 1 giorno.
Il timer per la scomparsa dei messaggi è impostato su 1 settimana.
Il timer per la scomparsa dei messaggi è impostato su 4 settimane.
-
Protezione chat disattivata.
Protezione chat abilitata.
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index fa1f86a3c..af2335db3 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -39,6 +39,12 @@
Profiel
Hoofdmenu
Gesprek beginnen
+ Volledige bericht tonen…
+ Externe afbeeldingen laden
+
+ Externe afbeeldingen kunnen worden gebruikt om je gedrag te volgen.\n\nDeze instelling staat tevens toe om andere externe inhoud, zoals lettertypen, te laden, en zelfs na uitschakeling kunnen er ingesloten of gecachte afbeeldingen worden getoond.\n\nWeet je het zeker?
+ Altijd
+ Eenmalig
Wachtwoord tonen
Wachtwoord verbergen
Niet nu
@@ -187,6 +193,7 @@
Ga naar het begin
Hulp
Alles selecteren
+ Meer selecteren
Uitvouwen
Naam wijzigen
Instellingen
@@ -600,7 +607,6 @@
De tijdklok van verdwijnende berichten is ingesteld op 1 dag.
De tijdklok van verdwijnende berichten is ingesteld op 1 week.
De tijdklok van verdwijnende berichten is ingesteld op 4 weken.
-
Gespreksbeveiliging is uitgeschakeld.
Gespreksbeveiliging is ingeschakeld.
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index fe4a0ad9b..e1b8d6a1c 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -597,7 +597,6 @@
Ustawiono zegar znikających wiadomości na 1 dzień.
Ustawiono zegar znikających wiadomości na 1 tydzień.
Ustawiono zegar znikających wiadomości na 4 tygodnie.
-
Ochrona czatu wyłączona.
Ochrona czatu włączona.
diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml
index 544783f3f..2e0a08a12 100644
--- a/res/values-pt-rBR/strings.xml
+++ b/res/values-pt-rBR/strings.xml
@@ -600,7 +600,6 @@
Temporizador de desaparecimento de mensagens definido para 1 dia.
Temporizador de desaparecimento de mensagens definido para 1 semana.
Temporizador de desaparecimento de mensagens definido para 4 semanas.
-
Proteção da conversa desligada.
Proteção da conversa ligada.
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 850008e4a..933e4c647 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -39,6 +39,12 @@
Профиль
Главное меню
Начать чат
+ Показать сообщение…
+ Загружать удаленные изображения
+
+ Удаленные изображения могут использоваться для отслеживания вас.\n\nЭта настройка также позволяет загружать удаленные шрифты и другое содержимое. Даже если она отключена, вы все равно сможете видеть встроенные или кешированные изображения.\n\nЗагружать удаленные изображения?
+ Всегда
+ Только сейчас
Показать пароль
Скрыть пароль
Не сейчас
@@ -199,6 +205,7 @@
Прокрутить вверх
Помощь
Выбрать всё
+ Выбрать больше
Расширить
Изменить имя
Настройки
@@ -577,6 +584,8 @@
Сразу
Через 30 секунд
Через 1 минуту
+ Через 5 минут
+ Через 30 минут
Через 1 час
Через 1 день
Через 1 неделю
@@ -626,6 +635,10 @@
Таймер исчезающих сообщений установлен на 1 день.
Таймер исчезающих сообщений установлен на 1 неделю.
Таймер исчезающих сообщений установлен на 4 недели.
+ Таймер исчезающих сообщений установлен в %1$s минут.
+ Таймер исчезающих сообщений установлен в %1$s часов.
+ Таймер исчезающих сообщений установлен в %1$s дней.
+ Таймер исчезающих сообщений установлен в %1$s недель.
Защита чата отключена.
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 01e0e84ec..a83fc4630 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -39,6 +39,12 @@
Profile
Menuja kryesore
Nisni fjalosje
+ Shfaq mesazhin e plotë…
+ Ngarko figura të largëta
+
+ Figurat e largëta mund të përdoren për t’ju ndjekur.\n\nRregullimi lejon gjithashtu të ngarkohen shkronja dhe lëndë tjetër. Edhe nëse është e çaktivizuar, prapë mund të shihni figura të trupëzuara ose të ruajtura në fshehtinë.\n\nTë ngarkohen figura të largëta?
+ Përherë
+ Vetëm njëherë
Shfaqe fjalëkalimin
Fshihe fjalëkalimin
Jo tani
@@ -186,6 +192,7 @@
Rrëshqit sipër në krye
Nidhmë
Përzgjidhi krejt
+ Përzgjidhni më tepër
Zgjeroje
Përpunoni emrin
Rregullime
@@ -598,7 +605,6 @@
Kohëmatësi për mesazhe që zhduken është vënë në 1 ditë.
Kohëmatësi për mesazhe që zhduken është vënë në 1 javë.
Kohëmatësi për mesazhe që zhduken është vënë në 4 javë.
-
Mbrojtja e fjalosjes u çaktivizua.
Mbrojtja e fjalosjes u aktivizua.
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 95a545f2e..d89ae2340 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -529,7 +529,6 @@
Tiduret för meddelandeborttagning är satt till 1 dag.
Tiduret för meddelandeborttagning är satt till 1 vecka.
Tiduret för meddelandeborttagning är satt till 4 veckor.
-
Skärmlås
Lås åtkomsten med Androids skärmlås eller fingeravtryck. Aktivera även \"Skärmsäkerhet\", för att undvika att visa tidigare innehåll.
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 6ce535437..87ea069bf 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -39,6 +39,12 @@
Profil
Ana menü
Sohbete başla
+ Tam iletiyi göster…
+ Uzak görselleri yükle
+
+ Uzak görseller sizi izlemek için kullanılabilir.\n\nAyar, yazı tiplerinin ve diğer içeriğin de yüklenmesine izin verir. Etkisizleştirildiyse bile katıştırılan ya da önbelleklenen görselleri hâlâ görebilirsiniz.\n\nUzak görseller yüklensin mi?
+ Her zaman
+ Bir kez
Parolayı göster
Parolayı gizle
Şimdi değil
@@ -187,6 +193,7 @@
Üste kaydır
Yardım
Tümünü seç
+ Daha fazlasını seç
Genişlet
Adı düzenle
Ayarlar
@@ -600,7 +607,6 @@
Kaybolan iletiler zamanlayıcısı 1 güne ayarlandı.
Kaybolan iletiler zamanlayıcısı 1 haftaya ayarlandı.
Kaybolan iletiler zamanlayıcısı 4 haftaya ayarlandı.
-
Sohbet koruması etkisizleştirildi.
Sohbet koruması etkinleştirildi.
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 8068dc1fd..3add32728 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -199,6 +199,7 @@
Прокрутити на початок
Допомога
Вибрати все
+ Вибрати ще
Розширити
Редагувати ім\'я
Налаштування
@@ -624,7 +625,6 @@
Таймер зникнення повідомлень встановлено на 1 день.
Таймер зникнення повідомлень встановлено на 1 тиждень.
Таймер зникнення повідомлень встановлено на 4 тижні.
-
Захист чату вимкнено.
Захист чату увімкнено.
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 21f920250..26e2739f4 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -39,6 +39,12 @@
概要
主菜单
开始聊天
+ 显示完整消息…
+ 加载远程图片
+
+ 远程图片可被用来跟踪你。\n\n该设置还允许加载字体和其他内容。即使禁用了它,你仍然可以看到嵌入或缓存的图像。\n\n要加载远程图片吗?
+ 始终
+ 一次
显示密码
隐藏密码
现在不
@@ -181,6 +187,7 @@
滚动至顶部
帮助
全选
+ 选择更多
展开
编辑名称
设置
@@ -588,7 +595,6 @@
设置消息定时销毁计时器为 1 天
设置消息定时销毁计时器为 1 周
设置消息定时销毁计时器为 4 周
-
聊天保护已禁用
聊天保护已启用
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 03f1ea35f..2add81723 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -131,13 +131,14 @@
- - @string/off
- - @string/after_30_seconds
- - @string/after_1_minute
- - @string/autodel_after_1_hour
- - @string/autodel_after_1_day
- - @string/autodel_after_1_week
- - @string/autodel_after_4_weeks
+ - @string/off
+ - @string/after_1_minute
+ - @string/after_5_minutes
+ - @string/after_30_minutes
+ - @string/autodel_after_1_hour
+ - @string/autodel_after_1_day
+ - @string/autodel_after_1_week
+ - @string/autodel_after_4_weeks
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3e283063d..4f4993d11 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -39,6 +39,12 @@
Profile
Main menu
Start chat
+ Show full message…
+ Load remote images
+
+ Remote images can be used to track you.\n\nThe setting also allows loading fonts and other content. Even if it is disabled, you may still see embedded or cached images.\n\nLoad remote images?
+ Always
+ Once
Show password
Hide password
Not now
@@ -552,6 +558,8 @@
At once
After 30 seconds
After 1 minute
+ After 5 minutes
+ After 30 minutes
After 1 hour
After 1 day
After 1 week
@@ -601,6 +609,10 @@
Disappearing messages timer set to 1 day.
Disappearing messages timer set to 1 week.
Disappearing messages timer set to 4 weeks.
+ Disappearing messages timer set to %1$s minutes.
+ Disappearing messages timer set to %1$s hours.
+ Disappearing messages timer set to %1$s days.
+ Disappearing messages timer set to %1$s weeks.
Chat protection disabled.
diff --git a/src/com/b44t/messenger/DcContext.java b/src/com/b44t/messenger/DcContext.java
index 7a6998f58..e74c00563 100644
--- a/src/com/b44t/messenger/DcContext.java
+++ b/src/com/b44t/messenger/DcContext.java
@@ -156,6 +156,7 @@ public class DcContext {
public native void deleteChat (int chat_id);
public @NonNull DcMsg getMsg (int msg_id) { return new DcMsg(getMsgCPtr(msg_id)); }
public native String getMsgInfo (int id);
+ public native String getMsgHtml (int msg_id);
public native int getFreshMsgCount (int chat_id);
public native int estimateDeletionCount(boolean from_server, long seconds);
public native void deleteMsgs (int msg_ids[]);
diff --git a/src/com/b44t/messenger/DcMsg.java b/src/com/b44t/messenger/DcMsg.java
index dc4715b42..25c47ee13 100644
--- a/src/com/b44t/messenger/DcMsg.java
+++ b/src/com/b44t/messenger/DcMsg.java
@@ -114,6 +114,7 @@ public class DcMsg {
public native boolean isForwarded ();
public native boolean isInfo ();
public native boolean isSetupMessage ();
+ public native boolean hasHtml ();
public native String getSetupCodeBegin ();
public native String getVideochatUrl ();
public native int getVideochatType ();
diff --git a/src/org/thoughtcrime/securesms/BindableConversationItem.java b/src/org/thoughtcrime/securesms/BindableConversationItem.java
index 11a0ed6b0..ba9de3407 100644
--- a/src/org/thoughtcrime/securesms/BindableConversationItem.java
+++ b/src/org/thoughtcrime/securesms/BindableConversationItem.java
@@ -28,5 +28,6 @@ public interface BindableConversationItem extends Unbindable {
interface EventListener {
void onQuoteClicked(DcMsg messageRecord);
+ void onShowFullClicked(DcMsg messageRecord);
}
}
diff --git a/src/org/thoughtcrime/securesms/ConversationFragment.java b/src/org/thoughtcrime/securesms/ConversationFragment.java
index fda9a2b8a..6bf5b7b7d 100644
--- a/src/org/thoughtcrime/securesms/ConversationFragment.java
+++ b/src/org/thoughtcrime/securesms/ConversationFragment.java
@@ -872,6 +872,14 @@ public class ConversationFragment extends Fragment
}
}
}
+
+ @Override
+ public void onShowFullClicked(DcMsg messageRecord) {
+ Intent intent = new Intent(getActivity(), FullMsgActivity.class);
+ intent.putExtra(FullMsgActivity.MSG_ID_EXTRA, messageRecord.getId());
+ startActivity(intent);
+ getActivity().overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out);
+ }
}
@Override
diff --git a/src/org/thoughtcrime/securesms/ConversationItem.java b/src/org/thoughtcrime/securesms/ConversationItem.java
index 020fb0545..5051ca971 100644
--- a/src/org/thoughtcrime/securesms/ConversationItem.java
+++ b/src/org/thoughtcrime/securesms/ConversationItem.java
@@ -32,6 +32,7 @@ import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -110,6 +111,7 @@ public class ConversationItem extends LinearLayout
private AvatarImageView contactPhoto;
protected ViewGroup contactPhotoHolder;
private ViewGroup container;
+ private Button showFullMessage;
private @NonNull Set batchSelected = new HashSet<>();
private @NonNull Recipient conversationRecipient;
@@ -162,6 +164,7 @@ public class ConversationItem extends LinearLayout
this.quoteView = findViewById(R.id.quote_view);
this.container = findViewById(R.id.container);
this.replyView = findViewById(R.id.reply_icon);
+ this.showFullMessage = findViewById(R.id.show_full_message);
setOnClickListener(new ClickListener(null));
@@ -361,6 +364,18 @@ public class ConversationItem extends LinearLayout
bodyText.setVisibility(View.VISIBLE);
}
+ if (messageRecord.hasHtml()) {
+ showFullMessage.setVisibility(View.VISIBLE);
+ showFullMessage.setOnClickListener(view -> {
+ if (eventListener != null && batchSelected.isEmpty()) {
+ eventListener.onShowFullClicked(messageRecord);
+ } else {
+ passthroughClickListener.onClick(view);
+ }
+ });
+ } else {
+ showFullMessage.setVisibility(View.GONE);
+ }
}
private void setMediaAttributes(@NonNull DcMsg messageRecord,
diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java
index c9a809580..6020e1050 100644
--- a/src/org/thoughtcrime/securesms/ConversationListActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java
@@ -282,7 +282,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
final ApplicationDcContext dcContext = DcHelper.getContext(this);
if (isForwarding(this) && dcContext.getChat(chatId).isSelfTalk()) {
SendRelayedMessageUtil.immediatelyRelay(this, chatId);
- Toast.makeText(this, "✔️ " + getString(R.string.saved), Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, DynamicTheme.getCheckmarkEmoji(this) + " " + getString(R.string.saved), Toast.LENGTH_SHORT).show();
handleResetRelaying();
finish();
} else {
diff --git a/src/org/thoughtcrime/securesms/EphemeralMessagesDialog.java b/src/org/thoughtcrime/securesms/EphemeralMessagesDialog.java
index e8e33951c..97c960c0a 100644
--- a/src/org/thoughtcrime/securesms/EphemeralMessagesDialog.java
+++ b/src/org/thoughtcrime/securesms/EphemeralMessagesDialog.java
@@ -60,12 +60,13 @@ public class EphemeralMessagesDialog {
.setPositiveButton(R.string.ok, (dialog, which) -> {
final long burnAfter;
switch (selectedChoice[0]) {
- case 1: burnAfter = TimeUnit.SECONDS.toSeconds(30); break;
- case 2: burnAfter = TimeUnit.MINUTES.toSeconds(1); break;
- case 3: burnAfter = TimeUnit.HOURS.toSeconds(1); break;
- case 4: burnAfter = TimeUnit.DAYS.toSeconds(1); break;
- case 5: burnAfter = TimeUnit.DAYS.toSeconds(7); break;
- case 6: burnAfter = TimeUnit.DAYS.toSeconds(28); break;
+ case 1: burnAfter = TimeUnit.MINUTES.toSeconds(1); break;
+ case 2: burnAfter = TimeUnit.MINUTES.toSeconds(5); break;
+ case 3: burnAfter = TimeUnit.MINUTES.toSeconds(30); break;
+ case 4: burnAfter = TimeUnit.HOURS.toSeconds(1); break;
+ case 5: burnAfter = TimeUnit.DAYS.toSeconds(1); break;
+ case 6: burnAfter = TimeUnit.DAYS.toSeconds(7); break;
+ case 7: burnAfter = TimeUnit.DAYS.toSeconds(28); break;
default: burnAfter = 0; break;
}
listener.onTimeSelected(burnAfter);
@@ -78,25 +79,30 @@ public class EphemeralMessagesDialog {
}
private static int getPreselection(int timespan) {
- if (timespan == TimeUnit.DAYS.toSeconds(28)) {
- return 6;
- } else if (timespan == TimeUnit.DAYS.toSeconds(7)) {
- return 5;
- } else if (timespan == TimeUnit.DAYS.toSeconds(1)) {
- return 4;
- } else if (timespan == TimeUnit.HOURS.toSeconds(1)) {
- return 3;
- } else if (timespan == TimeUnit.MINUTES.toSeconds(1)) {
- return 2;
- } else if (timespan == TimeUnit.SECONDS.toSeconds(30)) {
- return 1;
- } else {
- if (timespan != 0) {
- Log.e(TAG, "Invalid ephemeral messages timespan, falling back to OFF");
- }
- return 0;
+ if (timespan == 0) {
+ return 0; // off
}
+ // Choose timespan close to the current one out of available options.
+ if (timespan < TimeUnit.MINUTES.toSeconds(5)) {
+ return 1; // 1 minute
+ }
+ if (timespan < TimeUnit.MINUTES.toSeconds(30)) {
+ return 2; // 5 minutes
+ }
+ if (timespan < TimeUnit.HOURS.toSeconds(1)) {
+ return 3; // 30 minutes
+ }
+ if (timespan < TimeUnit.DAYS.toSeconds(1)) {
+ return 4; // 1 hour
+ }
+ if (timespan < TimeUnit.DAYS.toSeconds(7)) {
+ return 5; // 1 day
+ }
+ if (timespan < TimeUnit.DAYS.toSeconds(28)) {
+ return 6; // 1 week
+ }
+ return 7; // 4 weeks
}
}
diff --git a/src/org/thoughtcrime/securesms/FullMsgActivity.java b/src/org/thoughtcrime/securesms/FullMsgActivity.java
new file mode 100644
index 000000000..ae3f1bf13
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/FullMsgActivity.java
@@ -0,0 +1,159 @@
+package org.thoughtcrime.securesms;
+
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.webkit.WebSettings;
+
+import androidx.appcompat.app.AlertDialog;
+
+import org.thoughtcrime.securesms.connect.ApplicationDcContext;
+import org.thoughtcrime.securesms.connect.DcHelper;
+import org.thoughtcrime.securesms.util.DynamicTheme;
+import org.thoughtcrime.securesms.util.Prefs;
+import org.thoughtcrime.securesms.util.Util;
+
+import java.lang.ref.WeakReference;
+
+import static android.util.Base64.DEFAULT;
+import static android.util.Base64.encodeToString;
+
+public class FullMsgActivity extends WebViewActivity
+{
+ public static final String MSG_ID_EXTRA = "msg_id";
+ private int msgId;
+ private ApplicationDcContext dcContext;
+ private boolean loadRemoteContent = false;
+
+ enum LoadRemoteContent {
+ NEVER,
+ ONCE,
+ ALWAYS
+ }
+
+ @Override
+ protected void onCreate(Bundle state, boolean ready) {
+ super.onCreate(state, ready);
+
+ loadRemoteContent = Prefs.getAlwaysLoadRemoteContent(this);
+ webView.getSettings().setBlockNetworkLoads(!loadRemoteContent);
+
+ // setBuiltInZoomControls() adds pinch-to-zoom as well as two on-screen zoom control buttons.
+ // The latter are a bit annoying, however, they are deprecated anyway,
+ // and also Android docs recommend to disable them with setDisplayZoomControls().
+ webView.getSettings().setBuiltInZoomControls(true);
+ webView.getSettings().setDisplayZoomControls(false);
+
+ // disable useless and unwanted features:
+ // - JavaScript and Plugins are disabled by default, however,
+ // doing it explicitly here protects against changed base classes or bugs
+ // - Content- and File-access is enabled by default and disabled here
+ // - the other setAllow*() functions are related to enabled JavaScript only
+ // - "safe browsing" comes with privacy issues and already disabled in the base class
+ webView.getSettings().setJavaScriptEnabled(false);
+ webView.getSettings().setPluginState(WebSettings.PluginState.OFF);
+ webView.getSettings().setAllowContentAccess(false);
+ webView.getSettings().setAllowFileAccess(false);
+
+ getSupportActionBar().setTitle(getString(R.string.chat_input_placeholder));
+
+ dcContext = DcHelper.getContext(this);
+ msgId = getIntent().getIntExtra(MSG_ID_EXTRA, 0);
+
+ loadHtmlAsync(new WeakReference<>(this));
+ }
+
+ private static void loadHtmlAsync(final WeakReference activityReference) {
+ Util.runOnBackground(() -> {
+ // android9 seems to make problems for non-base64-encoded html,
+ // see eg. https://stackoverflow.com/questions/54516798/webview-loaddata-not-working-on-android-9-0-api-29
+ String html;
+
+ try {
+ FullMsgActivity activity = activityReference.get();
+ html = activity.dcContext.getMsgHtml(activity.msgId);
+ html = encodeToString(html.getBytes("UTF-8"), DEFAULT);
+ String finalHtml = html;
+
+ activity.runOnUiThread(() -> {
+ try {
+ activityReference.get().webView.loadData(finalHtml, "text/html; charset=utf-8", "base64");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ });
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ });
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (isFinishing()) {
+ overridePendingTransition(R.anim.fade_scale_in, R.anim.slide_to_right);
+ }
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+ this.getMenuInflater().inflate(R.menu.full_msg, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ super.onOptionsItemSelected(item);
+ switch (item.getItemId()) {
+ case R.id.load_remote_content:
+ AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(R.string.load_remote_content)
+ .setMessage(R.string.load_remote_content_ask);
+
+ // we are using the buttons "[Always] [Never][Once]" in that order.
+ // 1. Checkmarks before [Always] and [Never] show the current state.
+ // 2. [Once] is also shown in always-mode and disables always-mode if selected
+ // (there was the idea to hide [Once] in always mode, but that looks more like a bug in the end)
+ // (maybe a usual Always-Checkbox and "[Cancel][OK]" buttons are an alternative, however, a [Once]
+ // would be required as well - probably as the leftmost button which is not that usable in
+ // not-always-mode where the dialog is used more often. Or [Ok] would mean "Once" as well as "Change checkbox setting",
+ // which is also a bit weird. Anyway, let's give the three buttons a try :)
+ final String checkmarkPrefix = DynamicTheme.getCheckmarkEmoji(this) + " ";
+ if (Prefs.getAlwaysLoadRemoteContent(this)) {
+ builder.setNeutralButton(checkmarkPrefix + getString(R.string.always), (dialog, which) -> onChangeLoadRemoteContent(LoadRemoteContent.ALWAYS));
+ builder.setNegativeButton(R.string.never, (dialog, which) -> onChangeLoadRemoteContent(LoadRemoteContent.NEVER));
+ builder.setPositiveButton(R.string.once, (dialog, which) -> onChangeLoadRemoteContent(LoadRemoteContent.ONCE));
+ } else {
+ builder.setNeutralButton(R.string.always, (dialog, which) -> onChangeLoadRemoteContent(LoadRemoteContent.ALWAYS));
+ builder.setNegativeButton((loadRemoteContent? "" : checkmarkPrefix) + getString(R.string.never), (dialog, which) -> onChangeLoadRemoteContent(LoadRemoteContent.NEVER));
+ builder.setPositiveButton((loadRemoteContent? checkmarkPrefix : "") + getString(R.string.once), (dialog, which) -> onChangeLoadRemoteContent(LoadRemoteContent.ONCE));
+ }
+
+ builder.show();
+ return true;
+ }
+ return false;
+ }
+
+ private void onChangeLoadRemoteContent(LoadRemoteContent loadRemoteContent) {
+ switch (loadRemoteContent) {
+ case NEVER:
+ this.loadRemoteContent = false;
+ Prefs.setBooleanPreference(this, Prefs.ALWAYS_LOAD_REMOTE_CONTENT, false);
+ break;
+ case ONCE:
+ this.loadRemoteContent = true;
+ Prefs.setBooleanPreference(this, Prefs.ALWAYS_LOAD_REMOTE_CONTENT, false);
+ break;
+ case ALWAYS:
+ this.loadRemoteContent = true;
+ Prefs.setBooleanPreference(this, Prefs.ALWAYS_LOAD_REMOTE_CONTENT, true);
+ break;
+ }
+ webView.getSettings().setBlockNetworkLoads(!this.loadRemoteContent);
+ webView.reload();
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/LocalHelpActivity.java b/src/org/thoughtcrime/securesms/LocalHelpActivity.java
index 532bc798f..6a8873ad4 100644
--- a/src/org/thoughtcrime/securesms/LocalHelpActivity.java
+++ b/src/org/thoughtcrime/securesms/LocalHelpActivity.java
@@ -1,269 +1,80 @@
package org.thoughtcrime.securesms;
-import android.content.ActivityNotFoundException;
-import android.content.Intent;
import android.content.res.AssetManager;
-import android.net.Uri;
import android.os.Bundle;
-import android.util.Log;
import android.view.Menu;
-import android.view.MenuInflater;
import android.view.MenuItem;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import android.widget.ImageView;
-import android.widget.Toast;
-
-import androidx.appcompat.widget.SearchView;
-
-import org.thoughtcrime.securesms.util.DynamicLanguage;
-import org.thoughtcrime.securesms.util.DynamicTheme;
import java.io.InputStream;
import java.util.Locale;
-public class LocalHelpActivity extends PassphraseRequiredActionBarActivity
- implements SearchView.OnQueryTextListener,
- WebView.FindListener
+public class LocalHelpActivity extends WebViewActivity
{
- private static final String TAG = LocalHelpActivity.class.getSimpleName();
+ @Override
+ protected void onCreate(Bundle state, boolean ready) {
+ super.onCreate(state, ready);
+ getSupportActionBar().setTitle(getString(R.string.menu_help));
- private WebView webView;
- private final DynamicTheme dynamicTheme = new DynamicTheme();
- private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
-
- @Override
- protected void onPreCreate() {
- dynamicTheme.onCreate(this);
- dynamicLanguage.onCreate(this);
- }
-
- private boolean assetExists(String fileName) {
- // test using AssetManager.open();
- // AssetManager.list() is unreliable eg. on my Android 7 Moto G
- // and also reported to be pretty slow.
- boolean exists = false;
- try {
- AssetManager assetManager = getResources().getAssets();
- InputStream is = assetManager.open(fileName);
- exists = true;
- is.close();
- } catch(Exception e) {
- ;
+ String helpPath = "help/LANG/help.html";
+ String helpLang = "en";
+ try {
+ Locale locale = dynamicLanguage.getCurrentLocale();
+ String appLang = locale.getLanguage();
+ if (assetExists(helpPath.replace("LANG", appLang))) {
+ helpLang = appLang;
+ } else {
+ appLang = appLang.substring(0, 2);
+ if (assetExists(helpPath.replace("LANG", appLang))) {
+ helpLang = appLang;
}
- return exists;
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
}
- @Override
- protected void onCreate(Bundle state, boolean ready) {
- setContentView(R.layout.local_help_activity);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setTitle(getString(R.string.menu_help));
+ webView.loadUrl("file:///android_asset/" + helpPath.replace("LANG", helpLang));
+ }
- String helpPath = "help/LANG/help.html";
- String helpLang = "en";
- try {
- Locale locale = dynamicLanguage.getCurrentLocale();
- String appLang = locale.getLanguage();
- if (assetExists(helpPath.replace("LANG", appLang))) {
- helpLang = appLang;
- } else {
- appLang = appLang.substring(0, 2);
- if (assetExists(helpPath.replace("LANG", appLang))) {
- helpLang = appLang;
- }
- }
- } catch(Exception e) {
- e.printStackTrace();
- }
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+ this.getMenuInflater().inflate(R.menu.local_help, menu);
+ return true;
+ }
- webView = findViewById(R.id.webview);
- webView.setWebViewClient(new WebViewClient(){
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- if (url != null && (url.startsWith("http://") || url.startsWith("https://"))) {
- openOnlineUrl(url);
- return true;
- }
- return false;
- }
- });
- webView.loadUrl("file:///android_asset/" + helpPath.replace("LANG", helpLang));
- webView.setFindListener(this);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- dynamicTheme.onResume(this);
- dynamicLanguage.onResume(this);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- MenuInflater inflater = this.getMenuInflater();
- menu.clear();
-
- inflater.inflate(R.menu.local_help, menu);
-
- try {
- MenuItem searchItem = menu.findItem(R.id.menu_search_localhelp);
- searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
- @Override
- public boolean onMenuItemActionExpand(final MenuItem item) {
- searchMenu = menu;
- LocalHelpActivity.this.lastQuery = "";
- LocalHelpActivity.this.makeSearchMenuVisible(menu, searchItem, true);
- return true;
- }
-
- @Override
- public boolean onMenuItemActionCollapse(final MenuItem item) {
- LocalHelpActivity.this.makeSearchMenuVisible(menu, searchItem, false);
- return true;
- }
- });
- SearchView searchView = (SearchView) searchItem.getActionView();
- searchView.setOnQueryTextListener(this);
- searchView.setQueryHint(getString(R.string.search));
- searchView.setIconifiedByDefault(true);
-
- // hide the [X] beside the search field - this is too much noise, search can be aborted eg. by "back"
- ImageView closeBtn = searchView.findViewById(R.id.search_close_btn);
- if (closeBtn!=null) {
- closeBtn.setEnabled(false);
- closeBtn.setImageDrawable(null);
- }
- } catch (Exception e) {
- Log.e(TAG, "cannot set up help-search: ", e);
- }
-
- super.onPrepareOptionsMenu(menu);
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ super.onOptionsItemSelected(item);
+ switch (item.getItemId()) {
+ case R.id.log_scroll_up:
+ webView.scrollTo(0, 0);
+ return true;
+ case R.id.learn_more:
+ openOnlineUrl("https://delta.chat");
+ return true;
+ case R.id.contribute:
+ openOnlineUrl("https://github.com/deltachat/deltachat-android");
+ return true;
+ case R.id.report_issue:
+ openOnlineUrl("https://github.com/deltachat/deltachat-android/issues");
return true;
}
+ return false;
+ }
-
- // search
-
- private Menu searchMenu = null;
- private String lastQuery = "";
- private Toast lastToast = null;
-
- private void updateResultCounter(int curr, int total) {
- if (searchMenu!=null) {
- MenuItem item = searchMenu.findItem(R.id.menu_search_counter);
- if (curr!=-1) {
- item.setTitle(String.format("%d/%d", total==0? 0 : curr+1, total));
- item.setVisible(true);
- } else {
- item.setVisible(false);
- }
- }
+ private boolean assetExists(String fileName) {
+ // test using AssetManager.open();
+ // AssetManager.list() is unreliable eg. on my Android 7 Moto G
+ // and also reported to be pretty slow.
+ boolean exists = false;
+ try {
+ AssetManager assetManager = getResources().getAssets();
+ InputStream is = assetManager.open(fileName);
+ exists = true;
+ is.close();
+ } catch(Exception e) {
+ ; // a non-existent asset is no error, the function's purpose is to check exactly that.
}
-
- @Override
- public boolean onQueryTextSubmit(String query) {
- return true; // action handled by listener
- }
-
- @Override
- public boolean onQueryTextChange(String query) {
- String normQuery = query.trim();
- lastQuery = normQuery;
- if (lastToast!=null) {
- lastToast.cancel();
- lastToast = null;
- }
- webView.findAllAsync(normQuery);
- return true; // action handled by listener
- }
-
- @Override
- public void onFindResultReceived (int activeMatchOrdinal, int numberOfMatches, boolean isDoneCounting)
- {
- if (isDoneCounting) {
- if (numberOfMatches>0) {
- updateResultCounter(activeMatchOrdinal, numberOfMatches);
- } else {
- if (lastQuery.isEmpty()) {
- updateResultCounter(-1, 0); // hide
- } else {
- String msg = getString(R.string.search_no_result_for_x, lastQuery);
- if (lastToast != null) {
- lastToast.cancel();
- }
- lastToast = Toast.makeText(this, msg, Toast.LENGTH_SHORT);
- lastToast.show();
- updateResultCounter(0, 0); // show as "0/0"
- }
- }
- }
- }
-
-
- // other actions
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- super.onOptionsItemSelected(item);
- switch (item.getItemId()) {
- case android.R.id.home:
- finish();
- return true;
- case R.id.menu_search_up:
- if (lastQuery.isEmpty()) {
- webView.scrollTo(0, 0);
- } else {
- webView.findNext(false);
- }
- return true;
- case R.id.menu_search_down:
- if (lastQuery.isEmpty()) {
- webView.scrollTo(0, 1000000000);
- } else {
- webView.findNext(true);
- }
- return true;
- case R.id.log_scroll_up:
- webView.scrollTo(0, 0);
- return true;
- case R.id.learn_more:
- openOnlineUrl("https://delta.chat");
- return true;
- case R.id.contribute:
- openOnlineUrl("https://github.com/deltachat/deltachat-android");
- return true;
- case R.id.report_issue:
- openOnlineUrl("https://github.com/deltachat/deltachat-android/issues");
- return true;
- }
- return false;
- }
-
- @Override
- public void onBackPressed() {
- if (webView.canGoBack()) {
- webView.goBack();
- } else {
- super.onBackPressed();
- }
- }
-
- private void openOnlineUrl(String url) {
- try {
- startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
- } catch (ActivityNotFoundException e) {
- Toast.makeText(this, R.string.no_browser_installed, Toast.LENGTH_LONG).show();
- }
- }
-
- @Override
- public void onActivityResult(int reqCode, int resultCode, final Intent data) {
- super.onActivityResult(reqCode, resultCode, data);
- }
-
+ return exists;
+ }
}
diff --git a/src/org/thoughtcrime/securesms/WebViewActivity.java b/src/org/thoughtcrime/securesms/WebViewActivity.java
new file mode 100644
index 000000000..7057ab94e
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/WebViewActivity.java
@@ -0,0 +1,241 @@
+package org.thoughtcrime.securesms;
+
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+import androidx.appcompat.widget.SearchView;
+
+import org.thoughtcrime.securesms.util.DynamicLanguage;
+import org.thoughtcrime.securesms.util.DynamicTheme;
+
+public class WebViewActivity extends PassphraseRequiredActionBarActivity
+ implements SearchView.OnQueryTextListener,
+ WebView.FindListener
+{
+ private static final String TAG = WebViewActivity.class.getSimpleName();
+
+ protected WebView webView;
+ private final DynamicTheme dynamicTheme = new DynamicTheme();
+ protected final DynamicLanguage dynamicLanguage = new DynamicLanguage();
+
+ @Override
+ protected void onPreCreate() {
+ dynamicTheme.onCreate(this);
+ dynamicLanguage.onCreate(this);
+ }
+
+ @Override
+ protected void onCreate(Bundle state, boolean ready) {
+ setContentView(R.layout.web_view_activity);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ webView = findViewById(R.id.webview);
+ webView.setWebViewClient(new WebViewClient() {
+ // `shouldOverrideUrlLoading()` is called when the user clicks a URL,
+ // returning `true` means, the URL is passed to `loadUrl()`, `false` aborts loading.
+ // the method is not called for POST request nor for on-page-links.
+ //
+ // nb: from API 24, `shouldOverrideUrlLoading(String)` is deprecated and
+ // `shouldOverrideUrlLoading(WebResourceRequest)` shall be used.
+ // the new one has the same functionality, and the old one still exist,
+ // so, to support all systems, for now, using the old one seems to be the simplest way.
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ if (url != null) {
+ if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("mailto:")) {
+ openOnlineUrl(url);
+ // URL opened externally, returning `true` causes the WebView to abort loading
+ return true;
+ }
+ }
+ // by returning `true`, we also abort loading other URLs in our WebView;
+ // eg. that might be weird or internal protocols.
+ // if we come over really useful things, we should allow that explicitly.
+ return true;
+ }
+ });
+ webView.setFindListener(this);
+
+ // disable "safe browsing" as this has privacy issues,
+ // eg. at least false positives are sent to the "Safe Browsing Lookup API".
+ // as all URLs opened in the WebView are local anyway,
+ // "safe browsing" will never be able to report issues, so it can be disabled.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ webView.getSettings().setSafeBrowsingEnabled(false);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ dynamicTheme.onResume(this);
+ dynamicLanguage.onResume(this);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ MenuInflater inflater = this.getMenuInflater();
+ menu.clear();
+
+ inflater.inflate(R.menu.web_view, menu);
+
+ try {
+ MenuItem searchItem = menu.findItem(R.id.menu_search_web_view);
+ searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
+ @Override
+ public boolean onMenuItemActionExpand(final MenuItem item) {
+ searchMenu = menu;
+ WebViewActivity.this.lastQuery = "";
+ WebViewActivity.this.makeSearchMenuVisible(menu, searchItem, true);
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(final MenuItem item) {
+ WebViewActivity.this.makeSearchMenuVisible(menu, searchItem, false);
+ return true;
+ }
+ });
+ SearchView searchView = (SearchView) searchItem.getActionView();
+ searchView.setOnQueryTextListener(this);
+ searchView.setQueryHint(getString(R.string.search));
+ searchView.setIconifiedByDefault(true);
+
+ // hide the [X] beside the search field - this is too much noise, search can be aborted eg. by "back"
+ ImageView closeBtn = searchView.findViewById(R.id.search_close_btn);
+ if (closeBtn!=null) {
+ closeBtn.setEnabled(false);
+ closeBtn.setImageDrawable(null);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "cannot set up web-view-search: ", e);
+ }
+
+ super.onPrepareOptionsMenu(menu);
+ return true;
+ }
+
+
+ // search
+
+ private Menu searchMenu = null;
+ private String lastQuery = "";
+ private Toast lastToast = null;
+
+ private void updateResultCounter(int curr, int total) {
+ if (searchMenu!=null) {
+ MenuItem item = searchMenu.findItem(R.id.menu_search_counter);
+ if (curr!=-1) {
+ item.setTitle(String.format("%d/%d", total==0? 0 : curr+1, total));
+ item.setVisible(true);
+ } else {
+ item.setVisible(false);
+ }
+ }
+ }
+
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ return true; // action handled by listener
+ }
+
+ @Override
+ public boolean onQueryTextChange(String query) {
+ String normQuery = query.trim();
+ lastQuery = normQuery;
+ if (lastToast!=null) {
+ lastToast.cancel();
+ lastToast = null;
+ }
+ webView.findAllAsync(normQuery);
+ return true; // action handled by listener
+ }
+
+ @Override
+ public void onFindResultReceived (int activeMatchOrdinal, int numberOfMatches, boolean isDoneCounting)
+ {
+ if (isDoneCounting) {
+ if (numberOfMatches>0) {
+ updateResultCounter(activeMatchOrdinal, numberOfMatches);
+ } else {
+ if (lastQuery.isEmpty()) {
+ updateResultCounter(-1, 0); // hide
+ } else {
+ String msg = getString(R.string.search_no_result_for_x, lastQuery);
+ if (lastToast != null) {
+ lastToast.cancel();
+ }
+ lastToast = Toast.makeText(this, msg, Toast.LENGTH_SHORT);
+ lastToast.show();
+ updateResultCounter(0, 0); // show as "0/0"
+ }
+ }
+ }
+ }
+
+
+ // other actions
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ super.onOptionsItemSelected(item);
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
+ case R.id.menu_search_up:
+ if (lastQuery.isEmpty()) {
+ webView.scrollTo(0, 0);
+ } else {
+ webView.findNext(false);
+ }
+ return true;
+ case R.id.menu_search_down:
+ if (lastQuery.isEmpty()) {
+ webView.scrollTo(0, 1000000000);
+ } else {
+ webView.findNext(true);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (webView.canGoBack()) {
+ webView.goBack();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ protected void openOnlineUrl(String url) {
+ try {
+ startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(this, R.string.no_browser_installed, Toast.LENGTH_LONG).show();
+ }
+ }
+
+ @Override
+ public void onActivityResult(int reqCode, int resultCode, final Intent data) {
+ super.onActivityResult(reqCode, resultCode, data);
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/connect/ApplicationDcContext.java b/src/org/thoughtcrime/securesms/connect/ApplicationDcContext.java
index 8d70fd513..791cfc8fc 100644
--- a/src/org/thoughtcrime/securesms/connect/ApplicationDcContext.java
+++ b/src/org/thoughtcrime/securesms/connect/ApplicationDcContext.java
@@ -161,13 +161,16 @@ public class ApplicationDcContext extends DcContext {
setStockTranslation(78, context.getString(R.string.systemmsg_ephemeral_timer_hour));
setStockTranslation(79, context.getString(R.string.systemmsg_ephemeral_timer_day));
setStockTranslation(80, context.getString(R.string.systemmsg_ephemeral_timer_week));
- setStockTranslation(81, context.getString(R.string.systemmsg_ephemeral_timer_four_weeks));
setStockTranslation(82, context.getString(R.string.videochat_invitation));
setStockTranslation(83, context.getString(R.string.videochat_invitation_body));
setStockTranslation(84, context.getString(R.string.configuration_failed_with_error));
setStockTranslation(88, context.getString(R.string.systemmsg_chat_protection_enabled));
setStockTranslation(89, context.getString(R.string.systemmsg_chat_protection_disabled));
setStockTranslation(90, context.getString(R.string.reply_noun));
+ setStockTranslation(93, context.getString(R.string.systemmsg_ephemeral_timer_minutes));
+ setStockTranslation(94, context.getString(R.string.systemmsg_ephemeral_timer_hours));
+ setStockTranslation(95, context.getString(R.string.systemmsg_ephemeral_timer_days));
+ setStockTranslation(96, context.getString(R.string.systemmsg_ephemeral_timer_weeks));
}
public File getImexDir() {
diff --git a/src/org/thoughtcrime/securesms/util/DynamicTheme.java b/src/org/thoughtcrime/securesms/util/DynamicTheme.java
index 12836761d..6864f59e3 100644
--- a/src/org/thoughtcrime/securesms/util/DynamicTheme.java
+++ b/src/org/thoughtcrime/securesms/util/DynamicTheme.java
@@ -80,6 +80,11 @@ public class DynamicTheme {
}
}
+ // return a checkmark emoji that fits to the background of the selected theme.
+ public static String getCheckmarkEmoji(@NonNull Context context) {
+ return isDarkTheme(context) ? "✅" /*blue, white or white in a box*/ : "✔️" /*blue or black*/;
+ }
+
private static boolean isSystemInDarkTheme(@NonNull Context context) {
return (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
}
diff --git a/src/org/thoughtcrime/securesms/util/Prefs.java b/src/org/thoughtcrime/securesms/util/Prefs.java
index 00a7dadfd..b70ecaf09 100644
--- a/src/org/thoughtcrime/securesms/util/Prefs.java
+++ b/src/org/thoughtcrime/securesms/util/Prefs.java
@@ -68,6 +68,9 @@ public class Prefs {
private static final String MAP_CENTER_LONGITUDE = "pref_map_center_longitude";
private static final String MAP_ZOOM = "pref_map_zoom";
+ public static final String ALWAYS_LOAD_REMOTE_CONTENT = "pref_always_load_remote_content";
+ public static final boolean ALWAYS_LOAD_REMOTE_CONTENT_DEFAULT = false;
+
public enum VibrateState {
DEFAULT(0), ENABLED(1), DISABLED(2);
private final int id;
@@ -303,6 +306,11 @@ public class Prefs {
return getBooleanPreference(context, SYSTEM_EMOJI_PREF, false);
}
+ public static boolean getAlwaysLoadRemoteContent(Context context) {
+ return getBooleanPreference(context, Prefs.ALWAYS_LOAD_REMOTE_CONTENT,
+ Prefs.ALWAYS_LOAD_REMOTE_CONTENT_DEFAULT);
+ }
+
// generic preference functions
public static void setBooleanPreference(Context context, String key, boolean value) {