mirror of
https://github.com/ArcaneChat/android.git
synced 2026-07-03 14:05:24 +02:00
Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f89ecea5c | |||
| 30a63dd13b | |||
| a5d2642c37 | |||
| 79e5eedab0 | |||
| 27da217eb7 | |||
| a0c71a5387 | |||
| 4d19a59cb6 | |||
| 819a9f415a | |||
| dff1ef3778 | |||
| 7131f5774e | |||
| aa572508dc | |||
| 1f880efb3f | |||
| 72ace5c156 | |||
| 2f34a6ffa4 | |||
| afdfe7bbaa | |||
| 14f43f2a79 | |||
| f3353cf6e9 | |||
| 722f2cad1e | |||
| b135093628 | |||
| be44789b01 | |||
| 233197095b | |||
| 494cb728c3 | |||
| 60e9a9101a | |||
| c23dac6f99 | |||
| 3965319ef4 | |||
| c4db7c515f | |||
| fbf88e102b | |||
| d5ea043893 | |||
| f70c79ba4a | |||
| 0b2a26e2f1 | |||
| b968ba67b3 | |||
| e234cc864b | |||
| b47e75eed6 | |||
| 6b684bf030 | |||
| ea89d37461 | |||
| 814af177d1 | |||
| 1ccbb7177f | |||
| 3118dbd32b | |||
| 1a5e062dac | |||
| 0bcaea6f01 | |||
| 8283a6c4ad | |||
| cd159bac6c | |||
| 21f917004f | |||
| 0abf2ff00c | |||
| 0ca92ec6b5 | |||
| e66fa53537 | |||
| a51013cc00 | |||
| 171500a97d | |||
| 9a4851961e | |||
| e5ddbacb2e | |||
| 03c7d78e2b | |||
| 00498b02b3 | |||
| ad6e2d1a9f | |||
| 5cfa71cd72 | |||
| 0551044ef9 | |||
| 5126b3eeb2 | |||
| 8d7078f96f | |||
| c446440537 | |||
| 333e4c9eca | |||
| fb1f5df234 | |||
| cc419a9f62 | |||
| 2469b6efef | |||
| 92bc53b672 |
@@ -37,7 +37,7 @@ jobs:
|
||||
- uses: nttld/setup-ndk@v1
|
||||
id: setup-ndk
|
||||
with:
|
||||
ndk-version: r23c
|
||||
ndk-version: r27
|
||||
|
||||
- name: Compile core
|
||||
env:
|
||||
|
||||
@@ -1,5 +1,33 @@
|
||||
# ArcaneChat Android Changelog
|
||||
|
||||
## v1.49.0
|
||||
2024-11
|
||||
|
||||
* New emoji picker with support for more emojis
|
||||
* Webxdc apps can now trigger notifications
|
||||
* Webxdc apps can now deep-link to internal sections when you click their info-messages in chat
|
||||
* Add "Show in Chat" to the menu of opened Webxdc apps
|
||||
* Reverse order of messages in the notification group
|
||||
* Notify reactions to own messages
|
||||
* Improve the button to start Webxdc apps
|
||||
* Make account deletion confirmation dialog faster
|
||||
* Rename "Back up Chats to External Storage" to "Export Backup"
|
||||
* Improve compatibility with classic email clients in the outgoing messages
|
||||
* Removed internal font scaling setting in favor of the better system settings
|
||||
* Use privacy-preserving webxdc addresses
|
||||
* Use Rustls for connections with strict TLS
|
||||
* fix: Trim whitespace from scanned QR codes.
|
||||
* fix quotes: Line-before-quote may be up to 120 character long instead of 80
|
||||
* fix: Prevent accidental wrong-password-notifications
|
||||
* fix: Remove footers from "Show Full Message..."
|
||||
* fix: Only add "member added/removed" messages if they actually do that
|
||||
* fix: Update state of message when fully downloading it
|
||||
* fix: send message: Do not fail if the message does not exist anymore
|
||||
* fix: Do not percent-encode dot when passing to autoconfig server (so, fix handling of some servers)
|
||||
* fix displaynames not being updated when intially scanned by a QR code
|
||||
* several bug fixes
|
||||
* update to core 1.151.2
|
||||
|
||||
## v1.48.3
|
||||
2024-10
|
||||
|
||||
|
||||
+2
-2
@@ -33,8 +33,8 @@ android {
|
||||
useLibrary 'org.apache.http.legacy'
|
||||
|
||||
defaultConfig {
|
||||
versionCode 30000696
|
||||
versionName "1.48.5"
|
||||
versionCode 30000698
|
||||
versionName "1.49.0"
|
||||
|
||||
applicationId "chat.delta.lite"
|
||||
multiDexEnabled true
|
||||
|
||||
+11
-4
@@ -729,12 +729,10 @@ JNIEXPORT jint Java_com_b44t_messenger_DcContext_sendVideochatInvitation(JNIEnv
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jboolean Java_com_b44t_messenger_DcContext_sendWebxdcStatusUpdate(JNIEnv *env, jobject obj, jint msg_id, jstring payload, jstring descr)
|
||||
JNIEXPORT jboolean Java_com_b44t_messenger_DcContext_sendWebxdcStatusUpdate(JNIEnv *env, jobject obj, jint msg_id, jstring payload)
|
||||
{
|
||||
CHAR_REF(payload);
|
||||
CHAR_REF(descr);
|
||||
jboolean ret = dc_send_webxdc_status_update(get_dc_context(env, obj), msg_id, payloadPtr, descrPtr) != 0;
|
||||
CHAR_UNREF(descr);
|
||||
jboolean ret = dc_send_webxdc_status_update(get_dc_context(env, obj), msg_id, payloadPtr, NULL) != 0;
|
||||
CHAR_UNREF(payload);
|
||||
return ret;
|
||||
}
|
||||
@@ -1575,6 +1573,15 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcMsg_getWebxdcInfoJson(JNIEnv *env, j
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jstring Java_com_b44t_messenger_DcMsg_getWebxdcHref(JNIEnv *env, jobject obj)
|
||||
{
|
||||
char* temp = dc_msg_get_webxdc_href(get_dc_msg(env, obj));
|
||||
jstring ret = JSTRING_NEW(temp);
|
||||
dc_str_unref(temp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jboolean Java_com_b44t_messenger_DcMsg_isForwarded(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return dc_msg_is_forwarded(get_dc_msg(env, obj))!=0;
|
||||
|
||||
+1
-1
Submodule jni/deltachat-core-rust updated: 8f8178d5e0...405cd37550
@@ -1 +1 @@
|
||||
../deltachat-pages/tools/create-local-help.py ../deltachat-pages/_site src/main/assets/help
|
||||
../deltachat-pages/tools/create-local-help.py ../deltachat-pages/result src/main/assets/help
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
VERSION=$1
|
||||
|
||||
|
||||
@@ -28,11 +28,6 @@ public class FcmReceiveService extends FirebaseMessagingService {
|
||||
private static volatile String prefixedToken;
|
||||
|
||||
public static void register(Context context) {
|
||||
if (Build.VERSION.SDK_INT < 19) {
|
||||
Log.w(TAG, "FCM not available on SDK < 19");
|
||||
triedRegistering = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (FcmReceiveService.prefixedToken != null) {
|
||||
Log.i(TAG, "FCM already registered");
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<li><a href="#lze-delta-chat-používat-s-protonmail--tutanota--criptext">Lze Delta Chat používat s Protonmail / Tutanota / Criptext?</a></li>
|
||||
<li><a href="#remove-account">How can I delete my account?</a></li>
|
||||
<li><a href="#mám-zájem-o-technické-podrobnosti-kde-najdu-víc">Mám zájem o technické podrobnosti. Kde najdu víc?</a></li>
|
||||
<li><a href="#where-can-my-friends-find-delta-chat">Where can my friends find Delta Chat?</a></li>
|
||||
<li><a href="#jak-je-financován-vývoj-delta-chatu">Jak je financován vývoj Delta Chatu?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -297,8 +298,7 @@ Archived chats remain accessible above the chat list or via search.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>To archive or pin a chat, long tap (Android), use the chat’s menu (Android/Desktop) or swipe to the left (iOS);
|
||||
to mute a chat, use the chat’s menu (Android/Desktop) or the chat’s profile (iOS).</p>
|
||||
<p>To use the functions, long tap or right click a chat in the chat list.</p>
|
||||
|
||||
<h3 id="what-does-the-green-dot-mean">
|
||||
|
||||
@@ -1309,6 +1309,12 @@ One device is not needed for the other to work.</p>
|
||||
and make sure, <strong>Private Network</strong> is selected as “Network profile type”
|
||||
(after transfer, you can change back to the original value)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>iOS</strong>, make sure “System Settings / Apps / Delta Chat / <strong>Local Network</strong>” access is granted</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>macOS</strong>, enable “System Settings / Privacy & Security / <strong>Local Network</strong> / Delta Chat”</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Your system might have a “personal firewall”,
|
||||
which is known to cause problems (especially on Windows).
|
||||
@@ -1425,6 +1431,7 @@ you only open attachments from senders you trust, and not from spammers.</li>
|
||||
<ul>
|
||||
<li>In general, anyone can share webxdc apps with each
|
||||
other without restrictions.</li>
|
||||
<li>From <a href="https://webxdc.org/apps/">webxdc.org/apps</a>.</li>
|
||||
<li>You can <a href="https://delta.chat/en/2023-08-11-xstore">send ‘hi’ to xstore@testrun.org</a>
|
||||
to see an experimental webxdc appstore.
|
||||
All of the apps are open source and for free.</li>
|
||||
@@ -1874,6 +1881,35 @@ Otherwise you might receive undecryptable messages from those group chats.</p>
|
||||
<li>Dobrý začátek je <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md#standards-used-in-delta-chat">Standards used in Delta Chat</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="where-can-my-friends-find-delta-chat">
|
||||
|
||||
|
||||
Where can my friends find Delta Chat? <a href="#where-can-my-friends-find-delta-chat" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat is available for all major and some minor platforms:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>The <strong>official website</strong>, <a href="https://delta.chat/download">https://delta.chat/download</a> shows all options in detail</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>If unavailable, use the <strong>mirror</strong> at <a href="https://deltachat.github.io/deltachat-pages">https://deltachat.github.io/deltachat-pages</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Open one of the following <strong>app stores and search for “Delta Chat”:</strong>
|
||||
Google Play Store, F-Droid, Huawei App Gallery, Amazon App Store, iOS and macOS App Store, Microsoft Store</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Check the <strong>package manager</strong> of your Linux distributions</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Android APKs</strong> are also available on <a href="https://github.com/deltachat/deltachat-android/releases">https://github.com/deltachat/deltachat-android/releases</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="jak-je-financován-vývoj-delta-chatu">
|
||||
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<li><a href="#ist-delta-chat-kompatibel-mit-protonmail--tutanota--criptext">Ist Delta Chat kompatibel mit Protonmail / Tutanota / Criptext?</a></li>
|
||||
<li><a href="#remove-account">Wie kann ich mein Konto löschen?</a></li>
|
||||
<li><a href="#ich-bin-an-technischen-details-interessiert-gibt-es-hierzu-weitere-infos">Ich bin an technischen Details interessiert. Gibt es hierzu weitere Infos?</a></li>
|
||||
<li><a href="#wo-können-meine-freunde-delta-chat-finden">Wo können meine Freunde Delta Chat finden?</a></li>
|
||||
<li><a href="#wie-wird-delta-chat-finanziert">Wie wird Delta Chat finanziert?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -286,7 +287,7 @@ unter dem Text Ihrer Nachrichten.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Zum Archivieren oder Anheften, tippen Sie lange auf den Chat (Android), verwenden Sie das Chatmenü (Android/Desktop) oder wischen Sie nach links (iOS); zum Stummschalten, verwenden Sie das Chatmenü (Android/Desktop) oder das Chatprofil (iOS).</p>
|
||||
<p>Um die Funktionen zu nutzen, lang auf einen Chat in der Chatliste tippen oder den Chat mit der rechten Maustaste anklicken.</p>
|
||||
|
||||
<h3 id="was-bedeutet-der-grüne-punkt">
|
||||
|
||||
@@ -1215,6 +1216,12 @@ Es wurden keine kritischen Probleme gefunden, aber zwei Probleme mit hohem Schwe
|
||||
und sicherstellen, dass <strong>Privates Netzwerk</strong> als “Netzwerkprofiltyp” ausgewählt ist.
|
||||
(nach der Übertragung kann wieder der ursprüngliche Wert verwendet werden)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Auf <strong>iOS</strong>, sicherstellen, dass „Systemeinstellungen / Apps / Delta Chat / <strong>Lokales Netzwerk</strong>“ eingeschaltet ist</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Auf <strong>macOS,</strong> „Systemeinstellungen / Datenschutz & Sicherheit / <strong>Lokales Netzwerk</strong> / Delta Chat“ aktivieren</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Ihr System verfügt möglicherweise über eine “Personal Firewall”, diese sind dafür bekannt, Probleme zu verursachen (insbesondere bei Windows). <strong>Deaktivieren Sie die Personal Firewall</strong> für Delta Chat auf beiden Seiten und versuchen Sie es erneut</p>
|
||||
</li>
|
||||
@@ -1305,6 +1312,7 @@ Verwenden Sie nach Möglichkeit ein Nicht-Gast-Netzwerk. Wenn Sie Zugriff auf de
|
||||
|
||||
<ul>
|
||||
<li>Grundsätzlich kann jeder Webxdc Apps mit anderen ohne Einschränkungen teilen.</li>
|
||||
<li>Von <a href="https://webxdc.org/apps/">webxdc.org/apps</a>.</li>
|
||||
<li>Sie können <a href="https://delta.chat/en/2023-08-11-xstore">‘hallo’ an xstore@testrun.org senden</a>
|
||||
um einen experimentellen Webxdc Appstore zu sehen.
|
||||
Alle Apps dort sind Open Source und umsonst.</li>
|
||||
@@ -1680,6 +1688,34 @@ Andernfalls könnten Sie unverschlüsselte Nachrichten aus diesen Gruppenchats e
|
||||
<li>Siehe hierzu <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md#standards-used-in-delta-chat">in Delta Chat genutzte Standards</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="wo-können-meine-freunde-delta-chat-finden">
|
||||
|
||||
|
||||
Wo können meine Freunde Delta Chat finden? <a href="#wo-können-meine-freunde-delta-chat-finden" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat ist für alle großen und einige kleinere Plattformen verfügbar:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>Die <strong>offizielle Website</strong>, <a href="https://delta.chat/download">https://delta.chat/download</a> zeigt alle Optionen im Detail</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Falls nicht verfügbar, verwenden Sie den <strong>Mirror</strong> auf <a href="https://deltachat.github.io/deltachat-pages">https://deltachat.github.io/deltachat-pages</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Öffnen Sie einen der folgenden <strong>App-Stores und suchen Sie nach „Delta Chat“:</strong> Google Play Store, F-Droid, Huawei App Gallery, Amazon App Store, iOS und macOS App Store, Microsoft Store</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Im <strong>Paketmanager</strong> Ihrer Linux-Distribution nachschauen</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Android APKs</strong> sind auch auf <a href="https://github.com/deltachat/deltachat-android/releases">https://github.com/deltachat/deltachat-android/releases</a> verfügbar.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="wie-wird-delta-chat-finanziert">
|
||||
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<li><a href="#is-delta-chat-compatible-with-protonmail--tutanota--criptext">Is Delta Chat compatible with Protonmail / Tutanota / Criptext?</a></li>
|
||||
<li><a href="#remove-account">How can I delete my account?</a></li>
|
||||
<li><a href="#im-interested-in-the-technical-details-can-you-tell-me-more">I’m interested in the technical details. Can you tell me more?</a></li>
|
||||
<li><a href="#where-can-my-friends-find-delta-chat">Where can my friends find Delta Chat?</a></li>
|
||||
<li><a href="#how-are-delta-chat-developments-funded">How are Delta Chat developments funded?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -301,8 +302,7 @@ Archived chats remain accessible above the chat list or via search.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>To archive or pin a chat, long tap (Android), use the chat’s menu (Android/Desktop) or swipe to the left (iOS);
|
||||
to mute a chat, use the chat’s menu (Android/Desktop) or the chat’s profile (iOS).</p>
|
||||
<p>To use the functions, long tap or right click a chat in the chat list.</p>
|
||||
|
||||
<h3 id="what-does-the-green-dot-mean">
|
||||
|
||||
@@ -1315,6 +1315,12 @@ One device is not needed for the other to work.</p>
|
||||
and make sure, <strong>Private Network</strong> is selected as “Network profile type”
|
||||
(after transfer, you can change back to the original value)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>iOS</strong>, make sure “System Settings / Apps / Delta Chat / <strong>Local Network</strong>” access is granted</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>macOS</strong>, enable “System Settings / Privacy & Security / <strong>Local Network</strong> / Delta Chat”</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Your system might have a “personal firewall”,
|
||||
which is known to cause problems (especially on Windows).
|
||||
@@ -1432,6 +1438,7 @@ you only open attachments from senders you trust, and not from spammers.</li>
|
||||
<ul>
|
||||
<li>In general, anyone can share webxdc apps with each
|
||||
other without restrictions.</li>
|
||||
<li>From <a href="https://webxdc.org/apps/">webxdc.org/apps</a>.</li>
|
||||
<li>You can <a href="https://delta.chat/en/2023-08-11-xstore">send ‘hi’ to xstore@testrun.org</a>
|
||||
to see an experimental webxdc appstore.
|
||||
All of the apps are open source and for free.</li>
|
||||
@@ -1883,6 +1890,35 @@ Otherwise you might receive undecryptable messages from those group chats.</p>
|
||||
<li>See <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md#standards-used-in-delta-chat">Standards used in Delta Chat</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="where-can-my-friends-find-delta-chat">
|
||||
|
||||
|
||||
Where can my friends find Delta Chat? <a href="#where-can-my-friends-find-delta-chat" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat is available for all major and some minor platforms:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>The <strong>official website</strong>, <a href="https://delta.chat/download">https://delta.chat/download</a> shows all options in detail</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>If unavailable, use the <strong>mirror</strong> at <a href="https://deltachat.github.io/deltachat-pages">https://deltachat.github.io/deltachat-pages</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Open one of the following <strong>app stores and search for “Delta Chat”:</strong>
|
||||
Google Play Store, F-Droid, Huawei App Gallery, Amazon App Store, iOS and macOS App Store, Microsoft Store</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Check the <strong>package manager</strong> of your Linux distributions</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Android APKs</strong> are also available on <a href="https://github.com/deltachat/deltachat-android/releases">https://github.com/deltachat/deltachat-android/releases</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="how-are-delta-chat-developments-funded">
|
||||
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<li><a href="#delta-chat-es-compatible-con-protonmail--tutanota--criptext">¿Delta Chat es compatible con Protonmail / Tutanota / Criptext?</a></li>
|
||||
<li><a href="#remove-account">¿Cómo puedo eliminar mi cuenta?</a></li>
|
||||
<li><a href="#estoy-interesado-en-los-detalles-técnicos-pueden-decirme-más">Estoy interesado en los detalles técnicos. ¿Pueden decirme más?</a></li>
|
||||
<li><a href="#where-can-my-friends-find-delta-chat">Where can my friends find Delta Chat?</a></li>
|
||||
<li><a href="#cómo-se-financia-el-desarrollo-de-delta-chat">¿Cómo se financia el desarrollo de Delta Chat?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -1277,6 +1278,12 @@ No es necesario un dispositivo para que el otro funcione.</p>
|
||||
and make sure, <strong>Private Network</strong> is selected as “Network profile type”
|
||||
(after transfer, you can change back to the original value)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>iOS</strong>, make sure “System Settings / Apps / Delta Chat / <strong>Local Network</strong>” access is granted</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>macOS</strong>, enable “System Settings / Privacy & Security / <strong>Local Network</strong> / Delta Chat”</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Your system might have a “personal firewall”,
|
||||
which is known to cause problems (especially on Windows).
|
||||
@@ -1394,6 +1401,7 @@ solo abre archivos adjuntos de remitentes en los que confía y no de spammers.</
|
||||
<ul>
|
||||
<li>In general, anyone can share webxdc apps with each
|
||||
other without restrictions.</li>
|
||||
<li>From <a href="https://webxdc.org/apps/">webxdc.org/apps</a>.</li>
|
||||
<li>You can <a href="https://delta.chat/en/2023-08-11-xstore">send ‘hi’ to xstore@testrun.org</a>
|
||||
to see an experimental webxdc appstore.
|
||||
All of the apps are open source and for free.</li>
|
||||
@@ -1827,6 +1835,35 @@ Otherwise you might receive undecryptable messages from those group chats.</p>
|
||||
<li>Visita la página <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md#standards-used-in-delta-chat">Estándares usados en Delta Chat</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="where-can-my-friends-find-delta-chat">
|
||||
|
||||
|
||||
Where can my friends find Delta Chat? <a href="#where-can-my-friends-find-delta-chat" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat is available for all major and some minor platforms:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>The <strong>official website</strong>, <a href="https://delta.chat/download">https://delta.chat/download</a> shows all options in detail</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>If unavailable, use the <strong>mirror</strong> at <a href="https://deltachat.github.io/deltachat-pages">https://deltachat.github.io/deltachat-pages</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Open one of the following <strong>app stores and search for “Delta Chat”:</strong>
|
||||
Google Play Store, F-Droid, Huawei App Gallery, Amazon App Store, iOS and macOS App Store, Microsoft Store</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Check the <strong>package manager</strong> of your Linux distributions</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Android APKs</strong> are also available on <a href="https://github.com/deltachat/deltachat-android/releases">https://github.com/deltachat/deltachat-android/releases</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="cómo-se-financia-el-desarrollo-de-delta-chat">
|
||||
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<li><a href="#delta-chat-est-il-compatible-avec-protonmail--tutanota--criptext-">Delta Chat est-il compatible avec Protonmail / Tutanota / Criptext ?</a></li>
|
||||
<li><a href="#remove-account">Comment supprimer mon compte ?</a></li>
|
||||
<li><a href="#les-détails-techniques-mintéressent-pouvez-vous-men-dire-plus-">Les détails techniques m’intéressent. Pouvez-vous m’en dire plus ?</a></li>
|
||||
<li><a href="#where-can-my-friends-find-delta-chat">Where can my friends find Delta Chat?</a></li>
|
||||
<li><a href="#comment-est-financé-le-développement-de-delta-chat-">Comment est financé le développement de Delta Chat ?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -1262,6 +1263,12 @@ L’un n’a pas besoin de l’autre pour pouvoir fonctionner.</p>
|
||||
and make sure, <strong>Private Network</strong> is selected as “Network profile type”
|
||||
(after transfer, you can change back to the original value)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>iOS</strong>, make sure “System Settings / Apps / Delta Chat / <strong>Local Network</strong>” access is granted</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>macOS</strong>, enable “System Settings / Privacy & Security / <strong>Local Network</strong> / Delta Chat”</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Il se peut que votre système dispose d’un “pare-feu personnalisé”,
|
||||
source bien connue de dysfonctionnements (en particulier sur Windows).
|
||||
@@ -1375,6 +1382,7 @@ you only open attachments from senders you trust, and not from spammers.</li>
|
||||
<ul>
|
||||
<li>In general, anyone can share webxdc apps with each
|
||||
other without restrictions.</li>
|
||||
<li>From <a href="https://webxdc.org/apps/">webxdc.org/apps</a>.</li>
|
||||
<li>You can <a href="https://delta.chat/en/2023-08-11-xstore">send ‘hi’ to xstore@testrun.org</a>
|
||||
to see an experimental webxdc appstore.
|
||||
All of the apps are open source and for free.</li>
|
||||
@@ -1789,6 +1797,35 @@ Otherwise you might receive undecryptable messages from those group chats.</p>
|
||||
<li>Consultez les <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md#standards-used-in-delta-chat">standards utilisés dans Delta Chat</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="where-can-my-friends-find-delta-chat">
|
||||
|
||||
|
||||
Where can my friends find Delta Chat? <a href="#where-can-my-friends-find-delta-chat" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat is available for all major and some minor platforms:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>The <strong>official website</strong>, <a href="https://delta.chat/download">https://delta.chat/download</a> shows all options in detail</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>If unavailable, use the <strong>mirror</strong> at <a href="https://deltachat.github.io/deltachat-pages">https://deltachat.github.io/deltachat-pages</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Open one of the following <strong>app stores and search for “Delta Chat”:</strong>
|
||||
Google Play Store, F-Droid, Huawei App Gallery, Amazon App Store, iOS and macOS App Store, Microsoft Store</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Check the <strong>package manager</strong> of your Linux distributions</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Android APKs</strong> are also available on <a href="https://github.com/deltachat/deltachat-android/releases">https://github.com/deltachat/deltachat-android/releases</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="comment-est-financé-le-développement-de-delta-chat-">
|
||||
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<li><a href="#is-delta-chat-compatible-with-protonmail--tutanota--criptext">Is Delta Chat compatible with Protonmail / Tutanota / Criptext?</a></li>
|
||||
<li><a href="#remove-account">How can I delete my account?</a></li>
|
||||
<li><a href="#im-interested-in-the-technical-details-can-you-tell-me-more">I’m interested in the technical details. Can you tell me more?</a></li>
|
||||
<li><a href="#where-can-my-friends-find-delta-chat">Where can my friends find Delta Chat?</a></li>
|
||||
<li><a href="#how-are-delta-chat-developments-funded">How are Delta Chat developments funded?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -301,8 +302,7 @@ Archived chats remain accessible above the chat list or via search.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>To archive or pin a chat, long tap (Android), use the chat’s menu (Android/Desktop) or swipe to the left (iOS);
|
||||
to mute a chat, use the chat’s menu (Android/Desktop) or the chat’s profile (iOS).</p>
|
||||
<p>To use the functions, long tap or right click a chat in the chat list.</p>
|
||||
|
||||
<h3 id="what-does-the-green-dot-mean">
|
||||
|
||||
@@ -1315,6 +1315,12 @@ One device is not needed for the other to work.</p>
|
||||
and make sure, <strong>Private Network</strong> is selected as “Network profile type”
|
||||
(after transfer, you can change back to the original value)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>iOS</strong>, make sure “System Settings / Apps / Delta Chat / <strong>Local Network</strong>” access is granted</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>macOS</strong>, enable “System Settings / Privacy & Security / <strong>Local Network</strong> / Delta Chat”</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Your system might have a “personal firewall”,
|
||||
which is known to cause problems (especially on Windows).
|
||||
@@ -1432,6 +1438,7 @@ you only open attachments from senders you trust, and not from spammers.</li>
|
||||
<ul>
|
||||
<li>In general, anyone can share webxdc apps with each
|
||||
other without restrictions.</li>
|
||||
<li>From <a href="https://webxdc.org/apps/">webxdc.org/apps</a>.</li>
|
||||
<li>You can <a href="https://delta.chat/en/2023-08-11-xstore">send ‘hi’ to xstore@testrun.org</a>
|
||||
to see an experimental webxdc appstore.
|
||||
All of the apps are open source and for free.</li>
|
||||
@@ -1883,6 +1890,35 @@ Otherwise you might receive undecryptable messages from those group chats.</p>
|
||||
<li>See <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md#standards-used-in-delta-chat">Standards used in Delta Chat</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="where-can-my-friends-find-delta-chat">
|
||||
|
||||
|
||||
Where can my friends find Delta Chat? <a href="#where-can-my-friends-find-delta-chat" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat is available for all major and some minor platforms:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>The <strong>official website</strong>, <a href="https://delta.chat/download">https://delta.chat/download</a> shows all options in detail</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>If unavailable, use the <strong>mirror</strong> at <a href="https://deltachat.github.io/deltachat-pages">https://deltachat.github.io/deltachat-pages</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Open one of the following <strong>app stores and search for “Delta Chat”:</strong>
|
||||
Google Play Store, F-Droid, Huawei App Gallery, Amazon App Store, iOS and macOS App Store, Microsoft Store</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Check the <strong>package manager</strong> of your Linux distributions</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Android APKs</strong> are also available on <a href="https://github.com/deltachat/deltachat-android/releases">https://github.com/deltachat/deltachat-android/releases</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="how-are-delta-chat-developments-funded">
|
||||
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<li><a href="#delta-chat-è-compatibile-con-protonmail--tutanota--criptext">Delta Chat è compatibile con Protonmail / Tutanota / Criptext?</a></li>
|
||||
<li><a href="#remove-account">Come posso eliminare il mio profilo?</a></li>
|
||||
<li><a href="#sono-interessato-ai-dettagli-tecnici-mi-puoi-dire-di-più">Sono interessato ai dettagli tecnici. Mi puoi dire di più?</a></li>
|
||||
<li><a href="#dove-possono-trovare-delta-chat-i-miei-amici">Dove possono trovare Delta Chat i miei amici?</a></li>
|
||||
<li><a href="#come-viene-finanziato-lo-sviluppo-di-delta-chat">Come viene finanziato lo sviluppo di Delta Chat?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -1302,6 +1303,12 @@ Un dispositivo non è necessario perché l’altro funzioni.</p>
|
||||
e assicurati che <strong>Rete Privata</strong> sia selezionata come “Tipo di profilo di rete”
|
||||
(dopo il trasferimento è possibile ripristinare il valore originale)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Su <strong>iOS</strong>, assicurati che l’accesso a “Impostazioni di Sistema / App / Delta Chat / <strong>Rete locale</strong>” sia concesso</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Su <strong>macOS</strong>, abilita “Impostazioni di Sistema / Privacy & Sicurezza / <strong>Rete locale</strong> / Delta Chat”</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Il sistema potrebbe avere un “personal firewall”,
|
||||
che è noto per causare problemi (soprattutto su Windows).
|
||||
@@ -1845,6 +1852,35 @@ Altrimenti potresti ricevere messaggi non decifrabili da quelle chat di gruppo.<
|
||||
<li>Vedi <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md#standards-used-in-delta-chat">Standard usati in Delta Chat</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="dove-possono-trovare-delta-chat-i-miei-amici">
|
||||
|
||||
|
||||
Dove possono trovare Delta Chat i miei amici? <a href="#dove-possono-trovare-delta-chat-i-miei-amici" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat è disponibile per tutte le piattaforme principali e alcune minori:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>Il <strong>sito ufficiale</strong>, <a href="https://delta.chat/download">https://delta.chat/download</a> mostra tutte le opzioni in dettaglio</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Se non disponibile, utilizzare <strong>mirror</strong> su <a href="https://deltachat.github.io/deltachat-pages">https://deltachat.github.io/deltachat-pages</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Apri uno dei seguenti <strong>app store e cerca “Delta Chat”:</strong>
|
||||
Google Play Store, F-Droid, Huawei App Gallery, Amazon App Store, iOS e macOS App Store, Microsoft Store</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Controlla il <strong>gestore pacchetti</strong> delle tue distribuzioni Linux</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>APK Android</strong> sono disponibili anche su <a href="https://github.com/deltachat/deltachat-android/releases">https://github.com/deltachat/deltachat-android/releases</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="come-viene-finanziato-lo-sviluppo-di-delta-chat">
|
||||
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<li><a href="#is-delta-chat-compatibel-met-protonmailtutanotacriptext">Is Delta Chat compatibel met Protonmail/Tutanota/Criptext?</a></li>
|
||||
<li><a href="#remove-account">Hoe kan ik mijn account verwijderen?</a></li>
|
||||
<li><a href="#ik-wil-graag-meer-weten-over-de-gebruikte-technieken-waar-kan-ik-meer-informatie-vinden">Ik wil graag meer weten over de gebruikte technieken. Waar kan ik meer informatie vinden?</a></li>
|
||||
<li><a href="#where-can-my-friends-find-delta-chat">Where can my friends find Delta Chat?</a></li>
|
||||
<li><a href="#hoe-wordt-de-ontwikkeling-van-delta-chat-gefinancierd">Hoe wordt de ontwikkeling van Delta Chat gefinancierd?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -1306,6 +1307,12 @@ op beide apparaten</strong>. Hierdoor hoef je niet het ene apparaat bij de hand
|
||||
and make sure, <strong>Private Network</strong> is selected as “Network profile type”
|
||||
(after transfer, you can change back to the original value)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>iOS</strong>, make sure “System Settings / Apps / Delta Chat / <strong>Local Network</strong>” access is granted</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>macOS</strong>, enable “System Settings / Privacy & Security / <strong>Local Network</strong> / Delta Chat”</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Wellicht is een firewall actief op je apparaat,
|
||||
welke problemen kan veroorzaken (met name op Windows).
|
||||
@@ -1423,6 +1430,7 @@ you only open attachments from senders you trust, and not from spammers.</li>
|
||||
<ul>
|
||||
<li>In general, anyone can share webxdc apps with each
|
||||
other without restrictions.</li>
|
||||
<li>From <a href="https://webxdc.org/apps/">webxdc.org/apps</a>.</li>
|
||||
<li>You can <a href="https://delta.chat/en/2023-08-11-xstore">send ‘hi’ to xstore@testrun.org</a>
|
||||
to see an experimental webxdc appstore.
|
||||
All of the apps are open source and for free.</li>
|
||||
@@ -1868,6 +1876,35 @@ Otherwise you might receive undecryptable messages from those group chats.</p>
|
||||
<li>Bekijk de pagina <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md#standards-used-in-delta-chat">Door Delta Chat gebruikte standaarden</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="where-can-my-friends-find-delta-chat">
|
||||
|
||||
|
||||
Where can my friends find Delta Chat? <a href="#where-can-my-friends-find-delta-chat" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat is available for all major and some minor platforms:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>The <strong>official website</strong>, <a href="https://delta.chat/download">https://delta.chat/download</a> shows all options in detail</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>If unavailable, use the <strong>mirror</strong> at <a href="https://deltachat.github.io/deltachat-pages">https://deltachat.github.io/deltachat-pages</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Open one of the following <strong>app stores and search for “Delta Chat”:</strong>
|
||||
Google Play Store, F-Droid, Huawei App Gallery, Amazon App Store, iOS and macOS App Store, Microsoft Store</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Check the <strong>package manager</strong> of your Linux distributions</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Android APKs</strong> are also available on <a href="https://github.com/deltachat/deltachat-android/releases">https://github.com/deltachat/deltachat-android/releases</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="hoe-wordt-de-ontwikkeling-van-delta-chat-gefinancierd">
|
||||
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<li><a href="#czy-delta-chat-jest-kompatybilny-z-protonmail--tutanota--criptext">Czy Delta Chat jest kompatybilny z Protonmail / Tutanota / Criptext?</a></li>
|
||||
<li><a href="#remove-account">Jak mogę usunąć swoje konto?</a></li>
|
||||
<li><a href="#interesują-mnie-szczegóły-techniczne-możesz-powiedzieć-mi-coś-więcej">Interesują mnie szczegóły techniczne. Możesz powiedzieć mi coś więcej?</a></li>
|
||||
<li><a href="#gdzie-moi-znajomi-mogą-znaleźć-delta-chat">Gdzie moi znajomi mogą znaleźć Delta Chat?</a></li>
|
||||
<li><a href="#w-jaki-sposób-finansowany-jest-rozwój-delta-chat">W jaki sposób finansowany jest rozwój Delta Chat?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -265,8 +266,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Aby zarchiwizować lub przypiąć czat, dotknij i przytrzymaj (Android), użyj menu czatu (Android/Desktop) lub przesuń palcem w lewo (iOS);
|
||||
aby wyciszyć czat, użyj menu czatu (Android/Desktop) lub profilu czatu (iOS).</p>
|
||||
<p>Aby skorzystać z tych funkcji, przytrzymaj dłużej lub kliknij prawym przyciskiem myszy czat na liście czatów.</p>
|
||||
|
||||
<h3 id="co-oznacza-zielona-kropka">
|
||||
|
||||
@@ -941,6 +941,12 @@ W przypadku innych programów można znaleźć rozwiązanie online.</p>
|
||||
<p>Na <strong>Windowsie</strong>, przejdź do <strong>Panel sterowania / Sieć i internet</strong> i upewnij się, że <strong>Sieć prywatna</strong> jest wybrana jako “Typ profilu sieci”
|
||||
(po przeniesieniu możesz wrócić do pierwotnej wartości)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>W systemie <strong>iOS</strong> upewnij się, że jest przydzielony dostęp do opcji „Ustawienia » Aplikacje » Delta Chat » <strong>Sieć lokalna</strong>”</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>W systemie <strong>macOS</strong> włącz „Ustawienia systemowe » Prywatność i bezpieczeństwo » <strong>Sieć lokalna</strong> » Delta Chat”</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Twój system może mieć „zaporę ogniową”, o której wiadomo, że powoduje problemy (szczególnie w systemie Windows).
|
||||
<strong>Wyłącz zaporę</strong> dla Delta Chat po obu stronach i spróbuj ponownie</p>
|
||||
@@ -1031,6 +1037,7 @@ W przypadku innych programów można znaleźć rozwiązanie online.</p>
|
||||
|
||||
<ul>
|
||||
<li>Ogólnie rzecz biorąc, każdy może udostępniać sobie aplikacje webxdc bez ograniczeń.</li>
|
||||
<li>Od <a href="https://webxdc.org/apps/">webxdc.org/apps</a>.</li>
|
||||
<li>Możesz <a href="https://delta.chat/en/2023-08-11-xstore">wysłać „hi” na adres xstore@testrun.org</a>, aby zobaczyć eksperymentalny sklep z aplikacjami webxdc. Wszystkie aplikacje są otwarto źródłowe i są bezpłatne.</li>
|
||||
<li>Wiele osób pisze własne, aplikacje webxdc i publikuje je na <a href="https://support.delta.chat/c/webxdc/20">forum Delta Chat</a>.</li>
|
||||
</ul>
|
||||
@@ -1356,6 +1363,34 @@ jeśli chcesz sprawdzić, czy Twoje poświadczenia są przetwarzane w bezpieczny
|
||||
<li>Zobacz <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md#standards-used-in-delta-chat">Standardy używane w Delta Chat</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="gdzie-moi-znajomi-mogą-znaleźć-delta-chat">
|
||||
|
||||
|
||||
Gdzie moi znajomi mogą znaleźć Delta Chat? <a href="#gdzie-moi-znajomi-mogą-znaleźć-delta-chat" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat jest dostępny na wszystkich głównych i niektórych mniejszych platformach:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>Oficjalna <strong>strona internetowa</strong> <a href="https://delta.chat/download">https://delta.chat/download</a> pokazuje wszystkie opcje szczegółowo</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Jeśli nie jest dostępna, użyj <strong>kopii strony</strong> pod adresem <a href="https://deltachat.github.io/deltachat-pages">https://deltachat.github.io/deltachat-pages</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Otwórz jeden z następujących <strong>sklepów z aplikacjami i wyszukaj „Delta Chat”</strong>: Google Play Store, F-Droid, Huawei App Gallery, Amazon App Store, iOS i macOS App Store, Microsoft Store</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Sprawdź <strong>menedżera pakietów</strong> swoich dystrybucji Linuksa</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Aplikacje na Androida</strong> są również dostępne na <a href="https://github.com/deltachat/deltachat-android/releases">https://github.com/deltachat/deltachat-android/releases</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="w-jaki-sposób-finansowany-jest-rozwój-delta-chat">
|
||||
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<li><a href="#o-delta-chat-é-compatível-com-protonmail--tutanota--criptext">O Delta Chat é compatível com Protonmail / Tutanota / Criptext?</a></li>
|
||||
<li><a href="#remove-account">How can I delete my account?</a></li>
|
||||
<li><a href="#estou-interessado-nos-detalhes-técnicos-pode-me-dizer-mais">Estou interessado nos detalhes técnicos. Pode me dizer mais?</a></li>
|
||||
<li><a href="#where-can-my-friends-find-delta-chat">Where can my friends find Delta Chat?</a></li>
|
||||
<li><a href="#como-são-os-desenvolvimentos-do-delta-chat-financiados">Como são os desenvolvimentos do Delta Chat financiados?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -297,8 +298,7 @@ Archived chats remain accessible above the chat list or via search.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>To archive or pin a chat, long tap (Android), use the chat’s menu (Android/Desktop) or swipe to the left (iOS);
|
||||
to mute a chat, use the chat’s menu (Android/Desktop) or the chat’s profile (iOS).</p>
|
||||
<p>To use the functions, long tap or right click a chat in the chat list.</p>
|
||||
|
||||
<h3 id="what-does-the-green-dot-mean">
|
||||
|
||||
@@ -1309,6 +1309,12 @@ One device is not needed for the other to work.</p>
|
||||
and make sure, <strong>Private Network</strong> is selected as “Network profile type”
|
||||
(after transfer, you can change back to the original value)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>iOS</strong>, make sure “System Settings / Apps / Delta Chat / <strong>Local Network</strong>” access is granted</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>macOS</strong>, enable “System Settings / Privacy & Security / <strong>Local Network</strong> / Delta Chat”</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Your system might have a “personal firewall”,
|
||||
which is known to cause problems (especially on Windows).
|
||||
@@ -1426,6 +1432,7 @@ you only open attachments from senders you trust, and not from spammers.</li>
|
||||
<ul>
|
||||
<li>In general, anyone can share webxdc apps with each
|
||||
other without restrictions.</li>
|
||||
<li>From <a href="https://webxdc.org/apps/">webxdc.org/apps</a>.</li>
|
||||
<li>You can <a href="https://delta.chat/en/2023-08-11-xstore">send ‘hi’ to xstore@testrun.org</a>
|
||||
to see an experimental webxdc appstore.
|
||||
All of the apps are open source and for free.</li>
|
||||
@@ -1869,6 +1876,35 @@ Otherwise you might receive undecryptable messages from those group chats.</p>
|
||||
<li>Veja <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md#standards-used-in-delta-chat">As normas usadas no Delta Chat</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="where-can-my-friends-find-delta-chat">
|
||||
|
||||
|
||||
Where can my friends find Delta Chat? <a href="#where-can-my-friends-find-delta-chat" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat is available for all major and some minor platforms:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>The <strong>official website</strong>, <a href="https://delta.chat/download">https://delta.chat/download</a> shows all options in detail</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>If unavailable, use the <strong>mirror</strong> at <a href="https://deltachat.github.io/deltachat-pages">https://deltachat.github.io/deltachat-pages</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Open one of the following <strong>app stores and search for “Delta Chat”:</strong>
|
||||
Google Play Store, F-Droid, Huawei App Gallery, Amazon App Store, iOS and macOS App Store, Microsoft Store</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Check the <strong>package manager</strong> of your Linux distributions</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Android APKs</strong> are also available on <a href="https://github.com/deltachat/deltachat-android/releases">https://github.com/deltachat/deltachat-android/releases</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="como-são-os-desenvolvimentos-do-delta-chat-financiados">
|
||||
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<li><a href="#совместим-ли-delta-chat-с-protonmail--tutanota--criptext">Совместим ли Delta Chat с Protonmail / Tutanota / Criptext?</a></li>
|
||||
<li><a href="#remove-account">Как я могу удалить свой аккаунт?</a></li>
|
||||
<li><a href="#меня-интересуют-технические-детали-можете-рассказать-больше">Меня интересуют технические детали. Можете рассказать больше?</a></li>
|
||||
<li><a href="#где-мои-друзья-могут-найти-delta-chat">Где мои друзья могут найти Delta Chat?</a></li>
|
||||
<li><a href="#как-финансируются-разработки-delta-chat">Как финансируются разработки Delta Chat?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -301,8 +302,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Чтобы архивировать или закрепить чат, нажмите на него долгим нажатием (Android), воспользуйтесь меню чата (Android/Desktop) или проведите пальцем влево (iOS);
|
||||
чтобы отключить уведомления, воспользуйтесь меню чата (Android/Desktop) или профиль чата (iOS).</p>
|
||||
<p>Чтобы использовать функции, нажмите долгим нажатием или щелкните правой кнопкой мыши по чату в списке чатов.</p>
|
||||
|
||||
<h3 id="что-означает-зеленая-точка">
|
||||
|
||||
@@ -1312,6 +1312,12 @@ Chat <a href="https://github.com/rpgp/rpgp">PGP</a> и
|
||||
и убедитесь, что в качестве “Типа сетевого профиля” выбрана <strong>Частная сеть</strong>.
|
||||
(после передачи, вы можете изменить обратно на исходное значение)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>На <strong>iOS</strong>, убедитесь, что предоставлен доступ “Настройки системы / Приложения / Delta Chat / <strong>Локальная сеть</strong>”</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>На <strong>macOS</strong>, включите “Системные настройки / Конфиденциальность и безопасность / <strong>Локальная сеть</strong> / Delta Chat”</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>В вашей системе может быть установлен “персональный брандмауэр”,
|
||||
который может вызвать проблемы (особенно на Windows).
|
||||
@@ -1430,11 +1436,11 @@ PIN-код разблокировки экрана, графический кл
|
||||
<ul>
|
||||
<li>В общем, каждый может поделиться приложениями webxdc
|
||||
друг с другом без ограничений.</li>
|
||||
<li>Из <a href="https://webxdc.org/apps/">webxdc.org/apps</a>.</li>
|
||||
<li>Вы можете <a href="https://delta.chat/en/2023-08-11-xstore">отправить ‘hi’ на xstore@testrun.org</a>
|
||||
чтобы увидеть экспериментальный магазин приложений webxdc.
|
||||
Все приложения с открытым исходным кодом и бесплатны.</li>
|
||||
<li>Многие люди пишут свои собственные приложения webxdc и публикуют их на <a href="https://support.delta.chat/c/webxdc/20">форуме
|
||||
Delta Chat</a>.</li>
|
||||
<li>Многие люди создают свои собственные приложения webxdc и публикуют их на <a href="https://support.delta.chat/c/webxdc/20">форуме Delta Chat</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="как-я-могу-создать-свои-собственные-приложения-webxdc">
|
||||
@@ -1879,6 +1885,35 @@ Chat.</li>
|
||||
<li>Смотрите <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md#standards-used-in-delta-chat">Стандарты, используемые в Delta Chat</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="где-мои-друзья-могут-найти-delta-chat">
|
||||
|
||||
|
||||
Где мои друзья могут найти Delta Chat? <a href="#где-мои-друзья-могут-найти-delta-chat" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat доступен на всех популярных и некоторых менее известных платформах:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>На <strong>официальной странице загрузки</strong>, <a href="https://delta.chat/download">https://delta.chat/download</a> можно найти подробную информацию о всех вариантах</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Если основной сайт недоступен, используйте <strong>зеркало</strong> <a href="https://deltachat.github.io/deltachat-pages">https://deltachat.github.io/deltachat-pages</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Откройте один из следующих <strong>онлайн-магазинов приложений и введите запрос “Delta Chat”:</strong>
|
||||
Google Play Store, F-Droid, Huawei App Gallery, Amazon App Store, App Store для iOS и macOS, Microsoft Store</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Проверьте <strong>менеджер пакетов</strong> вашего дистрибутива Linux</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Файлы <strong>APK для Android</strong> доступны на <a href="https://github.com/deltachat/deltachat-android/releases">https://github.com/deltachat/deltachat-android/releases</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="как-финансируются-разработки-delta-chat">
|
||||
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<li><a href="#je-delta-chat-kompatibilný-s-protonmail--tutanota--criptext">Je Delta Chat kompatibilný s Protonmail / Tutanota / Criptext?</a></li>
|
||||
<li><a href="#remove-account">How can I delete my account?</a></li>
|
||||
<li><a href="#zaujímajú-ma-technické-detaily-môžete-mi-povedať-viac">Zaujímajú ma technické detaily. Môžete mi povedať viac?</a></li>
|
||||
<li><a href="#where-can-my-friends-find-delta-chat">Where can my friends find Delta Chat?</a></li>
|
||||
<li><a href="#ako-sa-financuje-vývoj-delta-chat">Ako sa financuje vývoj Delta Chat?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -301,8 +302,7 @@ Archived chats remain accessible above the chat list or via search.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>To archive or pin a chat, long tap (Android), use the chat’s menu (Android/Desktop) or swipe to the left (iOS);
|
||||
to mute a chat, use the chat’s menu (Android/Desktop) or the chat’s profile (iOS).</p>
|
||||
<p>To use the functions, long tap or right click a chat in the chat list.</p>
|
||||
|
||||
<h3 id="what-does-the-green-dot-mean">
|
||||
|
||||
@@ -1315,6 +1315,12 @@ One device is not needed for the other to work.</p>
|
||||
and make sure, <strong>Private Network</strong> is selected as “Network profile type”
|
||||
(after transfer, you can change back to the original value)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>iOS</strong>, make sure “System Settings / Apps / Delta Chat / <strong>Local Network</strong>” access is granted</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>macOS</strong>, enable “System Settings / Privacy & Security / <strong>Local Network</strong> / Delta Chat”</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Your system might have a “personal firewall”,
|
||||
which is known to cause problems (especially on Windows).
|
||||
@@ -1432,6 +1438,7 @@ you only open attachments from senders you trust, and not from spammers.</li>
|
||||
<ul>
|
||||
<li>In general, anyone can share webxdc apps with each
|
||||
other without restrictions.</li>
|
||||
<li>From <a href="https://webxdc.org/apps/">webxdc.org/apps</a>.</li>
|
||||
<li>You can <a href="https://delta.chat/en/2023-08-11-xstore">send ‘hi’ to xstore@testrun.org</a>
|
||||
to see an experimental webxdc appstore.
|
||||
All of the apps are open source and for free.</li>
|
||||
@@ -1879,6 +1886,35 @@ Otherwise you might receive undecryptable messages from those group chats.</p>
|
||||
<li>Pozrite si <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md#standards-used-in-delta-chat">Štandardy používané v Delta Chate</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="where-can-my-friends-find-delta-chat">
|
||||
|
||||
|
||||
Where can my friends find Delta Chat? <a href="#where-can-my-friends-find-delta-chat" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat is available for all major and some minor platforms:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>The <strong>official website</strong>, <a href="https://delta.chat/download">https://delta.chat/download</a> shows all options in detail</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>If unavailable, use the <strong>mirror</strong> at <a href="https://deltachat.github.io/deltachat-pages">https://deltachat.github.io/deltachat-pages</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Open one of the following <strong>app stores and search for “Delta Chat”:</strong>
|
||||
Google Play Store, F-Droid, Huawei App Gallery, Amazon App Store, iOS and macOS App Store, Microsoft Store</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Check the <strong>package manager</strong> of your Linux distributions</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Android APKs</strong> are also available on <a href="https://github.com/deltachat/deltachat-android/releases">https://github.com/deltachat/deltachat-android/releases</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="ako-sa-financuje-vývoj-delta-chat">
|
||||
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<li><a href="#a-është-i-përputhshëm-delta-chat-i-me-protonmail-in--tutanota-n--criptext-in">A është i përputhshëm Delta Chat-i me Protonmail-in / Tutanota-n / Criptext-in?</a></li>
|
||||
<li><a href="#remove-account">Si mund ta fshij llogarinë time?</a></li>
|
||||
<li><a href="#më-interesojnë-hollësitë-teknike-mund-të-më-tregoni-diçka-më-tepër">Më interesojnë hollësitë teknike. Mund të më tregoni diçka më tepër?</a></li>
|
||||
<li><a href="#where-can-my-friends-find-delta-chat">Where can my friends find Delta Chat?</a></li>
|
||||
<li><a href="#si-financohet-zhvillimi-i-delta-chat-it">Si financohet zhvillimi i Delta Chat-it?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -1315,6 +1316,12 @@ Njëra pajisja s’ka nevojë për tjetrën që të funksionojë.</p>
|
||||
and make sure, <strong>Private Network</strong> is selected as “Network profile type”
|
||||
(after transfer, you can change back to the original value)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>iOS</strong>, make sure “System Settings / Apps / Delta Chat / <strong>Local Network</strong>” access is granted</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>macOS</strong>, enable “System Settings / Privacy & Security / <strong>Local Network</strong> / Delta Chat”</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Sistemi juaj mund të ketë një “firewall personal”,
|
||||
që dihet se shkakton probleme (veçanërisht në Windows).
|
||||
@@ -1890,6 +1897,35 @@ Otherwise you might receive undecryptable messages from those group chats.</p>
|
||||
<li>Shihni <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md#standards-used-in-delta-chat">Standarde të përdorur në Delta Chat</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="where-can-my-friends-find-delta-chat">
|
||||
|
||||
|
||||
Where can my friends find Delta Chat? <a href="#where-can-my-friends-find-delta-chat" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat is available for all major and some minor platforms:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>The <strong>official website</strong>, <a href="https://delta.chat/download">https://delta.chat/download</a> shows all options in detail</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>If unavailable, use the <strong>mirror</strong> at <a href="https://deltachat.github.io/deltachat-pages">https://deltachat.github.io/deltachat-pages</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Open one of the following <strong>app stores and search for “Delta Chat”:</strong>
|
||||
Google Play Store, F-Droid, Huawei App Gallery, Amazon App Store, iOS and macOS App Store, Microsoft Store</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Check the <strong>package manager</strong> of your Linux distributions</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Android APKs</strong> are also available on <a href="https://github.com/deltachat/deltachat-android/releases">https://github.com/deltachat/deltachat-android/releases</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="si-financohet-zhvillimi-i-delta-chat-it">
|
||||
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
<li><a href="#чи-сумісний-delta-chat-із-protonmail--tutanota--criptext">Чи сумісний Delta Chat із Protonmail / Tutanota / Criptext?</a></li>
|
||||
<li><a href="#remove-account">Як мені видалити свій обліковий запис?</a></li>
|
||||
<li><a href="#мене-цікавлять-технічні-деталі-можете-розповісти-більше">Мене цікавлять технічні деталі. Можете розповісти більше?</a></li>
|
||||
<li><a href="#де-мої-друзі-можуть-знайти-delta-chat">Де мої друзі можуть знайти Delta Chat?</a></li>
|
||||
<li><a href="#як-фінансується-розробка-delta-chat">Як фінансується розробка Delta Chat?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -267,7 +268,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Щоб заархівувати або закріпити чат, довго торкніться (Android), скористайтеся меню чату (Android/комп’ютер) або проведіть пальцем ліворуч (iOS); щоб вимкнути звук чату, скористайтеся меню чату (Android/комп’ютер) або профілем чату (iOS).</p>
|
||||
<p>Щоб скористатися функціями, утримуйте натиснутою клавішу або клацніть правою кнопкою миші на чаті у списку чатів.</p>
|
||||
|
||||
<h3 id="що-означає-зелена-точка">
|
||||
|
||||
@@ -944,6 +945,12 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
|
||||
<li>
|
||||
<p>У <strong>Windows</strong> перейдіть до <strong>Панель керування / Мережа та Інтернет</strong> і переконайтеся, що <strong>Приватна мережа</strong> вибрано як “Тип мережевого профілю” (після перенесення ви можете повернути початкове значення)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>На <strong>iOS</strong> переконайтеся, що доступ до “Системні налаштування / Програми / Delta Chat / <strong>Локальна мережа</strong>” дозволено</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>У <strong>macOS</strong> увімкніть “Системні налаштування / Конфіденційність і безпека / <strong>Локальна мережа</strong> / Delta Chat”</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Ваша система може мати “персональний брандмауер”, який, як відомо, викликає проблеми (особливо у Windows). <strong>Вимкніть персональний брандмауер</strong> для Delta Chat на обох кінцях і повторіть спробу</p>
|
||||
</li>
|
||||
@@ -1032,9 +1039,11 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
|
||||
</h3>
|
||||
|
||||
<ul>
|
||||
<li>Загалом, будь-хто може ділитися додатками webxdc один з одним один з одним без обмежень.</li>
|
||||
<li>Ви можете <a href="https://delta.chat/en/2023-08-11-xstore">надіслати “hi” на xstore@testrun.org</a> щоб побачити експериментальний магазин додатків webxdc. Всі програми мають відкритий вихідний код і є безкоштовними.</li>
|
||||
<li>Багато людей пишуть власні програми для webxdc і публікують їх на <a href="https://support.delta.chat/c/webxdc/20">форумі Delta Chat</a>.</li>
|
||||
<li>Загалом, будь-хто може ділитися додатками webxdc один з одним без обмежень.</li>
|
||||
<li>З <a href="https://webxdc.org/apps/">webxdc.org/apps</a>.</li>
|
||||
<li>Ви можете <a href="https://delta.chat/en/2023-08-11-xstore">надіслати “привіт” на xstore@testrun.org</a> щоб побачити експериментальний магазин додатків webxdc.
|
||||
Всі програми мають відкритий вихідний код і є безкоштовними.</li>
|
||||
<li>Багато людей пишуть власні програми webxdc і публікують їх на <a href="https://support.delta.chat/c/webxdc/20">форумі Delta Chat</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="як-я-можу-створювати-власні-програми-webxdc">
|
||||
@@ -1374,6 +1383,34 @@ Look for something like <strong>Start Autocrypt Setup Transfer</strong> in the s
|
||||
<li>Дивіться <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md#standards-used-in-delta-chat">Стандарти, що використовуються у Delta Chat</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="де-мої-друзі-можуть-знайти-delta-chat">
|
||||
|
||||
|
||||
Де мої друзі можуть знайти Delta Chat? <a href="#де-мої-друзі-можуть-знайти-delta-chat" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat доступний для всіх основних і деяких другорядних платформ:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>На <strong>офіційному сайті</strong>, <a href="https://delta.chat/download">https://delta.chat/download</a> детально описані всі варіанти</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Якщо вона недоступна, скористайтеся <strong>дзеркалом</strong> за адресою <a href="https://deltachat.github.io/deltachat-pages">https://deltachat.github.io/deltachat-pages</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Відкрийте один з наступних <strong>магазинів додатків і введіть у пошук “Delta Chat”:</strong> Google Play Store, F-Droid, Huawei App Gallery, Amazon App Store, iOS та macOS App Store, Microsoft Store</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Перевірте <strong>менеджер пакунків</strong> вашого дистрибутива Linux</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>APK для Android</strong> також доступні на <a href="https://github.com/deltachat/deltachat-android/releases">https://github.com/deltachat/deltachat-android/releases</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="як-фінансується-розробка-delta-chat">
|
||||
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
</li>
|
||||
<li><a href="#instant-delivery">Instant message delivery and Push Notifications</a>
|
||||
<ul>
|
||||
<li><a href="#what-are-push-notifications-how-can-i-get-instant-message-delivery">What are Push Notifications? How can I get instant message delivery?</a></li>
|
||||
<li><a href="#are-push-notifications-enabled-on-ios-devices-is-there-an-alternative">Are Push Notifications enabled on iOS devices? Is there an alternative?</a></li>
|
||||
<li><a href="#android-push">Are Push notifications enabled / needed on Android devices?</a></li>
|
||||
<li><a href="#什么是推送通知如何获得即时消息传递">什么是推送通知?如何获得即时消息传递?</a></li>
|
||||
<li><a href="#ios-设备上是否启用了推送通知我还有其他的选择吗">iOS 设备上是否启用了推送通知?我还有其他的选择吗?</a></li>
|
||||
<li><a href="#android-设备上是否启用需要推送通知android-push">Android 设备上是否启用/需要推送通知?{#android-push}</a></li>
|
||||
<li><a href="#privacy-notifications">How private are Delta Chat Push Notifications?</a></li>
|
||||
<li><a href="#why-does-delta-chat-integrate-with-centralized-proprietary-applegoogle-push-services">Why does Delta Chat integrate with centralized proprietary Apple/Google push services?</a></li>
|
||||
</ul>
|
||||
@@ -44,7 +44,7 @@
|
||||
<li><a href="#are-attachments-pictures-files-audio-etc-end-to-end-encrypted">Are attachments (pictures, files, audio etc.) end-to-end encrypted?</a></li>
|
||||
<li><a href="#openpgp-secure">Is OpenPGP secure?</a></li>
|
||||
<li><a href="#openpgp-alternatives">Did you consider using alternatives to OpenPGP for end-to-end -encryption?</a></li>
|
||||
<li><a href="#is-delta-chat-vulnerable-to-efail">Is Delta Chat vulnerable to EFAIL?</a></li>
|
||||
<li><a href="#delta-chat-是否容易受到-efail-攻击">Delta Chat 是否容易受到 EFAIL 攻击?</a></li>
|
||||
<li><a href="#tls">Is a message exposed in cleartext if end-to-end encryption is not available?</a></li>
|
||||
<li><a href="#message-metadata">How does Delta Chat protect metadata in messages?</a></li>
|
||||
<li><a href="#device-seizure">How to protect metadata and contacts when a device is seized?</a></li>
|
||||
@@ -53,7 +53,7 @@
|
||||
<li><a href="#why-do-i-see-unencrypted-messages">Why do I see unencrypted messages?</a></li>
|
||||
<li><a href="#how-can-i-get-an-end-to-end-encrypted-chat-with-a-delta-chat-contact-who-sometimes-uses-webmail-or-another-non-autocrypt-e-mail-app">How can i get an end-to-end encrypted chat with a Delta Chat contact who sometimes uses webmail or another non-Autocrypt e-mail app?</a></li>
|
||||
<li><a href="#how-can-i-ensure-message-end-to-end-encryption-and-deletion">How can I ensure message end-to-end encryption and deletion?</a></li>
|
||||
<li><a href="#pfs">Does Delta Chat support Perfect Forward Secrecy?</a></li>
|
||||
<li><a href="#pfs">Delta Chat 是否支持完美前向保密?</a></li>
|
||||
<li><a href="#is-end-to-end-encryption-of-delta-chat-as-safe-as-signal">Is end-to-end encryption of Delta Chat as safe as Signal?</a></li>
|
||||
<li><a href="#importkey">我可以重复使用现有的私钥吗?</a></li>
|
||||
<li><a href="#我无法将现有的-pgp-密钥导入-delta-chat">我无法将现有的 PGP 密钥导入 Delta Chat。</a></li>
|
||||
@@ -99,6 +99,7 @@
|
||||
<li><a href="#delta-chat-与-protonmail--tutanota--criptext-兼容吗">Delta Chat 与 Protonmail / Tutanota / Criptext 兼容吗?</a></li>
|
||||
<li><a href="#remove-account">如何删除我的账户?</a></li>
|
||||
<li><a href="#我对技术细节很感兴趣能告诉我更多吗">我对技术细节很感兴趣。能告诉我更多吗?</a></li>
|
||||
<li><a href="#where-can-my-friends-find-delta-chat">Where can my friends find Delta Chat?</a></li>
|
||||
<li><a href="#delta-chat-的开发是如何被资助的">Delta Chat 的开发是如何被资助的?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -337,10 +338,8 @@ The messages are deleted
|
||||
both in each email account on the server,
|
||||
and in the app itself.</p>
|
||||
|
||||
<p>Note that you can rely on disappearing messages
|
||||
only as long as you trust your chat partners;
|
||||
malicious chat partners can take photos,
|
||||
or otherwise save, copy or forward messages before deletion.</p>
|
||||
<p>请注意,只有当您信任您的聊天伙伴时,您才可以依赖“消息定时销毁”;
|
||||
不怀好意的人可能会拍照,或者在删除之前以其他方式保存、复制或转发消息。</p>
|
||||
|
||||
<p>Apart from that,
|
||||
if one chat partner uninstalls Delta Chat,
|
||||
@@ -473,64 +472,59 @@ Delta Chat 收到的所有电子邮件都将在此期限后从服务器中删除
|
||||
|
||||
</h2>
|
||||
|
||||
<h3 id="what-are-push-notifications-how-can-i-get-instant-message-delivery">
|
||||
<h3 id="什么是推送通知如何获得即时消息传递">
|
||||
|
||||
|
||||
What are Push Notifications? How can I get instant message delivery? <a href="#what-are-push-notifications-how-can-i-get-instant-message-delivery" class="anchor"></a>
|
||||
什么是推送通知?如何获得即时消息传递? <a href="#什么是推送通知如何获得即时消息传递" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Push Notifications are sent by Apple and Google “Push services” to a user’s device
|
||||
so that an inactive Delta Chat app can fetch messages in the background
|
||||
and show notifications on a user’s phone if needed.</p>
|
||||
<p>推送通知由 Apple 和 Google 的“推送服务”发送到用户的设备,以便非活动状态的 Delta Chat 应用可以在后台获取消息,并在需要时在用户的手机上显示通知。</p>
|
||||
|
||||
<p>Push Notifications work with all <a href="https://delta.chat/chatmail">chatmail</a> servers on</p>
|
||||
<p>推送通知适用于以下所有 <a href="https://delta.chat/chatmail">chatmail</a> 服务器:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>iOS devices, by integrating with Apple Push services.</p>
|
||||
<p>iOS 设备通过与 Apple Push 服务集成。</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Android devices, by integrating with the Google FCM Push service,
|
||||
including on devices that use <a href="https://microg.org">microG</a>
|
||||
instead of proprietary Google code on the phone.</p>
|
||||
<p>Android 设备,通过与 Google FCM Push 服务集成,
|
||||
包括使用 <a href="https://microg.org">microG</a>
|
||||
而不是手机上专有 Google 代码的设备。</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>As of May 2024, classic e-mail servers do not support Push Notifications
|
||||
for Delta Chat users.</p>
|
||||
<p>自 2024 年 5 月起,传统电子邮件服务器不再支持 Delta Chat 用户的推送通知。</p>
|
||||
|
||||
<h3 id="are-push-notifications-enabled-on-ios-devices-is-there-an-alternative">
|
||||
<h3 id="ios-设备上是否启用了推送通知我还有其他的选择吗">
|
||||
|
||||
|
||||
Are Push Notifications enabled on iOS devices? Is there an alternative? <a href="#are-push-notifications-enabled-on-ios-devices-is-there-an-alternative" class="anchor"></a>
|
||||
iOS 设备上是否启用了推送通知?我还有其他的选择吗? <a href="#ios-设备上是否启用了推送通知我还有其他的选择吗" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Yes, Delta Chat automatically uses Push Notifications for <a href="https://delta.chat/chatmail">chatmail</a> profiles.
|
||||
And no, there is no alternative on Apple’s phones to achieve instant message delivery
|
||||
because Apple devices do not allow Delta Chat to fetch data in the background.
|
||||
Push notifications are automatically activated for iOS users because
|
||||
<a href="#privacy-notifications">Delta Chat’s privacy-preserving Push Notification system</a>
|
||||
does not expose data to Apple that it doesn’t already have.</p>
|
||||
<p>是的,Delta Chat 会自动使用推送通知来接收 <a href="https://delta.chat/chatmail">chatmail</a> 个人资料。
|
||||
而且,Apple 手机上没有其他方式可以实现即时消息传递
|
||||
因为 Apple 设备不允许 Delta Chat 在后台获取数据。
|
||||
推送通知会自动为 iOS 用户激活,因为
|
||||
<a href="#privacy-notifications">Delta Chat 的隐私保护推送通知系统</a>
|
||||
不会向 Apple 泄露其尚未拥有的数据。</p>
|
||||
|
||||
<h3 id="android-push">
|
||||
<h3 id="android-设备上是否启用需要推送通知android-push">
|
||||
|
||||
|
||||
Are Push notifications enabled / needed on Android devices? <a href="#android-push" class="anchor"></a>
|
||||
Android 设备上是否启用/需要推送通知?{#android-push} <a href="#android-设备上是否启用需要推送通知android-push" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>If a “Push Service” is available, Delta Chat enables Push Notifications
|
||||
to achieve instant message delivery for all chatmail users.
|
||||
If you are using a classic e-mail provider instead of <a href="https://delta.chat/chatmail">chatmail</a> servers,
|
||||
Push Notifications are not available.</p>
|
||||
<p>如果有“推送服务”可用,Delta Chat 将启用推送通知,
|
||||
以实现所有聊天邮件用户的即时消息传递。
|
||||
如果您使用的是传统电子邮件提供商而不是 <a href="https://delta.chat/chatmail">chatmail</a> 服务器,则无法使用推送通知。</p>
|
||||
|
||||
<p>In the Delta Chat “Notifications” settings for “Instant delivery”
|
||||
you can change the following settings effecting all chat profiles:</p>
|
||||
<p>在 Delta Chat“通知”的“推送通知”设置中,您可以更改以下影响所有聊天配置文件的设置:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
@@ -710,17 +704,13 @@ both sides will eventually see a (group or direct) chat with a green checkmark
|
||||
next to the title.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>If one of the devices is offline, the green checkmarks will only
|
||||
appear later when the device is internet-connected again
|
||||
and the Secure-Join network protocol completed.</p>
|
||||
<p>如果其中一个设备处于离线状态,则只有当设备再次连接互联网且安全加入网络协议完成时,绿色复选标记才会出现。</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Congratulations!
|
||||
You now will automatically use guaranteed end-to-end encryption
|
||||
with this contact and both of you can add each other to green-checkmarked groups
|
||||
<img style="vertical-align:middle; width:1.5em; margin:1px" src="../green-checkmark.png" alt="green checkmark" />,
|
||||
thereby automatically spreading guaranteed end-to-end encryption among its members.</p>
|
||||
<p>恭喜!
|
||||
从现在开始您将会自动使用安全的端到端加密与此联系人联系,并且双方都可以将对方添加到绿色复选标记的群组
|
||||
<img style="vertical-align:middle; width:1.5em; margin:1px" src="../green-checkmark.png" alt="green checkmark" />,从而自动在其成员之间传播有保障的端到端加密。</p>
|
||||
|
||||
<h3 id="e2eeguarantee">
|
||||
|
||||
@@ -891,19 +881,18 @@ only one part of “user outcomes”,
|
||||
see also our answers to <a href="#device-seizure">device-seizure</a>
|
||||
and <a href="#message-metadata">message-metadata</a> questions.</p>
|
||||
|
||||
<h3 id="is-delta-chat-vulnerable-to-efail">
|
||||
<h3 id="delta-chat-是否容易受到-efail-攻击">
|
||||
|
||||
|
||||
Is Delta Chat vulnerable to EFAIL? <a href="#is-delta-chat-vulnerable-to-efail" class="anchor"></a>
|
||||
Delta Chat 是否容易受到 EFAIL 攻击? <a href="#delta-chat-是否容易受到-efail-攻击" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>No, <a href="https://delta.chat/en/2018-05-15-delta-chat-not-vulnerable-to-efail">Delta Chat never was vulnerable to EFAIL</a>
|
||||
because its OpenPGP implementation <a href="https://github.com/rpgp/rpgp">rPGP</a>
|
||||
uses Modification Detection Code when encrypting messages
|
||||
and returns <a href="https://docs.rs/pgp/latest/pgp/errors/enum.Error.html#variant.MdcError">an error</a>
|
||||
if the Modification Detection Code is incorrect.</p>
|
||||
<p>不,<a href="https://delta.chat/en/2018-05-15-delta-chat-not-vulnerable-to-efail">Delta Chat 从未受到任何 EFAIL 攻击</a>
|
||||
因为所使用的 OpenPGP 实现了 <a href="https://github.com/rpgp/rpgp">rPGP</a>
|
||||
在加密消息时“修改检测代码”
|
||||
并且如果“修改检测代码”不正确则返回 <a href="https://docs.rs/pgp/latest/pgp/errors/enum.Error.html#variant.MdcError">错误</a>。</p>
|
||||
|
||||
<p>Delta Chat also never was vulnerable to the “Direct Exfiltration” EFAIL attack
|
||||
because it only decrypts <code class="language-plaintext highlighter-rouge">multipart/encrypted</code> messages
|
||||
@@ -1072,21 +1061,21 @@ you can also turn on <a href="#delold">“delete messages from server automatica
|
||||
<h3 id="pfs">
|
||||
|
||||
|
||||
Does Delta Chat support Perfect Forward Secrecy? <a href="#pfs" class="anchor"></a>
|
||||
Delta Chat 是否支持完美前向保密? <a href="#pfs" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>No, Delta Chat doesn’t support Perfect Forward Secrecy (PFS).
|
||||
This means that if your Delta Chat private decryption key is leaked,
|
||||
and someone has collected your prior in-transit messages,
|
||||
they will be able to decrypt and read them using the leaked decryption key.</p>
|
||||
<p>不,Delta Chat 不支持完美前向保密 (PFS)。
|
||||
这意味着,如果您的 Delta Chat 私人解密密钥泄露,
|
||||
并且有人收集了您之前传输的消息,
|
||||
他们将能够使用泄露的解密密钥解密并阅读这些消息。</p>
|
||||
|
||||
<p>Note, however, that if anyone obtains to your decryption keys,
|
||||
they will typically also be able to obtain your messages,
|
||||
irrespective if Perfect Forward Secrecy is in place or not.
|
||||
The typical real-world situation for leaked decryption keys is device seizure
|
||||
which we discuss in our answer <a href="#device-seizure">on metadata and device seizure</a>.</p>
|
||||
<p>但请注意,如果有人获得您的解密密钥,
|
||||
他们通常也能够获得您的消息,
|
||||
无论是否实施了完全前向保密。
|
||||
泄露解密密钥的典型现实情况是设备扣押,
|
||||
我们在<a href="#device-seizure">关于元数据和设备扣押</a>的回答中讨论了这一点。</p>
|
||||
|
||||
<p>It is possible that Delta Chat evolves to support Perfect Forward Secrecy,
|
||||
because OpenPGP is just a container for encrypted messages
|
||||
@@ -1272,6 +1261,12 @@ One device is not needed for the other to work.</p>
|
||||
and make sure, <strong>Private Network</strong> is selected as “Network profile type”
|
||||
(after transfer, you can change back to the original value)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>iOS</strong>, make sure “System Settings / Apps / Delta Chat / <strong>Local Network</strong>” access is granted</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>On <strong>macOS</strong>, enable “System Settings / Privacy & Security / <strong>Local Network</strong> / Delta Chat”</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Your system might have a “personal firewall”,
|
||||
which is known to cause problems (especially on Windows).
|
||||
@@ -1384,6 +1379,7 @@ you only open attachments from senders you trust, and not from spammers.</li>
|
||||
<ul>
|
||||
<li>In general, anyone can share webxdc apps with each
|
||||
other without restrictions.</li>
|
||||
<li>From <a href="https://webxdc.org/apps/">webxdc.org/apps</a>.</li>
|
||||
<li>You can <a href="https://delta.chat/en/2023-08-11-xstore">send ‘hi’ to xstore@testrun.org</a>
|
||||
to see an experimental webxdc appstore.
|
||||
All of the apps are open source and for free.</li>
|
||||
@@ -1794,6 +1790,35 @@ Otherwise you might receive undecryptable messages from those group chats.</p>
|
||||
<li>请参阅 <a href="https://github.com/deltachat/deltachat-core-rust/blob/master/standards.md#standards-used-in-delta-chat">Delta Chat 中使用的标准</a>。</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="where-can-my-friends-find-delta-chat">
|
||||
|
||||
|
||||
Where can my friends find Delta Chat? <a href="#where-can-my-friends-find-delta-chat" class="anchor"></a>
|
||||
|
||||
|
||||
</h3>
|
||||
|
||||
<p>Delta Chat is available for all major and some minor platforms:</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<p>The <strong>official website</strong>, <a href="https://delta.chat/download">https://delta.chat/download</a> shows all options in detail</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>If unavailable, use the <strong>mirror</strong> at <a href="https://deltachat.github.io/deltachat-pages">https://deltachat.github.io/deltachat-pages</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Open one of the following <strong>app stores and search for “Delta Chat”:</strong>
|
||||
Google Play Store, F-Droid, Huawei App Gallery, Amazon App Store, iOS and macOS App Store, Microsoft Store</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Check the <strong>package manager</strong> of your Linux distributions</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Android APKs</strong> are also available on <a href="https://github.com/deltachat/deltachat-android/releases">https://github.com/deltachat/deltachat-android/releases</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="delta-chat-的开发是如何被资助的">
|
||||
|
||||
|
||||
|
||||
@@ -2,16 +2,14 @@ package com.b44t.messenger;
|
||||
|
||||
public class DcContext {
|
||||
|
||||
public final static int DC_PREF_DEFAULT_MDNS_ENABLED = 1;
|
||||
public final static int DC_PREF_DEFAULT_TRIM_ENABLED = 0;
|
||||
public final static int DC_PREF_DEFAULT_TRIM_LENGTH = 500;
|
||||
|
||||
public final static int DC_EVENT_INFO = 100;
|
||||
public final static int DC_EVENT_WARNING = 300;
|
||||
public final static int DC_EVENT_ERROR = 400;
|
||||
public final static int DC_EVENT_ERROR_SELF_NOT_IN_GROUP = 410;
|
||||
public final static int DC_EVENT_MSGS_CHANGED = 2000;
|
||||
public final static int DC_EVENT_REACTIONS_CHANGED = 2001;
|
||||
public final static int DC_EVENT_INCOMING_REACTION = 2002;
|
||||
public final static int DC_EVENT_INCOMING_WEBXDC_NOTIFY = 2003;
|
||||
public final static int DC_EVENT_INCOMING_MSG = 2005;
|
||||
public final static int DC_EVENT_MSGS_NOTICED = 2008;
|
||||
public final static int DC_EVENT_MSG_DELIVERED = 2010;
|
||||
@@ -82,16 +80,13 @@ public class DcContext {
|
||||
public final static int DC_MEDIA_QUALITY_BALANCED = 0;
|
||||
public final static int DC_MEDIA_QUALITY_WORSE = 1;
|
||||
|
||||
public final static int DC_DECISION_START_CHAT = 0;
|
||||
public final static int DC_DECISION_BLOCK = 1;
|
||||
public final static int DC_DECISION_NOT_NOW = 2;
|
||||
|
||||
public final static int DC_CONNECTIVITY_NOT_CONNECTED = 1000;
|
||||
public final static int DC_CONNECTIVITY_CONNECTING = 2000;
|
||||
public final static int DC_CONNECTIVITY_WORKING = 3000;
|
||||
public final static int DC_CONNECTIVITY_CONNECTED = 4000;
|
||||
|
||||
private static final String CONFIG_ACCOUNT_ENABLED = "ui.enabled";
|
||||
private static final String CONFIG_MENTION_NOTIF_ENABLED = "ui.notify_mentions";
|
||||
|
||||
// when using DcAccounts, use DcAccounts.addAccount() instead
|
||||
public DcContext(String osName, String dbfile) {
|
||||
@@ -204,7 +199,7 @@ public class DcContext {
|
||||
public native int sendMsg (int chat_id, DcMsg msg);
|
||||
public native int sendTextMsg (int chat_id, String text);
|
||||
public native int sendVideochatInvitation(int chat_id);
|
||||
public native boolean sendWebxdcStatusUpdate(int msg_id, String payload, String descr);
|
||||
public native boolean sendWebxdcStatusUpdate(int msg_id, String payload);
|
||||
public native String getWebxdcStatusUpdates(int msg_id, int last_known_serial);
|
||||
public native void setWebxdcIntegration (String file);
|
||||
public native int initWebxdcIntegration(int chat_id);
|
||||
@@ -232,22 +227,12 @@ public class DcContext {
|
||||
}
|
||||
}
|
||||
|
||||
public String getNameNAddr() {
|
||||
String displayname = getConfig("displayname");
|
||||
String addr = getConfig("addr");
|
||||
String ret = "";
|
||||
public boolean isMentionsEnabled() {
|
||||
return !"0".equals(getConfig(CONFIG_MENTION_NOTIF_ENABLED));
|
||||
}
|
||||
|
||||
if (!displayname.isEmpty() && !addr.isEmpty()) {
|
||||
ret = String.format("%s (%s)", displayname, addr);
|
||||
} else if (!addr.isEmpty()) {
|
||||
ret = addr;
|
||||
}
|
||||
|
||||
if (ret.isEmpty() || isConfigured() == 0) {
|
||||
ret += " (not configured)";
|
||||
}
|
||||
|
||||
return ret.trim();
|
||||
public void setMentionsEnabled(boolean enabled) {
|
||||
setConfigInt(CONFIG_MENTION_NOTIF_ENABLED, enabled? 1 : 0);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
||||
@@ -148,6 +148,7 @@ public class DcMsg {
|
||||
return new JSONObject();
|
||||
}
|
||||
}
|
||||
public native String getWebxdcHref ();
|
||||
public native boolean isForwarded ();
|
||||
public native boolean isInfo ();
|
||||
public native boolean isSetupMessage ();
|
||||
@@ -170,10 +171,10 @@ public class DcMsg {
|
||||
public native boolean isOutgoing();
|
||||
public native int getSenderColor();
|
||||
|
||||
public String getSenderName(DcContact dcContact, boolean markOverride) {
|
||||
public String getSenderName(DcContact dcContact) {
|
||||
String overrideName = getOverrideSenderName();
|
||||
if (overrideName != null) {
|
||||
return (markOverride ? "~" : "") + overrideName;
|
||||
return "~" + overrideName;
|
||||
} else {
|
||||
return dcContact.getDisplayName();
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.thoughtcrime.securesms.connect.KeepAliveService;
|
||||
import org.thoughtcrime.securesms.connect.NetworkStateReceiver;
|
||||
import org.thoughtcrime.securesms.crypto.DatabaseSecret;
|
||||
import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider;
|
||||
import org.thoughtcrime.securesms.crypto.PRNGFixes;
|
||||
import org.thoughtcrime.securesms.geolocation.DcLocationManager;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.notifications.FcmReceiveService;
|
||||
@@ -203,7 +202,6 @@ public class ApplicationContext extends MultiDexApplication {
|
||||
|
||||
KeepAliveService.maybeStartSelf(this);
|
||||
|
||||
initializeRandomNumberFix();
|
||||
initializeLogging();
|
||||
initializeJobManager();
|
||||
InChatSounds.getInstance(this);
|
||||
@@ -252,10 +250,6 @@ public class ApplicationContext extends MultiDexApplication {
|
||||
return jobManager;
|
||||
}
|
||||
|
||||
private void initializeRandomNumberFix() {
|
||||
PRNGFixes.apply();
|
||||
}
|
||||
|
||||
private void initializeLogging() {
|
||||
SignalProtocolLoggerProvider.setProvider(new AndroidSignalProtocolLogger());
|
||||
}
|
||||
|
||||
@@ -156,10 +156,6 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||
this.findPreference(PREFERENCE_CATEGORY_HELP)
|
||||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_HELP));
|
||||
|
||||
if (VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
tintIcons(getActivity());
|
||||
}
|
||||
|
||||
DcHelper.getEventCenter(getActivity()).addObserver(DcContext.DC_EVENT_CONNECTIVITY_CHANGED, this);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,9 +33,6 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
onPreCreate();
|
||||
if (BaseActivity.isMenuWorkaroundRequired()) {
|
||||
forceOverflowMenu();
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@@ -46,20 +43,6 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
|
||||
dynamicTheme.onResume(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
return (keyCode == KeyEvent.KEYCODE_MENU && BaseActivity.isMenuWorkaroundRequired()) || super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_MENU && BaseActivity.isMenuWorkaroundRequired()) {
|
||||
openOptionsMenu();
|
||||
return true;
|
||||
}
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
|
||||
private void initializeScreenshotSecurity() {
|
||||
if (Prefs.isScreenSecurityEnabled(this)) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
public abstract class BaseActivity extends FragmentActivity {
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
return (keyCode == KeyEvent.KEYCODE_MENU && isMenuWorkaroundRequired()) || super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_MENU && isMenuWorkaroundRequired()) {
|
||||
openOptionsMenu();
|
||||
return true;
|
||||
}
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
|
||||
public static boolean isMenuWorkaroundRequired() {
|
||||
return VERSION.SDK_INT < VERSION_CODES.KITKAT && ("LGE".equalsIgnoreCase(Build.MANUFACTURER) || "E6710".equalsIgnoreCase(Build.DEVICE));
|
||||
}
|
||||
}
|
||||
@@ -364,9 +364,7 @@ public abstract class BaseConversationListFragment extends Fragment implements A
|
||||
|
||||
mode.setTitle("1");
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
requireActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
}
|
||||
requireActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -395,11 +393,9 @@ public abstract class BaseConversationListFragment extends Fragment implements A
|
||||
actionMode = null;
|
||||
getListAdapter().initializeBatchMode(false);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
TypedArray color = requireActivity().getTheme().obtainStyledAttributes(new int[] {android.R.attr.statusBarColor});
|
||||
requireActivity().getWindow().setStatusBarColor(color.getColor(0, Color.BLACK));
|
||||
color.recycle();
|
||||
}
|
||||
TypedArray color = requireActivity().getTheme().obtainStyledAttributes(new int[]{android.R.attr.statusBarColor});
|
||||
requireActivity().getWindow().setStatusBarColor(color.getColor(0, Color.BLACK));
|
||||
color.recycle();
|
||||
|
||||
Context context = getContext();
|
||||
if (context != null) {
|
||||
|
||||
@@ -136,9 +136,7 @@ public class ContactSelectionListFragment extends Fragment
|
||||
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
|
||||
MenuInflater inflater = getActivity().getMenuInflater();
|
||||
inflater.inflate(R.menu.contact_list, menu);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
}
|
||||
getActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
setCorrectMenuVisibility(menu);
|
||||
actionMode.setTitle("1");
|
||||
return true;
|
||||
@@ -170,11 +168,9 @@ public class ContactSelectionListFragment extends Fragment
|
||||
ContactSelectionListFragment.this.actionMode = null;
|
||||
getContactSelectionListAdapter().resetActionModeSelection();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
TypedArray color = getActivity().getTheme().obtainStyledAttributes(new int[] {android.R.attr.statusBarColor});
|
||||
getActivity().getWindow().setStatusBarColor(color.getColor(0, Color.BLACK));
|
||||
color.recycle();
|
||||
}
|
||||
TypedArray color = getActivity().getTheme().obtainStyledAttributes(new int[]{android.R.attr.statusBarColor});
|
||||
getActivity().getWindow().setStatusBarColor(color.getColor(0, Color.BLACK));
|
||||
color.recycle();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -708,7 +708,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
DcMsg draft = dcContext.getDraft(chatId);
|
||||
final String sharedText = RelayUtil.getSharedText(this);
|
||||
|
||||
if (draft == null) {
|
||||
if (!draft.isOk()) {
|
||||
if (TextUtils.isEmpty(sharedText)) {
|
||||
composeText.setText("");
|
||||
future.set(false);
|
||||
@@ -929,12 +929,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
case AttachmentTypeSelector.TAKE_PHOTO:
|
||||
attachmentManager.capturePhoto(this, TAKE_PHOTO); break;
|
||||
case AttachmentTypeSelector.RECORD_VIDEO:
|
||||
if(VideoRecoder.canRecode()) {
|
||||
attachmentManager.captureVideo(this, RECORD_VIDEO);
|
||||
}
|
||||
else {
|
||||
Toast.makeText(this, "This device does not support video-compression (requires Android 4.4 KitKat)", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
attachmentManager.captureVideo(this, RECORD_VIDEO);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
@@ -442,7 +441,7 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
|
||||
if (msg.getFromId() != prevMsg.getFromId() && !singleMsg) {
|
||||
DcContact contact = dcContext.getContact(msg.getFromId());
|
||||
result.append(msg.getSenderName(contact, false)).append(":\n");
|
||||
result.append(msg.getSenderName(contact)).append(":\n");
|
||||
}
|
||||
if (msg.getType() == DcMsg.DC_MSG_TEXT || (singleMsg && !msg.getText().isEmpty())) {
|
||||
result.append(msg.getText());
|
||||
@@ -810,7 +809,7 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
DozeReminder.dozeReminderTapped(getContext());
|
||||
}
|
||||
else if(messageRecord.getInfoType() == DcMsg.DC_INFO_WEBXDC_INFO_MESSAGE) {
|
||||
scrollMaybeSmoothToMsgId(messageRecord.getParent().getId());
|
||||
WebxdcActivity.openWebxdcActivity(getContext(), messageRecord.getParent(), messageRecord.getWebxdcHref());
|
||||
}
|
||||
else {
|
||||
String self_mail = dcContext.getConfig("configured_mail_user");
|
||||
@@ -911,11 +910,9 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
|
||||
mode.setTitle("1");
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
Window window = getActivity().getWindow();
|
||||
statusBarColor = window.getStatusBarColor();
|
||||
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
}
|
||||
Window window = getActivity().getWindow();
|
||||
statusBarColor = window.getStatusBarColor();
|
||||
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
|
||||
setCorrectMenuVisibility(menu);
|
||||
ConversationAdaptiveActionsToolbar.adjustMenuActions(menu, 10, requireActivity().getWindow().getDecorView().getMeasuredWidth());
|
||||
@@ -932,9 +929,7 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
((ConversationAdapter)list.getAdapter()).clearSelection();
|
||||
list.getAdapter().notifyDataSetChanged();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getActivity().getWindow().setStatusBarColor(statusBarColor);
|
||||
}
|
||||
getActivity().getWindow().setStatusBarColor(statusBarColor);
|
||||
|
||||
actionMode = null;
|
||||
hideAddReactionView();
|
||||
|
||||
@@ -22,12 +22,10 @@ import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.text.Spannable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
@@ -72,7 +70,6 @@ import org.thoughtcrime.securesms.util.Linkifier;
|
||||
import org.thoughtcrime.securesms.util.LongClickMovementMethod;
|
||||
import org.thoughtcrime.securesms.util.MarkdownUtil;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.Prefs;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.views.Stub;
|
||||
@@ -387,7 +384,6 @@ public class ConversationItem extends BaseConversationItem
|
||||
private void setBodyText(DcMsg messageRecord) {
|
||||
bodyText.setClickable(false);
|
||||
bodyText.setFocusable(false);
|
||||
bodyText.setTextSize(TypedValue.COMPLEX_UNIT_SP, Prefs.getMessageBodyTextSize(context));
|
||||
|
||||
String text = messageRecord.getText();
|
||||
|
||||
@@ -498,9 +494,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
audioViewStub.get().setAudio(new AudioSlide(context, messageRecord), duration);
|
||||
audioViewStub.get().setOnClickListener(passthroughClickListener);
|
||||
audioViewStub.get().setOnLongClickListener(passthroughClickListener);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
audioViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
}
|
||||
audioViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
|
||||
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
@@ -518,9 +512,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
documentViewStub.get().setDocument(new DocumentSlide(context, messageRecord));
|
||||
documentViewStub.get().setDocumentClickListener(new ThumbnailClickListener());
|
||||
documentViewStub.get().setOnLongClickListener(passthroughClickListener);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
documentViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
}
|
||||
documentViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
|
||||
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
@@ -537,9 +529,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
webxdcViewStub.get().setWebxdc(messageRecord, context.getString(R.string.webxdc_app));
|
||||
webxdcViewStub.get().setWebxdcClickListener(new ThumbnailClickListener());
|
||||
webxdcViewStub.get().setOnLongClickListener(passthroughClickListener);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
webxdcViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
}
|
||||
webxdcViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
|
||||
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
@@ -557,9 +547,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
vcardViewStub.get().setVcardClickListener(new ThumbnailClickListener());
|
||||
vcardViewStub.get().setOnLongClickListener(passthroughClickListener);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
vcardViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
}
|
||||
vcardViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
|
||||
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
@@ -595,9 +583,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
mediaThumbnailStub.get().setOnLongClickListener(passthroughClickListener);
|
||||
mediaThumbnailStub.get().setOnClickListener(passthroughClickListener);
|
||||
mediaThumbnailStub.get().showShade(TextUtils.isEmpty(messageRecord.getText()));
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
mediaThumbnailStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
}
|
||||
mediaThumbnailStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
|
||||
setThumbnailOutlineCorners(messageRecord, showSender);
|
||||
|
||||
@@ -620,9 +606,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
stickerStub.get().setThumbnailClickListener(new StickerClickListener());
|
||||
stickerStub.get().setOnLongClickListener(passthroughClickListener);
|
||||
stickerStub.get().setOnClickListener(passthroughClickListener);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
stickerStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
}
|
||||
stickerStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
|
||||
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
@@ -683,7 +667,7 @@ public class ConversationItem extends BaseConversationItem
|
||||
contactPhoto.setVisibility(View.GONE);
|
||||
} else {
|
||||
int color = messageRecord.getSenderColor();
|
||||
Recipient recipient = new Recipient(context, dcContact, messageRecord.getSenderName(dcContact, !dcContext.isCommunity()), color);
|
||||
Recipient recipient = new Recipient(context, dcContact, messageRecord.getSenderName(dcContact), color);
|
||||
contactPhoto.setAvatar(glideRequests, recipient, true);
|
||||
contactPhoto.setVisibility(View.VISIBLE);
|
||||
}
|
||||
@@ -808,14 +792,14 @@ public class ConversationItem extends BaseConversationItem
|
||||
|
||||
if (messageRecord.isForwarded()) {
|
||||
if (showSender && dcContact !=null) {
|
||||
this.groupSender.setText(context.getString(R.string.forwarded_by, messageRecord.getSenderName(dcContact, false)));
|
||||
this.groupSender.setText(context.getString(R.string.forwarded_by, messageRecord.getSenderName(dcContact)));
|
||||
} else {
|
||||
this.groupSender.setText(context.getString(R.string.forwarded_message));
|
||||
}
|
||||
this.groupSender.setTextColor(context.getResources().getColor(R.color.unknown_sender));
|
||||
}
|
||||
else if (showSender && dcContact !=null) {
|
||||
this.groupSender.setText(messageRecord.getSenderName(dcContact, true));
|
||||
this.groupSender.setText(messageRecord.getSenderName(dcContact));
|
||||
int color = messageRecord.getSenderColor();
|
||||
this.groupSender.setTextColor(Util.rgbToArgbColor(color!=0? color : dcContact.getColor()));
|
||||
}
|
||||
|
||||
@@ -174,9 +174,7 @@ class ConversationItemSwipeCallback extends ItemTouchHelper.SimpleCallback {
|
||||
vibrate(viewHolder.itemView.getContext());
|
||||
}
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
recyclerView.cancelPendingInputEvents();
|
||||
}
|
||||
recyclerView.cancelPendingInputEvents();
|
||||
}
|
||||
|
||||
private static void resetProgress(RecyclerView.ViewHolder viewHolder) {
|
||||
@@ -199,11 +197,7 @@ class ConversationItemSwipeCallback extends ItemTouchHelper.SimpleCallback {
|
||||
}
|
||||
|
||||
private static float getSignFromDirection(@NonNull View view) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
return view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? -1f : 1f;
|
||||
} else {
|
||||
return 1f;
|
||||
}
|
||||
return view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? -1f : 1f;
|
||||
}
|
||||
|
||||
private static boolean sameSign(float dX, float sign) {
|
||||
|
||||
@@ -564,7 +564,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
|
||||
private void shareInvite() {
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.setType("text/plain");
|
||||
String inviteURL = Util.QrDataToInviteURL(DcHelper.getContext(this).getSecurejoinQr(0));
|
||||
String inviteURL = DcHelper.getContext(this).getSecurejoinQr(0);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, getString(R.string.invite_friends_text, inviteURL));
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.chat_share_with_title)));
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ public class ConversationListItemInboxZero extends LinearLayout implements Binda
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public ConversationListItemInboxZero(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
@@ -246,16 +246,22 @@ public class InstantOnboardingActivity extends BaseActionBarActivity implements
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
final String displayName = name.getText().toString();
|
||||
DcHelper.set(this, DcHelper.CONFIG_DISPLAY_NAME, TextUtils.isEmpty(displayName)? null : displayName);
|
||||
// Save display name and avatar in the unconfigured profile.
|
||||
// If the currently selected profile is configured, then this means that rollbackAccountCreation()
|
||||
// was called (see handleOnBackPressed() above), i.e. the newly created profile was removed already
|
||||
// and we can't save the display name & avatar.
|
||||
if (DcHelper.getContext(this).isConfigured() == 0) {
|
||||
final String displayName = name.getText().toString();
|
||||
DcHelper.set(this, DcHelper.CONFIG_DISPLAY_NAME, TextUtils.isEmpty(displayName) ? null : displayName);
|
||||
|
||||
if (avatarChanged) {
|
||||
try {
|
||||
AvatarHelper.setSelfAvatar(InstantOnboardingActivity.this, avatarBmp);
|
||||
Prefs.setProfileAvatarId(InstantOnboardingActivity.this, new SecureRandom().nextInt());
|
||||
avatarChanged = false;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to save avatar", e);
|
||||
if (avatarChanged) {
|
||||
try {
|
||||
AvatarHelper.setSelfAvatar(InstantOnboardingActivity.this, avatarBmp);
|
||||
Prefs.setProfileAvatarId(InstantOnboardingActivity.this, new SecureRandom().nextInt());
|
||||
avatarChanged = false;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to save avatar", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,12 +197,11 @@ public class LogViewFragment extends Fragment {
|
||||
asMegs(info.maxMemory()));
|
||||
}
|
||||
|
||||
@TargetApi(VERSION_CODES.KITKAT)
|
||||
public static String getMemoryClass(Context context) {
|
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
String lowMem = "";
|
||||
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT && activityManager.isLowRamDevice()) {
|
||||
if (activityManager.isLowRamDevice()) {
|
||||
lowMem = ", low-mem device";
|
||||
}
|
||||
return activityManager.getMemoryClass() + lowMem;
|
||||
@@ -252,10 +251,8 @@ public class LogViewFragment extends Fragment {
|
||||
|
||||
Locale locale = Util.getLocale();
|
||||
builder.append("lang=").append(locale.toString()).append("\n");
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
boolean isRtl = Util.getLayoutDirection(context) == View.LAYOUT_DIRECTION_RTL;
|
||||
builder.append("rtl=").append(isRtl).append("\n");
|
||||
}
|
||||
boolean isRtl = Util.getLayoutDirection(context) == View.LAYOUT_DIRECTION_RTL;
|
||||
builder.append("rtl=").append(isRtl).append("\n");
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
boolean notifPermGranted = PermissionChecker.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) == PermissionChecker.PERMISSION_GRANTED;
|
||||
|
||||
@@ -229,11 +229,9 @@ public class ProfileDocumentsFragment
|
||||
mode.getMenuInflater().inflate(R.menu.profile_context, menu);
|
||||
mode.setTitle("1");
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
Window window = getActivity().getWindow();
|
||||
originalStatusBarColor = window.getStatusBarColor();
|
||||
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
}
|
||||
Window window = getActivity().getWindow();
|
||||
originalStatusBarColor = window.getStatusBarColor();
|
||||
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
setCorrectMenuVisibility(menu);
|
||||
return true;
|
||||
}
|
||||
@@ -283,9 +281,7 @@ public class ProfileDocumentsFragment
|
||||
actionMode = null;
|
||||
getListAdapter().clearSelection();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getActivity().getWindow().setStatusBarColor(originalStatusBarColor);
|
||||
}
|
||||
getActivity().getWindow().setStatusBarColor(originalStatusBarColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,11 +219,9 @@ public class ProfileGalleryFragment
|
||||
mode.getMenuInflater().inflate(R.menu.profile_context, menu);
|
||||
mode.setTitle("1");
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
Window window = getActivity().getWindow();
|
||||
originalStatusBarColor = window.getStatusBarColor();
|
||||
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
}
|
||||
Window window = getActivity().getWindow();
|
||||
originalStatusBarColor = window.getStatusBarColor();
|
||||
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
setCorrectMenuVisibility(menu);
|
||||
return true;
|
||||
}
|
||||
@@ -269,9 +267,7 @@ public class ProfileGalleryFragment
|
||||
actionMode = null;
|
||||
getListAdapter().clearSelection();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getActivity().getWindow().setStatusBarColor(originalStatusBarColor);
|
||||
}
|
||||
getActivity().getWindow().setStatusBarColor(originalStatusBarColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,11 +268,9 @@ public class ProfileSettingsFragment extends Fragment
|
||||
menu.findItem(R.id.menu_select_all).setVisible(false);
|
||||
mode.setTitle("1");
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
Window window = getActivity().getWindow();
|
||||
originalStatusBarColor = window.getStatusBarColor();
|
||||
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
}
|
||||
Window window = getActivity().getWindow();
|
||||
originalStatusBarColor = window.getStatusBarColor();
|
||||
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -314,9 +312,7 @@ public class ProfileSettingsFragment extends Fragment
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
actionMode = null;
|
||||
adapter.clearSelection();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getActivity().getWindow().setStatusBarColor(originalStatusBarColor);
|
||||
}
|
||||
getActivity().getWindow().setStatusBarColor(originalStatusBarColor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ import com.b44t.messenger.DcEvent;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
import com.b44t.messenger.rpc.Rpc;
|
||||
import com.b44t.messenger.rpc.RpcException;
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.thoughtcrime.securesms.connect.AccountManager;
|
||||
@@ -54,6 +55,8 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -63,6 +66,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
private static final String EXTRA_ACCOUNT_ID = "accountId";
|
||||
private static final String EXTRA_APP_MSG_ID = "appMessageId";
|
||||
private static final String EXTRA_HIDE_ACTION_BAR = "hideActionBar";
|
||||
private static final String EXTRA_HREF = "href";
|
||||
private static final int REQUEST_CODE_FILE_PICKER = 51426;
|
||||
|
||||
private ValueCallback<Uri[]> filePathCallback;
|
||||
@@ -71,6 +75,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
private DcMsg dcAppMsg;
|
||||
private String baseURL;
|
||||
private String sourceCodeUrl = "";
|
||||
private String selfAddr = "";
|
||||
private boolean internetAccess = false;
|
||||
private boolean hideActionBar = false;
|
||||
|
||||
@@ -92,33 +97,34 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
return;
|
||||
}
|
||||
}
|
||||
openWebxdcActivity(context, msgId, true);
|
||||
openWebxdcActivity(context, msgId, true, "");
|
||||
}
|
||||
|
||||
public static void openWebxdcActivity(Context context, DcMsg instance) {
|
||||
openWebxdcActivity(context, instance.getId(), false);
|
||||
openWebxdcActivity(context, instance, "");
|
||||
}
|
||||
|
||||
public static void openWebxdcActivity(Context context, int msgId, boolean hideActionBar) {
|
||||
public static void openWebxdcActivity(Context context, DcMsg instance, String href) {
|
||||
openWebxdcActivity(context, instance.getId(), false, href);
|
||||
}
|
||||
|
||||
public static void openWebxdcActivity(Context context, int msgId, boolean hideActionBar, String href) {
|
||||
if (!Util.isClickedRecently()) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (Prefs.isDeveloperModeEnabled(context)) {
|
||||
WebView.setWebContentsDebuggingEnabled(true);
|
||||
}
|
||||
context.startActivity(getWebxdcIntent(context, msgId, hideActionBar));
|
||||
} else {
|
||||
Toast.makeText(context, "At least Android 5.0 (Lollipop) required for Webxdc.", Toast.LENGTH_LONG).show();
|
||||
if (Prefs.isDeveloperModeEnabled(context)) {
|
||||
WebView.setWebContentsDebuggingEnabled(true);
|
||||
}
|
||||
context.startActivity(getWebxdcIntent(context, msgId, hideActionBar, href));
|
||||
}
|
||||
}
|
||||
|
||||
private static Intent getWebxdcIntent(Context context, int msgId, boolean hideActionBar) {
|
||||
private static Intent getWebxdcIntent(Context context, int msgId, boolean hideActionBar, String href) {
|
||||
DcContext dcContext = DcHelper.getContext(context);
|
||||
Intent intent = new Intent(context, WebxdcActivity.class);
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.putExtra(EXTRA_ACCOUNT_ID, dcContext.getAccountId());
|
||||
intent.putExtra(EXTRA_APP_MSG_ID, msgId);
|
||||
intent.putExtra(EXTRA_HIDE_ACTION_BAR, hideActionBar);
|
||||
intent.putExtra(EXTRA_HREF, href);
|
||||
return intent;
|
||||
}
|
||||
|
||||
@@ -129,7 +135,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
.putExtra(ConversationActivity.CHAT_ID_EXTRA, dcContext.getMsg(msgId).getChatId())
|
||||
.setAction(Intent.ACTION_VIEW);
|
||||
|
||||
final Intent webxdcIntent = getWebxdcIntent(context, msgId, false);
|
||||
final Intent webxdcIntent = getWebxdcIntent(context, msgId, false, "");
|
||||
|
||||
return TaskStackBuilder.create(context)
|
||||
.addNextIntentWithParentStack(chatIntent)
|
||||
@@ -197,6 +203,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
// this is needed here because if the app is opened while already in landscape mode, onConfigurationChanged() is not triggered
|
||||
setScreenMode(getResources().getConfiguration());
|
||||
}
|
||||
selfAddr = JsonUtils.optString(info, "self_addr");
|
||||
|
||||
toggleFakeProxy(!internetAccess);
|
||||
|
||||
@@ -213,7 +220,15 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
webView.setNetworkAvailable(internetAccess); // this does not block network but sets `window.navigator.isOnline` in js land
|
||||
webView.addJavascriptInterface(new InternalJSApi(), "InternalJSApi");
|
||||
|
||||
webView.loadUrl(this.baseURL + "/index.html");
|
||||
String href = b.getString(EXTRA_HREF, "");
|
||||
String encodedHref = "";
|
||||
try {
|
||||
encodedHref = URLEncoder.encode(href, Charsets.UTF_8.name());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
webView.loadUrl(this.baseURL + "/webxdc_bootstrap324567869.html?i=1&href=" + encodedHref);
|
||||
|
||||
Util.runOnAnyBackgroundThread(() -> {
|
||||
final DcChat chat = dcContext.getChat(dcAppMsg.getChatId());
|
||||
@@ -223,6 +238,18 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
DcHelper.getNotificationCenter(this).updateVisibleWebxdc(dcContext.getAccountId(), dcAppMsg.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
DcHelper.getNotificationCenter(this).clearVisibleWebxdc();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
DcHelper.getEventCenter(this.getApplicationContext()).removeObservers(this);
|
||||
@@ -249,6 +276,9 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
case R.id.source_code:
|
||||
openUrlInBrowser(this, sourceCodeUrl);
|
||||
return true;
|
||||
case R.id.show_in_chat:
|
||||
showInChat();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -334,7 +364,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
res = new WebResourceResponse("text/plain", "UTF-8", targetStream);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !internetAccess) {
|
||||
if (!internetAccess) {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("Content-Security-Policy",
|
||||
"default-src 'self'; "
|
||||
@@ -353,9 +383,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
}
|
||||
|
||||
private void callJavaScriptFunction(String func) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
webView.evaluateJavascript("window." + func + ";", null);
|
||||
}
|
||||
webView.evaluateJavascript("document.getElementById('frame').contentWindow." + func + ";", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -394,6 +422,13 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
}
|
||||
}
|
||||
|
||||
private void showInChat() {
|
||||
Intent intent = new Intent(this, ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.CHAT_ID_EXTRA, dcAppMsg.getChatId());
|
||||
intent.putExtra(ConversationActivity.STARTING_POSITION_EXTRA, DcMsg.getMessagePosition(dcAppMsg, dcContext));
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
public static void addToHomeScreen(Activity activity, int msgId) {
|
||||
Context context = activity.getApplicationContext();
|
||||
try {
|
||||
@@ -475,7 +510,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
int flags = Base64.NO_WRAP | Base64.NO_PADDING | Base64.URL_SAFE;
|
||||
return Base64.encodeToString(dcContext.getCommunityUser().getBytes(), flags);
|
||||
}
|
||||
return dcContext.getConfig("configured_addr");
|
||||
return WebxdcActivity.this.selfAddr;
|
||||
}
|
||||
|
||||
/** @noinspection unused*/
|
||||
@@ -493,9 +528,9 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||
|
||||
/** @noinspection unused*/
|
||||
@JavascriptInterface
|
||||
public boolean sendStatusUpdate(String payload, String descr) {
|
||||
public boolean sendStatusUpdate(String payload) {
|
||||
Log.i(TAG, "sendStatusUpdate");
|
||||
if (!WebxdcActivity.this.dcContext.sendWebxdcStatusUpdate(WebxdcActivity.this.dcAppMsg.getId(), payload, descr)) {
|
||||
if (!WebxdcActivity.this.dcContext.sendWebxdcStatusUpdate(WebxdcActivity.this.dcAppMsg.getId(), payload)) {
|
||||
DcChat dcChat = WebxdcActivity.this.dcContext.getChat(WebxdcActivity.this.dcAppMsg.getChatId());
|
||||
Toast.makeText(WebxdcActivity.this,
|
||||
dcChat.isContactRequest() ?
|
||||
|
||||
@@ -166,7 +166,7 @@ public class WelcomeActivity extends BaseActionBarActivity implements DcEventCen
|
||||
.withPermanentDenialDialog(getString(R.string.perm_explain_access_to_storage_denied))
|
||||
.onAllGranted(() -> {
|
||||
File imexDir = DcHelper.getImexDir();
|
||||
if (Build.VERSION.SDK_INT >= 30) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
AttachmentManager.selectMediaType(this, "application/x-tar", null, PICK_BACKUP, StorageUtil.getDownloadUri());
|
||||
} else {
|
||||
final String backupFile = dcContext.imexHasBackup(imexDir.getAbsolutePath());
|
||||
|
||||
@@ -130,33 +130,23 @@ public class AttachmentTypeSelector extends PopupWindow {
|
||||
public void onGlobalLayout() {
|
||||
getContentView().getViewTreeObserver().removeGlobalOnLayoutListener(this);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
animateWindowInCircular(anchor, getContentView());
|
||||
} else {
|
||||
animateWindowInTranslate(getContentView());
|
||||
}
|
||||
animateWindowInCircular(anchor, getContentView());
|
||||
}
|
||||
});
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
//animateButtonIn(cameraButton, ANIMATION_DURATION / 2);
|
||||
animateButtonIn(videoButton, ANIMATION_DURATION / 2);
|
||||
animateButtonIn(imageButton, ANIMATION_DURATION / 3);
|
||||
animateButtonIn(contactButton, ANIMATION_DURATION / 3);
|
||||
animateButtonIn(locationButton, ANIMATION_DURATION / 4);
|
||||
animateButtonIn(documentButton, ANIMATION_DURATION / 4);
|
||||
animateButtonIn(videoChatButton, 0);
|
||||
animateButtonIn(closeButton, 0);
|
||||
}
|
||||
//animateButtonIn(cameraButton, ANIMATION_DURATION / 2);
|
||||
animateButtonIn(videoButton, ANIMATION_DURATION / 2);
|
||||
animateButtonIn(imageButton, ANIMATION_DURATION / 3);
|
||||
animateButtonIn(contactButton, ANIMATION_DURATION / 3);
|
||||
animateButtonIn(locationButton, ANIMATION_DURATION / 4);
|
||||
animateButtonIn(documentButton, ANIMATION_DURATION / 4);
|
||||
animateButtonIn(videoChatButton, 0);
|
||||
animateButtonIn(closeButton, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
animateWindowOutCircular(currentAnchor, getContentView());
|
||||
} else {
|
||||
animateWindowOutTranslate(getContentView());
|
||||
}
|
||||
animateWindowOutCircular(currentAnchor, getContentView());
|
||||
}
|
||||
|
||||
public void setListener(@Nullable AttachmentClickedListener listener) {
|
||||
@@ -186,7 +176,6 @@ public class AttachmentTypeSelector extends PopupWindow {
|
||||
button.startAnimation(animation);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private void animateWindowInCircular(@Nullable View anchor, @NonNull View contentView) {
|
||||
Pair<Integer, Integer> coordinates = getClickOrigin(anchor, contentView);
|
||||
Animator animator = ViewAnimationUtils.createCircularReveal(contentView,
|
||||
@@ -205,7 +194,6 @@ public class AttachmentTypeSelector extends PopupWindow {
|
||||
getContentView().startAnimation(animation);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private void animateWindowOutCircular(@Nullable View anchor, @NonNull View contentView) {
|
||||
Pair<Integer, Integer> coordinates = getClickOrigin(anchor, contentView);
|
||||
Animator animator = ViewAnimationUtils.createCircularReveal(getContentView(),
|
||||
|
||||
@@ -71,12 +71,10 @@ public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener
|
||||
this.pauseButton.setOnClickListener(new PauseClickedListener());
|
||||
this.seekBar.setOnSeekBarChangeListener(new SeekBarModifiedListener());
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
this.playButton.setImageDrawable(context.getDrawable(R.drawable.play_icon));
|
||||
this.pauseButton.setImageDrawable(context.getDrawable(R.drawable.pause_icon));
|
||||
this.playButton.setBackground(context.getDrawable(R.drawable.ic_circle_fill_white_48dp));
|
||||
this.pauseButton.setBackground(context.getDrawable(R.drawable.ic_circle_fill_white_48dp));
|
||||
}
|
||||
this.playButton.setImageDrawable(context.getDrawable(R.drawable.play_icon));
|
||||
this.pauseButton.setImageDrawable(context.getDrawable(R.drawable.pause_icon));
|
||||
this.playButton.setBackground(context.getDrawable(R.drawable.ic_circle_fill_white_48dp));
|
||||
this.pauseButton.setBackground(context.getDrawable(R.drawable.ic_circle_fill_white_48dp));
|
||||
|
||||
setTint(getContext().getResources().getColor(R.color.audio_icon));
|
||||
}
|
||||
@@ -192,13 +190,8 @@ public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener
|
||||
}
|
||||
|
||||
public void setTint(int foregroundTint) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
this.playButton.setBackgroundTintList(ColorStateList.valueOf(foregroundTint));
|
||||
this.pauseButton.setBackgroundTintList(ColorStateList.valueOf(foregroundTint));
|
||||
} else {
|
||||
this.playButton.setColorFilter(foregroundTint, PorterDuff.Mode.SRC_IN);
|
||||
this.pauseButton.setColorFilter(foregroundTint, PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
this.playButton.setBackgroundTintList(ColorStateList.valueOf(foregroundTint));
|
||||
this.pauseButton.setBackgroundTintList(ColorStateList.valueOf(foregroundTint));
|
||||
|
||||
this.seekBar.getProgressDrawable().setColorFilter(foregroundTint, PorterDuff.Mode.SRC_IN);
|
||||
|
||||
@@ -220,25 +213,20 @@ public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener
|
||||
private void togglePlayToPause() {
|
||||
controlToggle.displayQuick(pauseButton);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
AnimatedVectorDrawable playToPauseDrawable = (AnimatedVectorDrawable)getContext().getDrawable(R.drawable.play_to_pause_animation);
|
||||
pauseButton.setImageDrawable(playToPauseDrawable);
|
||||
playToPauseDrawable.start();
|
||||
}
|
||||
AnimatedVectorDrawable playToPauseDrawable = (AnimatedVectorDrawable) getContext().getDrawable(R.drawable.play_to_pause_animation);
|
||||
pauseButton.setImageDrawable(playToPauseDrawable);
|
||||
playToPauseDrawable.start();
|
||||
}
|
||||
|
||||
private void togglePauseToPlay() {
|
||||
controlToggle.displayQuick(playButton);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
AnimatedVectorDrawable pauseToPlayDrawable = (AnimatedVectorDrawable)getContext().getDrawable(R.drawable.pause_to_play_animation);
|
||||
playButton.setImageDrawable(pauseToPlayDrawable);
|
||||
pauseToPlayDrawable.start();
|
||||
}
|
||||
AnimatedVectorDrawable pauseToPlayDrawable = (AnimatedVectorDrawable) getContext().getDrawable(R.drawable.pause_to_play_animation);
|
||||
playButton.setImageDrawable(pauseToPlayDrawable);
|
||||
pauseToPlayDrawable.start();
|
||||
}
|
||||
|
||||
private class PlayClickedListener implements View.OnClickListener {
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
try {
|
||||
@@ -279,7 +267,6 @@ public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener
|
||||
}
|
||||
|
||||
private class PauseClickedListener implements View.OnClickListener {
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Log.w(TAG, "pausebutton onClick");
|
||||
|
||||
@@ -122,7 +122,6 @@ public class ComposeText extends AppCompatEditText {
|
||||
editorInfo.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < 21) return inputConnection;
|
||||
if (mediaListener == null) return inputConnection;
|
||||
if (inputConnection == null) return null;
|
||||
|
||||
|
||||
@@ -120,15 +120,10 @@ public class ConversationItemThumbnail extends FrameLayout {
|
||||
@SuppressWarnings("SuspiciousNameCombination")
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
if (cornerMask.isLegacy()) {
|
||||
cornerMask.mask(canvas);
|
||||
}
|
||||
|
||||
super.dispatchDraw(canvas);
|
||||
|
||||
if (!cornerMask.isLegacy()) {
|
||||
cornerMask.mask(canvas);
|
||||
}
|
||||
cornerMask.mask(canvas);
|
||||
|
||||
final float halfStrokeWidth = outlinePaint.getStrokeWidth() / 2;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import android.graphics.Path;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import android.view.View;
|
||||
|
||||
@@ -20,11 +20,7 @@ public class CornerMask {
|
||||
private final RectF bounds = new RectF();
|
||||
|
||||
public CornerMask(@NonNull View view) {
|
||||
if (isLegacy()) {
|
||||
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
|
||||
} else {
|
||||
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
}
|
||||
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
|
||||
clearPaint.setColor(Color.BLACK);
|
||||
clearPaint.setStyle(Paint.Style.FILL);
|
||||
@@ -44,19 +40,10 @@ public class CornerMask {
|
||||
// Note: There's a bug in the P beta where most PorterDuff modes aren't working. But CLEAR does.
|
||||
// So we find and inverse path and use Mode.CLEAR for versions that support Path.op().
|
||||
// See issue https://issuetracker.google.com/issues/111394085.
|
||||
if (!isLegacy()) {
|
||||
outline.reset();
|
||||
outline.addRect(bounds, Path.Direction.CW);
|
||||
outline.op(corners, Path.Op.DIFFERENCE);
|
||||
canvas.drawPath(outline, clearPaint);
|
||||
} else {
|
||||
corners.addRoundRect(bounds, radii, Path.Direction.CW);
|
||||
canvas.clipPath(corners);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isLegacy() {
|
||||
return Build.VERSION.SDK_INT < 19;
|
||||
outline.reset();
|
||||
outline.addRect(bounds, Path.Direction.CW);
|
||||
outline.op(corners, Path.Op.DIFFERENCE);
|
||||
canvas.drawPath(outline, clearPaint);
|
||||
}
|
||||
|
||||
public void setRadius(int radius) {
|
||||
|
||||
@@ -104,7 +104,7 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
|
||||
}
|
||||
|
||||
private void updateKeyboardState() {
|
||||
if (viewInset == 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) viewInset = getViewInset();
|
||||
if (viewInset == 0) viewInset = getViewInset();
|
||||
|
||||
getWindowVisibleDisplayFrame(rect);
|
||||
|
||||
@@ -148,7 +148,6 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
||||
private int getViewInset() {
|
||||
try {
|
||||
Field attachInfoField = View.class.getDeclaredField("mAttachInfo");
|
||||
@@ -219,11 +218,7 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
|
||||
return Surface.ROTATION_0;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
return ServiceUtil.getWindowManager(getContext()).getDefaultDisplay().getRotation();
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 30) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
getContext().getDisplay().getRealMetrics(displayMetrics);
|
||||
} else {
|
||||
ServiceUtil.getWindowManager(getContext()).getDefaultDisplay().getRealMetrics(displayMetrics);
|
||||
|
||||
@@ -40,7 +40,6 @@ public class MediaView extends FrameLayout {
|
||||
initialize();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public MediaView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
initialize();
|
||||
|
||||
@@ -4,7 +4,6 @@ package org.thoughtcrime.securesms.components;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.text.Spannable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
@@ -17,7 +16,6 @@ import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
import com.b44t.messenger.DcContact;
|
||||
@@ -80,7 +78,6 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
|
||||
initialize(attrs);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public QuoteView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
initialize(attrs);
|
||||
@@ -158,7 +155,7 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
|
||||
if (contact == null) {
|
||||
authorView.setText(getContext().getString(R.string.forwarded_message));
|
||||
} else {
|
||||
authorView.setText(getContext().getString(R.string.forwarded_by, quotedMsg.getSenderName(contact, false)));
|
||||
authorView.setText(getContext().getString(R.string.forwarded_by, quotedMsg.getSenderName(contact)));
|
||||
}
|
||||
authorView.setTextColor(getForwardedColor());
|
||||
quoteBarView.setBackgroundColor(getForwardedColor());
|
||||
@@ -169,7 +166,7 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
|
||||
quoteBarView.setBackgroundColor(getForwardedColor());
|
||||
} else {
|
||||
authorView.setVisibility(VISIBLE);
|
||||
authorView.setText(quotedMsg.getSenderName(contact, true));
|
||||
authorView.setText(quotedMsg.getSenderName(contact));
|
||||
if (hasSticker) {
|
||||
authorView.setTextColor(getResources().getColor(R.color.core_dark_05));
|
||||
quoteBarView.setBackgroundColor(getResources().getColor(R.color.core_dark_05));
|
||||
|
||||
@@ -29,7 +29,6 @@ public class RepeatableImageKey extends ImageButton {
|
||||
init();
|
||||
}
|
||||
|
||||
@TargetApi(VERSION_CODES.LOLLIPOP)
|
||||
public RepeatableImageKey(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
int defStyleRes)
|
||||
{
|
||||
|
||||
@@ -130,15 +130,11 @@ public class SearchToolbar extends LinearLayout {
|
||||
|
||||
searchItem.expandActionView();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
Animator animator = ViewAnimationUtils.createCircularReveal(this, (int)x, (int)y, 0, getWidth());
|
||||
animator.setDuration(400);
|
||||
Animator animator = ViewAnimationUtils.createCircularReveal(this, (int) x, (int) y, 0, getWidth());
|
||||
animator.setDuration(400);
|
||||
|
||||
setVisibility(View.VISIBLE);
|
||||
animator.start();
|
||||
} else {
|
||||
setVisibility(View.VISIBLE);
|
||||
}
|
||||
setVisibility(View.VISIBLE);
|
||||
animator.start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,19 +149,15 @@ public class SearchToolbar extends LinearLayout {
|
||||
|
||||
if (listener != null) listener.onSearchClosed();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
Animator animator = ViewAnimationUtils.createCircularReveal(this, (int)x, (int)y, getWidth(), 0);
|
||||
animator.setDuration(400);
|
||||
animator.addListener(new AnimationCompleteListener() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
setVisibility(View.INVISIBLE);
|
||||
}
|
||||
});
|
||||
animator.start();
|
||||
} else {
|
||||
setVisibility(View.INVISIBLE);
|
||||
}
|
||||
Animator animator = ViewAnimationUtils.createCircularReveal(this, (int) x, (int) y, getWidth(), 0);
|
||||
animator.setDuration(400);
|
||||
animator.addListener(new AnimationCompleteListener() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
setVisibility(View.INVISIBLE);
|
||||
}
|
||||
});
|
||||
animator.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ public class SwitchPreferenceCompat extends CheckBoxPreference {
|
||||
setLayoutRes();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public SwitchPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
setLayoutRes();
|
||||
|
||||
+50
-28
File diff suppressed because one or more lines are too long
@@ -171,7 +171,15 @@ public class DcEventCenter {
|
||||
|
||||
switch (id) {
|
||||
case DcContext.DC_EVENT_INCOMING_MSG:
|
||||
DcHelper.getNotificationCenter(context).addNotification(accountId, event.getData1Int(), event.getData2Int());
|
||||
DcHelper.getNotificationCenter(context).notifyMessage(accountId, event.getData1Int(), event.getData2Int());
|
||||
break;
|
||||
|
||||
case DcContext.DC_EVENT_INCOMING_REACTION:
|
||||
DcHelper.getNotificationCenter(context).notifyReaction(accountId, event.getData1Int(), event.getData2Int(), event.getData2Str());
|
||||
break;
|
||||
|
||||
case DcContext.DC_EVENT_INCOMING_WEBXDC_NOTIFY:
|
||||
DcHelper.getNotificationCenter(context).notifyWebxdc(accountId, event.getData1Int(), event.getData2Int(), event.getData2Str());
|
||||
break;
|
||||
|
||||
case DcContext.DC_EVENT_MSGS_NOTICED:
|
||||
|
||||
@@ -263,9 +263,7 @@ public class DcHelper {
|
||||
}
|
||||
|
||||
public static File getImexDir() {
|
||||
// DIRECTORY_DOCUMENTS is only available since KitKat;
|
||||
// as we also support Ice Cream Sandwich and Jellybean (2017: 11% in total), this is no option.
|
||||
// Moreover, DIRECTORY_DOWNLOADS seems to be easier accessible by the user,
|
||||
// DIRECTORY_DOCUMENTS could be used but DIRECTORY_DOWNLOADS seems to be easier accessible by the user,
|
||||
// eg. "Download Managers" are nearly always installed.
|
||||
// CAVE: do not use DownloadManager to add the file as it is deleted on uninstall then ...
|
||||
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||
|
||||
@@ -1,345 +0,0 @@
|
||||
/*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will Google be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, as long as the origin is not misrepresented.
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.crypto;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.SecureRandomSpi;
|
||||
import java.security.Security;
|
||||
|
||||
/**
|
||||
* This class is taken directly from the Android blog post announcing this bug:
|
||||
* http://android-developers.blogspot.com/2013/08/some-securerandom-thoughts.html
|
||||
*
|
||||
* Since I still don't know exactly what the source of this bug was, I'm using
|
||||
* this class verbatim under the assumption that the Android team knows what
|
||||
* they're doing. Although, at this point, that is perhaps a foolish assumption.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fixes for the output of the default PRNG having low entropy.
|
||||
*
|
||||
* The fixes need to be applied via {@link #apply()} before any use of Java
|
||||
* Cryptography Architecture primitives. A good place to invoke them is in the
|
||||
* application's {@code onCreate}.
|
||||
*/
|
||||
public final class PRNGFixes {
|
||||
|
||||
private static final int VERSION_CODE_JELLY_BEAN = 16;
|
||||
private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18;
|
||||
private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL =
|
||||
getBuildFingerprintAndDeviceSerial();
|
||||
|
||||
/** Hidden constructor to prevent instantiation. */
|
||||
private PRNGFixes() {}
|
||||
|
||||
/**
|
||||
* Applies all fixes.
|
||||
*
|
||||
* @throws SecurityException if a fix is needed but could not be applied.
|
||||
*/
|
||||
public static void apply() {
|
||||
applyOpenSSLFix();
|
||||
installLinuxPRNGSecureRandom();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the
|
||||
* fix is not needed.
|
||||
*
|
||||
* @throws SecurityException if the fix is needed but could not be applied.
|
||||
*/
|
||||
private static void applyOpenSSLFix() throws SecurityException {
|
||||
if ((Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN)
|
||||
|| (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2)) {
|
||||
// No need to apply the fix
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Mix in the device- and invocation-specific seed.
|
||||
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||
.getMethod("RAND_seed", byte[].class)
|
||||
.invoke(null, generateSeed());
|
||||
|
||||
// Mix output of Linux PRNG into OpenSSL's PRNG
|
||||
int bytesRead = (Integer) Class.forName(
|
||||
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||
.getMethod("RAND_load_file", String.class, long.class)
|
||||
.invoke(null, "/dev/urandom", 1024);
|
||||
if (bytesRead != 1024) {
|
||||
throw new IOException(
|
||||
"Unexpected number of bytes read from Linux PRNG: "
|
||||
+ bytesRead);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new SecurityException("Failed to seed OpenSSL PRNG", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs a Linux PRNG-backed {@code SecureRandom} implementation as the
|
||||
* default. Does nothing if the implementation is already the default or if
|
||||
* there is not need to install the implementation.
|
||||
*
|
||||
* @throws SecurityException if the fix is needed but could not be applied.
|
||||
*/
|
||||
private static void installLinuxPRNGSecureRandom()
|
||||
throws SecurityException {
|
||||
if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) {
|
||||
// No need to apply the fix
|
||||
return;
|
||||
}
|
||||
|
||||
// Install a Linux PRNG-based SecureRandom implementation as the
|
||||
// default, if not yet installed.
|
||||
Provider[] secureRandomProviders =
|
||||
Security.getProviders("SecureRandom.SHA1PRNG");
|
||||
if ((secureRandomProviders == null)
|
||||
|| (secureRandomProviders.length < 1)
|
||||
|| (!LinuxPRNGSecureRandomProvider.class.equals(
|
||||
secureRandomProviders[0].getClass()))) {
|
||||
Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1);
|
||||
}
|
||||
|
||||
// Assert that new SecureRandom() and
|
||||
// SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed
|
||||
// by the Linux PRNG-based SecureRandom implementation.
|
||||
SecureRandom rng1 = new SecureRandom();
|
||||
if (!LinuxPRNGSecureRandomProvider.class.equals(
|
||||
rng1.getProvider().getClass())) {
|
||||
throw new SecurityException(
|
||||
"new SecureRandom() backed by wrong Provider: "
|
||||
+ rng1.getProvider().getClass());
|
||||
}
|
||||
|
||||
SecureRandom rng2;
|
||||
try {
|
||||
rng2 = SecureRandom.getInstance("SHA1PRNG");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new SecurityException("SHA1PRNG not available", e);
|
||||
}
|
||||
if (!LinuxPRNGSecureRandomProvider.class.equals(
|
||||
rng2.getProvider().getClass())) {
|
||||
throw new SecurityException(
|
||||
"SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
|
||||
+ " Provider: " + rng2.getProvider().getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code Provider} of {@code SecureRandom} engines which pass through
|
||||
* all requests to the Linux PRNG.
|
||||
*/
|
||||
private static class LinuxPRNGSecureRandomProvider extends Provider {
|
||||
|
||||
public LinuxPRNGSecureRandomProvider() {
|
||||
super("LinuxPRNG",
|
||||
1.0,
|
||||
"A Linux-specific random number provider that uses"
|
||||
+ " /dev/urandom");
|
||||
// Although /dev/urandom is not a SHA-1 PRNG, some apps
|
||||
// explicitly request a SHA1PRNG SecureRandom and we thus need to
|
||||
// prevent them from getting the default implementation whose output
|
||||
// may have low entropy.
|
||||
put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom.class.getName());
|
||||
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link SecureRandomSpi} which passes all requests to the Linux PRNG
|
||||
* ({@code /dev/urandom}).
|
||||
*/
|
||||
public static class LinuxPRNGSecureRandom extends SecureRandomSpi {
|
||||
|
||||
/*
|
||||
* IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed
|
||||
* are passed through to the Linux PRNG (/dev/urandom). Instances of
|
||||
* this class seed themselves by mixing in the current time, PID, UID,
|
||||
* build fingerprint, and hardware serial number (where available) into
|
||||
* Linux PRNG.
|
||||
*
|
||||
* Concurrency: Read requests to the underlying Linux PRNG are
|
||||
* serialized (on sLock) to ensure that multiple threads do not get
|
||||
* duplicated PRNG output.
|
||||
*/
|
||||
|
||||
private static final File URANDOM_FILE = new File("/dev/urandom");
|
||||
|
||||
private static final Object sLock = new Object();
|
||||
|
||||
/**
|
||||
* Input stream for reading from Linux PRNG or {@code null} if not yet
|
||||
* opened.
|
||||
*
|
||||
* @GuardedBy("sLock")
|
||||
*/
|
||||
private static DataInputStream sUrandomIn;
|
||||
|
||||
/**
|
||||
* Output stream for writing to Linux PRNG or {@code null} if not yet
|
||||
* opened.
|
||||
*
|
||||
* @GuardedBy("sLock")
|
||||
*/
|
||||
private static OutputStream sUrandomOut;
|
||||
|
||||
/**
|
||||
* Whether this engine instance has been seeded. This is needed because
|
||||
* each instance needs to seed itself if the client does not explicitly
|
||||
* seed it.
|
||||
*/
|
||||
private boolean mSeeded;
|
||||
|
||||
@Override
|
||||
protected void engineSetSeed(byte[] bytes) {
|
||||
try {
|
||||
OutputStream out;
|
||||
synchronized (sLock) {
|
||||
out = getUrandomOutputStream();
|
||||
}
|
||||
out.write(bytes);
|
||||
out.flush();
|
||||
} catch (IOException e) {
|
||||
// On a small fraction of devices /dev/urandom is not writable.
|
||||
// Log and ignore.
|
||||
Log.w(PRNGFixes.class.getSimpleName(),
|
||||
"Failed to mix seed into " + URANDOM_FILE);
|
||||
} finally {
|
||||
mSeeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineNextBytes(byte[] bytes) {
|
||||
if (!mSeeded) {
|
||||
// Mix in the device- and invocation-specific seed.
|
||||
engineSetSeed(generateSeed());
|
||||
}
|
||||
|
||||
try {
|
||||
DataInputStream in;
|
||||
synchronized (sLock) {
|
||||
in = getUrandomInputStream();
|
||||
}
|
||||
synchronized (in) {
|
||||
in.readFully(bytes);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new SecurityException(
|
||||
"Failed to read from " + URANDOM_FILE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] engineGenerateSeed(int size) {
|
||||
byte[] seed = new byte[size];
|
||||
engineNextBytes(seed);
|
||||
return seed;
|
||||
}
|
||||
|
||||
private DataInputStream getUrandomInputStream() {
|
||||
synchronized (sLock) {
|
||||
if (sUrandomIn == null) {
|
||||
// NOTE: Consider inserting a BufferedInputStream between
|
||||
// DataInputStream and FileInputStream if you need higher
|
||||
// PRNG output performance and can live with future PRNG
|
||||
// output being pulled into this process prematurely.
|
||||
try {
|
||||
sUrandomIn = new DataInputStream(
|
||||
new FileInputStream(URANDOM_FILE));
|
||||
} catch (IOException e) {
|
||||
throw new SecurityException("Failed to open "
|
||||
+ URANDOM_FILE + " for reading", e);
|
||||
}
|
||||
}
|
||||
return sUrandomIn;
|
||||
}
|
||||
}
|
||||
|
||||
private OutputStream getUrandomOutputStream() throws IOException {
|
||||
synchronized (sLock) {
|
||||
if (sUrandomOut == null) {
|
||||
sUrandomOut = new FileOutputStream(URANDOM_FILE);
|
||||
}
|
||||
return sUrandomOut;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a device- and invocation-specific seed to be mixed into the
|
||||
* Linux PRNG.
|
||||
*/
|
||||
private static byte[] generateSeed() {
|
||||
try {
|
||||
ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
|
||||
DataOutputStream seedBufferOut =
|
||||
new DataOutputStream(seedBuffer);
|
||||
seedBufferOut.writeLong(System.currentTimeMillis());
|
||||
seedBufferOut.writeLong(System.nanoTime());
|
||||
seedBufferOut.writeInt(Process.myPid());
|
||||
seedBufferOut.writeInt(Process.myUid());
|
||||
seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL);
|
||||
seedBufferOut.close();
|
||||
return seedBuffer.toByteArray();
|
||||
} catch (IOException e) {
|
||||
throw new SecurityException("Failed to generate seed", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hardware serial number of this device.
|
||||
*
|
||||
* @return serial number or {@code null} if not available.
|
||||
*/
|
||||
private static String getDeviceSerialNumber() {
|
||||
// We're using the Reflection API because Build.SERIAL is only available
|
||||
// since API Level 9 (Gingerbread, Android 2.3).
|
||||
try {
|
||||
return (String) Build.class.getField("SERIAL").get(null);
|
||||
} catch (Exception ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] getBuildFingerprintAndDeviceSerial() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
String fingerprint = Build.FINGERPRINT;
|
||||
if (fingerprint != null) {
|
||||
result.append(fingerprint);
|
||||
}
|
||||
String serial = getDeviceSerialNumber();
|
||||
if (serial != null) {
|
||||
result.append(serial);
|
||||
}
|
||||
try {
|
||||
return result.toString().getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("UTF-8 encoding not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -584,7 +584,7 @@ public class AttachmentManager {
|
||||
final Intent intent = new Intent();
|
||||
intent.setType(type);
|
||||
|
||||
if (extraMimeType != null && Build.VERSION.SDK_INT >= 19) {
|
||||
if (extraMimeType != null) {
|
||||
intent.putExtra(Intent.EXTRA_MIME_TYPES, extraMimeType);
|
||||
}
|
||||
|
||||
@@ -596,14 +596,12 @@ public class AttachmentManager {
|
||||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
|
||||
try {
|
||||
activity.startActivityForResult(intent, requestCode);
|
||||
return;
|
||||
} catch (ActivityNotFoundException anfe) {
|
||||
Log.w(TAG, "couldn't complete ACTION_OPEN_DOCUMENT, no activity found. falling back.");
|
||||
}
|
||||
intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
|
||||
try {
|
||||
activity.startActivityForResult(intent, requestCode);
|
||||
return;
|
||||
} catch (ActivityNotFoundException anfe) {
|
||||
Log.w(TAG, "couldn't complete ACTION_OPEN_DOCUMENT, no activity found. falling back.");
|
||||
}
|
||||
|
||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||
|
||||
@@ -20,16 +20,19 @@ import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.core.app.RemoteInput;
|
||||
import androidx.core.app.TaskStackBuilder;
|
||||
|
||||
import com.b44t.messenger.DcChat;
|
||||
import com.b44t.messenger.DcContact;
|
||||
import com.b44t.messenger.DcContext;
|
||||
import com.b44t.messenger.DcMsg;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.ConversationActivity;
|
||||
import org.thoughtcrime.securesms.ConversationListActivity;
|
||||
@@ -40,6 +43,8 @@ import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPrefere
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.thoughtcrime.securesms.util.IntentUtils;
|
||||
import org.thoughtcrime.securesms.util.JsonUtils;
|
||||
import org.thoughtcrime.securesms.util.Pair;
|
||||
import org.thoughtcrime.securesms.util.Prefs;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
@@ -54,6 +59,7 @@ public class NotificationCenter {
|
||||
private static final String TAG = NotificationCenter.class.getSimpleName();
|
||||
@NonNull private final ApplicationContext context;
|
||||
private volatile ChatData visibleChat = null;
|
||||
private volatile Pair<Integer, Integer> visibleWebxdc = null;
|
||||
private volatile long lastAudibleNotification = 0;
|
||||
private static final long MIN_AUDIBLE_PERIOD_MILLIS = TimeUnit.SECONDS.toMillis(2);
|
||||
|
||||
@@ -331,17 +337,90 @@ public class NotificationCenter {
|
||||
// add notifications & co.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
public void addNotification(int accountId, int chatId, int msgId) {
|
||||
Util.runOnAnyBackgroundThread(() -> {
|
||||
public void notifyMessage(int accountId, int chatId, int msgId) {
|
||||
Util.runOnAnyBackgroundThread(() -> {
|
||||
DcContext dcContext = context.dcAccounts.getAccount(accountId);
|
||||
DcChat dcChat = dcContext.getChat(chatId);
|
||||
|
||||
DcMsg dcMsg = dcContext.getMsg(msgId);
|
||||
NotificationPrivacyPreference privacy = Prefs.getNotificationPrivacy(context);
|
||||
|
||||
String shortLine = privacy.isDisplayMessage()? dcMsg.getSummarytext(2000) : context.getString(R.string.notify_new_message);
|
||||
if (dcChat.isMultiUser() && privacy.isDisplayContact()) {
|
||||
shortLine = dcMsg.getSenderName(dcContext.getContact(dcMsg.getFromId())) + ": " + shortLine;
|
||||
}
|
||||
String tickerLine = shortLine;
|
||||
if (!dcChat.isMultiUser() && privacy.isDisplayContact()) {
|
||||
tickerLine = dcMsg.getSenderName(dcContext.getContact(dcMsg.getFromId())) + ": " + tickerLine;
|
||||
|
||||
if (dcMsg.getOverrideSenderName() != null) {
|
||||
// There is an "overridden" display name on the message, so, we need to prepend the display name to the message,
|
||||
// i.e. set the shortLine to be the same as the tickerLine.
|
||||
shortLine = tickerLine;
|
||||
}
|
||||
}
|
||||
|
||||
DcMsg quotedMsg = dcMsg.getQuotedMsg();
|
||||
boolean isMention = dcChat.isMultiUser() && quotedMsg != null && quotedMsg.isOutgoing();
|
||||
|
||||
maybeAddNotification(accountId, dcChat, msgId, shortLine, tickerLine, true, isMention);
|
||||
});
|
||||
}
|
||||
|
||||
public void notifyReaction(int accountId, int contactId, int msgId, String reaction) {
|
||||
Util.runOnAnyBackgroundThread(() -> {
|
||||
DcContext dcContext = context.dcAccounts.getAccount(accountId);
|
||||
DcMsg dcMsg = dcContext.getMsg(msgId);
|
||||
|
||||
NotificationPrivacyPreference privacy = Prefs.getNotificationPrivacy(context);
|
||||
if (!privacy.isDisplayContact() || !privacy.isDisplayMessage()) {
|
||||
return; // showing "New Message" is wrong and showing "New Reaction" is already content. just do nothing.
|
||||
}
|
||||
|
||||
DcContact sender = dcContext.getContact(contactId);
|
||||
String shortLine = context.getString(R.string.reaction_by_other, sender.getDisplayName(), reaction, dcMsg.getSummarytext(2000));
|
||||
DcChat dcChat = dcContext.getChat(dcMsg.getChatId());
|
||||
maybeAddNotification(accountId, dcChat, msgId, shortLine, shortLine, false, dcChat.isMultiUser());
|
||||
});
|
||||
}
|
||||
|
||||
public void notifyWebxdc(int accountId, int contactId, int msgId, String text) {
|
||||
Util.runOnAnyBackgroundThread(() -> {
|
||||
NotificationPrivacyPreference privacy = Prefs.getNotificationPrivacy(context);
|
||||
if (!privacy.isDisplayContact() || !privacy.isDisplayMessage()) {
|
||||
return; // showing "New Message" is wrong, just do nothing.
|
||||
}
|
||||
|
||||
DcContext dcContext = context.dcAccounts.getAccount(accountId);
|
||||
DcMsg dcMsg = dcContext.getMsg(msgId);
|
||||
DcMsg parentMsg;
|
||||
if(dcMsg.getType() == DcMsg.DC_MSG_WEBXDC) {
|
||||
parentMsg = dcMsg;
|
||||
} else { // info message, get parent xdc
|
||||
parentMsg = dcMsg.getParent() != null? dcMsg.getParent() : dcMsg;
|
||||
}
|
||||
|
||||
if (Util.equals(visibleWebxdc, new Pair<>(accountId, parentMsg.getId()))) {
|
||||
return; // do not notify if the app is already open
|
||||
}
|
||||
|
||||
JSONObject info = parentMsg.getWebxdcInfo();
|
||||
final String name = JsonUtils.optString(info, "name");
|
||||
String shortLine = name.isEmpty()? text : (name + ": " + text);
|
||||
DcChat dcChat = dcContext.getChat(dcMsg.getChatId());
|
||||
maybeAddNotification(accountId, dcChat, msgId, shortLine, shortLine, false, dcChat.isMultiUser());
|
||||
});
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private void maybeAddNotification(int accountId, DcChat dcChat, int msgId, String shortLine, String tickerLine, boolean playInChatSound, boolean isMention) {
|
||||
|
||||
DcContext dcContext = context.dcAccounts.getAccount(accountId);
|
||||
DcChat dcChat = dcContext.getChat(chatId);
|
||||
DcMsg dcMsg = dcContext.getMsg(msgId);
|
||||
DcMsg quotedMsg = dcMsg.getQuotedMsg();
|
||||
boolean isGroupMention = quotedMsg != null && dcChat.isMultiUser() && quotedMsg.isOutgoing();
|
||||
int chatId = dcChat.getId();
|
||||
ChatData chatData = new ChatData(accountId, chatId);
|
||||
isMention = isMention && dcContext.isMentionsEnabled();
|
||||
|
||||
if (dcContext.isMuted() || (!isGroupMention && dcChat.isMuted())) {
|
||||
if (dcContext.isMuted() || (!isMention && dcChat.isMuted())) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -351,21 +430,13 @@ public class NotificationCenter {
|
||||
}
|
||||
|
||||
if (Util.equals(visibleChat, chatData)) {
|
||||
if (Prefs.isInChatNotifications(context)) {
|
||||
if (playInChatSound && Prefs.isInChatNotifications(context)) {
|
||||
InChatSounds.getInstance(context).playIncomingSound();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// get notification text as a single line
|
||||
NotificationPrivacyPreference privacy = Prefs.getNotificationPrivacy(context);
|
||||
|
||||
String line = privacy.isDisplayMessage()? dcMsg.getSummarytext(2000) : context.getString(R.string.notify_new_message);
|
||||
if (dcChat.isMultiUser() && privacy.isDisplayContact()) {
|
||||
line = dcMsg.getSenderName(dcContext.getContact(dcMsg.getFromId()), false) + ": " + line;
|
||||
}
|
||||
|
||||
// play signal?
|
||||
long now = System.currentTimeMillis();
|
||||
boolean signal = (now - lastAudibleNotification) > MIN_AUDIBLE_PERIOD_MILLIS;
|
||||
if (signal) {
|
||||
@@ -383,7 +454,7 @@ public class NotificationCenter {
|
||||
.setPriority(Prefs.getNotificationPriority(context))
|
||||
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
|
||||
.setOnlyAlertOnce(!signal)
|
||||
.setContentText(line)
|
||||
.setContentText(shortLine)
|
||||
.setDeleteIntent(getMarkAsReadIntent(chatData, msgId, false))
|
||||
.setContentIntent(getOpenChatIntent(chatData));
|
||||
|
||||
@@ -403,12 +474,6 @@ public class NotificationCenter {
|
||||
}
|
||||
}
|
||||
|
||||
// if privacy allows, for better accessibility,
|
||||
// prepend the sender in the ticker also for one-to-one chats (for group-chats, this is already done)
|
||||
String tickerLine = line;
|
||||
if (!dcChat.isMultiUser() && privacy.isDisplayContact()) {
|
||||
line = dcMsg.getSenderName(dcContext.getContact(dcMsg.getFromId()), false) + ": " + line;
|
||||
}
|
||||
builder.setTicker(tickerLine);
|
||||
|
||||
// set sound, vibrate, led for systems that do not have notification channels
|
||||
@@ -504,9 +569,9 @@ public class NotificationCenter {
|
||||
lines = new ArrayList<>();
|
||||
accountInbox.put(chatId, lines);
|
||||
}
|
||||
lines.add(line);
|
||||
lines.add(shortLine);
|
||||
|
||||
for (int l = lines.size() - 1; l >= 0; l--) {
|
||||
for (int l = 0; l < lines.size(); l++) {
|
||||
inboxStyle.addLine(lines.get(l));
|
||||
}
|
||||
}
|
||||
@@ -552,7 +617,6 @@ public class NotificationCenter {
|
||||
Log.e(TAG, "cannot add notification summary", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void removeNotifications(int accountId, int chatId) {
|
||||
@@ -610,6 +674,22 @@ public class NotificationCenter {
|
||||
visibleChat = null;
|
||||
}
|
||||
|
||||
public void updateVisibleWebxdc(int accountId, int msgId) {
|
||||
Util.runOnAnyBackgroundThread(() -> {
|
||||
|
||||
if (accountId != 0 && msgId != 0) {
|
||||
visibleWebxdc = new Pair<>(accountId, msgId);
|
||||
} else {
|
||||
visibleWebxdc = null;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public void clearVisibleWebxdc() {
|
||||
visibleWebxdc = null;
|
||||
}
|
||||
|
||||
public void maybePlaySendSound(DcChat dcChat) {
|
||||
if (Prefs.isInChatNotifications(context) && !dcChat.isMuted()) {
|
||||
InChatSounds.getInstance(context).playSendSound();
|
||||
|
||||
@@ -69,7 +69,6 @@ public class RemoteReplyReceiver extends BroadcastReceiver {
|
||||
|
||||
DcMsg msg = new DcMsg(dcContext, DcMsg.DC_MSG_TEXT);
|
||||
msg.setText(responseText.toString());
|
||||
msg.setQuote(dcContext.getMsg(msgId));
|
||||
dcContext.sendMsg(chatId, msg);
|
||||
|
||||
DcHelper.getNotificationCenter(context).removeNotifications(accountId, chatId);
|
||||
|
||||
+1
-3
@@ -136,9 +136,7 @@ public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment
|
||||
|
||||
Preference developerModeEnabled = this.findPreference("pref_developer_mode_enabled");
|
||||
developerModeEnabled.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
WebView.setWebContentsDebuggingEnabled((Boolean) newValue);
|
||||
}
|
||||
WebView.setWebContentsDebuggingEnabled((Boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
@@ -23,8 +23,6 @@ public class AppearancePreferenceFragment extends ListSummaryPreferenceFragment
|
||||
this.findPreference(Prefs.THEME_PREF).setOnPreferenceChangeListener(new ListSummaryListener());
|
||||
initializeListSummary((ListPreference)findPreference(Prefs.THEME_PREF));
|
||||
this.findPreference(Prefs.BACKGROUND_PREF).setOnPreferenceClickListener(new BackgroundClickListener());
|
||||
this.findPreference(Prefs.MESSAGE_BODY_TEXT_SIZE_PREF).setOnPreferenceChangeListener(new ListSummaryListener());
|
||||
initializeListSummary((ListPreference) findPreference(Prefs.MESSAGE_BODY_TEXT_SIZE_PREF));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+9
@@ -36,6 +36,7 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
|
||||
|
||||
private CheckBoxPreference ignoreBattery;
|
||||
private CheckBoxPreference notificationsEnabled;
|
||||
private CheckBoxPreference mentionNotifEnabled;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle paramBundle) {
|
||||
@@ -99,6 +100,13 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
|
||||
dcContext.setMuted(!enabled);
|
||||
return true;
|
||||
});
|
||||
|
||||
mentionNotifEnabled = this.findPreference("pref_enable_mention_notifications");
|
||||
mentionNotifEnabled.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
boolean enabled = (Boolean) newValue;
|
||||
dcContext.setMentionsEnabled(enabled);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -114,6 +122,7 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
|
||||
// update ignoreBattery in onResume() to reflects changes done in the system settings
|
||||
ignoreBattery.setChecked(isIgnoringBatteryOptimizations());
|
||||
notificationsEnabled.setChecked(!dcContext.isMuted());
|
||||
mentionNotifEnabled.setChecked(dcContext.isMentionsEnabled());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,7 +26,6 @@ public class ProfilePreference extends Preference {
|
||||
private TextView profileNameView;
|
||||
private TextView profileAddressView;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public ProfilePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
initialize();
|
||||
|
||||
@@ -63,15 +63,10 @@ public class AvatarHelper {
|
||||
}
|
||||
|
||||
public static void cropAvatar(Activity context, Uri imageUri) {
|
||||
if (Build.VERSION.SDK_INT >= 19) { // Image editor requires Android 4.4 KitKat or newer.
|
||||
Intent intent = new Intent(context, ScribbleActivity.class);
|
||||
intent.setData(imageUri);
|
||||
intent.putExtra(ScribbleActivity.CROP_AVATAR, true);
|
||||
context.startActivityForResult(intent, ScribbleActivity.SCRIBBLE_REQUEST_CODE);
|
||||
} else {
|
||||
Uri outputFile = Uri.fromFile(new File(context.getCacheDir(), "cropped"));
|
||||
Crop.of(imageUri, outputFile).asSquare().start(context);
|
||||
}
|
||||
Intent intent = new Intent(context, ScribbleActivity.class);
|
||||
intent.setData(imageUri);
|
||||
intent.putExtra(ScribbleActivity.CROP_AVATAR, true);
|
||||
context.startActivityForResult(intent, ScribbleActivity.SCRIBBLE_REQUEST_CODE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -98,6 +98,9 @@ public class QrCodeHandler {
|
||||
dcContext.restartIo();
|
||||
showDoneToast(activity);
|
||||
});
|
||||
if (rawString.toLowerCase().startsWith("http")) {
|
||||
builder.setNeutralButton(R.string.open, (d, b) -> IntentUtils.showBrowserIntent(activity, rawString));
|
||||
}
|
||||
builder.setNegativeButton(R.string.cancel, null);
|
||||
builder.setCancelable(false);
|
||||
break;
|
||||
|
||||
@@ -119,38 +119,11 @@ public class QrShowFragment extends Fragment implements DcEventCenter.DcEventDel
|
||||
return svg.replace("y=\"281.136\"", "y=\"296\"");
|
||||
}
|
||||
|
||||
public void shareQr() {
|
||||
try {
|
||||
File file = new File(requireActivity().getExternalCacheDir(), "share-qr.png");
|
||||
file.createNewFile();
|
||||
file.setReadable(true, false);
|
||||
FileOutputStream stream = new FileOutputStream(file);
|
||||
Bitmap bitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
canvas.drawARGB(0,255, 255, 255); // Clear background
|
||||
SVG svg = SVG.getFromString(fixSVG(dcContext.getSecurejoinQrSvg(chatId)));
|
||||
svg.renderToCanvas(canvas);
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 90, stream);
|
||||
stream.flush();
|
||||
stream.close();
|
||||
Uri uri = FileProviderUtil.getUriFor(requireActivity(), file);
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.setType("image/png");
|
||||
intent.putExtra(Intent.EXTRA_STREAM, uri);
|
||||
String inviteURL = Util.QrDataToInviteURL(dcContext.getSecurejoinQr(chatId));
|
||||
intent.putExtra(Intent.EXTRA_TEXT, inviteURL);
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.chat_share_with_title)));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "failed to share QR", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void shareInviteURL() {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.setType("text/plain");
|
||||
String inviteURL = Util.QrDataToInviteURL(dcContext.getSecurejoinQr(chatId));
|
||||
String inviteURL = dcContext.getSecurejoinQr(chatId);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, inviteURL);
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.chat_share_with_title)));
|
||||
} catch (Exception e) {
|
||||
@@ -159,7 +132,7 @@ public class QrShowFragment extends Fragment implements DcEventCenter.DcEventDel
|
||||
}
|
||||
|
||||
public void copyQrData() {
|
||||
String inviteURL = Util.QrDataToInviteURL(dcContext.getSecurejoinQr(chatId));
|
||||
String inviteURL = dcContext.getSecurejoinQr(chatId);
|
||||
Util.writeTextToClipboard(getActivity(), inviteURL);
|
||||
Toast.makeText(getActivity(), getString(R.string.copied_to_clipboard), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
@@ -179,7 +152,7 @@ public class QrShowFragment extends Fragment implements DcEventCenter.DcEventDel
|
||||
|
||||
public void showInviteLinkDialog() {
|
||||
View view = View.inflate(getActivity(), R.layout.dialog_share_invite_link, null);
|
||||
String inviteURL = Util.QrDataToInviteURL(dcContext.getSecurejoinQr(chatId));
|
||||
String inviteURL = dcContext.getSecurejoinQr(chatId);
|
||||
((TextView)view.findViewById(R.id.invite_link)).setText(inviteURL);
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setView(view)
|
||||
|
||||
@@ -59,9 +59,6 @@ public class AddReactionView extends LinearLayout {
|
||||
}
|
||||
anyReactionView = findViewById(R.id.reaction_any);
|
||||
anyReactionView.setOnClickListener(v -> anyReactionClicked());
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
anyReactionView.setVisibility(View.GONE); // EmojiPickerView requires SDK 21 or newer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,11 +94,7 @@ public final class ImageEditorHud extends LinearLayout {
|
||||
}
|
||||
|
||||
private void initializeVisibilityMap() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
setVisibleViewsWhenInMode(Mode.NONE, drawButton, highlightButton, blurButton, textButton, cropButton, undoButton, saveButton);
|
||||
} else {
|
||||
setVisibleViewsWhenInMode(Mode.NONE, drawButton, highlightButton, textButton, cropButton, undoButton, saveButton);
|
||||
}
|
||||
setVisibleViewsWhenInMode(Mode.NONE, drawButton, highlightButton, blurButton, textButton, cropButton, undoButton, saveButton);
|
||||
|
||||
setVisibleViewsWhenInMode(Mode.DRAW, confirmButton, undoButton, colorPicker, colorPalette);
|
||||
|
||||
|
||||
@@ -117,16 +117,13 @@ final class UriGlideRenderer implements Renderer {
|
||||
|
||||
rendererContext.restore();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
renderBlurOverlay(rendererContext);
|
||||
}
|
||||
renderBlurOverlay(rendererContext);
|
||||
} else if (rendererContext.isBlockingLoad()) {
|
||||
// If failed to load, we draw a black out, in case image was sticker positioned to cover private info.
|
||||
rendererContext.canvas.drawRect(Bounds.FULL_BOUNDS, paint);
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(17)
|
||||
private void renderBlurOverlay(RendererContext rendererContext) {
|
||||
boolean renderMask = false;
|
||||
|
||||
|
||||
-1
@@ -98,7 +98,6 @@ public class VerticalSlideColorPicker extends View {
|
||||
init();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public VerticalSlideColorPicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
init();
|
||||
|
||||
@@ -15,10 +15,6 @@ public final class AccessibilityUtil {
|
||||
Log.e("AccessibilityUtil", "animationsDisabled: context was null");
|
||||
return false;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
return Settings.Global.getFloat(context.getContentResolver(), Settings.Global.ANIMATOR_DURATION_SCALE, 1) == 0f;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return Settings.Global.getFloat(context.getContentResolver(), Settings.Global.ANIMATOR_DURATION_SCALE, 1) == 0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,11 +110,7 @@ public class DateUtils extends android.text.format.DateUtils {
|
||||
}
|
||||
|
||||
private static String getLocalizedPattern(String template) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
return DateFormat.getBestDateTimePattern(Util.getLocale(), template);
|
||||
} else {
|
||||
return new SimpleDateFormat(template, Util.getLocale()).toLocalizedPattern();
|
||||
}
|
||||
return DateFormat.getBestDateTimePattern(Util.getLocale(), template);
|
||||
}
|
||||
|
||||
public static String getFormatedDuration(long millis) {
|
||||
|
||||
@@ -16,8 +16,7 @@ public class FileProviderUtil {
|
||||
private static final String AUTHORITY = BuildConfig.APPLICATION_ID+".fileprovider";
|
||||
|
||||
public static Uri getUriFor(@NonNull Context context, @NonNull File file) throws IllegalStateException, NullPointerException {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) return FileProvider.getUriForFile(context, AUTHORITY, file);
|
||||
else return Uri.fromFile(file);
|
||||
return FileProvider.getUriForFile(context, AUTHORITY, file);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ public class Prefs {
|
||||
public static final String DOZE_ASKED_DIRECTLY = "pref_doze_asked_directly";
|
||||
public static final String ASKED_FOR_NOTIFICATION_PERMISSION= "pref_asked_for_notification_permission";
|
||||
private static final String IN_THREAD_NOTIFICATION_PREF = "pref_key_inthread_notifications";
|
||||
public static final String MESSAGE_BODY_TEXT_SIZE_PREF = "pref_message_body_text_size";
|
||||
|
||||
public static final String NOTIFICATION_PRIVACY_PREF = "pref_notification_privacy";
|
||||
public static final String NOTIFICATION_PRIORITY_PREF = "pref_notification_priority";
|
||||
@@ -104,10 +103,6 @@ public class Prefs {
|
||||
return Integer.valueOf(getStringPreference(context, NOTIFICATION_PRIORITY_PREF, String.valueOf(NotificationCompat.PRIORITY_HIGH)));
|
||||
}
|
||||
|
||||
public static int getMessageBodyTextSize(Context context) {
|
||||
return Integer.valueOf(getStringPreference(context, MESSAGE_BODY_TEXT_SIZE_PREF, "16"));
|
||||
}
|
||||
|
||||
public static void setDirectCaptureCameraId(Context context, int value) {
|
||||
setIntegerPreference(context, DIRECT_CAPTURE_CAMERA_ID, value);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ public class ScreenLockUtil {
|
||||
public static boolean applyScreenLock(Activity activity, String title, String descr, int requestCode) {
|
||||
KeyguardManager keyguardManager = (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
|
||||
Intent intent;
|
||||
if (keyguardManager != null && isScreenLockAvailable()) {
|
||||
if (keyguardManager != null) {
|
||||
intent = keyguardManager.createConfirmDeviceCredentialIntent(title, descr);
|
||||
if (intent != null) {
|
||||
activity.startActivityForResult(intent, requestCode);
|
||||
@@ -22,7 +22,4 @@ public class ScreenLockUtil {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isScreenLockAvailable() {
|
||||
return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,28 +20,16 @@ public class StorageUtil {
|
||||
}
|
||||
|
||||
public static @NonNull Uri getVideoUri() {
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
return getLegacyUri(Environment.DIRECTORY_MOVIES);
|
||||
} else {
|
||||
return MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||
}
|
||||
return MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||
}
|
||||
|
||||
public static @NonNull
|
||||
Uri getAudioUri() {
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
return getLegacyUri(Environment.DIRECTORY_MUSIC);
|
||||
} else {
|
||||
return MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
}
|
||||
return MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
}
|
||||
|
||||
public static @NonNull Uri getImageUri() {
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
return getLegacyUri(Environment.DIRECTORY_PICTURES);
|
||||
} else {
|
||||
return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
}
|
||||
return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
}
|
||||
|
||||
public static @NonNull Uri getDownloadUri() {
|
||||
|
||||
@@ -89,10 +89,6 @@ public class Util {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String QrDataToInviteURL(String qrData) {
|
||||
return "https://" + INVITE_DOMAIN + "/#" + qrData.split(":", 2)[1].replaceFirst("#", "&");
|
||||
}
|
||||
|
||||
public static CharSequence getBoldedString(String value) {
|
||||
SpannableString spanned = new SpannableString(value);
|
||||
spanned.setSpan(new StyleSpan(Typeface.BOLD), 0,
|
||||
@@ -261,12 +257,10 @@ public class Util {
|
||||
return Arrays.hashCode(objects);
|
||||
}
|
||||
|
||||
@TargetApi(VERSION_CODES.KITKAT)
|
||||
public static boolean isLowMemory(Context context) {
|
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
|
||||
return (VERSION.SDK_INT >= VERSION_CODES.KITKAT && activityManager.isLowRamDevice()) ||
|
||||
activityManager.getLargeMemoryClass() <= 64;
|
||||
return activityManager.isLowRamDevice() || activityManager.getLargeMemoryClass() <= 64;
|
||||
}
|
||||
|
||||
public static int clamp(int value, int min, int max) {
|
||||
@@ -390,10 +384,7 @@ public class Util {
|
||||
}
|
||||
|
||||
public static int getLayoutDirection(Context context) {
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
Configuration configuration = context.getResources().getConfiguration();
|
||||
return configuration.getLayoutDirection();
|
||||
}
|
||||
return ViewCompat.LAYOUT_DIRECTION_LTR;
|
||||
Configuration configuration = context.getResources().getConfiguration();
|
||||
return configuration.getLayoutDirection();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,9 @@ import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -166,18 +168,15 @@ public class ViewUtil {
|
||||
|
||||
@SuppressLint("RtlHardcoded")
|
||||
public static void setTextViewGravityStart(final @NonNull TextView textView, @NonNull Context context) {
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
if (Util.getLayoutDirection(context) == View.LAYOUT_DIRECTION_RTL) {
|
||||
textView.setGravity(Gravity.RIGHT);
|
||||
} else {
|
||||
textView.setGravity(Gravity.LEFT);
|
||||
}
|
||||
if (Util.getLayoutDirection(context) == View.LAYOUT_DIRECTION_RTL) {
|
||||
textView.setGravity(Gravity.RIGHT);
|
||||
} else {
|
||||
textView.setGravity(Gravity.LEFT);
|
||||
}
|
||||
}
|
||||
|
||||
public static void mirrorIfRtl(View view, Context context) {
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1 &&
|
||||
Util.getLayoutDirection(context) == View.LAYOUT_DIRECTION_RTL) {
|
||||
if (Util.getLayoutDirection(context) == View.LAYOUT_DIRECTION_RTL) {
|
||||
view.setScaleX(-1.0f);
|
||||
}
|
||||
}
|
||||
@@ -202,6 +201,18 @@ public class ViewUtil {
|
||||
return (int)((dp * context.getResources().getDisplayMetrics().density) + 0.5);
|
||||
}
|
||||
|
||||
public static float pxToSp(Context context, int px) {
|
||||
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
return TypedValue.deriveDimension(TypedValue.COMPLEX_UNIT_SP, px, metrics);
|
||||
} else {
|
||||
if (metrics.scaledDensity == 0) {
|
||||
return 0;
|
||||
}
|
||||
return px / metrics.scaledDensity;
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateLayoutParams(@NonNull View view, int width, int height) {
|
||||
view.getLayoutParams().width = width;
|
||||
view.getLayoutParams().height = height;
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
package org.thoughtcrime.securesms.video.recode;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.media.MediaCodec;
|
||||
import android.media.MediaCodecInfo;
|
||||
import android.media.MediaCodecList;
|
||||
import android.media.MediaExtractor;
|
||||
import android.media.MediaFormat;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
@@ -36,14 +32,7 @@ public class VideoRecoder {
|
||||
|
||||
private static final String TAG = VideoRecoder.class.getSimpleName();
|
||||
|
||||
private boolean videoConvertFirstWrite = true;
|
||||
private final static String MIME_TYPE = "video/avc";
|
||||
private final static int PROCESSOR_TYPE_OTHER = 0;
|
||||
private final static int PROCESSOR_TYPE_QCOM = 1;
|
||||
private final static int PROCESSOR_TYPE_INTEL = 2;
|
||||
private final static int PROCESSOR_TYPE_MTK = 3;
|
||||
private final static int PROCESSOR_TYPE_SEC = 4;
|
||||
private final static int PROCESSOR_TYPE_TI = 5;
|
||||
private final boolean cancelCurrentVideoConversion = false;
|
||||
private final Object videoConvertSync = new Object();
|
||||
|
||||
@@ -57,7 +46,6 @@ public class VideoRecoder {
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(16)
|
||||
private int selectTrack(MediaExtractor extractor, boolean audio) {
|
||||
int numTracks = extractor.getTrackCount();
|
||||
for (int i = 0; i < numTracks; i++) {
|
||||
@@ -76,60 +64,6 @@ public class VideoRecoder {
|
||||
return -5;
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private static MediaCodecInfo selectCodec(String mimeType) {
|
||||
int numCodecs = MediaCodecList.getCodecCount();
|
||||
MediaCodecInfo lastCodecInfo = null;
|
||||
for (int i = 0; i < numCodecs; i++) {
|
||||
MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
|
||||
if (!codecInfo.isEncoder()) {
|
||||
continue;
|
||||
}
|
||||
String[] types = codecInfo.getSupportedTypes();
|
||||
for (String type : types) {
|
||||
if (type.equalsIgnoreCase(mimeType)) {
|
||||
lastCodecInfo = codecInfo;
|
||||
if (!lastCodecInfo.getName().equals("OMX.SEC.avc.enc")) {
|
||||
return lastCodecInfo;
|
||||
} else if (lastCodecInfo.getName().equals("OMX.SEC.AVC.Encoder")) {
|
||||
return lastCodecInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return lastCodecInfo;
|
||||
}
|
||||
|
||||
private static boolean isRecognizedFormat(int colorFormat) {
|
||||
switch (colorFormat) {
|
||||
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
|
||||
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
|
||||
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
|
||||
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
|
||||
case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private static int selectColorFormat(MediaCodecInfo codecInfo, String mimeType) {
|
||||
MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
|
||||
int lastColorFormat = 0;
|
||||
for (int i = 0; i < capabilities.colorFormats.length; i++) {
|
||||
int colorFormat = capabilities.colorFormats[i];
|
||||
if (isRecognizedFormat(colorFormat)) {
|
||||
lastColorFormat = colorFormat;
|
||||
if (!(codecInfo.getName().equals("OMX.SEC.AVC.Encoder") && colorFormat == 19)) {
|
||||
return colorFormat;
|
||||
}
|
||||
}
|
||||
}
|
||||
return lastColorFormat;
|
||||
}
|
||||
|
||||
@TargetApi(16)
|
||||
private long readAndWriteTrack(MediaExtractor extractor, MP4Builder mediaMuxer, MediaCodec.BufferInfo info, long start, long end, File file, boolean isAudio) throws Exception {
|
||||
int trackIndex = selectTrack(extractor, isAudio);
|
||||
if (trackIndex >= 0) {
|
||||
@@ -199,7 +133,6 @@ public class VideoRecoder {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@TargetApi(16)
|
||||
private boolean convertVideo(final VideoEditedInfo videoEditedInfo, String destPath) {
|
||||
|
||||
long startTime = videoEditedInfo.startTime;
|
||||
@@ -214,29 +147,21 @@ public class VideoRecoder {
|
||||
int rotateRender = 0;
|
||||
File cacheFile = new File(destPath);
|
||||
|
||||
if (Build.VERSION.SDK_INT < 18 && resultHeight > resultWidth && resultWidth != originalWidth && resultHeight != originalHeight) {
|
||||
if (rotationValue == 90) {
|
||||
int temp = resultHeight;
|
||||
resultHeight = resultWidth;
|
||||
resultWidth = temp;
|
||||
rotationValue = 90;
|
||||
rotationValue = 0;
|
||||
rotateRender = 270;
|
||||
} else if (Build.VERSION.SDK_INT > 20) {
|
||||
if (rotationValue == 90) {
|
||||
int temp = resultHeight;
|
||||
resultHeight = resultWidth;
|
||||
resultWidth = temp;
|
||||
rotationValue = 0;
|
||||
rotateRender = 270;
|
||||
} else if (rotationValue == 180) {
|
||||
rotateRender = 180;
|
||||
rotationValue = 0;
|
||||
} else if (rotationValue == 270) {
|
||||
int temp = resultHeight;
|
||||
resultHeight = resultWidth;
|
||||
resultWidth = temp;
|
||||
rotationValue = 0;
|
||||
rotateRender = 90;
|
||||
}
|
||||
} else if (rotationValue == 180) {
|
||||
rotateRender = 180;
|
||||
rotationValue = 0;
|
||||
} else if (rotationValue == 270) {
|
||||
int temp = resultHeight;
|
||||
resultHeight = resultWidth;
|
||||
resultWidth = temp;
|
||||
rotationValue = 0;
|
||||
rotateRender = 90;
|
||||
}
|
||||
|
||||
File inputFile = new File(videoEditedInfo.originalPath);
|
||||
@@ -246,7 +171,6 @@ public class VideoRecoder {
|
||||
return false;
|
||||
}
|
||||
|
||||
videoConvertFirstWrite = true;
|
||||
boolean error = false;
|
||||
long videoStartTime = startTime;
|
||||
|
||||
@@ -282,71 +206,12 @@ public class VideoRecoder {
|
||||
boolean outputDone = false;
|
||||
boolean inputDone = false;
|
||||
boolean decoderDone = false;
|
||||
int swapUV = 0;
|
||||
int videoTrackIndex = -5;
|
||||
|
||||
int colorFormat;
|
||||
int processorType = PROCESSOR_TYPE_OTHER;
|
||||
String manufacturer = Build.MANUFACTURER.toLowerCase();
|
||||
if (Build.VERSION.SDK_INT < 18) {
|
||||
MediaCodecInfo codecInfo = selectCodec(MIME_TYPE);
|
||||
colorFormat = selectColorFormat(codecInfo, MIME_TYPE);
|
||||
if (colorFormat == 0) {
|
||||
throw new RuntimeException("no supported color format");
|
||||
}
|
||||
String codecName = codecInfo.getName();
|
||||
if (codecName.contains("OMX.qcom.")) {
|
||||
processorType = PROCESSOR_TYPE_QCOM;
|
||||
if (Build.VERSION.SDK_INT == 16) {
|
||||
if (manufacturer.equals("lge") || manufacturer.equals("nokia")) {
|
||||
swapUV = 1;
|
||||
}
|
||||
}
|
||||
} else if (codecName.contains("OMX.Intel.")) {
|
||||
processorType = PROCESSOR_TYPE_INTEL;
|
||||
} else if (codecName.equals("OMX.MTK.VIDEO.ENCODER.AVC")) {
|
||||
processorType = PROCESSOR_TYPE_MTK;
|
||||
} else if (codecName.equals("OMX.SEC.AVC.Encoder")) {
|
||||
processorType = PROCESSOR_TYPE_SEC;
|
||||
swapUV = 1;
|
||||
} else if (codecName.equals("OMX.TI.DUCATI1.VIDEO.H264E")) {
|
||||
processorType = PROCESSOR_TYPE_TI;
|
||||
}
|
||||
//Log.i("DeltaChat", "codec = " + codecInfo.getName() + " manufacturer = " + manufacturer + "device = " + Build.MODEL);
|
||||
} else {
|
||||
colorFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface;
|
||||
}
|
||||
colorFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface;
|
||||
//Log.i("DeltaChat", "colorFormat = " + colorFormat);
|
||||
|
||||
int resultHeightAligned = resultHeight;
|
||||
int padding = 0;
|
||||
int bufferSize = resultWidth * resultHeight * 3 / 2;
|
||||
if (processorType == PROCESSOR_TYPE_OTHER) {
|
||||
if (resultHeight % 16 != 0) {
|
||||
resultHeightAligned += (16 - (resultHeight % 16));
|
||||
padding = resultWidth * (resultHeightAligned - resultHeight);
|
||||
bufferSize += padding * 5 / 4;
|
||||
}
|
||||
} else if (processorType == PROCESSOR_TYPE_QCOM) {
|
||||
if (!manufacturer.toLowerCase().equals("lge")) {
|
||||
int uvoffset = (resultWidth * resultHeight + 2047) & ~2047;
|
||||
padding = uvoffset - (resultWidth * resultHeight);
|
||||
bufferSize += padding;
|
||||
}
|
||||
} else if (processorType == PROCESSOR_TYPE_TI) {
|
||||
//resultHeightAligned = 368;
|
||||
//bufferSize = resultWidth * resultHeightAligned * 3 / 2;
|
||||
//resultHeightAligned += (16 - (resultHeight % 16));
|
||||
//padding = resultWidth * (resultHeightAligned - resultHeight);
|
||||
//bufferSize += padding * 5 / 4;
|
||||
} else if (processorType == PROCESSOR_TYPE_MTK) {
|
||||
if (manufacturer.equals("baidu")) {
|
||||
resultHeightAligned += (16 - (resultHeight % 16));
|
||||
padding = resultWidth * (resultHeightAligned - resultHeight);
|
||||
bufferSize += padding * 5 / 4;
|
||||
}
|
||||
}
|
||||
|
||||
extractor.selectTrack(videoIndex);
|
||||
if (startTime > 0) {
|
||||
extractor.seekTo(startTime, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
|
||||
@@ -360,39 +225,21 @@ public class VideoRecoder {
|
||||
outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, resultVideoBitrate != 0 ? resultVideoBitrate : 921600);
|
||||
outputFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25);
|
||||
outputFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
|
||||
if (Build.VERSION.SDK_INT < 18) {
|
||||
outputFormat.setInteger("stride", resultWidth + 32);
|
||||
outputFormat.setInteger("slice-height", resultHeight);
|
||||
}
|
||||
|
||||
encoder = MediaCodec.createEncoderByType(MIME_TYPE);
|
||||
encoder.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
|
||||
if (Build.VERSION.SDK_INT >= 18) {
|
||||
inputSurface = new InputSurface(encoder.createInputSurface());
|
||||
inputSurface.makeCurrent();
|
||||
}
|
||||
inputSurface = new InputSurface(encoder.createInputSurface());
|
||||
inputSurface.makeCurrent();
|
||||
encoder.start();
|
||||
|
||||
decoder = MediaCodec.createDecoderByType(inputFormat.getString(MediaFormat.KEY_MIME));
|
||||
if (Build.VERSION.SDK_INT >= 18) {
|
||||
outputSurface = new OutputSurface();
|
||||
} else {
|
||||
outputSurface = new OutputSurface(resultWidth, resultHeight, rotateRender);
|
||||
}
|
||||
outputSurface = new OutputSurface();
|
||||
decoder.configure(inputFormat, outputSurface.getSurface(), null, 0);
|
||||
decoder.start();
|
||||
|
||||
final int TIMEOUT_USEC = 2500;
|
||||
ByteBuffer[] decoderInputBuffers = null;
|
||||
ByteBuffer[] encoderOutputBuffers = null;
|
||||
ByteBuffer[] encoderInputBuffers = null;
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
decoderInputBuffers = decoder.getInputBuffers();
|
||||
encoderOutputBuffers = encoder.getOutputBuffers();
|
||||
if (Build.VERSION.SDK_INT < 18) {
|
||||
encoderInputBuffers = encoder.getInputBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
checkConversionCanceled();
|
||||
|
||||
@@ -405,11 +252,7 @@ public class VideoRecoder {
|
||||
int inputBufIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC);
|
||||
if (inputBufIndex >= 0) {
|
||||
ByteBuffer inputBuf;
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
inputBuf = decoderInputBuffers[inputBufIndex];
|
||||
} else {
|
||||
inputBuf = decoder.getInputBuffer(inputBufIndex);
|
||||
}
|
||||
inputBuf = decoder.getInputBuffer(inputBufIndex);
|
||||
int chunkSize = extractor.readSampleData(inputBuf, 0);
|
||||
if (chunkSize < 0) {
|
||||
decoder.queueInputBuffer(inputBufIndex, 0, 0, 0L, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
|
||||
@@ -439,9 +282,6 @@ public class VideoRecoder {
|
||||
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
|
||||
encoderOutputAvailable = false;
|
||||
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
encoderOutputBuffers = encoder.getOutputBuffers();
|
||||
}
|
||||
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
|
||||
MediaFormat newFormat = encoder.getOutputFormat();
|
||||
if (videoTrackIndex == -5) {
|
||||
@@ -451,11 +291,7 @@ public class VideoRecoder {
|
||||
throw new RuntimeException("unexpected result from encoder.dequeueOutputBuffer: " + encoderStatus);
|
||||
} else {
|
||||
ByteBuffer encodedData;
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
encodedData = encoderOutputBuffers[encoderStatus];
|
||||
} else {
|
||||
encodedData = encoder.getOutputBuffer(encoderStatus);
|
||||
}
|
||||
encodedData = encoder.getOutputBuffer(encoderStatus);
|
||||
if (encodedData == null) {
|
||||
throw new RuntimeException("encoderOutputBuffer " + encoderStatus + " was null");
|
||||
}
|
||||
@@ -513,11 +349,7 @@ public class VideoRecoder {
|
||||
throw new RuntimeException("unexpected result from decoder.dequeueOutputBuffer: " + decoderStatus);
|
||||
} else {
|
||||
boolean doRender;
|
||||
if (Build.VERSION.SDK_INT >= 18) {
|
||||
doRender = info.size != 0;
|
||||
} else {
|
||||
doRender = info.size != 0 || info.presentationTimeUs != 0;
|
||||
}
|
||||
doRender = info.size != 0;
|
||||
if (endTime > 0 && info.presentationTimeUs >= endTime) {
|
||||
inputDone = true;
|
||||
decoderDone = true;
|
||||
@@ -542,40 +374,15 @@ public class VideoRecoder {
|
||||
Log.w(TAG, "error while waiting for recording output surface", e);
|
||||
}
|
||||
if (!errorWait) {
|
||||
if (Build.VERSION.SDK_INT >= 18) {
|
||||
outputSurface.drawImage(false);
|
||||
inputSurface.setPresentationTime(info.presentationTimeUs * 1000);
|
||||
inputSurface.swapBuffers();
|
||||
} else {
|
||||
Log.w(TAG, "Cannot proceed with the current SDK version");
|
||||
return false; // TODO: this should be caught much earlier
|
||||
/*
|
||||
int inputBufIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC);
|
||||
if (inputBufIndex >= 0) {
|
||||
outputSurface.drawImage(true);
|
||||
ByteBuffer rgbBuf = outputSurface.getFrame();
|
||||
ByteBuffer yuvBuf = encoderInputBuffers[inputBufIndex];
|
||||
yuvBuf.clear();
|
||||
Utilities.convertVideoFrame(rgbBuf, yuvBuf, colorFormat, resultWidth, resultHeight, padding, swapUV);
|
||||
encoder.queueInputBuffer(inputBufIndex, 0, bufferSize, info.presentationTimeUs, 0);
|
||||
} else {
|
||||
//Log.i("DeltaChat", "input buffer not available");
|
||||
}
|
||||
*/
|
||||
}
|
||||
outputSurface.drawImage(false);
|
||||
inputSurface.setPresentationTime(info.presentationTimeUs * 1000);
|
||||
inputSurface.swapBuffers();
|
||||
}
|
||||
}
|
||||
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
|
||||
decoderOutputAvailable = false;
|
||||
//Log.i("DeltaChat", "decoder stream end");
|
||||
if (Build.VERSION.SDK_INT >= 18) {
|
||||
encoder.signalEndOfInputStream();
|
||||
} else {
|
||||
int inputBufIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC);
|
||||
if (inputBufIndex >= 0) {
|
||||
encoder.queueInputBuffer(inputBufIndex, 0, 1, info.presentationTimeUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
|
||||
}
|
||||
}
|
||||
encoder.signalEndOfInputStream();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -662,45 +469,6 @@ public class VideoRecoder {
|
||||
int estimatedBytes;
|
||||
}
|
||||
|
||||
public static boolean canRecode()
|
||||
{
|
||||
boolean canRecode = true;
|
||||
if (Build.VERSION.SDK_INT < 16 /*= Jelly Bean 4.1 (before that codecInfo.getName() was not there) */) {
|
||||
Log.w(TAG, "Cannot recode: API < 16");
|
||||
canRecode = false;
|
||||
}
|
||||
else if (Build.VERSION.SDK_INT < 18 /*= Jelly Bean 4.3*/) {
|
||||
try {
|
||||
MediaCodecInfo codecInfo = VideoRecoder.selectCodec(VideoRecoder.MIME_TYPE);
|
||||
if (codecInfo == null) {
|
||||
Log.w(TAG, "Cannot recode: cannot select codec");
|
||||
canRecode = false;
|
||||
} else {
|
||||
String name = codecInfo.getName();
|
||||
if (name.equals("OMX.google.h264.encoder") ||
|
||||
name.equals("OMX.ST.VFM.H264Enc") ||
|
||||
name.equals("OMX.Exynos.avc.enc") ||
|
||||
name.equals("OMX.MARVELL.VIDEO.HW.CODA7542ENCODER") ||
|
||||
name.equals("OMX.MARVELL.VIDEO.H264ENCODER") ||
|
||||
name.equals("OMX.k3.video.encoder.avc") ||
|
||||
name.equals("OMX.TI.DUCATI1.VIDEO.H264E")) {
|
||||
Log.w(TAG, "Cannot recode: no supported codec found");
|
||||
canRecode = false;
|
||||
} else {
|
||||
if (VideoRecoder.selectColorFormat(codecInfo, VideoRecoder.MIME_TYPE) == 0) {
|
||||
Log.w(TAG, "Cannot recode: cannot select color format");
|
||||
canRecode = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Cannot recode: Determinating recoding capabilities failed unexpectedly", e);
|
||||
canRecode = false;
|
||||
}
|
||||
}
|
||||
return canRecode;
|
||||
}
|
||||
|
||||
private static VideoEditedInfo getVideoEditInfoFromFile(String videoPath) {
|
||||
// load information for the given video
|
||||
VideoEditedInfo vei = new VideoEditedInfo();
|
||||
@@ -811,11 +579,6 @@ public class VideoRecoder {
|
||||
}
|
||||
msg.setDuration((int)vei.originalDurationMs);
|
||||
|
||||
if (!canRecode()) {
|
||||
alert(context, String.format("Recoding failed for %s: this system cannot recode videos", inPath));
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if video bitrate is already reasonable
|
||||
final int MAX_KBPS = 1500000;
|
||||
final long MAX_BYTES = DcHelper.getInt(context, "sys.msgsize_max_recommended");
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/def_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/def_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/blue_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/blue_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/gray_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/gray_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/green_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/green_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/pink_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/pink_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/purple_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/purple_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/red_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/red_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -1,7 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/def_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/def_primary_alpha33" android:state_pressed="true" />
|
||||
<item android:drawable="@color/primary_alpha_focus" android:state_focused="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/def_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/def_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/blue_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/blue_primary_alpha33" android:state_pressed="true" />
|
||||
<item android:drawable="@color/primary_alpha_focus" android:state_focused="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/blue_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/blue_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/gray_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/gray_primary_alpha33" android:state_pressed="true" />
|
||||
<item android:drawable="@color/primary_alpha_focus" android:state_focused="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/gray_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/gray_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/green_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/green_primary_alpha33" android:state_pressed="true" />
|
||||
<item android:drawable="@color/primary_alpha_focus" android:state_focused="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/green_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/green_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/pink_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/pink_primary_alpha33" android:state_pressed="true" />
|
||||
<item android:drawable="@color/primary_alpha_focus" android:state_focused="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/pink_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/pink_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/purple_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/purple_primary_alpha33" android:state_pressed="true" />
|
||||
<item android:drawable="@color/primary_alpha_focus" android:state_focused="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/purple_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/purple_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/red_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/red_primary_alpha33" android:state_pressed="true" />
|
||||
<item android:drawable="@color/primary_alpha_focus" android:state_focused="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/red_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/red_primary_alpha33" android:state_selected="true" />
|
||||
<item android:drawable="@color/pinned_bg_dark" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/touch_highlight">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="@color/black" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/def_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/def_primary_alpha33" android:state_selected="true" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/blue_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/blue_primary_alpha33" android:state_selected="true" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/gray_primary">
|
||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:drawable="@color/gray_primary_alpha33" android:state_selected="true" />
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user