Compare commits

...

110 Commits

Author SHA1 Message Date
copilot-swe-agent[bot] 71652290d5 Fix XML attribute ordering in context menu resource
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-02-11 19:33:59 +00:00
copilot-swe-agent[bot] 12a373b48d Refactor: Extract clearContextMenuState() helper method
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-02-11 19:32:17 +00:00
copilot-swe-agent[bot] 5df63855f9 Clean up context menu state to prevent memory leaks
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-02-11 19:31:24 +00:00
copilot-swe-agent[bot] 6d44e74dc2 Replace edit/delete buttons with context menu in relay list items
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-02-11 19:29:14 +00:00
copilot-swe-agent[bot] 29ec07a478 Initial plan 2026-02-11 19:26:06 +00:00
adbenitez 6fa114c6c0 Merge remote-tracking branch 'upstream/main' 2026-02-11 20:22:37 +01:00
adbenitez 767b5f2bae update channel link 2026-02-11 20:04:59 +01:00
adb ed3a21b992 Merge pull request #4222 from deltachat/prep-2.42.0
prepare 2.42.0
2026-02-10 21:41:59 +01:00
adbenitez 899c2b5647 prepare 2.42.0 2026-02-10 21:24:21 +01:00
adb 61e616a53b Merge pull request #4221 from deltachat/update-core-and-stuff-2026-02-10
Update core to 2.42.0
2026-02-10 21:23:09 +01:00
adbenitez 3bf4504de9 update strings 2026-02-10 21:20:52 +01:00
adbenitez cd0740d895 update changelog 2026-02-10 20:31:58 +01:00
adbenitez 9f99edc159 update deltachat-core-rust to 'chore(release): prepare for 2.42.0' of 'v2.42.0' 2026-02-10 20:26:54 +01:00
wchen342 60568f23f5 Merge pull request #4217 from deltachat/wch423/onboarding-invite-code
Allow scanning invite codes from onboarding
2026-02-09 16:36:23 -05:00
wchen342 6dc8bd7ba8 Merge branch 'main' into wch423/onboarding-invite-code 2026-02-09 16:25:34 -05:00
wch423 ff4ecb3bda Fix missing callback on successful scan 2026-02-09 22:19:11 +01:00
wch423 c71e71359a Remove extra constant 2026-02-09 22:11:13 +01:00
wch423 d5a09fa25e Only show confirmation dialog if it is an invitation link for contact or group 2026-02-09 21:56:43 +01:00
adb f52c2be2c2 Merge pull request #4219 from deltachat/adb/disable-chat-edge-to-edge
disable insets change in setComposePanelVisibility()
2026-02-09 21:46:46 +01:00
wch423 2233b93108 Remove color change on the OK button 2026-02-09 21:42:06 +01:00
adbenitez d76eb3239a disable insets change in setComposePanelVisibility() 2026-02-09 21:05:00 +01:00
adb a982bd2bbb Merge branch 'main' into wch423/onboarding-invite-code 2026-02-09 18:52:33 +01:00
wchen342 9ef3edcc0e Merge pull request #4189 from deltachat/wch423/audio-background-play
Add background audio playback for voice messages
2026-02-09 12:45:01 -05:00
wch423 de7c54b886 Revert changes to rpc 2026-02-09 15:32:37 +01:00
adb 9c9f966597 Merge branch 'main' into wch423/audio-background-play 2026-02-09 02:24:54 +01:00
adb 12e77789d3 Merge branch 'main' into wch423/onboarding-invite-code 2026-02-09 02:24:39 +01:00
adb bd640072ab Merge pull request #4216 from deltachat/adb/avoid-races-loading-chatlist
use conversationListFragment.loadChatlistAsync() to avoid races
2026-02-09 01:00:38 +01:00
adb 0a9c46fbfc Merge branch 'main' into adb/avoid-races-loading-chatlist 2026-02-07 17:37:29 +01:00
wch423 1cc4f11484 Add CHANGELOG 2026-02-07 17:04:27 +01:00
wch423 9386f2f9cb Fix QR scanning not restarted after cancel 2026-02-07 16:56:49 +01:00
wchen342 45d73d4604 Merge pull request #4215 from deltachat/adb/issue-4207
tune down error icon, don't use special bright alarming color
2026-02-07 10:22:32 -05:00
adbenitez 8a0e2d72a4 use conversationListFragment.loadChatlistAsync() to avoid races 2026-02-07 06:22:31 +01:00
adbenitez 2629c65564 tune down error icon, don't use special bright alarming color 2026-02-07 02:39:37 +01:00
wch423 bbaba3cd33 Fix observer not added in a specific case 2026-02-06 22:30:35 +01:00
wch423 d1f002a132 Remove desugar and use AndroidX version of Consumer 2026-02-06 21:04:31 +01:00
wch423 9ce9a91c95 Add confirm dialog for invitation qr codes 2026-02-06 21:01:36 +01:00
wch423 7844e146b1 Join and launch chat directly after creating new account 2026-02-06 18:50:57 +01:00
adbenitez eb997eca00 fix bug introduced by upstream merge 2026-02-06 18:35:30 +01:00
adb 30124de2a8 Merge branch 'main' into wch423/audio-background-play 2026-02-06 18:34:56 +01:00
wch423 1dcf7e4860 Adding new text for scanning invite code when onboarding 2026-02-06 16:50:45 +01:00
B. Petersen 9ffc904ae5 more detailed call strings 2026-02-06 11:45:56 +01:00
wchen342 2704749b44 Merge pull request #4198 from deltachat/wch423/edge-to-edge-device-channel
Make device messages and subscribed channels edge to edge
2026-02-05 15:15:40 -05:00
wch423 1c174b5b70 Stop draft audio playback when (1) the conversation exits; or (2) the message is sent 2026-02-05 21:12:09 +01:00
adbenitez 8b5dd70d75 Merge remote-tracking branch 'upstream/main' 2026-02-05 20:25:35 +01:00
adb f022316ad7 Merge branch 'main' into wch423/audio-background-play 2026-02-05 19:44:50 +01:00
wch423 3386f5c5f7 Make !CanSend -> CanSend still trigger Inset changes 2026-02-05 19:07:43 +01:00
adb 57eead3a34 Merge branch 'main' into wch423/edge-to-edge-device-channel 2026-02-05 18:33:52 +01:00
adb 2868b51835 Merge pull request #4212 from deltachat/prep-2.40.0
prepare 2.40.0
2026-02-05 17:44:28 +01:00
wch423 82118db71b Make inset changes only happen on initialization of activity 2026-02-05 17:27:21 +01:00
adbenitez 14f55ca6b1 prepare 2.40.0 2026-02-05 17:25:20 +01:00
adb cebfa12142 Merge branch 'main' into wch423/edge-to-edge-device-channel 2026-02-05 16:55:30 +01:00
adb 96c8c21b78 Merge pull request #4209 from deltachat/adb/issue-4206
hide delete-contact icon in ContactSelectionListFragment
2026-02-05 16:55:07 +01:00
adb 9ab1b1f3a7 Merge branch 'main' into adb/issue-4206 2026-02-05 16:54:55 +01:00
adb 94a0e426f8 Merge pull request #4210 from deltachat/adb/allow-to-pick-call-mode
show menu to select audio/video call modes
2026-02-05 16:48:27 +01:00
adbenitez 006f8ae826 use hasVideo instead of audioOnly 2026-02-05 16:37:42 +01:00
wch423 2889266522 Force dispatch all inset changes immediately in ConversationFragment 2026-02-05 16:28:38 +01:00
adb 5e6fccf143 Merge branch 'main' into wch423/edge-to-edge-device-channel 2026-02-05 15:41:17 +01:00
adbenitez 3847e20d18 set tint for icons in the call submenu 2026-02-05 02:31:38 +01:00
adbenitez fc69212a51 update changelog 2026-02-05 01:41:29 +01:00
adbenitez 8999f54ba2 upgrade calls web app 2026-02-05 01:40:27 +01:00
adbenitez f470e92300 show different icon depending on voice/video call modes 2026-02-05 01:35:20 +01:00
adbenitez 3ac49e3e58 set the call mode when calling Rpc.placeOutgoingCall 2026-02-05 01:20:11 +01:00
adb 8dd9cfec5b Merge branch 'main' into adb/allow-to-pick-call-mode 2026-02-05 01:17:12 +01:00
adb 0588214ee7 Merge pull request #4211 from deltachat/update-core-and-stuff-2026-02-04
Update core to 2.40.0
2026-02-05 00:37:53 +01:00
adbenitez 4ec49a031c adapt to new Rpc.placeOutgoingCall API 2026-02-05 00:24:51 +01:00
adbenitez a31d7d6d3e update the RPC bindings 2026-02-04 23:39:04 +01:00
adbenitez 64bbe9866d update translations 2026-02-04 23:38:33 +01:00
adbenitez 5d7ab84efc update changelog 2026-02-04 23:35:14 +01:00
adbenitez 11f73a88e8 update deltachat-core-rust to 'chore(release): prepare for 2.40.0' of 'v2.40.0' 2026-02-04 23:31:30 +01:00
adbenitez dd0e847976 take into consideration if the call is audio/video call when accepting it 2026-02-04 23:17:42 +01:00
wch423 71ed333468 Stop playback when attachment preview is removed;
Stop playback when message containing audio is removed
2026-02-04 22:49:18 +01:00
adbenitez 41d94ae3ee if it is an audio-only call start with video disabled 2026-02-04 22:14:50 +01:00
wch423 b66bc1f863 Handle specific case when leaving group; also move floating button to avoid overaapping with system bar 2026-02-04 19:36:51 +01:00
adbenitez df7d80319c show menu to select audio/video call modes 2026-02-04 19:36:34 +01:00
adbenitez 134145d166 update changelog 2026-02-04 17:13:48 +01:00
adbenitez 688a103c10 hide delete-contact icon in ContactSelectionListFragment 2026-02-04 16:45:29 +01:00
adb e2efa1f913 Merge pull request #4201 from deltachat/adb/issue-4195
fix mailto handling and remove unused code
2026-02-03 23:27:48 +01:00
adb aebd5c66f7 Merge branch 'main' into wch423/audio-background-play 2026-02-03 21:51:02 +01:00
adbenitez 15c60c6b12 fix mailto handling 2026-02-03 20:57:47 +01:00
wch423 f319ba2b83 Allow list scrolling to extend to edge 2026-02-03 19:40:02 +01:00
wch423 93f12e7367 Make message list respect bottom bar height 2026-02-03 19:40:00 +01:00
wch423 10acb07f82 Make device messages and subscribed channels edge to edge 2026-02-03 19:39:56 +01:00
adb ee9a8dd53a Merge branch 'main' into adb/issue-4195 2026-02-03 18:22:39 +01:00
adb 0a55023bdb Merge pull request #4205 from deltachat/adb/issue-4202
remove deselected members
2026-02-03 18:22:02 +01:00
adb fec9f8b4d3 Merge pull request #4204 from deltachat/adb/issue-4203
Fix layout problems inside in-chat apps
2026-02-03 17:53:22 +01:00
wch423 13374df709 Bug fixes and minor changes 2026-02-03 17:41:31 +01:00
adbenitez 7fa04dc3c0 update changelog 2026-02-03 01:03:05 +01:00
adbenitez 9d4e0e4e21 remove deselected members 2026-02-03 00:40:00 +01:00
adbenitez 473c28ab07 Fix layout problems inside in-chat apps 2026-02-02 20:31:42 +01:00
adbenitez de47feac40 update changelog 2026-01-30 20:21:48 +01:00
adbenitez 84894ff538 fix mailto handling and remove unused code 2026-01-30 20:17:04 +01:00
adb f254c35749 Merge pull request #4199 from deltachat/r10s/improve-details-strings
message info: title-case the send/received titles
2026-01-30 17:48:37 +01:00
adb 1ff4e069ea Merge branch 'main' into wch423/audio-background-play 2026-01-29 22:17:24 +01:00
B. Petersen d574d33596 title-case the message-info send/received titles
we nowhere in the app use lower case field names
2026-01-29 18:33:57 +01:00
wchen342 f6756fc34b Merge pull request #4191 from deltachat/adb/issue-4157
use event emmiter before DcAccounts creation
2026-01-29 10:34:57 -05:00
wchen342 7c27eb47fc Merge pull request #4196 from deltachat/adb/issue-4194
avoid crash caused by custom Fragment constructors
2026-01-29 10:19:15 -05:00
adbenitez 8d5a55c24c update changelog 2026-01-29 03:47:53 +01:00
adbenitez 2e9aa79b02 simplify changes in AccountSelectionListFragment 2026-01-29 03:44:42 +01:00
adbenitez 1f9264225b remove custom constructor in ReactionsDetailsFragment 2026-01-29 03:30:17 +01:00
adbenitez 18e145faaf avoid custom constructor in AccountSelectionListFragment 2026-01-29 02:17:06 +01:00
adbenitez 47cf70120a add logcat to crash report 2026-01-28 23:21:37 +01:00
wch423 34be7aab17 Cleanup imports 2026-01-28 21:30:51 +01:00
wch423 59139ed242 Add support for attachment draft; Distinguish between different messages; Prevent unrelated activities from changing the pending intent 2026-01-28 21:28:43 +01:00
wch423 11f3964bdc Add support for attachment draft; Distinguish between different messages 2026-01-28 21:28:42 +01:00
wch423 7683408d18 Allow going back to previous activity from the notification; Support AllMediaActivity 2026-01-28 21:28:40 +01:00
wch423 b74e793654 Finish ViewModel for audio playback; Refine UI set up for AudioView 2026-01-28 21:28:38 +01:00
wch423 4bd74324d2 Try to decouple view and business logics 2026-01-28 21:28:36 +01:00
wch423 9a121b3039 Fix UI update problems;
Make notification clickable
2026-01-28 21:28:33 +01:00
wch423 a9832c9c53 Make audio view use Media3 for playback 2026-01-28 21:28:31 +01:00
adbenitez 1e4c8bc291 use event emmiter before DcAccounts creation 2026-01-27 19:05:38 +01:00
108 changed files with 3140 additions and 1530 deletions
+11 -2
View File
@@ -1,6 +1,7 @@
# Delta Chat Android Changelog
## Unreleased
## v2.42.0
2026-02
* Improve switch speed when changing profiles
* Allow to switch profile when sharing or forwarding
@@ -12,13 +13,21 @@
* Mark external links with " ↗" to make them clear
* Make QR code larger on "Add Second Device" screen
* Add indication for blocked contacts in user profile
* Allow to start calls with video disabled
* Show hint for empty contact search results
* Add background playing for voice messages and other audio files
* Allow scanning Invitation Code when creating a new profile
* Fix: Show dialog if pasted QR codes are invalid
* Fix: Refresh chat list when returning from conversation if selected profile changed
* Fix: Update menu when using "select all" in contact selection
* Fix: Avoid empty profiles after using "add as second device" from welcome screen
* Fix: Remove from group deselected members in the contact selection list
* Fix multi-device seen messages synchronization when using multiple relays
* Update to core 2.39.0
* Fix mailto handling
* Fix layout problems inside in-chat apps
* Avoid crash when the app is minimized with profile switcher or reactions dialogs open
* Remove "trash icon" option from contact selection list when adding members to group
* Update to core 2.42.0
## v2.35.0
2026-01
+8 -3
View File
@@ -33,8 +33,8 @@ android {
useLibrary 'org.apache.http.legacy'
defaultConfig {
versionCode 30000736
versionName "2.36.0"
versionCode 30000737
versionName "2.42.0"
applicationId "chat.delta.lite"
multiDexEnabled true
@@ -211,6 +211,8 @@ dependencies {
implementation "io.noties.markwon:inline-parser:$markwon_version"
implementation 'com.airbnb.android:lottie:4.2.2' // Lottie animations support.
def media3_version = "1.8.0" // 1.9.0 need minSdkVersion 23
implementation 'androidx.concurrent:concurrent-futures:1.3.0'
implementation 'androidx.sharetarget:sharetarget:1.2.0'
implementation 'androidx.webkit:webkit:1.14.0'
@@ -231,8 +233,11 @@ dependencies {
implementation 'androidx.work:work-runtime:2.9.1'
implementation 'androidx.emoji2:emoji2-emojipicker:1.5.0'
implementation 'com.google.guava:guava:31.1-android'
implementation 'com.google.android.exoplayer:exoplayer-core:2.19.1' // plays video and audio
implementation 'com.google.android.exoplayer:exoplayer-core:2.19.1' // FIXME: exoplayer dependencies kept for Video, but we shall migrate them at some point
implementation 'com.google.android.exoplayer:exoplayer-ui:2.19.1'
implementation "androidx.media3:media3-exoplayer:$media3_version"
implementation "androidx.media3:media3-session:$media3_version"
implementation "androidx.media3:media3-ui:$media3_version"
implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
implementation 'com.google.zxing:core:3.3.0' // fixed version to support SDK<24
implementation ('com.journeyapps:zxing-android-embedded:4.3.0') { transitive = false } // QR Code scanner
+38 -2
View File
@@ -153,6 +153,42 @@ static uint32_t* jintArray2uint32Pointer(JNIEnv* env, jintArray ja, int* ret_icn
}
/************************************************************
* DcEventChannel
************************************************************/
static dc_event_channel_t* get_dc_event_channel(JNIEnv *env, jobject obj)
{
static jfieldID fid = 0;
if (fid==0) {
jclass cls = (*env)->GetObjectClass(env, obj);
fid = (*env)->GetFieldID(env, cls, "eventChannelCPtr", "J" /*Signature, J=long*/);
}
if (fid) {
return (dc_event_channel_t*)(*env)->GetLongField(env, obj, fid);
}
return NULL;
}
JNIEXPORT jlong Java_com_b44t_messenger_DcEventChannel_createEventChannelCPtr(JNIEnv *env, jobject obj)
{
return (jlong)dc_event_channel_new();
}
JNIEXPORT void Java_com_b44t_messenger_DcEventChannel_unrefEventChannelCPtr(JNIEnv *env, jobject obj)
{
dc_event_channel_unref(get_dc_event_channel(env, obj));
}
JNIEXPORT jlong Java_com_b44t_messenger_DcEventChannel_getEventEmitterCPtr(JNIEnv *env, jobject obj)
{
return (jlong)dc_event_channel_get_event_emitter(get_dc_event_channel(env, obj));
}
/*******************************************************************************
* DcAccounts
******************************************************************************/
@@ -172,11 +208,11 @@ static dc_accounts_t* get_dc_accounts(JNIEnv *env, jobject obj)
}
JNIEXPORT jlong Java_com_b44t_messenger_DcAccounts_createAccountsCPtr(JNIEnv *env, jobject obj, jstring dir)
JNIEXPORT jlong Java_com_b44t_messenger_DcAccounts_createAccountsCPtr(JNIEnv *env, jobject obj, jstring dir, jobject chanObj)
{
CHAR_REF(dir);
int writable = 1;
jlong accountsCPtr = (jlong)dc_accounts_new(dirPtr, writable);
jlong accountsCPtr = (jlong)dc_accounts_new_with_event_channel(dirPtr, writable, get_dc_event_channel(env, chanObj));
CHAR_UNREF(dir);
return accountsCPtr;
}
+11
View File
@@ -38,6 +38,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<!-- force compiling emojipicker on sdk<21; runtime checks are required then -->
<uses-sdk tools:overrideLibrary="androidx.emoji2.emojipicker"/>
@@ -92,6 +93,7 @@
<intent-filter>
<data android:scheme="mailto"/>
<action android:name="android.intent.action.SENDTO"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
@@ -390,6 +392,15 @@
android:name=".service.FetchForegroundService"
android:foregroundServiceType="dataSync" />
<service
android:name=".service.AudioPlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
<receiver android:name=".notifications.MarkReadReceiver"
android:enabled="true"
android:exported="false">
File diff suppressed because one or more lines are too long
+14 -12
View File
@@ -1188,6 +1188,10 @@ to exchange encryption setup information through QR-code scanning or “invite l
<li>
<p><a href="https://autocrypt.org">Autocrypt</a> is used for automatically
establishing end-to-end encryption between contacts and all members of a group chat.</p>
</li>
<li>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption and forward secrecy.</p>
</li>
<li>
<p><a href="https://github.com/chatmail/core/blob/main/spec.md#attaching-a-contact-to-a-message">Sharing a contact to a
@@ -1453,7 +1457,7 @@ but an implementation has not been agreed as a priority yet.</p>
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat today doesnt support Perfect Forward Secrecy (PFS).
This means that if your private decryption key is leaked,
@@ -1464,12 +1468,9 @@ Otherwise, someone obtaining your decryption keys
is typically also able to get all your non-deleted messages
and doesnt even need to decrypt any previously collected messages.</p>
<p>We designed a Forward Secrecy approach that withstood
initial examination from some cryptographers and implementation experts
but is pending a more formal write up
to ascertain it reliably works in federated messaging and with multi-device usage,
before it could be implemented in <a href="https://github.com/chatmail/core">chatmail core</a>,
which would make it available in all <a href="https://chatmail.at/clients">chatmail clients</a>.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will provide reliable deletion (forward secrecy) through automatic key rotation.
This approach is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="pqc">
@@ -1479,12 +1480,13 @@ which would make it available in all <a href="https://chatmail.at/clients">chatm
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
We aim to add PQC support in <a href="https://github.com/chatmail/core">chatmail core</a> after the draft is finalized at the IETF
in collaboration with other OpenPGP implementers.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption to protect against quantum computer attacks.
Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
The implementation is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="how-can-i-manually-check-encryption-information">
+67 -73
View File
@@ -1045,31 +1045,31 @@ für die <a href="https://chatmail.at/clients">Chatmail-Clients</a> umfasst, von
</h3>
<p>We would like to improve Delta Chat with your help,
which is why Delta Chat for Android asks whether you want
to send anonymous usage statistics.</p>
<p>Wir möchten Delta Chat mit deiner Hilfe verbessern.
Deshalb fragt Delta Chat für Android, ob du
anonyme Nutzungsstatistiken senden möchtest.</p>
<p>You can turn it on and off at
<strong>Settings → Advanced → Send statistics to Delta Chats developers</strong>.</p>
<p>Du kannst dies unter
<strong>Einstellungen → Erweitert → Statistik an Delta Chat Entwickler senden</strong> ein- und ausschalten.</p>
<p>When you turn it on,
weekly statistics will be automatically sent to a bot.</p>
<p>Wenn eingeschaltet,
werden wöchentlich Statistiken automatisch an einen Bot gesendet.</p>
<p>We are interested e.g. in statistics like:</p>
<p>Wir sind beispielsweise an folgenden Statistiken interessiert:</p>
<ul>
<li>
<p>How many contacts are introduced by personally scanning a QR code?</p>
<p>Wie viele Kontakte werden durch das persönliche Scannen eines QR-Codes hergestellt?</p>
</li>
<li>
<p>Which versions of Delta Chat are being used?</p>
<p>Welche Versionen von Delta Chat werden verwendet?</p>
</li>
<li>
<p>What errors occur for users?</p>
<p>Welche Fehler treten bei Benutzern auf?</p>
</li>
</ul>
<p>We will <em>not</em> collect any personally identifiable information about you.</p>
<p>Wir werden <em>keinerlei</em> personenbezogene Daten über dich sammeln.</p>
<h3 id="ich-bin-an-technischen-details-interessiert-gibt-es-hierzu-weitere-infos">
@@ -1107,6 +1107,10 @@ zum Austausch von Verschlüsselungsinformationen durch Scannen von QR-Codes oder
<li>
<p><a href="https://autocrypt.org">Autocrypt</a> wird verwendet, um automatisch eine Ende-zu-Ende-Verschlüsselung zwischen Kontakten und allen Mitgliedern einer Gruppe herzustellen.</p>
</li>
<li>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, dessen vollständige Implementierung für 2026 geplant ist,
wir post-quantum-resistente Verschlüsselung und Forward Secrecy einführen.</p>
</li>
<li>
<p><a href="https://github.com/chatmail/core/blob/main/spec.md#attaching-a-contact-to-a-message">Teilen eines Kontakts im Chat</a>
ermöglicht es den Empfängern, eine Ende-zu-Ende-Verschlüsselung mit dem Kontakt zu verwenden.</p>
@@ -1280,14 +1284,14 @@ selbst wenn die Nachricht nicht Ende-zu-Ende-verschlüsselt war.</p>
speichern Delta-Chat-Apps keine Metadaten über Kontakte oder Gruppen auf Servern. Auch nicht in verschlüsselter Form.
Stattdessen werden alle Gruppen-Metadaten durchgängig verschlüsselt und ausschließlich auf den Endgeräten der Nutzer gespeichert.</p>
<p>Servers can therefore only see:</p>
<p>Server können daher nur das folgende sehen:</p>
<ul>
<li>the sender and receiver addresses</li>
<li>and the message size.</li>
<li>die Absender- und Empfängeradressen</li>
<li>und die Größe der Nachricht.</li>
</ul>
<p>By default, the addresses are randomly generated.</p>
<p>Standardmäßig werden die Adressen zufällig generiert.</p>
<p>Alle anderen Metadaten zu Nachrichten, Kontakten und Gruppen befinden sich im Ende-zu-Ende-verschlüsselten Teil der Nachrichten.</p>
@@ -1299,15 +1303,15 @@ Stattdessen werden alle Gruppen-Metadaten durchgängig verschlüsselt und aussch
</h3>
<p>Both for protecting against metadata-collecting servers
as well as against the threat of device seizure
we recommend to use a <a href="https://chatmail.at/relays">chatmail relay</a>
to create chat profiles using random addresses for transport.
Note that Delta Chat apps on all platforms support multiple profiles
so you can easily use situation-specific profiles next to your “main” profile
with the knowledge that all their data, along with all metadata, will be deleted.
Moreover, if a device is seized then chat contacts using short-lived profiles
can not be identified easily.</p>
<p>Sowohl zum Schutz vor Servern, die Metadaten sammeln,
als auch als Schutz bei Beschlagnahmung von Geräten
empfehlen wir die Verwendung eines <a href="https://chatmail.at/relays">Chatmail-Relays</a>,
um Chat-Profile mit zufälligen Adressen für den Transport zu erstellen.
Beachte, dass Delta-Chat-Apps mehrere Profile unterstützen,
sodass du neben deinem „Hauptprofil” ganz einfach situationsspezifische Profile verwenden kannst,
mit der Gewissheit, dass alle Daten sowie alle Metadaten gelöscht werden.
Darüber hinaus können Chat-Kontakte, die kurzlebige Profile verwenden,
im Falle einer Beschlagnahmung des Geräts nicht ohne Weiteres identifiziert werden.</p>
<h3 id="wer-sieht-meine-ip-adresse">
@@ -1350,11 +1354,10 @@ um seine Serverinfrastruktur darüber im Unklaren zu lassen, wer eine Nachricht
Dies ist besonders wichtig, weil der Signal-Server die Handynummer jedes Kontos kennt,
die in der Regel mit einer Passidentität verbunden ist.</p>
<p>Even if <a href="https://chatmail.at/relays">chatmail relays</a>
do not ask for any private data (including no phone numbers),
it might still be worthwhile to protect relational metadata between addresses.
We dont foresee bigger problems in using random throw-away addresses for sealed sending
but an implementation has not been agreed as a priority yet.</p>
<p>Auch wenn <a href="https://chatmail.at/relays">Chatmail-Relays</a>
keine privaten Daten (einschließlich Telefonnummern) abfragen,
könnte es dennoch sinnvoll sein, Metadaten zwischen Adressen zu schützen.
Wir sehen keine größeren Probleme bei der Verwendung von zufälligen Wegwerfadressen für aber eine Umsetzung wurde noch nicht als priorisiert.</p>
<h3 id="pfs">
@@ -1364,23 +1367,20 @@ but an implementation has not been agreed as a priority yet.</p>
</h3>
<p>Nein, noch nicht.</p>
<p>Noch nicht, aber es kommt mit <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat today doesnt support Perfect Forward Secrecy (PFS).
This means that if your 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.
Note that Forward Secrecy only increases security if you delete messages.
Otherwise, someone obtaining your decryption keys
is typically also able to get all your non-deleted messages
and doesnt even need to decrypt any previously collected messages.</p>
<p>Delta Chat unterstützt derzeit keine Perfect Forward Secrecy (PFS).
Das bedeutet, dass, wenn Ihr privater Schlüssel offengelegt wird
und jemand Ihre früheren Nachrichten während der Übertragung gesammelt hat,
diese mit dem offengelegten Schlüssel entschlüsselt und gelesen werden können.
Beachten Sie, dass Forward Secrecy die Sicherheit nur erhöht, wenn du Nachrichten löschst.
Andernfalls kann jemand, der deinen Schlüssel erhält,
in der Regel auch alle deine nicht gelöschten Nachrichten abrufen
und muss zuvor gesammelte Nachrichten nicht einmal entschlüsseln.</p>
<p>We designed a Forward Secrecy approach that withstood
initial examination from some cryptographers and implementation experts
but is pending a more formal write up
to ascertain it reliably works in federated messaging and with multi-device usage,
before it could be implemented in <a href="https://github.com/chatmail/core">chatmail core</a>,
which would make it available in all <a href="https://chatmail.at/clients">chatmail clients</a>.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, dessen vollständige Implementierung für 2026 geplant ist,
wird durch automatische Schlüsselrotation eine zuverlässige Löschung (Forward Secrecy) gewährleisten.
Dieser Ansatz ist im Entwurf <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> festgelegt.</p>
<h3 id="pqc">
@@ -1390,11 +1390,13 @@ which would make it available in all <a href="https://chatmail.at/clients">chatm
</h3>
<p>Nein, noch nicht.</p>
<p>Noch nicht, aber es kommt mit <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat verwendet die Rust OpenPGP-Bibliothek <a href="https://github.com/rpgp/rpgp">rPGP</a>
die den neuesten <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP Entwurf</a> unterstützt.
Wir beabsichtigen, PQC-Unterstützung zum <a href="https://github.com/chatmail/core">chatmail core</a> hinzuzufügen, sobald der Entwurf bei der IETF in Zusammenarbeit mit anderen OpenPGP-Implementierern fertiggestellt ist.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, dessen vollständige Implementierung für 2026 geplant ist,
wird eine post-quantum-resistente Verschlüsselung zum Schutz vor Angriffen durch Quantencomputer bieten.
Delta Chat verwendet die Rust-OpenPGP-Bibliothek <a href="https://github.com/rpgp/rpgp">rPGP</a>,
die den neuesten <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Kryptografie OpenPGP Entfurf</a> unterstützt.
Die Implementierung ist in <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> festgelegt.</p>
<h3 id="wie-kann-ich-die-verschlüsselung-manuell-überprüfen">
@@ -1420,12 +1422,11 @@ ist die Verbindung sicher.</p>
<p>Nein.</p>
<p>Delta Chat generates secure OpenPGP keys according to the Autocrypt specification 1.1.
We do not recommend or offer users to perform manual key management.
We want to ensure that security audits can focus on a few proven cryptographic algorithms
instead of the full breadth of possible algorithms allowed with OpenPGP.
If you want to extract your OpenPGP key, there only is an expert method:
you need to look it up in the “keypairs” SQLite table of a profile backup tar-file.</p>
<p>Delta Chat generiert sichere OpenPGP-Schlüssel gemäß der Autocrypt-Spezifikation 1.1.
Wir bieten Benutzern keine manuelle Schlüsselverwaltung an, noch empfehlen diese.
Wir wollen sicherstellen, dass sich Sicherheitsüberprüfungen auf einige wenige bewährte kryptografische Algorithmen konzentrieren können,
anstatt auf die gesamte Bandbreite der mit OpenPGP zulässigen Algorithmen.
Wenn Sie Ihren OpenPGP-Schlüssel extrahieren möchten, gibt es nur eine Methode für Experten: Sie müssen ihn in der SQLite-Tabelle „keypairs” des Backups nachschlagen.</p>
<h3 id="security-audits">
@@ -1535,31 +1536,24 @@ Wir nutzen vielmehr öffentliche Finanzierungsquellen, die bisher aus der EU und
<ul>
<li>
<p>In 2023 and 2024 we got accepted in the Next Generation Internet (NGI)
program for our work in <a href="https://nlnet.nl/project/WebXDC-Push/">webxdc PUSH</a>,
along with collaboration partners working on
<a href="https://nlnet.nl/project/Webxdc-Evolve/">webxdc evolve</a>,
<a href="https://nlnet.nl/project/WebXDC-XMPP/">webxdc XMPP</a>,
<a href="https://nlnet.nl/project/DeltaTouch/">DeltaTouch</a> and
<a href="https://nlnet.nl/project/DeltaTauri/">DeltaTauri</a>.
All of these projects are partially completed or to be completed in early 2025.</p>
<p>2023 und 2024 wurden wir in das Next-Generation-Internet-Programm (NGI)
für unsere Arbeit an <a href="https://nlnet.nl/project/WebXDC-Push/">Webxdc-PUSH</a> aufgenommen,
zusammen mit Kooperationspartnern, die an
<a href="https://nlnet.nl/project/Webxdc-Evolve/">Webxdc-Evolve</a>,
<a href="https://nlnet.nl/project/WebXDC-XMPP/">Webxdc-XMPP</a>,
<a href="https://nlnet.nl/project/DeltaTouch/">DeltaTouch</a> und
<a href="https://nlnet.nl/project/DeltaTauri/">DeltaTauri</a>.
Alle diese Projekte sind teilweise abgeschlossen oder sollen Anfang 2025 abgeschlossen werden.</p>
</li>
<li>
<p>Im Jahr 2021 erhielten wir weitere EU-Mittel für zwei “Next-Generation-Internet”-Anträge, nämlich für <a href="https://dapsi.ngi.eu/hall-of-fame/eppd/">EPPD - E-Mail-Provider-Portabilitätsverzeichnis</a> (~97K EUR) und <a href="https://nlnet.nl/project/EmailPorting/">AEAP - E-Mail-Adressportierung</a> (~90K EUR). Ziel sind bessere Unterstützung von Mehrfachkonten, verbesserten QR-Code-Kontakt- und -Gruppen-Setups sowie Netzwerkverbesserungen auf allen Plattformen.</p>
</li>
<li>
<p>The <a href="https://nlnet.nl/">NLnet foundation</a> granted in 2019/2020 EUR 46K for
completing Rust/Python bindings and instigating a Chat-bot eco-system.</p>
<p>Die <a href="https://nlnet.nl/">NLnet-Stiftung</a> bewilligte 2019/2020 46K EUR für die Fertigstellung von Rust-/Python-Bindungs und die Einrichtung eines Chat-Bot-Ökosystems.</p>
</li>
<li>
<p>The <a href="https://opentechfund.org">Open Technology Fund</a> gave us a
first 2018/2019 grant (~$200K) during which we majorly improved the Android app
and released a first Desktop app beta version, and which moreover
moored our feature developments in UX research in human rights contexts,
see our concluding <a href="https://delta.chat/en/2019-07-19-uxreport">Needfinding and UX report</a>.
The second 2019/2020 grant (~$300K) helped us to
release Delta/iOS versions, to convert our core library to Rust, and
to provide new features for all platforms.</p>
<p>Der <a href="https://opentechfund.org">Open Technology Fund</a> hat Delta Chat erstmals 2018/2019 bezuschusst; mit dieser Förderung (~$200K) wurden hauptsächlich die Android-App verbessert sowie das Release der Desktop-App in einer Betaversion ermöglicht. Basierend auf Nutzererfahrungen im Menschenrechtskontext wurden zudem verschiedene Funktionen entwickelt, siehe unseren Bericht <a href="https://delta.chat/en/2019-07-19-uxreport">Needfinding and UX report</a>.
Die zweite Förderung 2019/2020 (~$300K) half uns bei der Erstellung der iOS-Version, unsere Kernbibliothek in die Programmiersprache “Rust” zu konvertieren und neue Funktionen für alle Plattformen bereitzustellen.</p>
</li>
<li>
<p>Das EU-Projekt <a href="https://nextleap.eu">NEXTLEAP</a> finanzierte 2017 und 2018 die Entwicklung und Implementierung von “Verifizierten Gruppen” und “Setup Kontakt” und half auch bei der Integration der Ende-zu-Ende-Verschlüsselung durch <a href="https://autocrypt.org">Autocrypt</a>.</p>
+14 -12
View File
@@ -1188,6 +1188,10 @@ to exchange encryption setup information through QR-code scanning or “invite l
<li>
<p><a href="https://autocrypt.org">Autocrypt</a> is used for automatically
establishing end-to-end encryption between contacts and all members of a group chat.</p>
</li>
<li>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption and forward secrecy.</p>
</li>
<li>
<p><a href="https://github.com/chatmail/core/blob/main/spec.md#attaching-a-contact-to-a-message">Sharing a contact to a
@@ -1453,7 +1457,7 @@ but an implementation has not been agreed as a priority yet.</p>
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat today doesnt support Perfect Forward Secrecy (PFS).
This means that if your private decryption key is leaked,
@@ -1464,12 +1468,9 @@ Otherwise, someone obtaining your decryption keys
is typically also able to get all your non-deleted messages
and doesnt even need to decrypt any previously collected messages.</p>
<p>We designed a Forward Secrecy approach that withstood
initial examination from some cryptographers and implementation experts
but is pending a more formal write up
to ascertain it reliably works in federated messaging and with multi-device usage,
before it could be implemented in <a href="https://github.com/chatmail/core">chatmail core</a>,
which would make it available in all <a href="https://chatmail.at/clients">chatmail clients</a>.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will provide reliable deletion (forward secrecy) through automatic key rotation.
This approach is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="pqc">
@@ -1479,12 +1480,13 @@ which would make it available in all <a href="https://chatmail.at/clients">chatm
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
We aim to add PQC support in <a href="https://github.com/chatmail/core">chatmail core</a> after the draft is finalized at the IETF
in collaboration with other OpenPGP implementers.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption to protect against quantum computer attacks.
Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
The implementation is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="how-can-i-manually-check-encryption-information">
+14 -12
View File
@@ -1184,6 +1184,10 @@ to exchange encryption setup information through QR-code scanning or “invite l
<li>
<p><a href="https://autocrypt.org">Autocrypt</a> is used for automatically
establishing end-to-end encryption between contacts and all members of a group chat.</p>
</li>
<li>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption and forward secrecy.</p>
</li>
<li>
<p><a href="https://github.com/chatmail/core/blob/main/spec.md#attaching-a-contact-to-a-message">Sharing a contact to a
@@ -1446,7 +1450,7 @@ but an implementation has not been agreed as a priority yet.</p>
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat today doesnt support Perfect Forward Secrecy (PFS).
This means that if your private decryption key is leaked,
@@ -1457,12 +1461,9 @@ Otherwise, someone obtaining your decryption keys
is typically also able to get all your non-deleted messages
and doesnt even need to decrypt any previously collected messages.</p>
<p>We designed a Forward Secrecy approach that withstood
initial examination from some cryptographers and implementation experts
but is pending a more formal write up
to ascertain it reliably works in federated messaging and with multi-device usage,
before it could be implemented in <a href="https://github.com/chatmail/core">chatmail core</a>,
which would make it available in all <a href="https://chatmail.at/clients">chatmail clients</a>.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will provide reliable deletion (forward secrecy) through automatic key rotation.
This approach is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="pqc">
@@ -1472,12 +1473,13 @@ which would make it available in all <a href="https://chatmail.at/clients">chatm
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
We aim to add PQC support in <a href="https://github.com/chatmail/core">chatmail core</a> after the draft is finalized at the IETF
in collaboration with other OpenPGP implementers.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption to protect against quantum computer attacks.
Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
The implementation is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="how-can-i-manually-check-encryption-information">
+14 -12
View File
@@ -1174,6 +1174,10 @@ to exchange encryption setup information through QR-code scanning or “invite l
<li>
<p><a href="https://autocrypt.org">Autocrypt</a> is used for automatically
establishing end-to-end encryption between contacts and all members of a group chat.</p>
</li>
<li>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption and forward secrecy.</p>
</li>
<li>
<p><a href="https://github.com/chatmail/core/blob/main/spec.md#attaching-a-contact-to-a-message">Sharing a contact to a
@@ -1439,7 +1443,7 @@ but an implementation has not been agreed as a priority yet.</p>
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat today doesnt support Perfect Forward Secrecy (PFS).
This means that if your private decryption key is leaked,
@@ -1450,12 +1454,9 @@ Otherwise, someone obtaining your decryption keys
is typically also able to get all your non-deleted messages
and doesnt even need to decrypt any previously collected messages.</p>
<p>We designed a Forward Secrecy approach that withstood
initial examination from some cryptographers and implementation experts
but is pending a more formal write up
to ascertain it reliably works in federated messaging and with multi-device usage,
before it could be implemented in <a href="https://github.com/chatmail/core">chatmail core</a>,
which would make it available in all <a href="https://chatmail.at/clients">chatmail clients</a>.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will provide reliable deletion (forward secrecy) through automatic key rotation.
This approach is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="pqc">
@@ -1465,12 +1466,13 @@ which would make it available in all <a href="https://chatmail.at/clients">chatm
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
We aim to add PQC support in <a href="https://github.com/chatmail/core">chatmail core</a> after the draft is finalized at the IETF
in collaboration with other OpenPGP implementers.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption to protect against quantum computer attacks.
Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
The implementation is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="how-can-i-manually-check-encryption-information">
+14 -12
View File
@@ -1188,6 +1188,10 @@ to exchange encryption setup information through QR-code scanning or “invite l
<li>
<p><a href="https://autocrypt.org">Autocrypt</a> is used for automatically
establishing end-to-end encryption between contacts and all members of a group chat.</p>
</li>
<li>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption and forward secrecy.</p>
</li>
<li>
<p><a href="https://github.com/chatmail/core/blob/main/spec.md#attaching-a-contact-to-a-message">Sharing a contact to a
@@ -1453,7 +1457,7 @@ but an implementation has not been agreed as a priority yet.</p>
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat today doesnt support Perfect Forward Secrecy (PFS).
This means that if your private decryption key is leaked,
@@ -1464,12 +1468,9 @@ Otherwise, someone obtaining your decryption keys
is typically also able to get all your non-deleted messages
and doesnt even need to decrypt any previously collected messages.</p>
<p>We designed a Forward Secrecy approach that withstood
initial examination from some cryptographers and implementation experts
but is pending a more formal write up
to ascertain it reliably works in federated messaging and with multi-device usage,
before it could be implemented in <a href="https://github.com/chatmail/core">chatmail core</a>,
which would make it available in all <a href="https://chatmail.at/clients">chatmail clients</a>.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will provide reliable deletion (forward secrecy) through automatic key rotation.
This approach is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="pqc">
@@ -1479,12 +1480,13 @@ which would make it available in all <a href="https://chatmail.at/clients">chatm
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
We aim to add PQC support in <a href="https://github.com/chatmail/core">chatmail core</a> after the draft is finalized at the IETF
in collaboration with other OpenPGP implementers.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption to protect against quantum computer attacks.
Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
The implementation is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="how-can-i-manually-check-encryption-information">
+32 -30
View File
@@ -1173,6 +1173,10 @@ per scambiare informazioni sulla configurazione della crittografia tramite la sc
<li>
<p><a href="https://autocrypt.org">Autocrypt</a> viene utilizzato per stabilire
automaticamente la crittografia end-to-end tra i contatti e tutti i membri di una chat di gruppo.</p>
</li>
<li>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption and forward secrecy.</p>
</li>
<li>
<p><a href="https://github.com/chatmail/core/blob/main/spec.md#attaching-a-contact-to-a-message">Condivisione di un contatto con una
@@ -1436,7 +1440,7 @@ ma unimplementazione non è stata ancora concordata come priorità.</p>
</h3>
<p>No, non ancora.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat al momento non supporta la tecnologia Perfect Forward Secrecy (PFS).
Ciò significa che se la tua chiave di decrittazione privata viene divulgata
@@ -1447,12 +1451,9 @@ In caso contrario, chi ottiene le tue chiavi di decrittazione
in genere è in grado di ottenere anche tutti i tuoi messaggi non eliminati
e non ha nemmeno bisogno di decifrare i messaggi raccolti in precedenza.</p>
<p>Abbiamo progettato un approccio Forward Secrecy che ha superato
lesame iniziale di alcuni crittografi ed esperti di implementazione
ma è in attesa di una stesura più formale
per accertarne laffidabilità nella messaggistica federata e nellutilizzo su più dispositivi,
prima di poter essere implementato in <a href="https://github.com/chatmail/core">chatmail core</a>,
che lo renderebbe disponibile in tutti i <a href="https://chatmail.at/clients">clients di chatmail</a>.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will provide reliable deletion (forward secrecy) through automatic key rotation.
This approach is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="pqc">
@@ -1462,12 +1463,13 @@ che lo renderebbe disponibile in tutti i <a href="https://chatmail.at/clients">c
</h3>
<p>No, non ancora.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat utilizza la libreria Rust OpenPGP <a href="https://github.com/rpgp/rpgp">rPGP</a>
che supporta lultima <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">bozza IETF Post-Quantum-Cryptography OpenPGP</a>.
Il nostro obiettivo è aggiungere il supporto PQC nel <a href="https://github.com/chatmail/core">core di chatmail</a> dopo che la bozza sarà stata finalizzata dallIETF
in collaborazione con altri implementatori di OpenPGP.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption to protect against quantum computer attacks.
Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
The implementation is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="come-posso-controllare-manualmente-le-informazioni-di-crittografia">
@@ -1647,31 +1649,31 @@ ordinate cronologicamente:</p>
<ul>
<li>
<p>In 2023 and 2024 we got accepted in the Next Generation Internet (NGI)
program for our work in <a href="https://nlnet.nl/project/WebXDC-Push/">webxdc PUSH</a>,
along with collaboration partners working on
<a href="https://nlnet.nl/project/Webxdc-Evolve/">webxdc evolve</a>,
<a href="https://nlnet.nl/project/WebXDC-XMPP/">webxdc XMPP</a>,
<a href="https://nlnet.nl/project/DeltaTouch/">DeltaTouch</a> and
<a href="https://nlnet.nl/project/DeltaTauri/">DeltaTauri</a>.
All of these projects are partially completed or to be completed in early 2025.</p>
<p>Nel 2023 e nel 2024 siamo stati accettati nel programma Next Generation Internet (NGI)
per il nostro lavoro in <a href="https://nlnet.nl/project/WebXDC-Push/">webxdc PUSH</a>,
insieme ai partner di collaborazione che lavorano su
<a href="https://nlnet.nl/project/Webxdc-Evolve/">webxdc evolve</a>,
<a href="https://nlnet.nl/project/WebXDC-XMPP/">webxdc XMPP</a>,
<a href="https://nlnet.nl/project/DeltaTouch/">DeltaTouch</a> e
<a href="https://nlnet.nl/project/DeltaTauri/">DeltaTauri</a>.
Tutti questi progetti sono parzialmente completati o saranno completati allinizio del 2025.</p>
</li>
<li>
<p>Nel 2021 abbiamo ricevuto ulteriori finanziamenti dallUE per due proposte di Next-Generation-Internet, ovvero per <a href="https://dapsi.ngi.eu/hall-of-fame/eppd/">EPPD - directory di portabilità dei provider di posta elettronica</a> (~97.000 EUR) e <a href="https://nlnet.nl/project/EmailPorting/">AEAP - portabilità degli indirizzi email</a> (~90.000 EUR), che hanno portato a un migliore supporto multi-profilo, a un miglioramento delle impostazioni di contatto e di gruppo tramite codice QR e a numerosi miglioramenti di rete su tutte le piattaforme.</p>
</li>
<li>
<p>The <a href="https://nlnet.nl/">NLnet foundation</a> granted in 2019/2020 EUR 46K for
completing Rust/Python bindings and instigating a Chat-bot eco-system.</p>
<p>La <a href="https://nlnet.nl/">fondazione NLnet</a> ha concesso nel 2019/2020 46.000 EUR per
completando i collegamenti Rust/Python e avviando un ecosistema Chat-bot.</p>
</li>
<li>
<p>The <a href="https://opentechfund.org">Open Technology Fund</a> gave us a
first 2018/2019 grant (~$200K) during which we majorly improved the Android app
and released a first Desktop app beta version, and which moreover
moored our feature developments in UX research in human rights contexts,
see our concluding <a href="https://delta.chat/en/2019-07-19-uxreport">Needfinding and UX report</a>.
The second 2019/2020 grant (~$300K) helped us to
release Delta/iOS versions, to convert our core library to Rust, and
to provide new features for all platforms.</p>
<p>L<a href="https://opentechfund.org">Open Technology Fund</a> ci ha dato una
prima sovvenzione 2018/2019 (~$200K) durante la quale abbiamo notevolmente migliorato lapp Android
e ha rilasciato una prima versione beta dellapp desktop, e che inoltre
ancorato i nostri sviluppi delle funzionalità nella ricerca sulla UX nei contesti dei diritti umani,
vedete il nostro <a href="https://delta.chat/en/2019-07-19-uxreport">Rapporto Needfinding e UX</a> conclusivo.
La seconda sovvenzione 2019/2020 (~$300K) ci ha aiutato a farlo
rilasciare nelle versioni Delta/iOS, per convertire la nostra libreria principale in Rust, e
per fornire nuove funzionalità per tutte le piattaforme.</p>
</li>
<li>
<p>Il progetto UE <a href="https://nextleap.eu">NEXTLEAP</a> ha finanziato la ricerca
+14 -12
View File
@@ -1182,6 +1182,10 @@ to exchange encryption setup information through QR-code scanning or “invite l
<li>
<p><a href="https://autocrypt.org">Autocrypt</a> is used for automatically
establishing end-to-end encryption between contacts and all members of a group chat.</p>
</li>
<li>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption and forward secrecy.</p>
</li>
<li>
<p><a href="https://github.com/chatmail/core/blob/main/spec.md#attaching-a-contact-to-a-message">Sharing a contact to a
@@ -1447,7 +1451,7 @@ but an implementation has not been agreed as a priority yet.</p>
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat today doesnt support Perfect Forward Secrecy (PFS).
This means that if your private decryption key is leaked,
@@ -1458,12 +1462,9 @@ Otherwise, someone obtaining your decryption keys
is typically also able to get all your non-deleted messages
and doesnt even need to decrypt any previously collected messages.</p>
<p>We designed a Forward Secrecy approach that withstood
initial examination from some cryptographers and implementation experts
but is pending a more formal write up
to ascertain it reliably works in federated messaging and with multi-device usage,
before it could be implemented in <a href="https://github.com/chatmail/core">chatmail core</a>,
which would make it available in all <a href="https://chatmail.at/clients">chatmail clients</a>.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will provide reliable deletion (forward secrecy) through automatic key rotation.
This approach is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="pqc">
@@ -1473,12 +1474,13 @@ which would make it available in all <a href="https://chatmail.at/clients">chatm
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
We aim to add PQC support in <a href="https://github.com/chatmail/core">chatmail core</a> after the draft is finalized at the IETF
in collaboration with other OpenPGP implementers.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption to protect against quantum computer attacks.
Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
The implementation is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="how-can-i-manually-check-encryption-information">
+19 -24
View File
@@ -1102,6 +1102,10 @@ weekly statistics will be automatically sent to a bot.</p>
<li>
<p><a href="https://autocrypt.org">Autocrypt</a> służy do automatycznego ustanawiania szyfrowania typu end-to-end między kontaktami a wszystkimi członkami czatu grupowego.</p>
</li>
<li>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption and forward secrecy.</p>
</li>
<li>
<p><a href="https://github.com/chatmail/core/blob/main/spec.md#attaching-a-contact-to-a-message">Udostępnienie kontaktu na czacie</a> umożliwia odbiorcom korzystanie z szyfrowania typu end-to-end z tym kontaktem.</p>
</li>
@@ -1314,11 +1318,13 @@ but an implementation has not been agreed as a priority yet.</p>
</h3>
<p>Nie, jeszcze nie.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat obecnie nie obsługuje mechanizmu Perfect Forward Secrecy (PFS). Oznacza to, że jeśli twój prywatny klucz deszyfrujący zostanie ujawniony, a ktoś zdobędzie twoje wcześniejsze wiadomości w trakcie transmisji, będzie mógł je odszyfrować i odczytać za pomocą ujawnionego klucza deszyfrującego. Należy pamiętać, że mechanizm Forward Secrecy zwiększa bezpieczeństwo tylko w przypadku usuwania wiadomości. W przeciwnym razie osoba, która uzyska twoje klucze deszyfrujące, zazwyczaj będzie mogła uzyskać dostęp do wszystkich nieusuniętych wiadomości i nie będzie musiała odszyfrowywać żadnych wcześniej zebranych wiadomości.</p>
<p>Opracowaliśmy metodę Forward Secrecy, która przeszła wstępną analizę niektórych kryptografów i ekspertów ds. wdrożeń, ale oczekuje na bardziej formalne opracowanie, które potwierdzi jej niezawodne działanie w federacyjnym przesyłaniu wiadomości i w przypadku korzystania z wielu urządzeń, zanim zostanie zaimplementowana w <a href="https://github.com/chatmail/core">rdzeniu chatmail</a>, co uczyniłoby ją dostępną we wszystkich <a href="https://chatmail.at/clients">klientach chatmail</a>.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will provide reliable deletion (forward secrecy) through automatic key rotation.
This approach is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="pqc">
@@ -1328,9 +1334,13 @@ but an implementation has not been agreed as a priority yet.</p>
</h3>
<p>Nie, jeszcze nie.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat korzysta z biblioteki Rust OpenPGP <a href="https://github.com/rpgp/rpgp">rPGP</a>, która obsługuje najnowszy <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">projekt OpenPGP IETF Post-Quantum-Cryptography</a>. Planujemy dodać obsługę PQC do <a href="https://github.com/chatmail/core">rdzenia chatmail</a> po sfinalizowaniu projektu w IETF we współpracy z innymi implementatorami OpenPGP.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption to protect against quantum computer attacks.
Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
The implementation is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="jak-mogę-ręcznie-sprawdzić-informacje-o-szyfrowaniu">
@@ -1463,32 +1473,17 @@ Raczej korzystamy z publicznych źródeł finansowania, jak dotąd pochodzących
<ul>
<li>
<p>In 2023 and 2024 we got accepted in the Next Generation Internet (NGI)
program for our work in <a href="https://nlnet.nl/project/WebXDC-Push/">webxdc PUSH</a>,
along with collaboration partners working on
<a href="https://nlnet.nl/project/Webxdc-Evolve/">webxdc evolve</a>,
<a href="https://nlnet.nl/project/WebXDC-XMPP/">webxdc XMPP</a>,
<a href="https://nlnet.nl/project/DeltaTouch/">DeltaTouch</a> and
<a href="https://nlnet.nl/project/DeltaTauri/">DeltaTauri</a>.
All of these projects are partially completed or to be completed in early 2025.</p>
<p>W latach 2023 i 2024 zostaliśmy przyjęci do programu Next Generation Internet (NGI) za naszą pracę w <a href="https://nlnet.nl/project/WebXDC-Push/">webxdc PUSH</a>, wraz z partnerami współpracującymi pracującymi nad <a href="https://nlnet.nl/project/Webxdc-Evolve/">webxdc evolve</a>, <a href="https://nlnet.nl/project/WebXDC-XMPP/">webxdc XMPP</a>, <a href="https://nlnet.nl/project/DeltaTouch/">DeltaTouch</a> i <a href="https://nlnet.nl/project/DeltaTauri/">DeltaTauri</a>. Wszystkie te projekty są częściowo ukończone lub zostaną ukończone na początku 2025 r.</p>
</li>
<li>
<p>In 2021 we received further EU funding for two Next-Generation-Internet
proposals, namely for <a href="https://dapsi.ngi.eu/hall-of-fame/eppd/">EPPD - email provider portability directory</a> (~97K EUR) and <a href="https://nlnet.nl/project/EmailPorting/">AEAP - email address porting</a> (~90K EUR) which resulted in better multi-profile support, improved QR-code contact and group setups and many networking improvements on all platforms.</p>
<p>W 2021 r. otrzymaliśmy kolejne dofinansowanie z UE na dwie propozycje dotyczące Internetu nowej generacji, a mianowicie na <a href="https://dapsi.ngi.eu/hall-of-fame/eppd/">EPPD katalog przenośności dostawcy poczty e-mail</a> ( ~97 tys. EUR) i <a href="https://nlnet.nl/project/EmailPorting/">AEAP przenoszenie adresu e-mail</a> (~90 tys. EUR), co zaowocowało lepszą obsługą wielu kont, ulepszonymi kontaktami i ustawieniami grup za pomocą kodów QR oraz wieloma ulepszeniami sieciowymi na wszystkich platformach.</p>
</li>
<li>
<p>The <a href="https://nlnet.nl/">NLnet foundation</a> granted in 2019/2020 EUR 46K for
completing Rust/Python bindings and instigating a Chat-bot eco-system.</p>
<p><a href="https://nlnet.nl/">Fundacja NLnet</a> przekazała w latach 2019/2020 kwotę 46 tys. EUR na wykonanie wiązań Rust/Python i uruchomienie ekosystemu Chat-bot.</p>
</li>
<li>
<p>The <a href="https://opentechfund.org">Open Technology Fund</a> gave us a
first 2018/2019 grant (~$200K) during which we majorly improved the Android app
and released a first Desktop app beta version, and which moreover
moored our feature developments in UX research in human rights contexts,
see our concluding <a href="https://delta.chat/en/2019-07-19-uxreport">Needfinding and UX report</a>.
The second 2019/2020 grant (~$300K) helped us to
release Delta/iOS versions, to convert our core library to Rust, and
to provide new features for all platforms.</p>
<p><a href="https://opentechfund.org">Open Technology Fund</a> przyznał nam pierwszy grant w 2018/2019 (~200 000 $), dzięki któremu znacznie ulepszyliśmy aplikację na Androida i wydaliśmy pierwszą wersję beta aplikacji na komputery stacjonarne, a także ugruntował rozwój naszych funkcji w badaniach UX w kontekście praw człowieka, zobacz nasz końcowy raport <a href="https://delta.chat/en/2019-07-19-uxreport">Needfinding and UX</a>.
Druga dotacja w 2019/2020 (~300 000 4) pomogła nam wydać wersje Delta/iOS, przekonwertować naszą podstawową bibliotekę na Rust i zapewnić nowe funkcje dla wszystkich platform.</p>
</li>
<li>
<p>Unijny projekt <a href="https://nextleap.eu">NEXTLEAP</a> sfinansował badania i wdrożenie zweryfikowanych grup i ustawień protokołów kontaktowych w latach 2017 i 2018, a także pomógł zintegrować szyfrowanie end-to-end poprzez <a href="https://autocrypt.org">Autocrypt</a>.</p>
+14 -12
View File
@@ -1183,6 +1183,10 @@ to exchange encryption setup information through QR-code scanning or “invite l
<li>
<p><a href="https://autocrypt.org">Autocrypt</a> is used for automatically
establishing end-to-end encryption between contacts and all members of a group chat.</p>
</li>
<li>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption and forward secrecy.</p>
</li>
<li>
<p><a href="https://github.com/chatmail/core/blob/main/spec.md#attaching-a-contact-to-a-message">Sharing a contact to a
@@ -1448,7 +1452,7 @@ but an implementation has not been agreed as a priority yet.</p>
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat today doesnt support Perfect Forward Secrecy (PFS).
This means that if your private decryption key is leaked,
@@ -1459,12 +1463,9 @@ Otherwise, someone obtaining your decryption keys
is typically also able to get all your non-deleted messages
and doesnt even need to decrypt any previously collected messages.</p>
<p>We designed a Forward Secrecy approach that withstood
initial examination from some cryptographers and implementation experts
but is pending a more formal write up
to ascertain it reliably works in federated messaging and with multi-device usage,
before it could be implemented in <a href="https://github.com/chatmail/core">chatmail core</a>,
which would make it available in all <a href="https://chatmail.at/clients">chatmail clients</a>.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will provide reliable deletion (forward secrecy) through automatic key rotation.
This approach is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="pqc">
@@ -1474,12 +1475,13 @@ which would make it available in all <a href="https://chatmail.at/clients">chatm
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
We aim to add PQC support in <a href="https://github.com/chatmail/core">chatmail core</a> after the draft is finalized at the IETF
in collaboration with other OpenPGP implementers.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption to protect against quantum computer attacks.
Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
The implementation is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="how-can-i-manually-check-encryption-information">
+33 -31
View File
@@ -1181,6 +1181,10 @@ Chatmail использует INBOX по умолчанию для ретран
<li>
<p><a href="https://autocrypt.org">Autocrypt</a> используется для автоматической
настройки сквозного шифрования между контактами и всеми членами группового чата.</p>
</li>
<li>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption and forward secrecy.</p>
</li>
<li>
<p><a href="https://github.com/chatmail/core/blob/main/spec.md#attaching-a-contact-to-a-message">Обмен контактом в
@@ -1427,7 +1431,7 @@ Delta Chat вместо этого использует реализацию Ope
</h3>
<p>Нет, еще нет.</p>
<p>Нет, пока нет.</p>
<p>Мессенджер Signal внедрил функцию <a href="https://signal.org/blog/sealed-sender/">“Sealed Sender” (Засекреченный отправитель) в 2018 году</a>,
чтобы их серверная инфраструктура не имела информации о том, кто отправляет сообщение группе получателей.
@@ -1448,7 +1452,7 @@ Delta Chat вместо этого использует реализацию Ope
</h3>
<p>Нет, еще нет.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>На данный момент, Delta Chat не поддерживает Perfect Forward Secrecy (PFS) (Совершенную прямую секретность).
Это означает, что если ваш приватный ключ дешифрования будет скомпрометирован,
@@ -1459,12 +1463,9 @@ Delta Chat вместо этого использует реализацию Ope
также может получить все ваши не удалённые сообщения
и ему даже не нужно расшифровывать какие-либо ранее собранные сообщения.</p>
<p>Мы разработали подход к Forward Secrecy (Прямой секретности), который прошёл
первичную проверку некоторыми криптографами и экспертами по реализации
но требует более формального описания
чтобы убедиться, что он надёжно работает в федеративном обмене сообщениями и при использовании нескольких устройств,
прежде чем он может быть внедрён в <a href="https://github.com/chatmail/core">ядро chatmail</a>,
что сделает его доступным во всех <a href="https://chatmail.at/clients">клиентах clients</a>.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will provide reliable deletion (forward secrecy) through automatic key rotation.
This approach is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="pqc">
@@ -1474,12 +1475,13 @@ Delta Chat вместо этого использует реализацию Ope
</h3>
<p>Нет, еще нет.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat использует библиотеку OpenPGP на Rust <a href="https://github.com/rpgp/rpgp">rPGP</a>,
которая поддерживает последний <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">черновик IETF Post-Quantum-Cryptography OpenPGP</a>.
Мы планируем добавить поддержку PQC в <a href="https://github.com/chatmail/core">ядро chatmail</a> после того, как черновик будет окончательно утвержден в IETF
в сотрудничестве с другими разработчиками OpenPGP.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption to protect against quantum computer attacks.
Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
The implementation is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="как-можно-вручную-проверить-информацию-о-шифровании">
@@ -1658,32 +1660,32 @@ Google Play Store, F-Droid, Huawei App Gallery, iOS и macOS App Store, Microsof
<ul>
<li>
<p>In 2023 and 2024 we got accepted in the Next Generation Internet (NGI)
program for our work in <a href="https://nlnet.nl/project/WebXDC-Push/">webxdc PUSH</a>,
along with collaboration partners working on
<a href="https://nlnet.nl/project/Webxdc-Evolve/">webxdc evolve</a>,
<a href="https://nlnet.nl/project/WebXDC-XMPP/">webxdc XMPP</a>,
<a href="https://nlnet.nl/project/DeltaTouch/">DeltaTouch</a> and
<a href="https://nlnet.nl/project/DeltaTauri/">DeltaTauri</a>.
All of these projects are partially completed or to be completed in early 2025.</p>
<p>В 2023 и 2024 годах мы были приняты в программу Next Generation Internet (NGI)
за нашу работу над <a href="https://nlnet.nl/project/WebXDC-Push/">webxdc PUSH</a>,
в сотрудничестве с партнерами, работающими над
<a href="https://nlnet.nl/project/Webxdc-Evolve/">webxdc evolve</a>,
<a href="https://nlnet.nl/project/WebXDC-XMPP/">webxdc XMPP</a>,
<a href="https://nlnet.nl/project/DeltaTouch/">DeltaTouch</a> и
<a href="https://nlnet.nl/project/DeltaTauri/">DeltaTauri</a>.
Все эти проекты частично завершены или будут завершены в начале 2025 года.</p>
</li>
<li>
<p>В 2021 г. мы получили дополнительное финансирование из ЕС для двух Next-Generation-Internet
целей, а именно для <a href="https://dapsi.ngi.eu/hall-of-fame/eppd/">EPPD - e-mail provider portability directory</a> (~97 тыс. евро) и <a href="https://nlnet.nl/project/EmailPorting/">AEAP - email address porting</a> (~90 тыс. евро). Это привело к улучшению поддержки нескольких профилей, улучшению настройки контактов и групп с помощью QR-кода и многим улучшениям в сетевом взаимодействии на всех платформах.</p>
</li>
<li>
<p>The <a href="https://nlnet.nl/">NLnet foundation</a> granted in 2019/2020 EUR 46K for
completing Rust/Python bindings and instigating a Chat-bot eco-system.</p>
<p><a href="https://nlnet.nl/">Фонд NLnet</a> выделил в 2019/2020 году 46 тыс. евро на
завершение привязки Rust/Python и создание экосистемы чат-ботов.</p>
</li>
<li>
<p>The <a href="https://opentechfund.org">Open Technology Fund</a> gave us a
first 2018/2019 grant (~$200K) during which we majorly improved the Android app
and released a first Desktop app beta version, and which moreover
moored our feature developments in UX research in human rights contexts,
see our concluding <a href="https://delta.chat/en/2019-07-19-uxreport">Needfinding and UX report</a>.
The second 2019/2020 grant (~$300K) helped us to
release Delta/iOS versions, to convert our core library to Rust, and
to provide new features for all platforms.</p>
<p>Фонд <a href="https://opentechfund.org">Open Technology Fund</a> предоставил нам
первый грант в 2018/2019 году (~$200 тыс.), благодаря которому мы существенно улучшили приложение для Android
и выпустили первую бета-версию приложения для настольных систем, а также провели
исследования в области UX в контексте прав человека,
см. наш заключительный отчет <a href="https://delta.chat/en/2019-07-19-uxreport">Needfinding and UX report</a>.
Второй грант, полученный в 2019/2020 году (~$300 тыс.), помог нам
выпустить версии Delta/iOS, перевести наш основной код на Rust и
предоставить новые функции для всех платформ.</p>
</li>
<li>
<p>Проект ЕС <a href="https://nextleap.eu">NEXTLEAP</a> финансировал исследование
+14 -12
View File
@@ -1188,6 +1188,10 @@ to exchange encryption setup information through QR-code scanning or “invite l
<li>
<p><a href="https://autocrypt.org">Autocrypt</a> is used for automatically
establishing end-to-end encryption between contacts and all members of a group chat.</p>
</li>
<li>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption and forward secrecy.</p>
</li>
<li>
<p><a href="https://github.com/chatmail/core/blob/main/spec.md#attaching-a-contact-to-a-message">Sharing a contact to a
@@ -1453,7 +1457,7 @@ but an implementation has not been agreed as a priority yet.</p>
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat today doesnt support Perfect Forward Secrecy (PFS).
This means that if your private decryption key is leaked,
@@ -1464,12 +1468,9 @@ Otherwise, someone obtaining your decryption keys
is typically also able to get all your non-deleted messages
and doesnt even need to decrypt any previously collected messages.</p>
<p>We designed a Forward Secrecy approach that withstood
initial examination from some cryptographers and implementation experts
but is pending a more formal write up
to ascertain it reliably works in federated messaging and with multi-device usage,
before it could be implemented in <a href="https://github.com/chatmail/core">chatmail core</a>,
which would make it available in all <a href="https://chatmail.at/clients">chatmail clients</a>.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will provide reliable deletion (forward secrecy) through automatic key rotation.
This approach is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="pqc">
@@ -1479,12 +1480,13 @@ which would make it available in all <a href="https://chatmail.at/clients">chatm
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
We aim to add PQC support in <a href="https://github.com/chatmail/core">chatmail core</a> after the draft is finalized at the IETF
in collaboration with other OpenPGP implementers.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption to protect against quantum computer attacks.
Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
The implementation is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="how-can-i-manually-check-encryption-information">
+33 -28
View File
@@ -1190,6 +1190,10 @@ to exchange encryption setup information through QR-code scanning or “invite l
<li>
<p><a href="https://autocrypt.org">Autocrypt</a> is used for automatically
establishing end-to-end encryption between contacts and all members of a group chat.</p>
</li>
<li>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption and forward secrecy.</p>
</li>
<li>
<p><a href="https://github.com/chatmail/core/blob/main/spec.md#attaching-a-contact-to-a-message">Sharing a contact to a
@@ -1455,7 +1459,7 @@ but an implementation has not been agreed as a priority yet.</p>
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat today doesnt support Perfect Forward Secrecy (PFS).
This means that if your private decryption key is leaked,
@@ -1466,12 +1470,9 @@ Otherwise, someone obtaining your decryption keys
is typically also able to get all your non-deleted messages
and doesnt even need to decrypt any previously collected messages.</p>
<p>We designed a Forward Secrecy approach that withstood
initial examination from some cryptographers and implementation experts
but is pending a more formal write up
to ascertain it reliably works in federated messaging and with multi-device usage,
before it could be implemented in <a href="https://github.com/chatmail/core">chatmail core</a>,
which would make it available in all <a href="https://chatmail.at/clients">chatmail clients</a>.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will provide reliable deletion (forward secrecy) through automatic key rotation.
This approach is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="pqc">
@@ -1481,12 +1482,13 @@ which would make it available in all <a href="https://chatmail.at/clients">chatm
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
We aim to add PQC support in <a href="https://github.com/chatmail/core">chatmail core</a> after the draft is finalized at the IETF
in collaboration with other OpenPGP implementers.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption to protect against quantum computer attacks.
Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
The implementation is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="how-can-i-manually-check-encryption-information">
@@ -1675,28 +1677,31 @@ along with collaboration partners working on
All of these projects are partially completed or to be completed in early 2025.</p>
</li>
<li>
<p>In 2021 we received further EU funding for two Next-Generation-Internet
proposals, namely for <a href="https://dapsi.ngi.eu/hall-of-fame/eppd/">EPPD - email provider portability directory</a> (~97K EUR) and <a href="https://nlnet.nl/project/EmailPorting/">AEAP - email address porting</a> (~90K EUR) which resulted in better multi-profile support, improved QR-code contact and group setups and many networking improvements on all platforms.</p>
<p> 2021-n morëm financime të mëtejshme nga BE për dy propozime që shtrihen në
“Internetin e Brezit Tjetër”, konkretisht për <a href="https://dapsi.ngi.eu/hall-of-fame/eppd/">EPPD - e-mail provider portability directory</a> (~97K euro) dhe <a href="https://nlnet.nl/project/EmailPorting/">AEAP - email address porting</a> (~90K euro) që sollën mbulim më të mirë për përdorues me shumë
llogari, përmirësim të gjërave për kontakte me kod QR dhe grupe, si dhe mjaft
përmirësime në punën në rrjet për krejt platformat.</p>
</li>
<li>
<p>The <a href="https://nlnet.nl/">NLnet foundation</a> granted in 2019/2020 EUR 46K for
completing Rust/Python bindings and instigating a Chat-bot eco-system.</p>
<p><a href="https://nlnet.nl/">Fondacioni NLnet</a> dhuroi 46K euro gjatë 2019/2020 r
plotësimin e <em>Rust/Python bindings</em> dhe për ti dhënë udhë një ekosistemi
Chat-bot.</p>
</li>
<li>
<p>The <a href="https://opentechfund.org">Open Technology Fund</a> gave us a
first 2018/2019 grant (~$200K) during which we majorly improved the Android app
and released a first Desktop app beta version, and which moreover
moored our feature developments in UX research in human rights contexts,
see our concluding <a href="https://delta.chat/en/2019-07-19-uxreport">Needfinding and UX report</a>.
The second 2019/2020 grant (~$300K) helped us to
release Delta/iOS versions, to convert our core library to Rust, and
to provide new features for all platforms.</p>
<p><a href="https://opentechfund.org">Open Technology Fund</a> na dha grantin e parë
për 2018/2019 (~200 mijë dollarë) me të cilin përmirësuam ndjeshëm aplikacionin
për Android dhe hodhëm në qarkullim një version të parë beta aplikacioni për Desktop,
si dhe i afroi më tepër zhvillimet tona për veçori me kërkime UX në kontekste të drejtash të njeriut,
shihni <a href="https://delta.chat/en/2019-07-19-uxreport">raportin tonë përfundimtar “Needfinding and UX</a>.
Granti i dytë për 2019/2020 (~$300K) na ndihmoi të hedhim në qarkullim
versione Delta/iOS, për të shndërruar bibliotekën tonë bazë në Rust, si dhe
për të sjellë veçori të reja për krejt platformat.</p>
</li>
<li>
<p>The <a href="https://nextleap.eu">NEXTLEAP</a> EU project funded the research
and implementation of verified groups and setup contact protocols
in 2017 and 2018 and also helped to integrate end-to-end Encryption
through <a href="https://autocrypt.org">Autocrypt</a>.</p>
<p>Projekti <a href="https://nextleap.eu">NEXTLEAP</a> i BE-së financoi kërkimin
për dhe sendërtimin e grupeve të verifikuara dhe protokolleve të
ujdisjes së kontakteve më 2017-n dhe 2018-n dhe ndihmoi gjithashtu
të integrohet Fshehtëzim Skaj-më-Skaj përmes <a href="https://autocrypt.org">Autocrypt</a>.</p>
</li>
<li>
<p>Ndonjëherë marrim dhurime unike nga individë privatë.
+28 -33
View File
@@ -140,17 +140,10 @@
<ul>
<li>
<p>If you are <strong>face to face</strong> with your friend or family,
tap the <strong>QR Code</strong> icon <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" />
on the main screen.<br />
Ask your chat partner to <strong>scan</strong> the QR image
with their Delta Chat app.</p>
<p>Якщо ви перебуваєте <strong>віч-на-віч</strong> зі своїм другом або родиною, торкніться піктограми <strong>QR-код</strong> на головному екрані <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> на головному екрані. Попросіть вашого партнера по чату <strong>сканувати</strong> QR-зображення за допомогою їхнього застосунку Delta Chat.</p>
</li>
<li>
<p>For a <strong>remote</strong> contact setup,
from the same screen,
click “Copy” or “Share” and send the <strong>invite link</strong>
through another private chat.</p>
<p>Для <strong>віддаленого</strong> налаштування контакту, на тому ж самому екрані, натисніть “Копіювати” або “Поділитися” і відправте <strong>запрошувальне посилання</strong> через інший приватний чат.</p>
</li>
</ul>
@@ -1095,6 +1088,10 @@ to send anonymous usage statistics.</p>
<li>
<p><a href="https://autocrypt.org">Autocrypt</a> використовується для автоматичного встановлення наскрізного шифрування між контактами і всіма учасниками групового чату.</p>
</li>
<li>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption and forward secrecy.</p>
</li>
<li>
<p><a href="https://github.com/chatmail/core/blob/main/spec.md#attaching-a-contact-to-a-message">Поширення контакту в чаті</a> дозволяє отримувачам використовувати наскрізне шифрування з контактом.</p>
</li>
@@ -1298,11 +1295,13 @@ but an implementation has not been agreed as a priority yet.</p>
</h3>
<p>Ні, поки ще ні.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat наразі не підтримує ідеальну пряму секретність (Perfect Forward Secrecy, PFS). Це означає, що якщо ваш приватний ключ для розшифрування буде скомпрометовано, а хтось заздалегідь зібрав ваші повідомлення під час передачі, він зможе розшифрувати та прочитати їх, використовуючи зламаний ключ. Зверніть увагу, що пряма секретність підвищує рівень безпеки лише в тому разі, якщо ви видаляєте повідомлення. Інакше, якщо хтось отримує доступ до ваших ключів розшифрування, він зазвичай також має доступ до всіх ваших невидалених повідомлень і навіть не потребує розшифровувати заздалегідь перехоплені дані.</p>
<p>Ми розробили підхід Forward Secrecy, який витримав початкову експертизу від деяких криптографів та експертів з реалізації але чекає на більш офіційний звіт щоб переконатися, що він надійно працює в об’єднаних системах обміну повідомленнями та при використанні декількох пристроїв, перш ніж його можна буде реалізувати в <a href="https://github.com/chatmail/core">ядрі чату</a>, що зробить його доступним у всіх <a href="https://chatmail.at/clients">клієнтах чату</a>.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will provide reliable deletion (forward secrecy) through automatic key rotation.
This approach is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="pqc">
@@ -1312,9 +1311,13 @@ but an implementation has not been agreed as a priority yet.</p>
</h3>
<p>Ні, поки ще ні.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat використовує бібліотеку Rust OpenPGP <a href="https://github.com/rpgp/rpgp">rPGP</a> яка підтримує останню версію <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>. Ми плануємо додати підтримку PQC у <a href="https://github.com/chatmail/core">chatmail core</a> після того, як проект буде завершено у IETF у співпраці з іншими розробниками OpenPGP.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption to protect against quantum computer attacks.
Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
The implementation is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="як-я-можу-вручну-перевірити-інформацію-про-шифрування">
@@ -1445,32 +1448,24 @@ Google Play Store, F-Droid, Huawei App Gallery, iOS and macOS App Store, Microso
<ul>
<li>
<p>In 2023 and 2024 we got accepted in the Next Generation Internet (NGI)
program for our work in <a href="https://nlnet.nl/project/WebXDC-Push/">webxdc PUSH</a>,
along with collaboration partners working on
<a href="https://nlnet.nl/project/Webxdc-Evolve/">webxdc evolve</a>,
<a href="https://nlnet.nl/project/WebXDC-XMPP/">webxdc XMPP</a>,
<a href="https://nlnet.nl/project/DeltaTouch/">DeltaTouch</a> and
<a href="https://nlnet.nl/project/DeltaTauri/">DeltaTauri</a>.
All of these projects are partially completed or to be completed in early 2025.</p>
<p>У 2023 та 2024 роках нас прийняли до програми Next Generation Internet (NGI) за нашу роботу над <a href="https://nlnet.nl/project/WebXDC-Push/">webxdc PUSH</a>, а також у співпраці з партнерами, які працюють над <a href="https://nlnet.nl/project/Webxdc-Evolve/">webxdc evolve</a>, <a href="https://nlnet.nl/project/WebXDC-XMPP/">webxdc XMPP</a>, <a href="https://nlnet.nl/project/DeltaTouch/">DeltaTouch</a> та <a href="https://nlnet.nl/project/DeltaTauri/">DeltaTauri</a>. Усі ці проєкти частково завершені або будуть завершені на початку 2025 року.</p>
</li>
<li>
<p>In 2021 we received further EU funding for two Next-Generation-Internet
proposals, namely for <a href="https://dapsi.ngi.eu/hall-of-fame/eppd/">EPPD - email provider portability directory</a> (~97K EUR) and <a href="https://nlnet.nl/project/EmailPorting/">AEAP - email address porting</a> (~90K EUR) which resulted in better multi-profile support, improved QR-code contact and group setups and many networking improvements on all platforms.</p>
<p>У 2021 році ми отримали подальше фінансування від ЄС на дві пропозиції щодо Інтернету наступного покоління а саме на <a href="https://dapsi.ngi.eu/hall-of-fame/eppd/">EPPD - каталог перенесення провайдерів електронної пошти</a> (~97 тис. євро) та <a href="https://nlnet.nl/project/EmailPorting/">AEAP - перенесення адрес електронної пошти</a> (~90 тис. євро), що дозволило нам покращити багатопрофільну підтримку, вдосконалити налаштування контактів та груп за допомогою QR-коду та багато інших мережевих покращень на всіх платформах.</p>
</li>
<li>
<p>The <a href="https://nlnet.nl/">NLnet foundation</a> granted in 2019/2020 EUR 46K for
completing Rust/Python bindings and instigating a Chat-bot eco-system.</p>
<p>Фонд <a href="https://nlnet.nl/">NLnet</a> виділив у 2019/2020 роках 46 тисяч євро на
завершення прив’язок Rust/Python та запуск екосистеми чат-ботів.</p>
</li>
<li>
<p>The <a href="https://opentechfund.org">Open Technology Fund</a> gave us a
first 2018/2019 grant (~$200K) during which we majorly improved the Android app
and released a first Desktop app beta version, and which moreover
moored our feature developments in UX research in human rights contexts,
see our concluding <a href="https://delta.chat/en/2019-07-19-uxreport">Needfinding and UX report</a>.
The second 2019/2020 grant (~$300K) helped us to
release Delta/iOS versions, to convert our core library to Rust, and
to provide new features for all platforms.</p>
<p><a href="https://opentechfund.org">Open Technology Fund</a> надав нам два гранти.
Перший грант 2018/2019 року (~$200K), допоміг значно покращили додаток для Android
і випустили першу бета-версію додатка для ПК, і який до того ж
закріпив наші розробки функцій у дослідженнях UX у контексті прав людини,
дивіться наш підсумковий звіт <a href="https://delta.chat/en/2019-07-19-uxreport">Needfinding and UX report</a>.
Другий грант 2019/2020 року (~$300K) допоміг нам
випустити Delta/iOS версію, конвертувати нашу основному бібліотеку на Rust,
і додати нові функції для всіх платформ.</p>
</li>
<li>
<p>Проект ЄС <a href="https://nextleap.eu">NEXTLEAP</a> фінансував дослідження та впровадження верифікованих груп і протоколів встановлення контактів у 2017 та 2018 роках, а також допоміг інтегрувати наскрізне шифрування через <a href="https://autocrypt.org">Autocrypt</a>.</p>
+24 -32
View File
@@ -1161,6 +1161,10 @@ weekly statistics will be automatically sent to a bot.</p>
<li>
<p><a href="https://autocrypt.org">Autocrypt</a> is used for automatically
用于在联系人和群聊的所有成员之间自动建立端到端加密。</p>
</li>
<li>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption and forward secrecy.</p>
</li>
<li>
<p><a href="https://github.com/chatmail/core/blob/main/spec.md#attaching-a-contact-to-a-message">将联系人分享到聊天中
@@ -1423,7 +1427,7 @@ but an implementation has not been agreed as a priority yet.</p>
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat today doesnt support Perfect Forward Secrecy (PFS).
This means that if your private decryption key is leaked,
@@ -1434,12 +1438,9 @@ Otherwise, someone obtaining your decryption keys
is typically also able to get all your non-deleted messages
and doesnt even need to decrypt any previously collected messages.</p>
<p>We designed a Forward Secrecy approach that withstood
initial examination from some cryptographers and implementation experts
but is pending a more formal write up
to ascertain it reliably works in federated messaging and with multi-device usage,
before it could be implemented in <a href="https://github.com/chatmail/core">chatmail core</a>,
which would make it available in all <a href="https://chatmail.at/clients">chatmail clients</a>.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will provide reliable deletion (forward secrecy) through automatic key rotation.
This approach is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="pqc">
@@ -1449,12 +1450,13 @@ which would make it available in all <a href="https://chatmail.at/clients">chatm
</h3>
<p>No, not yet.</p>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
We aim to add PQC support in <a href="https://github.com/chatmail/core">chatmail core</a> after the draft is finalized at the IETF
in collaboration with other OpenPGP implementers.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption to protect against quantum computer attacks.
Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">rPGP</a>
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
The implementation is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="how-can-i-manually-check-encryption-information">
@@ -1624,32 +1626,22 @@ Google Play Store, F-Droid, Huawei App Gallery, iOS and macOS App Store, Microso
<ul>
<li>
<p>In 2023 and 2024 we got accepted in the Next Generation Internet (NGI)
program for our work in <a href="https://nlnet.nl/project/WebXDC-Push/">webxdc PUSH</a>,
along with collaboration partners working on
<a href="https://nlnet.nl/project/Webxdc-Evolve/">webxdc evolve</a>,
<a href="https://nlnet.nl/project/WebXDC-XMPP/">webxdc XMPP</a>,
<a href="https://nlnet.nl/project/DeltaTouch/">DeltaTouch</a> and
<a href="https://nlnet.nl/project/DeltaTauri/">DeltaTauri</a>.
All of these projects are partially completed or to be completed in early 2025.</p>
<p> 2023 年和 2024 年,我们的 <a href="https://nlnet.nl/project/WebXDC-Push/">WebXDC PUSH</a> 工作已在下一代互联网 (NGI) 中获得认可,
并与致力于
<a href="https://nlnet.nl/project/Webxdc-Evolve/">WebXDC evolve</a>
<a href="https://nlnet.nl/project/WebXDC-XMPP/">WebXDC XMPP</a>
<a href="https://nlnet.nl/project/DeltaTouch/">DeltaTouch</a>
<a href="https://nlnet.nl/project/DeltaTauri/">DeltaTauri</a> 的合作伙伴合作。
所有这些项目都已部分完成或将在 2025 年初完成。</p>
</li>
<li>
<p>In 2021 we received further EU funding for two Next-Generation-Internet
proposals, namely for <a href="https://dapsi.ngi.eu/hall-of-fame/eppd/">EPPD - email provider portability directory</a> (~97K EUR) and <a href="https://nlnet.nl/project/EmailPorting/">AEAP - email address porting</a> (~90K EUR) which resulted in better multi-profile support, improved QR-code contact and group setups and many networking improvements on all platforms.</p>
<p> 2021 年,我们从两项下一代互联网提案收到了欧盟的进一步资助,即 <a href="https://dapsi.ngi.eu/hall-of-fame/eppd/">EPPD - 电子邮件提供商可移植性目录</a>(约 9.7 万欧元)和 <a href="https://nlnet.nl/project/EmailPorting/">AEAP - 电子邮件地址移植</a>(约 9 万欧元)。这带来了更好的多账户支持,改进的二维码联系人和群组设置,和所有平台上的多处网络改进。</p>
</li>
<li>
<p>The <a href="https://nlnet.nl/">NLnet foundation</a> granted in 2019/2020 EUR 46K for
completing Rust/Python bindings and instigating a Chat-bot eco-system.</p>
<p><a href="https://nlnet.nl/">NLnet 基金会</a> 2019/2020 年拨款 4.6 万欧元,用于完成 Rust/Python 绑定并建立聊天机器人生态系统。</p>
</li>
<li>
<p>The <a href="https://opentechfund.org">Open Technology Fund</a> gave us a
first 2018/2019 grant (~$200K) during which we majorly improved the Android app
and released a first Desktop app beta version, and which moreover
moored our feature developments in UX research in human rights contexts,
see our concluding <a href="https://delta.chat/en/2019-07-19-uxreport">Needfinding and UX report</a>.
The second 2019/2020 grant (~$300K) helped us to
release Delta/iOS versions, to convert our core library to Rust, and
to provide new features for all platforms.</p>
<p><a href="https://opentechfund.org">开放技术基金</a> 2018/2019 年提供的第一笔赠款(约 20 万美元)期间,我们显著改善了安卓应用,发布了第一个桌面测试版,并根据人权方面的用户体验研究进行了功能开发,请参阅我们的结论<a href="https://delta.chat/en/2019-07-19-uxreport">《需求发现与用户体验报告》</a>。2019/2020 年的第二笔赠款(约 30 万美元)对发布 Delta/iOS 版本,将核心库转换到 Rust ,以及为所有平台开发新功能提供了帮助。</p>
</li>
<li>
<p><a href="https://nextleap.eu">NEXTLEAP</a>欧盟项目资助了以下研究和实施工作:在 2017 年和 2018 年实施的验证组和设置联系协议和通过 <a href="https://autocrypt.org">Autocrypt</a>整合了端到端加密。</p>
+2 -2
View File
@@ -1315,8 +1315,8 @@ public class Rpc {
}
/** Starts an outgoing call. */
public Integer placeOutgoingCall(Integer accountId, Integer chatId, String placeCallInfo) throws RpcException {
return transport.callForResult(new TypeReference<Integer>(){}, "place_outgoing_call", mapper.valueToTree(accountId), mapper.valueToTree(chatId), mapper.valueToTree(placeCallInfo));
public Integer placeOutgoingCall(Integer accountId, Integer chatId, String placeCallInfo, Boolean hasVideo) throws RpcException {
return transport.callForResult(new TypeReference<Integer>(){}, "place_outgoing_call", mapper.valueToTree(accountId), mapper.valueToTree(chatId), mapper.valueToTree(placeCallInfo), mapper.valueToTree(hasVideo));
}
/** Accepts an incoming call. */
@@ -2,7 +2,7 @@
package chat.delta.rpc.types;
public class CallInfo {
/** True if SDP offer has a video. */
/** True if the call is started as a video call. */
public Boolean hasVideo;
/**
* SDP offer.
@@ -19,4 +19,4 @@ public enum SecurejoinSource {
/** The user scanned a QR code */
Scan,
}
}
@@ -10,4 +10,4 @@ public enum SecurejoinUiPath {
/** The user first clicked on the `+` button in the main screen, and then on "New Contact" */
NewContact,
}
}
@@ -2,8 +2,8 @@ package com.b44t.messenger;
public class DcAccounts {
public DcAccounts(String dir) {
accountsCPtr = createAccountsCPtr(dir);
public DcAccounts(String dir, DcEventChannel channel) {
accountsCPtr = createAccountsCPtr(dir, channel);
if (accountsCPtr == 0) throw new RuntimeException("createAccountsCPtr() returned null pointer");
}
@@ -46,7 +46,7 @@ public class DcAccounts {
// working with raw c-data
private long accountsCPtr; // CAVE: the name is referenced in the JNI
private native long createAccountsCPtr (String dir);
private native long createAccountsCPtr (String dir, DcEventChannel channel);
private native void unrefAccountsCPtr ();
private native long getEventEmitterCPtr ();
private native long getJsonrpcInstanceCPtr ();
@@ -0,0 +1,27 @@
package com.b44t.messenger;
public class DcEventChannel {
public DcEventChannel() {
eventChannelCPtr = createEventChannelCPtr();
}
@Override
protected void finalize() throws Throwable {
super.finalize();
if (eventChannelCPtr != 0) {
unrefEventChannelCPtr();
eventChannelCPtr = 0;
}
}
public DcEventEmitter getEventEmitter() {
return new DcEventEmitter(getEventEmitterCPtr());
}
// working with raw c-data
private long eventChannelCPtr; // CAVE: the name is referenced in the JNI
private native long createEventChannelCPtr ();
private native void unrefEventChannelCPtr ();
private native long getEventEmitterCPtr ();
}
@@ -1,16 +1,24 @@
package org.thoughtcrime.securesms;
import android.content.ComponentName;
import android.os.Bundle;
import android.util.Log;
import android.view.MenuItem;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.view.ActionMode;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.lifecycle.ViewModelProvider;
import androidx.media3.session.MediaController;
import androidx.media3.session.SessionCommand;
import androidx.media3.session.SessionToken;
import androidx.viewpager.widget.ViewPager;
import com.b44t.messenger.DcChat;
@@ -18,11 +26,13 @@ import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcEvent;
import com.b44t.messenger.DcMsg;
import com.google.android.material.tabs.TabLayout;
import com.google.common.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.components.audioplay.AudioPlaybackViewModel;
import org.thoughtcrime.securesms.connect.DcEventCenter;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.service.AudioPlaybackService;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.ArrayList;
@@ -30,6 +40,7 @@ import java.util.ArrayList;
public class AllMediaActivity extends PassphraseRequiredActionBarActivity
implements DcEventCenter.DcEventDelegate
{
private static final String TAG = AllMediaActivity.class.getSimpleName();
public static final String CHAT_ID_EXTRA = "chat_id";
public static final String CONTACT_ID_EXTRA = "contact_id";
@@ -57,6 +68,10 @@ public class AllMediaActivity extends PassphraseRequiredActionBarActivity
private TabLayout tabLayout;
private ViewPager viewPager;
private @Nullable MediaController mediaController;
private ListenableFuture<MediaController> mediaControllerFuture;
private AudioPlaybackViewModel playbackViewModel;
@Override
protected void onPreCreate() {
dynamicTheme = new DynamicNoActionBarTheme();
@@ -91,11 +106,19 @@ public class AllMediaActivity extends PassphraseRequiredActionBarActivity
DcEventCenter eventCenter = DcHelper.getEventCenter(this);
eventCenter.addObserver(DcContext.DC_EVENT_CHAT_MODIFIED, this);
eventCenter.addObserver(DcContext.DC_EVENT_CONTACTS_CHANGED, this);
playbackViewModel = new ViewModelProvider(this).get(AudioPlaybackViewModel.class);
initializeMediaController();
}
@Override
public void onDestroy() {
DcHelper.getEventCenter(this).removeObservers(this);
if (mediaController != null) {
MediaController.releaseFuture(mediaControllerFuture);
mediaController = null;
playbackViewModel.setMediaController(null);
}
super.onDestroy();
}
@@ -124,6 +147,40 @@ public class AllMediaActivity extends PassphraseRequiredActionBarActivity
this.tabLayout = ViewUtil.findById(this, R.id.tab_layout);
}
private void initializeMediaController() {
SessionToken sessionToken = new SessionToken(this,
new ComponentName(this, AudioPlaybackService.class));
mediaControllerFuture = new MediaController.Builder(this, sessionToken)
.buildAsync();
mediaControllerFuture.addListener(() -> {
try {
mediaController = mediaControllerFuture.get();
addActivityContext(
this.getIntent().getExtras(),
this.getClass().getName()
);
playbackViewModel.setMediaController(mediaController);
} catch (Exception e) {
Log.e(TAG, "Error connecting to audio playback service", e);
}
}, ContextCompat.getMainExecutor(this));
}
private void addActivityContext(Bundle extras, String activityClassName) {
if (mediaController == null) return;
Bundle commandArgs = new Bundle();
commandArgs.putString("activity_class", activityClassName);
if (extras != null) {
commandArgs.putAll(extras);
}
SessionCommand updateContextCommand =
new SessionCommand("UPDATE_ACTIVITY_CONTEXT", Bundle.EMPTY);
mediaController.sendCustomCommand(updateContextCommand, commandArgs);
}
private boolean isGlobalGallery() {
return contactId==0 && chatId==0;
}
@@ -11,9 +11,10 @@ import androidx.annotation.NonNull;
import com.b44t.messenger.DcMsg;
import com.codewaves.stickyheadergrid.StickyHeaderGridAdapter;
import org.thoughtcrime.securesms.components.AudioView;
import org.thoughtcrime.securesms.components.DocumentView;
import org.thoughtcrime.securesms.components.WebxdcView;
import org.thoughtcrime.securesms.components.audioplay.AudioPlaybackViewModel;
import org.thoughtcrime.securesms.components.audioplay.AudioView;
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.BucketedThreadMedia;
import org.thoughtcrime.securesms.mms.AudioSlide;
import org.thoughtcrime.securesms.mms.DocumentSlide;
@@ -31,7 +32,8 @@ class AllMediaDocumentsAdapter extends StickyHeaderGridAdapter {
private final ItemClickListener itemClickListener;
private final Set<DcMsg> selected;
private BucketedThreadMedia media;
private BucketedThreadMedia media;
private AudioPlaybackViewModel playbackViewModel;
private static class ViewHolder extends StickyHeaderGridAdapter.ItemViewHolder {
private final DocumentView documentView;
@@ -71,6 +73,10 @@ class AllMediaDocumentsAdapter extends StickyHeaderGridAdapter {
this.media = media;
}
public void setPlaybackViewModel(AudioPlaybackViewModel playbackViewModel) {
this.playbackViewModel = playbackViewModel;
}
@Override
public StickyHeaderGridAdapter.HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent, int headerType) {
return new HeaderHolder(LayoutInflater.from(context).inflate(R.layout.contact_selection_list_divider, parent, false));
@@ -97,6 +103,7 @@ class AllMediaDocumentsAdapter extends StickyHeaderGridAdapter {
viewHolder.webxdcView.setVisibility(View.GONE);
viewHolder.audioView.setVisibility(View.VISIBLE);
viewHolder.audioView.setPlaybackViewModel(playbackViewModel);
viewHolder.audioView.setAudio((AudioSlide)slide, dcMsg.getDuration());
viewHolder.audioView.setOnClickListener(view -> itemClickListener.onMediaClicked(dcMsg));
viewHolder.audioView.setOnLongClickListener(view -> { itemClickListener.onMediaLongClicked(dcMsg); return true; });
@@ -16,6 +16,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode;
import androidx.lifecycle.ViewModelProvider;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.RecyclerView;
@@ -25,6 +26,7 @@ import com.b44t.messenger.DcEvent;
import com.b44t.messenger.DcMsg;
import com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager;
import org.thoughtcrime.securesms.components.audioplay.AudioPlaybackViewModel;
import org.thoughtcrime.securesms.connect.DcEventCenter;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader;
@@ -72,9 +74,11 @@ public class AllMediaDocumentsFragment
// add padding to avoid content hidden behind system bars
ViewUtil.applyWindowInsets(recyclerView, true, false, true, true);
this.recyclerView.setAdapter(new AllMediaDocumentsAdapter(getContext(),
new BucketedThreadMediaLoader.BucketedThreadMedia(getContext()),
this));
AllMediaDocumentsAdapter adapter = new AllMediaDocumentsAdapter(getContext(),
new BucketedThreadMediaLoader.BucketedThreadMedia(getContext()),
this);
this.recyclerView.setAdapter(adapter);
adapter.setPlaybackViewModel(new ViewModelProvider(requireActivity()).get(AudioPlaybackViewModel.class));
this.recyclerView.setLayoutManager(gridManager);
this.recyclerView.setHasFixedSize(true);
@@ -239,12 +243,13 @@ public class AllMediaDocumentsFragment
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem) {
int itemId = menuItem.getItemId();
AudioPlaybackViewModel playbackViewModel = new ViewModelProvider(requireActivity()).get(AudioPlaybackViewModel.class);
if (itemId == R.id.details) {
handleDisplayDetails(getSelectedMessageRecord(getListAdapter().getSelectedMedia()));
mode.finish();
return true;
} else if (itemId == R.id.delete) {
handleDeleteMessages(chatId, getListAdapter().getSelectedMedia());
handleDeleteMessages(chatId, getListAdapter().getSelectedMedia(), playbackViewModel::stopByIds, playbackViewModel::stopByIds);
mode.finish();
return true;
} else if (itemId == R.id.share) {
@@ -23,6 +23,7 @@ import androidx.work.WorkManager;
import com.b44t.messenger.DcAccounts;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcEvent;
import com.b44t.messenger.DcEventChannel;
import com.b44t.messenger.DcEventEmitter;
import com.b44t.messenger.FFITransport;
@@ -164,7 +165,8 @@ public class ApplicationContext extends MultiDexApplication {
StringWriter stringWriter = new StringWriter();
throwable.printStackTrace(new PrintWriter(stringWriter, true));
String errorMsg = "Android " + Build.VERSION.RELEASE +":\n" + stringWriter.getBuffer().toString();
String subject = "ArcaneChat " + BuildConfig.VERSION_NAME + " Crash Report";
errorMsg += "\n" + LogViewFragment.grabLogcat();
String subject = "ArcaneChat " + BuildConfig.VERSION_NAME + "-" + BuildConfig.FLAVOR + " Crash Report";
Intent intent = new Intent(android.content.Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
@@ -195,7 +197,29 @@ public class ApplicationContext extends MultiDexApplication {
Util.runOnBackground(() -> {
synchronized (initLock) {
try {
dcAccounts = new DcAccounts(new File(getFilesDir(), "accounts").getAbsolutePath());
DcEventChannel eventChannel = new DcEventChannel();
DcEventEmitter emitter = eventChannel.getEventEmitter();
eventCenter = new DcEventCenter(this);
new Thread(() -> {
Log.i(TAG, "Starting event loop");
while (true) {
DcEvent event = emitter.getNextEvent();
if (event == null) {
break;
}
if (isInitialized) {
eventCenter.handleEvent(event);
} else {
// not fully initialized, only handle logging events,
// ex. account migrations during DcAccounts initialization
eventCenter.handleLogging(event);
}
}
Log.i("DeltaChat", "shutting down event handler");
}, "eventThread").start();
dcAccounts = new DcAccounts(new File(getFilesDir(), "accounts").getAbsolutePath(), eventChannel);
Log.i(TAG, "DcAccounts created");
rpc = new Rpc(new FFITransport(dcAccounts.getJsonrpcInstance()));
Log.i(TAG, "Rpc created");
@@ -235,28 +259,12 @@ public class ApplicationContext extends MultiDexApplication {
}
dcContext = dcAccounts.getSelectedAccount();
notificationCenter = new NotificationCenter(this);
eventCenter = new DcEventCenter(this);
dcLocationManager = new DcLocationManager(this, dcContext);
// Mark as initialized before starting threads that depend on it
isInitialized = true;
initLock.notifyAll();
Log.i(TAG, "DcAccounts initialization complete");
new Thread(() -> {
Log.i(TAG, "Starting event loop");
DcEventEmitter emitter = dcAccounts.getEventEmitter();
Log.i(TAG, "DcEventEmitter obtained");
while (true) {
DcEvent event = emitter.getNextEvent();
if (event==null) {
break;
}
eventCenter.handleEvent(event);
}
Log.i("DeltaChat", "shutting down event handler");
}, "eventThread").start();
// set translations before starting I/O to avoid sending untranslated MDNs (issue #2288)
DcHelper.setStockTranslations(this);
@@ -50,11 +50,11 @@ public abstract class BaseConversationItem extends LinearLayout
this.rpc = DcHelper.getRpc(context);
}
protected void bind(@NonNull DcMsg messageRecord,
@NonNull DcChat dcChat,
@NonNull Set<DcMsg> batchSelected,
boolean pulseHighlight,
@NonNull Recipient conversationRecipient)
protected void bindPartial(@NonNull DcMsg messageRecord,
@NonNull DcChat dcChat,
@NonNull Set<DcMsg> batchSelected,
boolean pulseHighlight,
@NonNull Recipient conversationRecipient)
{
this.messageRecord = messageRecord;
this.dcChat = dcChat;
@@ -126,6 +126,8 @@ public abstract class BaseConversationItem extends LinearLayout
public void onClick(View v) {
if (!shouldInterceptClicks(messageRecord) && parent != null) {
// The click workaround on ConversationItem shall be revised.
// In fact, it is probably better rethinking accessibility approach for the items.
if (batchSelected.isEmpty() && Util.isTouchExplorationEnabled(context)) {
BaseConversationItem.this.onAccessibilityClick();
}
@@ -6,6 +6,8 @@ import androidx.annotation.Nullable;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcMsg;
import org.thoughtcrime.securesms.components.audioplay.AudioPlaybackViewModel;
import org.thoughtcrime.securesms.components.audioplay.AudioView;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -17,7 +19,9 @@ public interface BindableConversationItem extends Unbindable {
@NonNull GlideRequests glideRequests,
@NonNull Set<DcMsg> batchSelected,
@NonNull Recipient recipients,
boolean pulseHighlight);
boolean pulseHighlight,
@Nullable AudioPlaybackViewModel playbackViewModel,
AudioView.OnActionListener audioPlayPauseListener);
DcMsg getMessageRecord();
@@ -34,6 +34,7 @@ import java.util.List;
public class ContactMultiSelectionActivity extends ContactSelectionActivity {
public static final String CONTACTS_EXTRA = "contacts_extra";
public static final String DESELECTED_CONTACTS_EXTRA = "deselected_contacts_extra";
@Override
protected void onCreate(Bundle icicle, boolean ready) {
@@ -71,7 +72,9 @@ public class ContactMultiSelectionActivity extends ContactSelectionActivity {
private void saveSelection() {
Intent resultIntent = getIntent();
List<Integer> selectedContacts = contactsFragment.getSelectedContacts();
List<Integer> deselectedContacts = contactsFragment.getDeselectedContacts();
resultIntent.putIntegerArrayListExtra(CONTACTS_EXTRA, new ArrayList<>(selectedContacts));
resultIntent.putIntegerArrayListExtra(DESELECTED_CONTACTS_EXTRA, new ArrayList<>(deselectedContacts));
setResult(RESULT_OK, resultIntent);
}
}
@@ -58,6 +58,7 @@ import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -83,6 +84,7 @@ public class ContactSelectionListFragment extends Fragment
private DcContext dcContext;
private Set<Integer> selectedContacts;
private Set<Integer> deselectedContacts;
private OnContactSelectedListener onContactSelectedListener;
private String cursorFilter;
private RecyclerView recyclerView;
@@ -127,6 +129,7 @@ public class ContactSelectionListFragment extends Fragment
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.contact_list, menu);
menu.findItem(R.id.menu_delete_selected).setVisible(!isMulti());
updateActionModeState(actionMode);
return true;
}
@@ -226,6 +229,15 @@ public class ContactSelectionListFragment extends Fragment
return selected;
}
public @NonNull List<Integer> getDeselectedContacts() {
List<Integer> deselected = new LinkedList<>();
if (deselectedContacts != null) {
deselected.addAll(deselectedContacts);
}
return deselected;
}
private boolean isMulti() {
return getActivity().getIntent().getBooleanExtra(MULTI_SELECT, false);
}
@@ -241,6 +253,7 @@ public class ContactSelectionListFragment extends Fragment
isMulti(),
true);
selectedContacts = adapter.getSelectedContacts();
deselectedContacts = new HashSet<>();
ArrayList<Integer> preselectedContacts = getActivity().getIntent().getIntegerArrayListExtra(PRESELECTED_CONTACTS);
if(preselectedContacts!=null) {
selectedContacts.addAll(preselectedContacts);
@@ -309,12 +322,14 @@ public class ContactSelectionListFragment extends Fragment
}
selectedContacts.add(contactId);
deselectedContacts.remove(contactId);
contact.setChecked(true);
if (onContactSelectedListener != null) {
onContactSelectedListener.onContactSelected(contactId);
}
} else {
selectedContacts.remove(contactId);
deselectedContacts.add(contactId);
contact.setChecked(false);
if (onContactSelectedListener != null) {
onContactSelectedListener.onContactDeselected(contactId);
@@ -355,6 +370,7 @@ public class ContactSelectionListFragment extends Fragment
int contactId = data.getIntExtra(NewContactActivity.CONTACT_ID_EXTRA, 0);
if (contactId != 0) {
selectedContacts.add(contactId);
deselectedContacts.remove(contactId);
}
getLoaderManager().restartLoader(0, null, ContactSelectionListFragment.this);
}
@@ -25,6 +25,7 @@ import android.Manifest;
import android.annotation.SuppressLint;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -65,7 +66,12 @@ import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.core.view.WindowCompat;
import androidx.lifecycle.ViewModelProvider;
import androidx.media3.session.MediaController;
import androidx.media3.session.SessionCommand;
import androidx.media3.session.SessionToken;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContact;
@@ -76,7 +82,7 @@ import com.b44t.messenger.DcMsg;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.UriAttachment;
import org.thoughtcrime.securesms.audio.AudioRecorder;
import org.thoughtcrime.securesms.audio.AudioSlidePlayer;
import org.thoughtcrime.securesms.calls.CallUtil;
import org.thoughtcrime.securesms.components.AnimatingToggle;
import org.thoughtcrime.securesms.components.AttachmentTypeSelector;
import org.thoughtcrime.securesms.components.ComposeText;
@@ -86,6 +92,8 @@ import org.thoughtcrime.securesms.components.InputPanel;
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout.OnKeyboardShownListener;
import org.thoughtcrime.securesms.components.ScaleStableImageView;
import org.thoughtcrime.securesms.components.SendButton;
import org.thoughtcrime.securesms.components.audioplay.AudioPlaybackViewModel;
import org.thoughtcrime.securesms.components.audioplay.AudioView;
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
import org.thoughtcrime.securesms.connect.AccountManager;
import org.thoughtcrime.securesms.connect.DcEventCenter;
@@ -104,19 +112,19 @@ import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.scribbles.ScribbleActivity;
import org.thoughtcrime.securesms.service.AudioPlaybackService;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.ShareUtil;
import org.thoughtcrime.securesms.util.SendRelayedMessageUtil;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.ShareUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener;
import org.thoughtcrime.securesms.util.guava.Optional;
import org.thoughtcrime.securesms.util.views.ProgressDialog;
import org.thoughtcrime.securesms.video.recode.VideoRecoder;
import org.thoughtcrime.securesms.calls.CallUtil;
import java.io.File;
import java.util.ArrayList;
@@ -138,14 +146,13 @@ import chat.delta.util.SettableFuture;
*/
@SuppressLint("StaticFieldLeak")
public class ConversationActivity extends PassphraseRequiredActionBarActivity
implements ConversationFragment.ConversationFragmentListener,
AttachmentManager.AttachmentListener,
SearchView.OnQueryTextListener,
DcEventCenter.DcEventDelegate,
OnKeyboardShownListener,
InputPanel.Listener,
InputPanel.MediaListener
{
implements ConversationFragment.ConversationFragmentListener,
AttachmentManager.AttachmentListener,
SearchView.OnQueryTextListener,
DcEventCenter.DcEventDelegate,
OnKeyboardShownListener,
InputPanel.Listener,
InputPanel.MediaListener, AudioView.OnActionListener {
private static final String TAG = ConversationActivity.class.getSimpleName();
public static final String ACCOUNT_ID_EXTRA = "account_id";
@@ -185,6 +192,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private MediaKeyboard emojiPicker;
protected HidingLinearLayout quickAttachmentToggle;
private InputPanel inputPanel;
private @Nullable MediaController mediaController;
private com.google.common.util.concurrent.ListenableFuture<MediaController> mediaControllerFuture;
private AudioPlaybackViewModel playbackViewModel;
private ApplicationContext context;
private Recipient recipient;
@@ -217,6 +227,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
initializeActionBar();
initializeViews();
initializeResources();
playbackViewModel = new ViewModelProvider(this).get(AudioPlaybackViewModel.class);
initializeMediaController();
initializeSecurity(false, isDefaultSms).addListener(new AssertedSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean result) {
@@ -267,6 +281,36 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
}
private void initializeMediaController() {
SessionToken sessionToken = new SessionToken(this,
new ComponentName(this, AudioPlaybackService.class));
mediaControllerFuture = new MediaController.Builder(this, sessionToken)
.buildAsync();
mediaControllerFuture.addListener(() -> {
try {
mediaController = mediaControllerFuture.get();
playbackViewModel.setMediaController(mediaController);
} catch (Exception e) {
Log.e(TAG, "Error connecting to audio playback service", e);
}
}, ContextCompat.getMainExecutor(this));
}
private void addActivityContext(Bundle extras, String activityClassName) {
if (mediaController == null) return;
Bundle commandArgs = new Bundle();
commandArgs.putString("activity_class", activityClassName);
if (extras != null) {
commandArgs.putAll(extras);
}
SessionCommand updateContextCommand =
new SessionCommand("UPDATE_ACTIVITY_CONTEXT", Bundle.EMPTY);
mediaController.sendCustomCommand(updateContextCommand, commandArgs);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
@@ -337,7 +381,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
DcHelper.getNotificationCenter(this).clearVisibleChat();
if (isFinishing()) overridePendingTransition(R.anim.fade_scale_in, R.anim.slide_to_right);
inputPanel.onPause();
AudioSlidePlayer.stopAll();
}
@Override
@@ -357,6 +400,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
protected void onDestroy() {
DcHelper.getEventCenter(this).removeObservers(this);
if (mediaController != null) {
MediaController.releaseFuture(mediaControllerFuture);
mediaController = null;
playbackViewModel.setMediaController(null);
}
super.onDestroy();
}
@@ -569,8 +617,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} else if (itemId == R.id.menu_show_map) {
WebxdcActivity.openMaps(this, chatId);
return true;
} else if (itemId == R.id.menu_start_call) {
CallUtil.startCall(this, chatId);
} else if (itemId == R.id.menu_start_audio_call) {
CallUtil.startCall(this, chatId, false);
return true;
} else if (itemId == R.id.menu_start_video_call) {
CallUtil.startCall(this, chatId, true);
return true;
} else if (itemId == R.id.menu_all_media) {
handleAllMedia();
@@ -631,6 +682,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
extras.putInt(ConversationListFragment.RELOAD_LIST, 1);
}
playbackViewModel.stopNonMessageAudioPlayback();
boolean archived = getIntent().getBooleanExtra(FROM_ARCHIVED_CHATS_EXTRA, false);
Intent intent = new Intent(this, (archived ? ConversationListArchiveActivity.class : ConversationListActivity.class));
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -1004,20 +1057,30 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
recipient = new Recipient(this, dcChat);
glideRequests = GlideApp.with(this);
setComposePanelVisibility();
setComposePanelVisibility(true);
initializeContactRequest();
}
private void setComposePanelVisibility() {
private void setComposePanelVisibility(boolean isInitialization) {
if (dcChat.canSend()) {
composePanel.setVisibility(View.VISIBLE);
attachmentManager.setHidden(false);
inputPanel.setSubjectVisible(!dcChat.isEncrypted());
// FIXME: disabled for now to avoid problems with chat scrolling and keyboard covering input bar
// ViewUtil.forceApplyWindowInsets(findViewById(R.id.root_layout), true, false, true, true);
// fragment.handleRemoveBottomInsets();
} else {
composePanel.setVisibility(View.GONE);
attachmentManager.setHidden(true);
hideSoftKeyboard();
inputPanel.setSubjectVisible(false);
// FIXME: disabled for now to avoid problems with chat scrolling and keyboard covering input bar
/*
if (isInitialization) {
ViewUtil.forceApplyWindowInsets(findViewById(R.id.root_layout), true, false, true, false);
fragment.handleAddBottomInsets();
}
*/
}
}
@@ -1054,11 +1117,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
return new SettableFuture<>(false);
}
return attachmentManager.setMedia(glideRequests, uri, null, mediaType, 0, 0, chatId);
return attachmentManager.setMedia(glideRequests, uri, null, mediaType, 0, 0, chatId, playbackViewModel);
}
private ListenableFuture<Boolean> setMedia(DcMsg msg, @NonNull MediaType mediaType) {
return attachmentManager.setMedia(glideRequests, Uri.fromFile(new File(msg.getFile())), msg, mediaType, 0, 0, chatId);
return attachmentManager.setMedia(glideRequests, Uri.fromFile(new File(msg.getFile())), msg, mediaType, 0, 0, chatId, playbackViewModel);
}
private void addAttachmentContactInfo(int contactId) {
@@ -1110,6 +1173,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
inputPanel.clearSubject();
}
// Stop draft audio playback regardless, since it is unlikely
// we will need background playback for drafts
playbackViewModel.stopNonMessageAudioPlayback();
DcContext dcContext = DcHelper.getContext(context);
Util.runOnAnyBackgroundThread(() -> {
DcMsg msg = null;
@@ -1422,6 +1489,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
// Listeners
@Override
public void onPlayPauseButtonClicked(View view) {
addActivityContext(
this.getIntent().getExtras(),
this.getClass().getName()
);
}
private class AttachmentTypeListener implements AttachmentTypeSelector.AttachmentClickedListener {
@Override
public void onClick(int type) {
@@ -1590,7 +1665,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
dcChat = dcContext.getChat(chatId);
titleView.setTitle(glideRequests, dcChat);
initializeSecurity(isSecureText, isDefaultSms);
setComposePanelVisibility();
setComposePanelVisibility(false);
initializeContactRequest();
} else if ((eventId == DcContext.DC_EVENT_INCOMING_MSG
|| eventId == DcContext.DC_EVENT_MSG_READ)
@@ -34,6 +34,8 @@ import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcMsg;
import org.thoughtcrime.securesms.ConversationAdapter.HeaderViewHolder;
import org.thoughtcrime.securesms.components.audioplay.AudioPlaybackViewModel;
import org.thoughtcrime.securesms.components.audioplay.AudioView;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -59,6 +61,7 @@ import java.util.Set;
* @author Moxie Marlinspike
*
*/
// FIXME: this breaks type checks, that is why there are so many casts.
public class ConversationAdapter <V extends View & BindableConversationItem>
extends RecyclerView.Adapter
implements StickyHeaderDecoration.StickyHeaderAdapter<HeaderViewHolder>
@@ -97,6 +100,8 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
private long pulseHighlightingSince = -1;
private int lastSeenPosition = -1;
private long lastSeen = -1;
private AudioPlaybackViewModel playbackViewModel;
private AudioView.OnActionListener audioPlayPauseListener;
protected static class ViewHolder extends RecyclerView.ViewHolder {
public <V extends View & BindableConversationItem> ViewHolder(final @NonNull V itemView) {
@@ -170,6 +175,14 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
return fromDb;
}
public void setPlaybackViewModel(AudioPlaybackViewModel playbackViewModel) {
this.playbackViewModel = playbackViewModel;
}
public void setAudioPlayPauseListener(AudioView.OnActionListener audioPlayPauseListener) {
this.audioPlayPauseListener = audioPlayPauseListener;
}
/**
* Returns the position of the message with msgId in the chat list, counted from the top
*/
@@ -237,7 +250,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
long elapsed = now - pulseHighlightingSince;
boolean pulseHighlight = (positionCurrentlyPulseHighlighting == position && elapsed < PULSE_HIGHLIGHT_MILLIS);
holder.getItem().bind(getMsg(position), dcChat, glideRequests, batchSelected, recipient, pulseHighlight);
holder.getItem().bind(getMsg(position), dcChat, glideRequests, batchSelected, recipient, pulseHighlight, playbackViewModel, audioPlayPauseListener);
}
@Override
@@ -41,6 +41,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
@@ -52,6 +53,7 @@ import com.b44t.messenger.DcEvent;
import com.b44t.messenger.DcMsg;
import org.thoughtcrime.securesms.ConversationAdapter.ItemClickListener;
import org.thoughtcrime.securesms.components.audioplay.AudioPlaybackViewModel;
import org.thoughtcrime.securesms.components.reminder.DozeReminder;
import org.thoughtcrime.securesms.connect.DcEventCenter;
import org.thoughtcrime.securesms.connect.DcHelper;
@@ -101,6 +103,7 @@ public class ConversationFragment extends MessageSelectorFragment
private StickyHeaderDecoration dateDecoration;
private View scrollToBottomButton;
private View floatingLocationButton;
private View bottomDivider;
private AddReactionView addReactionView;
private TextView noMessageTextView;
private Timer reloadTimer;
@@ -108,6 +111,8 @@ public class ConversationFragment extends MessageSelectorFragment
public boolean isPaused;
private Debouncer markseenDebouncer;
private Rpc rpc;
private boolean pendingAddBottomInsets;
private boolean pendingRemoveBottomInsets;
@Override
public void onCreate(Bundle icicle) {
@@ -141,6 +146,7 @@ public class ConversationFragment extends MessageSelectorFragment
floatingLocationButton = ViewUtil.findById(view, R.id.floating_location_button);
addReactionView = ViewUtil.findById(view, R.id.add_reaction_view);
noMessageTextView = ViewUtil.findById(view, R.id.no_messages_text_view);
bottomDivider = ViewUtil.findById(view, R.id.bottom_divider);
scrollToBottomButton.setOnClickListener(v -> scrollToBottom());
@@ -159,18 +165,32 @@ public class ConversationFragment extends MessageSelectorFragment
// with hardware layers, drawing may result in errors as "OpenGLRenderer: Path too large to be rendered into a texture"
list.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
if (pendingAddBottomInsets) {
bottomDivider.setVisibility(View.GONE);
ViewUtil.forceApplyWindowInsets(list, false, true, false, true);
ViewUtil.forceApplyWindowInsetsAsMargin(scrollToBottomButton, true, true, true, true);
pendingAddBottomInsets = false;
}
if (pendingRemoveBottomInsets) {
bottomDivider.setVisibility(View.VISIBLE);
ViewUtil.forceApplyWindowInsets(list, false, true, false, false);
ViewUtil.forceApplyWindowInsetsAsMargin(scrollToBottomButton, true, true, true, false);
pendingRemoveBottomInsets = false;
}
return view;
}
@Override
public void onActivityCreated(Bundle bundle) {
super.onActivityCreated(bundle);
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initializeResources();
initializeListAdapter();
}
private void setNoMessageText() {
private void setNoMessageText() {
DcChat dcChat = getListAdapter().getChat();
if(dcChat.isMultiUser()){
if (dcChat.isInBroadcast() || dcChat.isOutBroadcast()) {
@@ -197,6 +217,28 @@ public class ConversationFragment extends MessageSelectorFragment
}
}
public void handleAddBottomInsets() {
if (bottomDivider != null) {
bottomDivider.setVisibility(View.GONE);
ViewUtil.forceApplyWindowInsets(list, false, true, false, true);
ViewUtil.forceApplyWindowInsetsAsMargin(scrollToBottomButton, false, false, false, true);
pendingAddBottomInsets = false;
} else {
pendingAddBottomInsets = true;
}
}
public void handleRemoveBottomInsets() {
if (bottomDivider != null) {
bottomDivider.setVisibility(View.VISIBLE);
ViewUtil.forceApplyWindowInsets(list, false, true, false, false);
ViewUtil.forceApplyWindowInsetsAsMargin(scrollToBottomButton, false, false, false, false);
pendingRemoveBottomInsets = false;
} else {
pendingRemoveBottomInsets = true;
}
}
@Override
public void onDestroy() {
DcHelper.getEventCenter(getContext()).removeObservers(this);
@@ -291,6 +333,10 @@ public class ConversationFragment extends MessageSelectorFragment
if (this.recipient != null && this.chatId != -1) {
ConversationAdapter adapter = new ConversationAdapter(getActivity(), this.recipient.getChat(), GlideApp.with(this), selectionClickListener, this.recipient);
list.setAdapter(adapter);
AudioPlaybackViewModel playbackViewModel =
new ViewModelProvider(requireActivity()).get(AudioPlaybackViewModel.class);
adapter.setPlaybackViewModel(playbackViewModel);
adapter.setAudioPlayPauseListener(((ConversationActivity) requireActivity()));
if (dateDecoration != null) {
list.removeItemDecoration(dateDecoration);
@@ -407,7 +453,14 @@ public class ConversationFragment extends MessageSelectorFragment
}
public void handleClearChat() {
handleDeleteMessages((int) chatId, getListAdapter().getMessageIds());
AudioPlaybackViewModel playbackViewModel =
new ViewModelProvider(requireActivity()).get(AudioPlaybackViewModel.class);
handleDeleteMessages(
(int) chatId,
getListAdapter().getMessageIds(),
playbackViewModel::stopByIds,
playbackViewModel::stopByIds);
}
private ConversationAdapter getListAdapter() {
@@ -891,7 +944,7 @@ public class ConversationFragment extends MessageSelectorFragment
@Override
public void onReactionClicked(DcMsg messageRecord) {
ReactionsDetailsFragment dialog = new ReactionsDetailsFragment(messageRecord.getId());
ReactionsDetailsFragment dialog = ReactionsDetailsFragment.newInstance(messageRecord.getId());
dialog.show(getActivity().getSupportFragmentManager(), null);
}
}
@@ -940,12 +993,15 @@ public class ConversationFragment extends MessageSelectorFragment
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
hideAddReactionView();
int itemId = item.getItemId();
AudioPlaybackViewModel playbackViewModel =
new ViewModelProvider(requireActivity()).get(AudioPlaybackViewModel.class);
if (itemId == R.id.menu_context_copy) {
handleCopyMessage(getListAdapter().getSelectedItems());
actionMode.finish();
return true;
} else if (itemId == R.id.menu_context_delete_message) {
handleDeleteMessages((int) chatId, getListAdapter().getSelectedItems());
handleDeleteMessages((int) chatId, getListAdapter().getSelectedItems(), playbackViewModel::stopByIds, playbackViewModel::stopByIds);
return true;
} else if (itemId == R.id.menu_context_share) {
DcHelper.openForViewOrShare(getContext(), getSelectedMessageRecord(getListAdapter().getSelectedItems()).getId(), Intent.ACTION_SEND);
@@ -41,8 +41,7 @@ import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContact;
import com.b44t.messenger.DcMsg;
import org.thoughtcrime.securesms.audio.AudioSlidePlayer;
import org.thoughtcrime.securesms.components.AudioView;
import org.thoughtcrime.securesms.calls.CallUtil;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.BorderlessImageView;
import org.thoughtcrime.securesms.components.CallItemView;
@@ -52,6 +51,8 @@ import org.thoughtcrime.securesms.components.DocumentView;
import org.thoughtcrime.securesms.components.QuoteView;
import org.thoughtcrime.securesms.components.VcardView;
import org.thoughtcrime.securesms.components.WebxdcView;
import org.thoughtcrime.securesms.components.audioplay.AudioPlaybackViewModel;
import org.thoughtcrime.securesms.components.audioplay.AudioView;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.mms.AudioSlide;
import org.thoughtcrime.securesms.mms.DocumentSlide;
@@ -71,7 +72,6 @@ import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.views.Stub;
import org.thoughtcrime.securesms.calls.CallUtil;
import java.util.List;
import java.util.Set;
@@ -181,9 +181,11 @@ public class ConversationItem extends BaseConversationItem
@NonNull GlideRequests glideRequests,
@NonNull Set<DcMsg> batchSelected,
@NonNull Recipient recipients,
boolean pulseHighlight)
boolean pulseHighlight,
@Nullable AudioPlaybackViewModel playbackViewModel,
AudioView.OnActionListener audioPlayPauseListener)
{
bind(messageRecord, dcChat, batchSelected, pulseHighlight, recipients);
bindPartial(messageRecord, dcChat, batchSelected, pulseHighlight, recipients);
this.glideRequests = glideRequests;
this.showSender = ((dcChat.isMultiUser() || dcChat.isSelfTalk()) && !messageRecord.isOutgoing()) || messageRecord.getOverrideSenderName() != null;
@@ -204,7 +206,7 @@ public class ConversationItem extends BaseConversationItem
setGutterSizes(messageRecord, showSender);
setMessageShape(messageRecord);
setMediaAttributes(messageRecord, showSender);
setMediaAttributes(messageRecord, showSender, playbackViewModel, audioPlayPauseListener);
setBodyText(messageRecord);
setBubbleState(messageRecord);
setContactPhoto();
@@ -482,24 +484,10 @@ public class ConversationItem extends BaseConversationItem
}
private void setMediaAttributes(@NonNull DcMsg messageRecord,
boolean showSender)
boolean showSender,
AudioPlaybackViewModel playbackViewModel,
AudioView.OnActionListener audioPlayPauseListener)
{
class SetDurationListener implements AudioSlidePlayer.Listener {
@Override
public void onStart() {}
@Override
public void onStop() {}
@Override
public void onProgress(AudioSlide slide, double progress, long millis) {}
@Override
public void onReceivedDuration(int millis) {
messageRecord.lateFilingMediaSize(0,0, millis);
audioViewStub.get().setDuration(millis);
}
}
if (hasAudio(messageRecord)) {
audioViewStub.get().setVisibility(View.VISIBLE);
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
@@ -511,12 +499,9 @@ public class ConversationItem extends BaseConversationItem
//noinspection ConstantConditions
int duration = messageRecord.getDuration();
if (duration == 0) {
AudioSlide audio = new AudioSlide(context, messageRecord);
AudioSlidePlayer audioSlidePlayer = AudioSlidePlayer.createFor(getContext(), audio, new SetDurationListener());
audioSlidePlayer.requestDuration();
}
audioViewStub.get().setPlaybackViewModel(playbackViewModel);
audioViewStub.get().setOnActionListener(audioPlayPauseListener);
audioViewStub.get().setAudio(new AudioSlide(context, messageRecord), duration);
audioViewStub.get().setOnClickListener(passthroughClickListener);
audioViewStub.get().setOnLongClickListener(passthroughClickListener);
@@ -1000,9 +985,9 @@ public class ConversationItem extends BaseConversationItem
int chatId = messageRecord.getChatId();
if (!messageRecord.isOutgoing() && callInfo.state instanceof CallState.Alerting) {
int callId = messageRecord.getId();
CallUtil.openCall(getContext(), accId, chatId, callId, callInfo.sdpOffer);
CallUtil.openCall(getContext(), accId, chatId, callId, callInfo.sdpOffer, callInfo.hasVideo);
} else {
CallUtil.startCall(getContext(), accId, chatId);
CallUtil.startCall(getContext(), accId, chatId, callInfo.hasVideo);
}
}
}
@@ -99,6 +99,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
public static final String CLEAR_NOTIFICATIONS = "clear_notifications";
public static final String ACCOUNT_ID_EXTRA = "account_id";
public static final String FROM_WELCOME = "from_welcome";
public static final String FROM_WELCOME_RAW_QR = "from_welcome_raw_qr";
private static final int REQUEST_CODE_CONFIRM_CREDENTIALS_DELETE_PROFILE = ScreenLockUtil.REQUEST_CODE_CONFIRM_CREDENTIALS+1;
private ConversationListFragment conversationListFragment;
@@ -193,6 +194,13 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
if (BuildConfig.DEBUG) checkNdkArchitecture();
DcHelper.maybeShowMigrationError(this);
String rawQrString = getIntent().getStringExtra(FROM_WELCOME_RAW_QR);
// Launch chat directly, if coming from onboarding with a join chat/group QR
if (rawQrString != null) {
QrCodeHandler qrCodeHandler = new QrCodeHandler(this);
qrCodeHandler.secureJoinByQr(rawQrString, SecurejoinSource.Scan, SecurejoinUiPath.Unknown);
}
}
/**
@@ -621,7 +629,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
refreshAvatar();
refreshUnreadIndicator();
refreshTitle();
conversationListFragment.loadChatlist();
conversationListFragment.loadChatlistAsync();
}
public void onDeleteProfile(int profileId) {
@@ -233,7 +233,7 @@ public class ConversationListFragment extends BaseConversationListFragment
private final Object loadChatlistLock = new Object();
private boolean inLoadChatlist;
private boolean needsAnotherLoad;
private void loadChatlistAsync() {
public void loadChatlistAsync() {
synchronized (loadChatlistLock) {
needsAnotherLoad = true;
if (inLoadChatlist) {
@@ -260,7 +260,7 @@ public class ConversationListFragment extends BaseConversationListFragment
});
}
public void loadChatlist() {
private void loadChatlist() {
int listflags = 0;
if (archive) {
listflags |= DcContext.DC_GCL_ARCHIVED_ONLY;
@@ -15,6 +15,8 @@ import com.b44t.messenger.DcMsg;
import org.json.JSONObject;
import org.thoughtcrime.securesms.components.DeliveryStatusView;
import org.thoughtcrime.securesms.components.audioplay.AudioPlaybackViewModel;
import org.thoughtcrime.securesms.components.audioplay.AudioView;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.JsonUtils;
@@ -61,9 +63,11 @@ public class ConversationUpdateItem extends BaseConversationItem
@NonNull GlideRequests glideRequests,
@NonNull Set<DcMsg> batchSelected,
@NonNull Recipient conversationRecipient,
boolean pulseUpdate)
boolean pulseUpdate,
@Nullable AudioPlaybackViewModel playbackViewModel,
AudioView.OnActionListener audioPlayPauseListener)
{
bind(messageRecord, dcChat, batchSelected, pulseUpdate, conversationRecipient);
bindPartial(messageRecord, dcChat, batchSelected, pulseUpdate, conversationRecipient);
setGenericInfoRecord(messageRecord);
}
@@ -63,6 +63,8 @@ import java.io.File;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import chat.delta.rpc.Rpc;
import chat.delta.rpc.RpcException;
@@ -79,6 +81,7 @@ public class InstantOnboardingActivity extends BaseActionBarActivity implements
private ImageView avatar;
private EditText name;
private TextView invitationText;
private TextView privacyPolicyBtn;
private Button signUpBtn;
@@ -86,7 +89,11 @@ public class InstantOnboardingActivity extends BaseActionBarActivity implements
private boolean imageLoaded;
private String providerHost;
private String providerQrData;
private String rawQrData;
private DcLot parsedQrData;
private boolean isDcLogin;
private boolean isContactInvitation;
private boolean isGroupInvitation;
private AttachmentManager attachmentManager;
private Bitmap avatarBmp;
@@ -96,6 +103,8 @@ public class InstantOnboardingActivity extends BaseActionBarActivity implements
private DcContext dcContext;
private ExecutorService executor = Executors.newSingleThreadExecutor();
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
@@ -206,17 +215,31 @@ public class InstantOnboardingActivity extends BaseActionBarActivity implements
private void setProviderFromQr(String rawQr) {
DcLot qrParsed = dcContext.checkQr(rawQr);
boolean isDcLogin = qrParsed.getState() == DcContext.DC_QR_LOGIN;
if (isDcLogin || qrParsed.getState() == DcContext.DC_QR_ACCOUNT) {
this.isDcLogin = isDcLogin;
providerHost = qrParsed.getText1();
providerQrData = rawQr;
updateProvider();
} else {
new AlertDialog.Builder(this)
.setMessage(R.string.qraccount_qr_code_cannot_be_used)
.setPositiveButton(R.string.ok, null)
.show();
switch (qrParsed.getState()) {
case DcContext.DC_QR_LOGIN:
isDcLogin = true; // Intentional fall-through
case DcContext.DC_QR_ACCOUNT:
providerHost = qrParsed.getText1();
providerQrData = rawQr;
updateProvider();
break;
case DcContext.DC_QR_ASK_VERIFYCONTACT:
isContactInvitation = true;
rawQrData = rawQr;
parsedQrData = qrParsed;
updateProvider();
break;
case DcContext.DC_QR_ASK_VERIFYGROUP:
isGroupInvitation = true;
rawQrData = rawQr;
parsedQrData = qrParsed;
updateProvider();
break;
default:
new AlertDialog.Builder(this)
.setMessage(R.string.qraccount_qr_code_cannot_be_used)
.setPositiveButton(R.string.ok, null)
.show();
}
}
@@ -260,6 +283,7 @@ public class InstantOnboardingActivity extends BaseActionBarActivity implements
public void onDestroy() {
super.onDestroy();
DcHelper.getEventCenter(this).removeObservers(this);
executor.shutdown();
}
private void handleIntent() {
@@ -311,6 +335,7 @@ public class InstantOnboardingActivity extends BaseActionBarActivity implements
private void initializeResources() {
this.avatar = findViewById(R.id.avatar);
this.name = findViewById(R.id.name_text);
this.invitationText = findViewById(R.id.invitation_label);
this.privacyPolicyBtn = findViewById(R.id.privacy_policy_button);
this.signUpBtn = findViewById(R.id.signup_button);
@@ -358,6 +383,19 @@ public class InstantOnboardingActivity extends BaseActionBarActivity implements
privacyPolicyBtn.setText(TextUtil.markAsExternal(
getString(R.string.instant_onboarding_agree_instance, providerHost)));
}
if (parsedQrData != null) {
if (isContactInvitation) {
String name = dcContext.getContact(parsedQrData.getId()).getDisplayName();
invitationText.setText(this.getString(R.string.instant_onboarding_contact_info, name));
invitationText.setVisibility(View.VISIBLE);
} else if (isGroupInvitation) {
String groupName = parsedQrData.getText1();
invitationText.setText(this.getString(R.string.instant_onboarding_group_info, groupName));
invitationText.setVisibility(View.VISIBLE);
}
}
}
}
@@ -419,11 +457,14 @@ public class InstantOnboardingActivity extends BaseActionBarActivity implements
Intent intent = new Intent(getApplicationContext(), ConversationListActivity.class);
intent.putExtra(ConversationListActivity.FROM_WELCOME, true);
if (isContactInvitation || isGroupInvitation) {
intent.putExtra(ConversationListActivity.FROM_WELCOME_RAW_QR, rawQrData);
}
startActivity(intent);
finishAffinity();
}
@SuppressLint("StaticFieldLeak")
private void createProfile() {
if (TextUtils.isEmpty(this.name.getText())) {
Toast.makeText(this, R.string.please_enter_name, Toast.LENGTH_LONG).show();
@@ -431,37 +472,31 @@ public class InstantOnboardingActivity extends BaseActionBarActivity implements
}
final String name = this.name.getText().toString();
new AsyncTask<Void, Void, Boolean>() {
@Override
protected Boolean doInBackground(Void... params) {
Context context = InstantOnboardingActivity.this;
DcHelper.set(context, DcHelper.CONFIG_DISPLAY_NAME, name);
executor.execute(() -> {
Context context = InstantOnboardingActivity.this;
DcHelper.set(context, DcHelper.CONFIG_DISPLAY_NAME, name);
if (avatarChanged) {
try {
AvatarHelper.setSelfAvatar(InstantOnboardingActivity.this, avatarBmp);
Prefs.setProfileAvatarId(InstantOnboardingActivity.this, new SecureRandom().nextInt());
} catch (IOException e) {
Log.w(TAG, e);
return false;
}
boolean result = true;
if (avatarChanged) {
try {
AvatarHelper.setSelfAvatar(InstantOnboardingActivity.this, avatarBmp);
Prefs.setProfileAvatarId(InstantOnboardingActivity.this, new SecureRandom().nextInt());
} catch (IOException e) {
Log.w(TAG, e);
result = false;
}
return true;
}
@Override
public void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (result) {
boolean finalResult = result;
runOnUiThread(() -> {
if (finalResult) {
attachmentManager.cleanup();
startQrAccountCreation(providerQrData);
} else {
Toast.makeText(InstantOnboardingActivity.this, R.string.error, Toast.LENGTH_LONG).show();
}
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
});
});
}
private void startQrAccountCreation(String qrCode)
@@ -129,7 +129,7 @@ public class LogViewFragment extends Fragment {
return logFile;
}
private static String grabLogcat(LogViewFragment fragment) {
public static String grabLogcat() {
String command = "logcat -v threadtime -d -t 10000 *:I";
try {
final Process process = Runtime.getRuntime().exec(command);
@@ -165,7 +165,7 @@ public class LogViewFragment extends Fragment {
if (fragment == null) return null;
return "**This log may contain sensitive information. If you want to post it publicly you may examine and edit it beforehand.**\n\n" +
buildDescription(fragment) + "\n" + grabLogcat(fragment);
buildDescription(fragment) + "\n" + grabLogcat();
}
@Override
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms;
import android.Manifest;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
@@ -12,6 +13,7 @@ import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.view.ActionMode;
import androidx.core.util.Consumer;
import androidx.fragment.app.Fragment;
import com.b44t.messenger.DcChat;
@@ -57,10 +59,14 @@ public abstract class MessageSelectorFragment
}
protected void handleDeleteMessages(int chatId, final Set<DcMsg> messageRecords) {
handleDeleteMessages(chatId, DcMsg.msgSetToIds(messageRecords));
handleDeleteMessages(chatId, DcMsg.msgSetToIds(messageRecords), null, null);
}
protected void handleDeleteMessages(int chatId, final int[] messageIds) {
protected void handleDeleteMessages(int chatId, final Set<DcMsg> messageRecords, Consumer<int[]> deleteForMeListenerExtra, Consumer<int[]> deleteForAllListenerExtra) {
handleDeleteMessages(chatId, DcMsg.msgSetToIds(messageRecords), deleteForMeListenerExtra, deleteForAllListenerExtra);
}
protected void handleDeleteMessages(int chatId, final int[] messageIds, Consumer<int[]> deleteForMeListenerExtra, Consumer<int[]> deleteForAllListenerExtra) {
DcContext dcContext = DcHelper.getContext(getContext());
DcChat dcChat = dcContext.getChat(chatId);
boolean canDeleteForAll = true;
@@ -79,20 +85,24 @@ public abstract class MessageSelectorFragment
String text = getActivity().getResources().getQuantityString(R.plurals.ask_delete_messages, messageIds.length, messageIds.length);
int positiveBtnLabel = dcChat.isSelfTalk() ? R.string.delete : R.string.delete_for_me;
DialogInterface.OnClickListener deleteForMeListener = (d, which) -> {
Util.runOnAnyBackgroundThread(() -> dcContext.deleteMsgs(messageIds));
if (actionMode != null) actionMode.finish();
if (deleteForMeListenerExtra != null) deleteForMeListenerExtra.accept(messageIds);
};
AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity())
.setMessage(text)
.setCancelable(true)
.setNeutralButton(android.R.string.cancel, null)
.setPositiveButton(positiveBtnLabel, (d, which) -> {
Util.runOnAnyBackgroundThread(() -> dcContext.deleteMsgs(messageIds));
if (actionMode != null) actionMode.finish();
});
.setPositiveButton(positiveBtnLabel, deleteForMeListener);
if(canDeleteForAll) {
builder.setNegativeButton(R.string.delete_for_everyone, (d, which) -> {
DialogInterface.OnClickListener deleteForAllListener = (d, which) -> {
Util.runOnAnyBackgroundThread(() -> dcContext.sendDeleteRequest(messageIds));
if (actionMode != null) actionMode.finish();
});
if (deleteForAllListenerExtra != null) deleteForAllListenerExtra.accept(messageIds);
};
builder.setNegativeButton(R.string.delete_for_everyone, deleteForAllListener);
AlertDialog dialog = builder.show();
Util.redButton(dialog, AlertDialog.BUTTON_NEGATIVE);
Util.redPositiveButton(dialog);
@@ -22,10 +22,7 @@ import static org.thoughtcrime.securesms.util.ShareUtil.acquireRelayMessageConte
import static org.thoughtcrime.securesms.util.ShareUtil.isRelayingMessageContent;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
@@ -37,7 +34,6 @@ import com.google.zxing.integration.android.IntentResult;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.qr.QrActivity;
import org.thoughtcrime.securesms.qr.QrCodeHandler;
import org.thoughtcrime.securesms.util.MailtoUtil;
import chat.delta.rpc.types.SecurejoinSource;
import chat.delta.rpc.types.SecurejoinUiPath;
@@ -57,53 +53,6 @@ public class NewConversationActivity extends ContactSelectionActivity {
super.onCreate(bundle, ready);
assert getSupportActionBar() != null;
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
handleIntent();
}
private void handleIntent() {
Intent intent = getIntent();
String action = intent.getAction();
if(Intent.ACTION_VIEW.equals(action) || Intent.ACTION_SENDTO.equals(action)) {
try {
Uri uri = intent.getData();
if(uri != null) {
String scheme = uri.getScheme();
if(MailtoUtil.isMailto(uri)) {
String textToShare = MailtoUtil.getText(uri);
String[] recipientsArray = MailtoUtil.getRecipients(uri);
if (recipientsArray.length >= 1) {
if (!textToShare.isEmpty()) {
getIntent().putExtra(TEXT_EXTRA, textToShare);
}
final String addr = recipientsArray[0];
final DcContext dcContext = DcHelper.getContext(this);
int contactId = dcContext.lookupContactIdByAddr(addr);
if (contactId == 0 && dcContext.mayBeValidAddr(addr)) {
contactId = dcContext.createContact(null, recipientsArray[0]);
}
if (contactId == 0) {
Toast.makeText(this, R.string.bad_email_address, Toast.LENGTH_LONG).show();
} else {
onContactSelected(contactId);
}
} else {
Intent shareIntent = new Intent(this, ShareActivity.class);
shareIntent.putExtra(Intent.EXTRA_TEXT, textToShare);
startActivity(shareIntent);
finish();
}
} else if(scheme != null && scheme.startsWith("http")) {
Intent shareIntent = new Intent(this, ShareActivity.class);
shareIntent.putExtra(Intent.EXTRA_TEXT, uri.toString());
startActivity(shareIntent);
finish();
}
}
}
catch(Exception e) {
Log.e(TAG, "start activity from external 'mailto:' link failed", e);
}
}
}
@Override
@@ -310,11 +310,27 @@ public class ProfileFragment extends Fragment
super.onActivityResult(requestCode, resultCode, data);
if (requestCode==REQUEST_CODE_PICK_CONTACT && resultCode==Activity.RESULT_OK && data!=null) {
List<Integer> selected = data.getIntegerArrayListExtra(ContactMultiSelectionActivity.CONTACTS_EXTRA);
if(selected == null) return;
List<Integer> deselected = data.getIntegerArrayListExtra(ContactMultiSelectionActivity.DESELECTED_CONTACTS_EXTRA);
Util.runOnAnyBackgroundThread(() -> {
for (Integer contactId : selected) {
if (contactId!=null) {
dcContext.addContactToChat(chatId, contactId);
if (deselected != null) {
// Remove members that were deselected
int[] members = dcContext.getChatContacts(chatId);
for (int contactId : deselected) {
for (int memberId : members) {
if (memberId == contactId) {
dcContext.removeContactFromChat(chatId, memberId);
break;
}
}
}
}
if (selected != null) {
// Add new members
for (Integer contactId : selected) {
if (contactId != null) {
dcContext.addContactToChat(chatId, contactId);
}
}
}
});
@@ -104,26 +104,25 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity implement
resolvedExtras = new ArrayList<>();
List<Uri> streamExtras = new ArrayList<>();
if (MailtoUtil.isMailto(getIntent().getData())) {
String[] extraEmail = getIntent().getStringArrayExtra(Intent.EXTRA_EMAIL);
if (extraEmail == null || extraEmail.length == 0) {
getIntent().putExtra(Intent.EXTRA_EMAIL, MailtoUtil.getRecipients(getIntent().getData()));
}
String text = getIntent().getStringExtra(Intent.EXTRA_TEXT);
if (text == null || text.isEmpty()) {
getIntent().putExtra(Intent.EXTRA_TEXT, MailtoUtil.getText(getIntent().getData()));
}
}
if (Intent.ACTION_SEND.equals(getIntent().getAction()) &&
getIntent().getParcelableExtra(Intent.EXTRA_STREAM) != null) {
Uri uri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
streamExtras.add(uri);
} else if (getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM) != null) {
streamExtras = getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM);
} else {
Uri uri = getIntent().getData();
if (MailtoUtil.isMailto(uri)) {
String[] extraEmail = getIntent().getStringArrayExtra(Intent.EXTRA_EMAIL);
if (extraEmail == null || extraEmail.length == 0) {
getIntent().putExtra(Intent.EXTRA_EMAIL, MailtoUtil.getRecipients(uri));
}
String text = getIntent().getStringExtra(Intent.EXTRA_TEXT);
if (text == null || text.isEmpty()) {
getIntent().putExtra(Intent.EXTRA_TEXT, MailtoUtil.getText(uri));
}
} else if (uri != null) {
streamExtras.add(uri);
}
} else if (getIntent().getData() != null) {
streamExtras.add(getIntent().getData());
}
if (needsFilePermission(streamExtras)) {
@@ -265,6 +264,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity implement
}
chatId = dcContext.createChatByContactId(contactId);
accId = dcContext.getAccountId();
}
Intent composeIntent;
if (accId != -1 && chatId != -1) {
@@ -77,7 +77,7 @@ public class WebViewActivity extends PassphraseRequiredActionBarActivity
findViewById(R.id.status_bar_background).setBackgroundResource(R.drawable.search_toolbar_shadow);
} else {
// add padding to avoid content hidden behind system bars
ViewUtil.applyWindowInsets(findViewById(R.id.content_container));
ViewUtil.applyWindowInsets(findViewById(R.id.content_container), true, true, true, true, true, false);
}
webView.setWebViewClient(new WebViewClient() {
@@ -47,21 +47,24 @@ import chat.delta.rpc.RpcException;
public class AccountSelectionListFragment extends DialogFragment implements DcEventCenter.DcEventDelegate
{
private static final String TAG = AccountSelectionListFragment.class.getSimpleName();
private final ConversationListActivity activity;
private static final String ARG_SELECT_ONLY = "select_only";
private RecyclerView recyclerView;
private AccountSelectionListAdapter adapter;
private final boolean selectOnly;
private boolean selectOnly;
public AccountSelectionListFragment(ConversationListActivity activity, boolean selectOnly) {
super();
this.activity = activity;
this.selectOnly = selectOnly;
public static AccountSelectionListFragment newInstance(boolean selectOnly) {
AccountSelectionListFragment fragment = new AccountSelectionListFragment();
Bundle args = new Bundle();
args.putBoolean(ARG_SELECT_ONLY, selectOnly);
fragment.setArguments(args);
return fragment;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
selectOnly = getArguments() != null && getArguments().getBoolean(ARG_SELECT_ONLY, false);
AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity())
.setTitle(R.string.switch_account)
.setNegativeButton(R.string.cancel, null);
if (!selectOnly) {
@@ -179,6 +182,7 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv
}
private void onSetTag(int accountId) {
ConversationListActivity activity = (ConversationListActivity)requireActivity();
AccountSelectionListFragment.this.dismiss();
DcContext dcContext = DcHelper.getAccounts(activity).getAccount(accountId);
@@ -202,6 +206,7 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv
private void onDeleteProfile(int accountId) {
AccountSelectionListFragment.this.dismiss();
ConversationListActivity activity = (ConversationListActivity)requireActivity();
DcAccounts accounts = DcHelper.getAccounts(activity);
Rpc rpc = DcHelper.getRpc(activity);
@@ -254,6 +259,7 @@ public class AccountSelectionListFragment extends DialogFragment implements DcEv
@Override
public void onItemClick(AccountSelectionListItem contact) {
AccountSelectionListFragment.this.dismiss();
ConversationListActivity activity = (ConversationListActivity)requireActivity();
int accountId = contact.getAccountId();
if (accountId == DC_CONTACT_ID_ADD_ACCOUNT) {
AccountManager.getInstance().switchAccountAndStartActivity(activity, 0);
@@ -1,356 +0,0 @@
package org.thoughtcrime.securesms.audio;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.util.Pair;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.LoadControl;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.mms.AudioSlide;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.guava.Optional;
import org.thoughtcrime.securesms.video.exo.AttachmentDataSourceFactory;
import java.io.IOException;
import java.lang.ref.WeakReference;
public class AudioSlidePlayer {
private static final String TAG = AudioSlidePlayer.class.getSimpleName();
private static @NonNull Optional<AudioSlidePlayer> playing = Optional.absent();
private final @NonNull Context context;
private final @NonNull AudioSlide slide;
private final @NonNull Handler progressEventHandler;
private @NonNull WeakReference<Listener> listener;
private @Nullable SimpleExoPlayer mediaPlayer;
private @Nullable SimpleExoPlayer durationCalculator;
public synchronized static AudioSlidePlayer createFor(@NonNull Context context,
@NonNull AudioSlide slide,
@NonNull Listener listener)
{
if (playing.isPresent() && playing.get().getAudioSlide().equals(slide)) {
playing.get().setListener(listener);
return playing.get();
} else {
return new AudioSlidePlayer(context, slide, listener);
}
}
private AudioSlidePlayer(@NonNull Context context,
@NonNull AudioSlide slide,
@NonNull Listener listener)
{
this.context = context;
this.slide = slide;
this.listener = new WeakReference<>(listener);
this.progressEventHandler = new ProgressEventHandler(this);
}
public void requestDuration() {
try {
LoadControl loadControl = new DefaultLoadControl.Builder().setBufferDurationsMs(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE).build();
durationCalculator = new SimpleExoPlayer.Builder(context, new DefaultRenderersFactory(context))
.setTrackSelector(new DefaultTrackSelector(context))
.setLoadControl(loadControl)
.build();
durationCalculator.setPlayWhenReady(false);
durationCalculator.addListener(new Player.Listener() {
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if (playbackState == Player.STATE_READY) {
Util.runOnMain(() -> {
synchronized (AudioSlidePlayer.this) {
if (durationCalculator == null) return;
Log.d(TAG, "request duration " + durationCalculator.getDuration());
getListener().onReceivedDuration(Long.valueOf(durationCalculator.getDuration()).intValue());
durationCalculator.release();
durationCalculator.removeListener(this);
durationCalculator = null;
}
});
}
}
});
durationCalculator.prepare(createMediaSource(slide.getUri()));
} catch (Exception e) {
Log.w(TAG, e);
getListener().onReceivedDuration(0);
}
}
public void play(final double progress) throws IOException {
play(progress, false);
}
private void play(final double progress, boolean earpiece) throws IOException {
if (this.mediaPlayer != null) {
return;
}
if (slide.getUri() == null) {
throw new IOException("Slide has no URI!");
}
LoadControl loadControl = new DefaultLoadControl.Builder().setBufferDurationsMs(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE).build();
this.mediaPlayer = new SimpleExoPlayer.Builder(context, new DefaultRenderersFactory(context))
.setTrackSelector(new DefaultTrackSelector(context))
.setLoadControl(loadControl)
.build();
mediaPlayer.prepare(createMediaSource(slide.getUri()));
mediaPlayer.setPlayWhenReady(true);
mediaPlayer.setAudioAttributes(new AudioAttributes.Builder()
.setContentType(earpiece ? C.AUDIO_CONTENT_TYPE_SPEECH : C.AUDIO_CONTENT_TYPE_MUSIC)
.setUsage(earpiece ? C.USAGE_VOICE_COMMUNICATION : C.USAGE_MEDIA)
.build(), false);
mediaPlayer.addListener(new Player.Listener() {
boolean started = false;
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
Log.d(TAG, "onPlayerStateChanged(" + playWhenReady + ", " + playbackState + ")");
switch (playbackState) {
case Player.STATE_READY:
Log.i(TAG, "onPrepared() " + mediaPlayer.getBufferedPercentage() + "% buffered");
synchronized (AudioSlidePlayer.this) {
if (mediaPlayer == null) return;
Log.d(TAG, "DURATION: " + mediaPlayer.getDuration());
if (started) {
Log.d(TAG, "Already started. Ignoring.");
return;
}
started = true;
if (progress > 0) {
mediaPlayer.seekTo((long) (mediaPlayer.getDuration() * progress));
}
setPlaying(AudioSlidePlayer.this);
}
keepScreenOn(true);
notifyOnStart();
progressEventHandler.sendEmptyMessage(0);
break;
case Player.STATE_ENDED:
Log.i(TAG, "onComplete");
synchronized (AudioSlidePlayer.this) {
getListener().onReceivedDuration(Long.valueOf(mediaPlayer.getDuration()).intValue());
mediaPlayer.release();
mediaPlayer = null;
}
keepScreenOn(false);
notifyOnStop();
progressEventHandler.removeMessages(0);
}
}
@Override
public void onPlayerError(PlaybackException error) {
Log.w(TAG, "MediaPlayer Error: " + error);
synchronized (AudioSlidePlayer.this) {
mediaPlayer.release();
mediaPlayer = null;
}
notifyOnStop();
progressEventHandler.removeMessages(0);
// Failed to play media file, maybe another app can handle it
int msgId = getAudioSlide().getDcMsgId();
DcHelper.openForViewOrShare(context, msgId, Intent.ACTION_VIEW);
}
});
}
private MediaSource createMediaSource(@NonNull Uri uri) {
DefaultDataSourceFactory defaultDataSourceFactory = new DefaultDataSourceFactory(context, "GenericUserAgent", null);
AttachmentDataSourceFactory attachmentDataSourceFactory = new AttachmentDataSourceFactory(defaultDataSourceFactory);
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);
return new ProgressiveMediaSource.Factory(attachmentDataSourceFactory, extractorsFactory)
.createMediaSource(MediaItem.fromUri(uri));
}
public synchronized void stop() {
Log.i(TAG, "Stop called!");
keepScreenOn(false);
removePlaying(this);
if (this.mediaPlayer != null) {
this.mediaPlayer.stop();
this.mediaPlayer.release();
}
this.mediaPlayer = null;
}
public static void stopAll() {
if (playing.isPresent()) {
synchronized (AudioSlidePlayer.class) {
if (playing.isPresent()) {
playing.get().stop();
}
}
}
}
public void setListener(@NonNull Listener listener) {
this.listener = new WeakReference<>(listener);
if (this.mediaPlayer != null && this.mediaPlayer.getPlaybackState() == Player.STATE_READY) {
notifyOnStart();
}
}
public @NonNull AudioSlide getAudioSlide() {
return slide;
}
private Pair<Double, Integer> getProgress() {
if (mediaPlayer == null || mediaPlayer.getCurrentPosition() <= 0 || mediaPlayer.getDuration() <= 0) {
return new Pair<>(0D, 0);
} else {
return new Pair<>((double) mediaPlayer.getCurrentPosition() / (double) mediaPlayer.getDuration(),
(int) mediaPlayer.getCurrentPosition());
}
}
private void notifyOnStart() {
Util.runOnMain(new Runnable() {
@Override
public void run() {
getListener().onStart();
}
});
}
private void notifyOnStop() {
Util.runOnMain(new Runnable() {
@Override
public void run() {
getListener().onStop();
}
});
}
private void notifyOnProgress(final double progress, final long millis) {
Util.runOnMain(new Runnable() {
@Override
public void run() {
getListener().onProgress(slide, progress, millis);
}
});
}
private @NonNull Listener getListener() {
Listener listener = this.listener.get();
if (listener != null) return listener;
else return new Listener() {
@Override
public void onStart() {}
@Override
public void onStop() {}
@Override
public void onProgress(AudioSlide slide, double progress, long millis) {}
@Override
public void onReceivedDuration(int millis) {}
};
}
public void keepScreenOn(boolean keepOn) {
if (context instanceof Activity) {
if (keepOn) {
((Activity) context).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} else {
((Activity) context).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
}
private synchronized static void setPlaying(@NonNull AudioSlidePlayer player) {
if (playing.isPresent() && playing.get() != player) {
playing.get().notifyOnStop();
playing.get().stop();
}
playing = Optional.of(player);
}
private synchronized static void removePlaying(@NonNull AudioSlidePlayer player) {
if (playing.isPresent() && playing.get() == player) {
playing = Optional.absent();
}
}
public interface Listener {
void onStart();
void onStop();
void onProgress(AudioSlide slide, double progress, long millis);
void onReceivedDuration(int millis);
}
private static class ProgressEventHandler extends Handler {
private final WeakReference<AudioSlidePlayer> playerReference;
private ProgressEventHandler(@NonNull AudioSlidePlayer player) {
this.playerReference = new WeakReference<>(player);
}
@Override
public void handleMessage(@NonNull Message msg) {
AudioSlidePlayer player = playerReference.get();
if (player == null || player.mediaPlayer == null || !isPlayerActive(player.mediaPlayer)) {
return;
}
Pair<Double, Integer> progress = player.getProgress();
player.notifyOnProgress(progress.first, progress.second);
sendEmptyMessageDelayed(0, 50);
}
private boolean isPlayerActive(@NonNull SimpleExoPlayer player) {
return player.getPlaybackState() == Player.STATE_READY || player.getPlaybackState() == Player.STATE_BUFFERING;
}
}
}
@@ -43,12 +43,14 @@ public class CallActivity extends WebViewActivity implements DcEventCenter.DcEve
public static final String EXTRA_CHAT_ID = "chat_id";
public static final String EXTRA_CALL_ID = "call_id";
public static final String EXTRA_HASH = "hash";
public static final String EXTRA_HAS_VIDEO = "has_video";
private DcContext dcContext;
private Rpc rpc;
private int accId;
private int chatId;
private int callId;
private boolean hasVideo;
private boolean ended = false;
@SuppressLint("SetJavaScriptEnabled")
@@ -58,7 +60,9 @@ public class CallActivity extends WebViewActivity implements DcEventCenter.DcEve
Bundle bundle = getIntent().getExtras();
assert bundle != null;
hasVideo = bundle.getBoolean(EXTRA_HAS_VIDEO, true);
String hash = bundle.getString(EXTRA_HASH, "");
String query = hasVideo? "" : "?noOutgoingVideoInitially";
accId = bundle.getInt(EXTRA_ACCOUNT_ID, -1);
chatId = bundle.getInt(EXTRA_CHAT_ID, 0);
callId = bundle.getInt(EXTRA_CALL_ID, 0);
@@ -94,7 +98,7 @@ public class CallActivity extends WebViewActivity implements DcEventCenter.DcEve
.withPermanentDenialDialog(getString(R.string.perm_explain_access_to_camera_denied))
.onAllGranted(() -> {
String url = "file:///android_asset/calls/index.html";
webView.loadUrl(url + hash);
webView.loadUrl(url + query + hash);
}).onAnyDenied(this::finish)
.execute();
}
@@ -168,7 +172,7 @@ public class CallActivity extends WebViewActivity implements DcEventCenter.DcEve
@JavascriptInterface
public void startCall(String payload) {
try {
callId = rpc.placeOutgoingCall(accId, chatId, payload);
callId = rpc.placeOutgoingCall(accId, chatId, payload, hasVideo);
} catch (RpcException e) {
Log.e(TAG, "Error", e);
}
@@ -18,28 +18,29 @@ import java.nio.charset.StandardCharsets;
public class CallUtil {
private static final String TAG = CallUtil.class.getSimpleName();
public static void startCall(Activity activity, int chatId) {
public static void startCall(Activity activity, int chatId, boolean hasVideo) {
Permissions.with(activity)
.request(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
.ifNecessary()
.withPermanentDenialDialog(activity.getString(R.string.perm_explain_access_to_camera_denied))
.onAllGranted(() -> {
int accId = DcHelper.getContext(activity).getAccountId();
startCall(activity, accId, chatId);
startCall(activity, accId, chatId, hasVideo);
})
.execute();
}
public static void startCall(Context context, int accId, int chatId) {
public static void startCall(Context context, int accId, int chatId, boolean hasVideo) {
Intent intent = new Intent(context, CallActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(CallActivity.EXTRA_ACCOUNT_ID, accId);
intent.putExtra(CallActivity.EXTRA_CHAT_ID, chatId);
intent.putExtra(CallActivity.EXTRA_HAS_VIDEO, hasVideo);
intent.putExtra(CallActivity.EXTRA_HASH, "#startCall");
context.startActivity(intent);
}
public static void openCall(Context context, int accId, int chatId, int callId, String payload) {
public static void openCall(Context context, int accId, int chatId, int callId, String payload, boolean hasVideo) {
String base64 = Base64.encodeToString(payload.getBytes(StandardCharsets.UTF_8), Base64.NO_WRAP);
String hash = "";
try {
@@ -53,6 +54,7 @@ public class CallUtil {
intent.putExtra(CallActivity.EXTRA_ACCOUNT_ID, accId);
intent.putExtra(CallActivity.EXTRA_CHAT_ID, chatId);
intent.putExtra(CallActivity.EXTRA_CALL_ID, callId);
intent.putExtra(CallActivity.EXTRA_HAS_VIDEO, hasVideo);
intent.putExtra(CallActivity.EXTRA_HASH, hash);
context.startActivity(intent);
}
@@ -1,301 +0,0 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.media.AudioAttributes;
import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.audio.AudioSlidePlayer;
import org.thoughtcrime.securesms.mms.AudioSlide;
import org.thoughtcrime.securesms.util.DateUtils;
import java.io.IOException;
public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener {
private static final String TAG = AudioView.class.getSimpleName();
private final @NonNull AnimatingToggle controlToggle;
private final @NonNull ImageView playButton;
private final @NonNull ImageView pauseButton;
private final @NonNull SeekBar seekBar;
private final @NonNull TextView timestamp;
private final @NonNull TextView title;
private final @NonNull View mask;
private @Nullable AudioSlidePlayer audioSlidePlayer;
private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener;
private int backwardsCounter;
public AudioView(Context context) {
this(context, null);
}
public AudioView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AudioView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
inflate(context, R.layout.audio_view, this);
this.controlToggle = (AnimatingToggle) findViewById(R.id.control_toggle);
this.playButton = (ImageView) findViewById(R.id.play);
this.pauseButton = (ImageView) findViewById(R.id.pause);
this.seekBar = (SeekBar) findViewById(R.id.seek);
this.timestamp = (TextView) findViewById(R.id.timestamp);
this.title = (TextView) findViewById(R.id.title);
this.mask = findViewById(R.id.interception_mask);
this.timestamp.setText("00:00");
this.playButton.setOnClickListener(new PlayClickedListener());
this.pauseButton.setOnClickListener(new PauseClickedListener());
this.seekBar.setOnSeekBarChangeListener(new SeekBarModifiedListener());
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));
}
public void setAudio(final @NonNull AudioSlide audio, int duration)
{
controlToggle.displayQuick(playButton);
seekBar.setEnabled(true);
seekBar.setProgress(0);
audioSlidePlayer = AudioSlidePlayer.createFor(getContext(), audio, this);
timestamp.setText(DateUtils.getFormatedDuration(duration));
if(audio.asAttachment().isVoiceNote() || !audio.getFileName().isPresent()) {
title.setVisibility(View.GONE);
}
else {
title.setText(audio.getFileName().get());
title.setVisibility(View.VISIBLE);
}
}
@Override
public void setOnClickListener(OnClickListener listener) {
super.setOnClickListener(listener);
this.mask.setOnClickListener(listener);
}
@Override
public void setOnLongClickListener(OnLongClickListener listener) {
super.setOnLongClickListener(listener);
this.mask.setOnLongClickListener(listener);
this.playButton.setOnLongClickListener(listener);
this.pauseButton.setOnLongClickListener(listener);
}
public void togglePlay() {
if (this.playButton.getVisibility() == View.VISIBLE) {
playButton.performClick();
} else {
pauseButton.performClick();
}
}
public String getDescription() {
String desc;
if (this.title.getVisibility() == View.VISIBLE) {
desc = getContext().getString(R.string.audio);
} else {
desc = getContext().getString(R.string.voice_message);
}
desc += "\n" + this.timestamp.getText();
if (title.getVisibility() == View.VISIBLE) {
desc += "\n" + this.title.getText();
}
return desc;
}
public void setDuration(int duration) {
if (getProgress()==0)
this.timestamp.setText(DateUtils.getFormatedDuration(duration));
}
public void cleanup() {
if (this.audioSlidePlayer != null && pauseButton.getVisibility() == View.VISIBLE) {
this.audioSlidePlayer.stop();
}
}
@Override
public void onReceivedDuration(int millis) {
this.timestamp.setText(DateUtils.getFormatedDuration(millis));
}
@Override
public void onStart() {
if (this.pauseButton.getVisibility() != View.VISIBLE) {
togglePlayToPause();
}
}
@Override
public void onStop() {
if (this.playButton.getVisibility() != View.VISIBLE) {
togglePauseToPlay();
}
if (seekBar.getProgress() + 5 >= seekBar.getMax()) {
backwardsCounter = 4;
onProgress(audioSlidePlayer.getAudioSlide(), 0.0, -1);
}
}
public void disablePlayer(boolean disable) {
this.mask.setVisibility(disable? View.VISIBLE : View.GONE);
}
@Override
public void onProgress(AudioSlide slide, double progress, long millis) {
if (!audioSlidePlayer.getAudioSlide().equals(slide)) {
return;
}
int seekProgress = (int) Math.floor(progress * this.seekBar.getMax());
if (seekProgress > seekBar.getProgress() || backwardsCounter > 3) {
backwardsCounter = 0;
this.seekBar.setProgress(seekProgress);
if (millis != -1) {
this.timestamp.setText(DateUtils.getFormatedDuration(millis));
}
} else {
backwardsCounter++;
}
}
public void setTint(int foregroundTint) {
this.playButton.setBackgroundTintList(ColorStateList.valueOf(foregroundTint));
this.pauseButton.setBackgroundTintList(ColorStateList.valueOf(foregroundTint));
this.seekBar.getProgressDrawable().setColorFilter(foregroundTint, PorterDuff.Mode.SRC_IN);
this.seekBar.getThumb().setColorFilter(foregroundTint, PorterDuff.Mode.SRC_IN);
}
public void getSeekBarGlobalVisibleRect(@NonNull Rect rect) {
seekBar.getGlobalVisibleRect(rect);
}
private double getProgress() {
if (this.seekBar.getProgress() <= 0 || this.seekBar.getMax() <= 0) {
return 0;
} else {
return (double)this.seekBar.getProgress() / (double)this.seekBar.getMax();
}
}
private void togglePlayToPause() {
controlToggle.displayQuick(pauseButton);
AnimatedVectorDrawable playToPauseDrawable = (AnimatedVectorDrawable) getContext().getDrawable(R.drawable.play_to_pause_animation);
pauseButton.setImageDrawable(playToPauseDrawable);
playToPauseDrawable.start();
}
private void togglePauseToPlay() {
controlToggle.displayQuick(playButton);
AnimatedVectorDrawable pauseToPlayDrawable = (AnimatedVectorDrawable) getContext().getDrawable(R.drawable.pause_to_play_animation);
playButton.setImageDrawable(pauseToPlayDrawable);
pauseToPlayDrawable.start();
}
private class PlayClickedListener implements View.OnClickListener {
@Override
public void onClick(View v) {
try {
Log.w(TAG, "playbutton onClick");
if (audioSlidePlayer != null) {
if (Build.VERSION.SDK_INT >= 26) {
if (audioFocusChangeListener == null) {
audioFocusChangeListener = focusChange -> {
if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
pauseButton.performClick();
}
};
}
AudioAttributes playbackAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build();
AudioFocusRequest focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(playbackAttributes)
.setAcceptsDelayedFocusGain(false)
.setWillPauseWhenDucked(false)
.setOnAudioFocusChangeListener(audioFocusChangeListener)
.build();
AudioManager audioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
audioManager.requestAudioFocus(focusRequest);
}
togglePlayToPause();
audioSlidePlayer.play(getProgress());
}
} catch (IOException e) {
Log.w(TAG, e);
}
}
}
private class PauseClickedListener implements View.OnClickListener {
@Override
public void onClick(View v) {
Log.w(TAG, "pausebutton onClick");
if (audioSlidePlayer != null) {
togglePauseToPlay();
audioSlidePlayer.stop();
}
}
}
private class SeekBarModifiedListener implements SeekBar.OnSeekBarChangeListener {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {}
@Override
public synchronized void onStartTrackingTouch(SeekBar seekBar) {
if (audioSlidePlayer != null && pauseButton.getVisibility() == View.VISIBLE) {
audioSlidePlayer.stop();
}
}
@Override
public synchronized void onStopTrackingTouch(SeekBar seekBar) {
try {
if (audioSlidePlayer != null && pauseButton.getVisibility() == View.VISIBLE) {
audioSlidePlayer.play(getProgress());
}
} catch (IOException e) {
Log.w(TAG, e);
}
}
}
}
@@ -67,10 +67,14 @@ public class CallItemView extends FrameLayout {
title.setText(R.string.canceled_call);
} else if (callInfo.state instanceof CallState.Declined) {
title.setText(R.string.declined_call);
} else if (callInfo.hasVideo) {
title.setText(isOutgoing? R.string.outgoing_video_call : R.string.incoming_video_call);
} else {
title.setText(isOutgoing? R.string.outgoing_call : R.string.incoming_call);
title.setText(isOutgoing? R.string.outgoing_audio_call : R.string.incoming_audio_call);
}
icon.setImageResource(callInfo.hasVideo? R.drawable.ic_videocam_white_24dp : R.drawable.baseline_call_24);
int[] attrs;
if (isOutgoing) {
attrs = new int[]{
@@ -151,12 +151,6 @@ public class ConversationItemFooter extends LinearLayout {
else if (messageRecord.isDelivered()) deliveryStatusView.setSent();
else if (messageRecord.isPreparing()) deliveryStatusView.setPreparing();
else deliveryStatusView.setPending();
if (messageRecord.isFailed()) {
deliveryStatusView.setTint(Color.RED);
} else {
deliveryStatusView.setTint(textColor); // Reset the color to the standard color (because the footer is re-used in a RecyclerView)
}
}
public String getDescription() {
@@ -11,6 +11,9 @@ import android.widget.ImageView;
import org.thoughtcrime.securesms.R;
import java.util.ArrayList;
import java.util.List;
public class RemovableEditableMediaView extends FrameLayout {
private final @NonNull ImageView remove;
@@ -19,6 +22,7 @@ public class RemovableEditableMediaView extends FrameLayout {
private final int removeSize;
private @Nullable View current;
private final List<OnClickListener> removeClickListeners = new ArrayList<>();
public RemovableEditableMediaView(Context context) {
this(context, null);
@@ -72,8 +76,22 @@ public class RemovableEditableMediaView extends FrameLayout {
return current;
}
public void setRemoveClickListener(View.OnClickListener listener) {
this.remove.setOnClickListener(listener);
public void addRemoveClickListener(View.OnClickListener listener) {
removeClickListeners.add(listener);
updateRemoveClickListener();
}
public void removeRemoveClickListener(View.OnClickListener listener) {
removeClickListeners.remove(listener);
updateRemoveClickListener();
}
private void updateRemoveClickListener() {
this.remove.setOnClickListener(v -> {
for (OnClickListener listener : removeClickListeners) {
listener.onClick(v);
}
});
}
public void setEditClickListener(View.OnClickListener listener) {
@@ -0,0 +1,58 @@
package org.thoughtcrime.securesms.components.audioplay;
import android.net.Uri;
import androidx.annotation.Nullable;
public class AudioPlaybackState {
private final int msgId;
private final @Nullable Uri audioUri;
private final PlaybackStatus status;
private final long currentPosition;
private final long duration;
public enum PlaybackStatus {
IDLE,
LOADING,
PLAYING,
PAUSED,
ERROR
}
public AudioPlaybackState(int msgId,
@Nullable Uri audioUri,
PlaybackStatus status,
long currentPosition,
long duration) {
this.msgId = msgId;
this.audioUri = audioUri;
this.status = status;
this.currentPosition = currentPosition;
this.duration = duration;
}
public static AudioPlaybackState idle() {
return new AudioPlaybackState(0, null, PlaybackStatus.IDLE, 0, 0);
}
public int getMsgId() {
return msgId;
}
@Nullable
public Uri getAudioUri() {
return audioUri;
}
public PlaybackStatus getStatus() {
return status;
}
public long getCurrentPosition() {
return currentPosition;
}
public long getDuration() {
return duration;
}
}
@@ -0,0 +1,243 @@
package org.thoughtcrime.securesms.components.audioplay;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import androidx.media3.common.MediaItem;
import androidx.media3.common.Player;
import androidx.media3.session.MediaController;
public class AudioPlaybackViewModel extends ViewModel {
private static final String TAG = AudioPlaybackViewModel.class.getSimpleName();
private static final int NON_MESSAGE_AUDIO_MSG_ID = 0; // Audios not attached to a message doesn't have message id.
private final MutableLiveData<AudioPlaybackState> playbackState;
private @Nullable MediaController mediaController;
private final Handler handler;
private boolean isUserSeeking = false;
public AudioPlaybackViewModel() {
playbackState = new MutableLiveData<>(AudioPlaybackState.idle());
handler = new Handler(Looper.getMainLooper());
}
public LiveData<AudioPlaybackState> getPlaybackState() {
return playbackState;
}
public void setMediaController(@Nullable MediaController controller) {
this.mediaController = controller;
if (mediaController != null && mediaController.isPlaying()) {
startUpdateProgress();
}
updateCurrentState(true);
setupPlayerListener();
}
// Public methods
public void loadAudioAndPlay(int msgId, Uri audioUri) {
if (mediaController == null) return;
// Set media item if we have a different audio.
if (isDifferentAudio(msgId, audioUri)) {
updateState(msgId, audioUri, AudioPlaybackState.PlaybackStatus.LOADING, 0, 0);
MediaItem mediaItem = new MediaItem.Builder()
.setMediaId(String.valueOf(msgId))
.setUri(audioUri)
.build();
mediaController.setMediaItem(mediaItem);
mediaController.prepare();
}
play(msgId, audioUri);
}
private boolean isSameAudio(int msgId, Uri audioUri) {
return !isDifferentAudio(msgId, audioUri);
}
private boolean isDifferentAudio(int msgId, Uri audioUri) {
AudioPlaybackState currentState = playbackState.getValue();
return currentState != null && (
msgId != currentState.getMsgId() ||
currentState.getAudioUri() == null ||
currentState.getAudioUri() != null && !currentState.getAudioUri().equals(audioUri));
}
public void pause(int msgId, Uri audioUri) {
if (mediaController != null && isSameAudio(msgId, audioUri)) {
mediaController.pause();
}
}
public void play(int msgId, Uri audioUri) {
if (mediaController != null && isSameAudio(msgId, audioUri)) {
mediaController.play();
}
}
public void seekTo(long position, int msgId, Uri audioUri) {
if (mediaController != null && isSameAudio(msgId, audioUri)) {
mediaController.seekTo(position);
}
}
public void stop(int msgId, Uri audioUri) {
if (mediaController != null && isSameAudio(msgId, audioUri)) {
mediaController.stop();
stopUpdateProgress();
playbackState.setValue(AudioPlaybackState.idle());
}
}
public void stopNonMessageAudioPlayback() {
stopByIds(NON_MESSAGE_AUDIO_MSG_ID);
}
// A special method for deleting message, where we only use message Ids
public void stopByIds(int... msgIds) {
AudioPlaybackState currentState = playbackState.getValue();
if (mediaController != null && currentState != null) {
for (int msgId : msgIds) {
if (msgId == currentState.getMsgId()) {
mediaController.stop();
stopUpdateProgress();
playbackState.setValue(AudioPlaybackState.idle());
}
}
}
}
public void setUserSeeking(boolean isUserSeeking) {
this.isUserSeeking = isUserSeeking;
}
// Private methods
private void setupPlayerListener() {
if (mediaController == null) return;
mediaController.addListener(new Player.Listener() {
@Override
public void onEvents(Player player, Player.Events events) {
if (events.containsAny(Player.EVENT_IS_PLAYING_CHANGED)) {
if (player.isPlaying()) {
startUpdateProgress();
} else {
stopUpdateProgress();
}
updateCurrentState(false);
}
if (events.containsAny(Player.EVENT_PLAYBACK_STATE_CHANGED)) {
if (player.getPlaybackState() == Player.STATE_READY) {
updateCurrentState(false);
} else if (player.getPlaybackState() == Player.STATE_ENDED) {
// This is to prevent automatically playing after the audio
// has been play to the end once, then user dragged the seek bar again
mediaController.setPlayWhenReady(false);
}
}
if (events.containsAny(Player.EVENT_PLAYER_ERROR)) {
updateCurrentAudioState(AudioPlaybackState.PlaybackStatus.ERROR, 0, 0);
}
}
});
}
private void updateCurrentState(boolean queryPlaying) {
if (mediaController == null) return;
AudioPlaybackState.PlaybackStatus status;
if (mediaController.isPlaying()) {
status = AudioPlaybackState.PlaybackStatus.PLAYING;
} else if (mediaController.getPlaybackState() == Player.STATE_READY
|| mediaController.getPlaybackState() == Player.STATE_ENDED) {
status = AudioPlaybackState.PlaybackStatus.PAUSED;
} else {
status = AudioPlaybackState.PlaybackStatus.IDLE;
}
Uri currentUri = null;
int currentMsgId = 0;
if (playbackState.getValue() != null) {
currentMsgId = playbackState.getValue().getMsgId();
currentUri = playbackState.getValue().getAudioUri();
}
if (queryPlaying || playbackState.getValue() == null) {
MediaItem item = mediaController.getCurrentMediaItem();
if (item != null) {
try {
currentMsgId = Integer.parseInt(item.mediaId);
} catch (NumberFormatException e) {
Log.w(TAG, "Invalid integer", e);
}
if (item.localConfiguration != null) {
currentUri = item.localConfiguration.uri;
}
}
}
updateState(
currentMsgId,
currentUri,
status,
mediaController.getCurrentPosition(),
mediaController.getDuration());
}
private void updateState(int msgId,
Uri audioUri,
AudioPlaybackState.PlaybackStatus status,
long position,
long duration) {
playbackState.setValue(new AudioPlaybackState(
msgId, audioUri, status, position, duration
));
}
private void updateCurrentAudioState(AudioPlaybackState.PlaybackStatus status,
long position,
long duration) {
AudioPlaybackState current = playbackState.getValue();
if (current != null) {
updateState(current.getMsgId(), current.getAudioUri(), status, position, duration);
}
}
// Progress tracking
private final Runnable progressRunnable = new Runnable() {
@Override
public void run() {
if (mediaController != null && mediaController.isPlaying() && !isUserSeeking) {
updateCurrentAudioState(AudioPlaybackState.PlaybackStatus.PLAYING,
mediaController.getCurrentPosition(),
mediaController.getDuration());
handler.postDelayed(this, 100);
}
}
};
private void startUpdateProgress() {
stopUpdateProgress();
handler.post(progressRunnable);
}
private void stopUpdateProgress() {
handler.removeCallbacks(progressRunnable);
}
@Override
protected void onCleared() {
stopUpdateProgress();
super.onCleared();
}
}
@@ -0,0 +1,318 @@
package org.thoughtcrime.securesms.components.audioplay;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.lifecycle.Observer;
import androidx.vectordrawable.graphics.drawable.Animatable2Compat;
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.mms.AudioSlide;
import org.thoughtcrime.securesms.util.DateUtils;
public class AudioView extends FrameLayout {
private static final String TAG = AudioView.class.getSimpleName();
private final @NonNull ImageView playPauseButton;
private final AnimatedVectorDrawableCompat playToPauseDrawable;
private final AnimatedVectorDrawableCompat pauseToPlayDrawable;
private final Drawable playDrawable;
private final Drawable pauseDrawable;
private final Animatable2Compat.AnimationCallback animationCallback;
private final @NonNull SeekBar seekBar;
private final @NonNull TextView timestamp;
private final @NonNull TextView title;
private final @NonNull View mask;
private OnActionListener listener;
private int msgId;
private Uri audioUri;
private AudioPlaybackViewModel viewModel;
private final Observer<AudioPlaybackState> stateObserver = this::onPlaybackStateChanged;
private boolean isPlaying;
public AudioView(Context context) {
this(context, null);
}
public AudioView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AudioView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
inflate(context, R.layout.audio_view, this);
this.playPauseButton = findViewById(R.id.play_pause);
this.seekBar = findViewById(R.id.seek);
this.timestamp = findViewById(R.id.timestamp);
this.title = findViewById(R.id.title);
this.mask = findViewById(R.id.interception_mask);
this.timestamp.setText("00:00");
// Load drawables once
this.playToPauseDrawable = AnimatedVectorDrawableCompat.create(
getContext(), R.drawable.play_to_pause_animation);
this.pauseToPlayDrawable = AnimatedVectorDrawableCompat.create(
getContext(), R.drawable.pause_to_play_animation);
this.playDrawable = AppCompatResources.getDrawable(getContext(), R.drawable.play_icon);
this.pauseDrawable = AppCompatResources.getDrawable(getContext(), R.drawable.pause_icon);
this.animationCallback = new Animatable2Compat.AnimationCallback() {
@Override
public void onAnimationEnd(Drawable drawable) {
Drawable endState = isPlaying ? pauseDrawable : playDrawable;
playPauseButton.setImageDrawable(endState);
}
};
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
setupControls();
}
private void setupControls() {
// Set up observer in a very specific case when the view is detached and then re-attached,
// but binding from adapter has not happened yet
if (viewModel != null) {
viewModel.getPlaybackState().removeObserver(stateObserver);
viewModel.getPlaybackState().observeForever(stateObserver);
}
playPauseButton.setOnClickListener(v -> {
Log.w(TAG, "playPauseButton onClick");
if (viewModel == null || audioUri == null) return;
AudioPlaybackState state = viewModel.getPlaybackState().getValue();
if (state != null && msgId == state.getMsgId() && audioUri.equals(state.getAudioUri())) {
// Same audio
if (state.getStatus() == AudioPlaybackState.PlaybackStatus.PLAYING) {
viewModel.pause(msgId, audioUri);
} else {
viewModel.play(msgId, audioUri);
}
} else {
// Different audio
// Note: they can be the same *physical* file, but in different messages
viewModel.loadAudioAndPlay(msgId, audioUri);
}
if (listener != null) {
listener.onPlayPauseButtonClicked(v);
}
});
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
timestamp.setText(DateUtils.getFormatedDuration(progress));
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
viewModel.setUserSeeking(true);
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
viewModel.setUserSeeking(false);
viewModel.seekTo(seekBar.getProgress(), msgId, audioUri);
}
});
if (playToPauseDrawable != null) {
playToPauseDrawable.registerAnimationCallback(animationCallback);
}
if (pauseToPlayDrawable != null) {
pauseToPlayDrawable.registerAnimationCallback(animationCallback);
}
}
@Override
protected void onDetachedFromWindow() {
if (viewModel != null) {
viewModel.getPlaybackState().removeObserver(stateObserver);
}
if (playToPauseDrawable != null) {
playToPauseDrawable.clearAnimationCallbacks();
}
if (pauseToPlayDrawable != null) {
pauseToPlayDrawable.clearAnimationCallbacks();
}
super.onDetachedFromWindow();
}
public void setPlaybackViewModel(AudioPlaybackViewModel viewModel) {
if (this.viewModel != null) {
this.viewModel.getPlaybackState().removeObserver(stateObserver);
}
// ViewModel is used directly for simplicity, since there is no reuse yet
this.viewModel = viewModel;
if (viewModel != null) {
viewModel.getPlaybackState().observeForever(stateObserver);
}
}
public void setAudio(final @NonNull AudioSlide audio, int duration)
{
msgId = audio.getDcMsgId();
audioUri = audio.getUri();
playPauseButton.setImageDrawable(playDrawable);
seekBar.setEnabled(true);
seekBar.setProgress(0);
timestamp.setText(DateUtils.getFormatedDuration(duration));
if(audio.asAttachment().isVoiceNote() || !audio.getFileName().isPresent()) {
title.setVisibility(View.GONE);
}
else {
title.setText(audio.getFileName().get());
title.setVisibility(View.VISIBLE);
}
}
@Override
public void setOnClickListener(OnClickListener listener) {
super.setOnClickListener(listener);
this.mask.setOnClickListener(listener);
}
@Override
public void setOnLongClickListener(OnLongClickListener listener) {
super.setOnLongClickListener(listener);
this.mask.setOnLongClickListener(listener);
this.playPauseButton.setOnLongClickListener(listener);
}
public int getMsgId() {
return msgId;
}
public Uri getAudioUri() {
return audioUri;
}
public interface OnActionListener {
void onPlayPauseButtonClicked(View view);
}
public void setOnActionListener(OnActionListener listener) {
this.listener = listener;
}
public void togglePlay() {
playPauseButton.performClick();
}
public String getDescription() {
String desc;
if (this.title.getVisibility() == View.VISIBLE) {
desc = getContext().getString(R.string.audio);
} else {
desc = getContext().getString(R.string.voice_message);
}
desc += "\n" + this.timestamp.getText();
if (title.getVisibility() == View.VISIBLE) {
desc += "\n" + this.title.getText();
}
return desc;
}
private void updateProgress(AudioPlaybackState state) {
int duration = (int) state.getDuration();
int position = (int) state.getCurrentPosition();
if (duration > 0) {
seekBar.setMax(duration);
seekBar.setProgress(position);
timestamp.setText(DateUtils.getFormatedDuration(position));
}
}
public void disablePlayer(boolean disable) {
this.mask.setVisibility(disable? View.VISIBLE : View.GONE);
}
public void getSeekBarGlobalVisibleRect(@NonNull Rect rect) {
seekBar.getGlobalVisibleRect(rect);
}
private void togglePlayPause(boolean expectedPlaying) {
isPlaying = expectedPlaying;
Drawable expectedDrawable = expectedPlaying ? pauseDrawable : playDrawable;
boolean isAnimating = false;
Drawable currentDrawable = playPauseButton.getDrawable();
if (currentDrawable instanceof AnimatedVectorDrawableCompat) {
isAnimating = ((AnimatedVectorDrawableCompat) currentDrawable).isRunning();
}
if (!isAnimating && playPauseButton.getDrawable() != expectedDrawable) {
AnimatedVectorDrawableCompat animDrawable = expectedPlaying ? playToPauseDrawable : pauseToPlayDrawable;
String contentDescription = getContext().getString(
expectedPlaying ? R.string.menu_pause : R.string.menu_play);
if (animDrawable != null) {
playPauseButton.setImageDrawable(animDrawable);
playPauseButton.setContentDescription(contentDescription);
animDrawable.start();
}
}
}
private void onPlaybackStateChanged(AudioPlaybackState state) {
if (audioUri == null || state == null) return;
// Check if this state is about this message
boolean isThisMessage = msgId == state.getMsgId() && audioUri.equals(state.getAudioUri());
if (isThisMessage) {
updateUIForPlaybackState(state);
} else {
togglePlayPause(false);
}
}
private void updateUIForPlaybackState(AudioPlaybackState state) {
switch (state.getStatus()) {
case PLAYING:
togglePlayPause(true);
updateProgress(state);
break;
case PAUSED:
togglePlayPause(false);
updateProgress(state);
break;
case LOADING:
case ERROR:
// No special handling yet
break;
}
}
}
@@ -138,7 +138,7 @@ public class AccountManager {
// ui
public void showSwitchAccountMenu(ConversationListActivity activity, boolean selectOnly) {
AccountSelectionListFragment dialog = new AccountSelectionListFragment(activity, selectOnly);
AccountSelectionListFragment dialog = AccountSelectionListFragment.newInstance(selectOnly);
dialog.show(((FragmentActivity) activity).getSupportFragmentManager(), null);
}
@@ -164,6 +164,23 @@ public class DcEventCenter {
});
}
public void handleLogging(@NonNull DcEvent event) {
final String logPrefix = "[accId="+event.getAccountId()+ "] ";
switch (event.getId()) {
case DcContext.DC_EVENT_INFO:
Log.i("DeltaChat", logPrefix + event.getData2Str());
break;
case DcContext.DC_EVENT_WARNING:
Log.w("DeltaChat", logPrefix + event.getData2Str());
break;
case DcContext.DC_EVENT_ERROR:
Log.e("DeltaChat", logPrefix + event.getData2Str());
break;
}
}
public long handleEvent(@NonNull DcEvent event) {
int accountId = event.getAccountId();
int id = event.getId();
@@ -205,20 +222,7 @@ public class DcEventCenter {
return 0;
}
final String logPrefix = "[accId="+accountId + "] ";
switch (id) {
case DcContext.DC_EVENT_INFO:
Log.i("DeltaChat", logPrefix + event.getData2Str());
break;
case DcContext.DC_EVENT_WARNING:
Log.w("DeltaChat", logPrefix + event.getData2Str());
break;
case DcContext.DC_EVENT_ERROR:
Log.e("DeltaChat", logPrefix + event.getData2Str());
break;
}
handleLogging(event);
if (accountId != context.getDcContext().getAccountId()) {
return 0;
@@ -132,7 +132,7 @@ public class DcHelper {
dcContext.setStockTranslation(68, context.getString(R.string.device_talk));
dcContext.setStockTranslation(69, context.getString(R.string.saved_messages));
dcContext.setStockTranslation(70, context.getString(R.string.device_talk_explain));
dcContext.setStockTranslation(71, context.getString(R.string.device_welcome_message, "https://i.delta.chat/#0A45953086F0C166D3BAF1D4BB2025496E4C2704&x=MVPi07rQBEmHO4FRb3brpwDe&j=n8mkKqu42WAKKUCx1bQOVh23&s=RxuXoa0vhvTs0QLsWM45Ues0&a=adb%40arcanechat.me&n=adb&b=ArcaneChat+Channel"));
dcContext.setStockTranslation(71, context.getString(R.string.device_welcome_message, "https://i.delta.chat/#0A45953086F0C166D3BAF1D4BB2025496E4C2704&x=3KvvQZfzU4t-9u5s0PF3USGp&i=X-QDZ681F6Plz_uBu47CKdg4&s=IwE4LraLDcdPBW597wB7DBnI&a=arcanechat%40arcanechat.me&n=ArcaneChat&g=ArcaneChat+Community"));
dcContext.setStockTranslation(73, context.getString(R.string.systemmsg_subject_for_new_contact));
dcContext.setStockTranslation(74, context.getString(R.string.systemmsg_failed_sending_to));
dcContext.setStockTranslation(84, context.getString(R.string.configuration_failed_with_error));
@@ -210,8 +210,6 @@ public class DcHelper {
dcContext.setStockTranslation(178, context.getString(R.string.member_x_removed));
dcContext.setStockTranslation(190, context.getString(R.string.secure_join_wait));
dcContext.setStockTranslation(193, context.getString(R.string.donate_device_msg));
dcContext.setStockTranslation(194, context.getString(R.string.outgoing_call));
dcContext.setStockTranslation(195, context.getString(R.string.incoming_call));
dcContext.setStockTranslation(196, context.getString(R.string.declined_call));
dcContext.setStockTranslation(197, context.getString(R.string.canceled_call));
dcContext.setStockTranslation(198, context.getString(R.string.missed_call));
@@ -223,6 +221,10 @@ public class DcHelper {
dcContext.setStockTranslation(220, context.getString(R.string.proxy_enabled));
dcContext.setStockTranslation(221, context.getString(R.string.proxy_enabled_hint));
dcContext.setStockTranslation(230, context.getString(R.string.chat_unencrypted_explanation));
dcContext.setStockTranslation(232, context.getString(R.string.outgoing_audio_call));
dcContext.setStockTranslation(233, context.getString(R.string.outgoing_video_call));
dcContext.setStockTranslation(234, context.getString(R.string.incoming_audio_call));
dcContext.setStockTranslation(235, context.getString(R.string.incoming_video_call));
}
public static File getImexDir() {
@@ -49,13 +49,13 @@ import org.thoughtcrime.securesms.WebxdcActivity;
import org.thoughtcrime.securesms.WebxdcStoreActivity;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.UriAttachment;
import org.thoughtcrime.securesms.audio.AudioSlidePlayer;
import org.thoughtcrime.securesms.components.AudioView;
import org.thoughtcrime.securesms.components.DocumentView;
import org.thoughtcrime.securesms.components.RemovableEditableMediaView;
import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.components.VcardView;
import org.thoughtcrime.securesms.components.WebxdcView;
import org.thoughtcrime.securesms.components.audioplay.AudioPlaybackViewModel;
import org.thoughtcrime.securesms.components.audioplay.AudioView;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.geolocation.DcLocationManager;
@@ -121,7 +121,7 @@ public class AttachmentManager {
//this.mapView = ViewUtil.findById(root, R.id.attachment_location);
this.removableMediaView = ViewUtil.findById(root, R.id.removable_media_view);
removableMediaView.setRemoveClickListener(new RemoveButtonListener());
removableMediaView.addRemoveClickListener(new RemoveButtonListener());
removableMediaView.setEditClickListener(new EditButtonListener());
thumbnail.setOnClickListener(new ThumbnailClickListener());
}
@@ -152,8 +152,6 @@ public class AttachmentManager {
markGarbage(getSlideUri());
slide = Optional.absent();
audioView.cleanup();
}
}
@@ -235,7 +233,8 @@ public class AttachmentManager {
@NonNull final MediaType mediaType,
final int width,
final int height,
final int chatId)
final int chatId,
AudioPlaybackViewModel playbackViewModel)
{
inflateStub();
@@ -285,26 +284,12 @@ public class AttachmentManager {
setAttachmentPresent(true);
if (slide.hasAudio()) {
class SetDurationListener implements AudioSlidePlayer.Listener {
@Override
public void onStart() {}
@Override
public void onStop() {}
@Override
public void onProgress(AudioSlide slide, double progress, long millis) {}
@Override
public void onReceivedDuration(int millis) {
((AudioView) removableMediaView.getCurrent()).setDuration(millis);
}
}
AudioSlidePlayer audioSlidePlayer = AudioSlidePlayer.createFor(context, (AudioSlide) slide, new SetDurationListener());
audioSlidePlayer.requestDuration();
audioView.setPlaybackViewModel(playbackViewModel);
audioView.setAudio((AudioSlide) slide, 0);
removableMediaView.display(audioView, false);
removableMediaView.addRemoveClickListener(v -> {
playbackViewModel.stop(audioView.getMsgId(), audioView.getAudioUri());
});
result.set(true);
} else if (slide.isVcard()) {
vcardView.setVcard(glideRequests, (VcardSlide)slide, DcHelper.getRpc(context));
@@ -61,6 +61,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import chat.delta.rpc.RpcException;
public class NotificationCenter {
private static final String TAG = NotificationCenter.class.getSimpleName();
@NonNull private final ApplicationContext context;
@@ -164,7 +166,7 @@ public class NotificationCenter {
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | IntentUtils.FLAG_MUTABLE());
}
public PendingIntent getOpenCallIntent(ChatData chatData, int callId, String payload, boolean autoAccept) {
public PendingIntent getOpenCallIntent(ChatData chatData, int callId, String payload, boolean autoAccept, boolean hasVideo) {
final Intent chatIntent = new Intent(context, ConversationActivity.class)
.putExtra(ConversationActivity.ACCOUNT_ID_EXTRA, chatData.accountId)
.putExtra(ConversationActivity.CHAT_ID_EXTRA, chatData.chatId)
@@ -184,6 +186,7 @@ public class NotificationCenter {
intent.putExtra(CallActivity.EXTRA_CHAT_ID, chatData.chatId);
intent.putExtra(CallActivity.EXTRA_CALL_ID, callId);
intent.putExtra(CallActivity.EXTRA_HASH, hash);
intent.putExtra(CallActivity.EXTRA_HAS_VIDEO, hasVideo);
intent.setPackage(context.getPackageName());
return TaskStackBuilder.create(context)
.addNextIntentWithParentStack(chatIntent)
@@ -427,6 +430,13 @@ public class NotificationCenter {
Util.runOnAnyBackgroundThread(() -> {
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
DcContext dcContext = context.getDcAccounts().getAccount(accId);
boolean hasVideo;
try {
hasVideo = context.getRpc().callInfo(accId, callId).hasVideo;
} catch (RpcException e) {
Log.e(TAG, "Rpc.callInfo() failed", e);
hasVideo = false;
}
int chatId = dcContext.getMsg(callId).getChatId();
DcChat dcChat = dcContext.getChat(chatId);
String name = dcChat.getName();
@@ -434,7 +444,7 @@ public class NotificationCenter {
String notificationChannel = getCallNotificationChannel(notificationManager, chatData, name);
PendingIntent declineCallIntent = getDeclineCallIntent(chatData, callId);
PendingIntent openCallIntent = getOpenCallIntent(chatData, callId, payload, false);
PendingIntent openCallIntent = getOpenCallIntent(chatData, callId, payload, false, hasVideo);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, notificationChannel)
.setSmallIcon(R.drawable.icon_notification)
@@ -459,7 +469,7 @@ public class NotificationCenter {
new NotificationCompat.Action.Builder(
R.drawable.ic_videocam_white_24dp,
context.getString(R.string.answer_call),
getOpenCallIntent(chatData, callId, payload, true)).build());
getOpenCallIntent(chatData, callId, payload, true, hasVideo)).build());
Bitmap bitmap = getAvatar(dcChat);
if (bitmap != null) {
@@ -0,0 +1,35 @@
package org.thoughtcrime.securesms.qr;
import android.app.Activity;
import com.journeyapps.barcodescanner.BarcodeResult;
import com.journeyapps.barcodescanner.CaptureManager;
import com.journeyapps.barcodescanner.DecoratedBarcodeView;
public class CustomCaptureManager extends CaptureManager {
private OnResultInterceptor interceptor;
public CustomCaptureManager(Activity activity, DecoratedBarcodeView barcodeView) {
super(activity, barcodeView);
}
public void setResultInterceptor(OnResultInterceptor interceptor) {
this.interceptor = interceptor;
}
@Override
protected void returnResult(BarcodeResult rawResult) {
if (interceptor != null) {
interceptor.onResult(rawResult, () -> {
super.returnResult(rawResult);
});
} else {
super.returnResult(rawResult);
}
}
public interface OnResultInterceptor {
void onResult(BarcodeResult result, Runnable finishCallback);
}
}
@@ -325,25 +325,29 @@ public class QrCodeHandler {
}
builder.setMessage(msg);
builder.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> {
try {
int newChatId = DcHelper.getRpc(activity).secureJoinWithUxInfo(dcContext.getAccountId(), qrRawString, source, uipath);
if (newChatId == 0) throw new Exception("Securejoin failed to create a chat");
Intent intent = new Intent(activity, ConversationActivity.class);
intent.putExtra(ConversationActivity.CHAT_ID_EXTRA, newChatId);
activity.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
AlertDialog.Builder builder1 = new AlertDialog.Builder(activity);
builder1.setMessage(e.getMessage());
builder1.setPositiveButton(android.R.string.ok, null);
builder1.create().show();
}
secureJoinByQr(qrRawString, source, uipath);
});
builder.setNegativeButton(android.R.string.cancel, null);
}
public void addRelay(String qrData) {
public void secureJoinByQr(String qrRawString, SecurejoinSource source, SecurejoinUiPath uipath) {
try {
int newChatId = DcHelper.getRpc(activity).secureJoinWithUxInfo(dcContext.getAccountId(), qrRawString, source, uipath);
if (newChatId == 0) throw new Exception("Securejoin failed to create a chat");
Intent intent = new Intent(activity, ConversationActivity.class);
intent.putExtra(ConversationActivity.CHAT_ID_EXTRA, newChatId);
activity.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
AlertDialog.Builder builder1 = new AlertDialog.Builder(activity);
builder1.setMessage(e.getMessage());
builder1.setPositiveButton(android.R.string.ok, null);
builder1.create().show();
}
}
public void addRelay(String qrData) {
ProgressDialog progressDialog = new ProgressDialog(activity);
progressDialog.setMessage(activity.getResources().getString(R.string.one_moment));
progressDialog.setCanceledOnTouchOutside(false);
@@ -10,8 +10,12 @@ import android.view.MenuItem;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import com.journeyapps.barcodescanner.CaptureManager;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcLot;
import com.journeyapps.barcodescanner.CompoundBarcodeView;
import org.thoughtcrime.securesms.BaseActionBarActivity;
@@ -26,10 +30,12 @@ public class RegistrationQrActivity extends BaseActionBarActivity {
public static final String ADD_AS_SECOND_DEVICE_EXTRA = "add_as_second_device";
public static final String QRDATA_EXTRA = "qrdata";
private CaptureManager capture;
private CustomCaptureManager capture;
private CompoundBarcodeView barcodeScannerView;
private DcContext dcContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -66,6 +72,8 @@ public class RegistrationQrActivity extends BaseActionBarActivity {
.onAnyResult(this::handleQrScanWithPermissions)
.onAnyDenied(this::handleQrScanWithDeniedPermission)
.execute();
dcContext = DcHelper.getContext(this);
}
private void handleQrScanWithPermissions() {
@@ -89,23 +97,70 @@ public class RegistrationQrActivity extends BaseActionBarActivity {
DcHelper.openHelp(this, "#multiclient");
return true;
} else if (itemId == R.id.menu_paste) {
Intent intent = new Intent();
intent.putExtra(QRDATA_EXTRA, Util.getTextFromClipboard(this));
setResult(Activity.RESULT_OK, intent);
finish();
String rawQr = Util.getTextFromClipboard(this);
Runnable okCallback = () -> {
Intent intent = new Intent();
intent.putExtra(QRDATA_EXTRA, rawQr);
setResult(Activity.RESULT_OK, intent);
finish();
};
showConfirmDialog(rawQr, okCallback, null);
return true;
}
return false;
}
@Override
private void showConfirmDialog(String rawQr, @NonNull Runnable okCallback, @Nullable Runnable cancelCallback) {
DcLot qrParsed = dcContext.checkQr(rawQr);
String dialogMsg = "";
if (qrParsed.getState() == DcContext.DC_QR_ASK_VERIFYCONTACT) {
String name = dcContext.getContact(qrParsed.getId()).getDisplayName();
dialogMsg = getString(R.string.instant_onboarding_confirm_contact, name);
} else if (qrParsed.getState() == DcContext.DC_QR_ASK_VERIFYGROUP) {
String groupName = qrParsed.getText1();
dialogMsg = getString(R.string.instant_onboarding_confirm_group, groupName);
}
if (qrParsed.getState() == DcContext.DC_QR_ASK_VERIFYCONTACT
|| qrParsed.getState() == DcContext.DC_QR_ASK_VERIFYGROUP) {
AlertDialog confirmDialog = new AlertDialog.Builder(this)
.setMessage(dialogMsg)
.setPositiveButton("OK", (dialog, which) -> {
okCallback.run();
})
.setNegativeButton("Cancel", (dialog, which) -> {
if (cancelCallback != null) {
cancelCallback.run();
}
})
.show();
} else {
okCallback.run();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}
private void init(CompoundBarcodeView barcodeScannerView, Intent intent, Bundle savedInstanceState) {
capture = new CaptureManager(this, barcodeScannerView);
capture = new CustomCaptureManager(this, barcodeScannerView);
capture.setResultInterceptor((result, finishCallback) -> {
String rawQr = result.getText();
showConfirmDialog(rawQr, finishCallback, () -> {
barcodeScannerView.resume();
capture.decode();
});
});
capture.initializeFromIntent(intent, savedInstanceState);
capture.decode();
}
@@ -36,19 +36,24 @@ import chat.delta.rpc.types.Reactions;
public class ReactionsDetailsFragment extends DialogFragment implements DcEventCenter.DcEventDelegate {
private static final String TAG = ReactionsDetailsFragment.class.getSimpleName();
private static final String ARG_MSG_ID = "msg_id";
private RecyclerView recyclerView;
private ReactionRecipientsAdapter adapter;
private final int msgId;
private int msgId;
public ReactionsDetailsFragment(int msgId) {
super();
this.msgId = msgId;
public static ReactionsDetailsFragment newInstance(int msgId) {
ReactionsDetailsFragment fragment = new ReactionsDetailsFragment();
Bundle args = new Bundle();
args.putInt(ARG_MSG_ID, msgId);
fragment.setArguments(args);
return fragment;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
msgId = getArguments() != null? getArguments().getInt(ARG_MSG_ID, 0) : 0;
adapter = new ReactionRecipientsAdapter(requireActivity(), GlideApp.with(requireActivity()), new ListClickListener());
LayoutInflater inflater = requireActivity().getLayoutInflater();
@@ -3,7 +3,9 @@ package org.thoughtcrime.securesms.relay;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
@@ -43,6 +45,10 @@ public class RelayListActivity extends BaseActionBarActivity
private RelayListAdapter adapter;
private Rpc rpc;
private int accId;
/** Relay selected for context menu */
private EnteredLoginParam contextMenuRelay = null;
private boolean contextMenuRelayIsMain = false;
/** QR provided via Intent extras needs to be saved to pass it to QrCodeHandler when authorization finishes */
private String qrData = null;
@@ -166,6 +172,56 @@ public class RelayListActivity extends BaseActionBarActivity
.show();
}
@Override
public void onRelayLongClick(View view, EnteredLoginParam relay, boolean isMain) {
contextMenuRelay = relay;
contextMenuRelayIsMain = isMain;
registerForContextMenu(view);
openContextMenu(view);
unregisterForContextMenu(view);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
getMenuInflater().inflate(R.menu.relay_item_context, menu);
// Hide delete option if this is the main relay
if (contextMenuRelayIsMain) {
menu.findItem(R.id.menu_delete).setVisible(false);
}
}
@Override
public void onContextMenuClosed(android.view.Menu menu) {
super.onContextMenuClosed(menu);
clearContextMenuState();
}
@Override
public boolean onContextItemSelected(@NonNull MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.menu_edit) {
if (contextMenuRelay != null) {
onRelayEdit(contextMenuRelay);
clearContextMenuState();
}
return true;
} else if (itemId == R.id.menu_delete) {
if (contextMenuRelay != null) {
onRelayDelete(contextMenuRelay);
clearContextMenuState();
}
return true;
}
return super.onContextItemSelected(item);
}
private void clearContextMenuState() {
contextMenuRelay = null;
contextMenuRelayIsMain = false;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
@@ -27,6 +27,7 @@ public class RelayListAdapter extends RecyclerView.Adapter<RelayListAdapter.Rela
void onRelayClick(EnteredLoginParam relay);
void onRelayEdit(EnteredLoginParam relay);
void onRelayDelete(EnteredLoginParam relay);
void onRelayLongClick(View view, EnteredLoginParam relay, boolean isMain);
}
public RelayListAdapter(OnRelayClickListener listener) {
@@ -67,16 +68,12 @@ public class RelayListAdapter extends RecyclerView.Adapter<RelayListAdapter.Rela
private final TextView titleText;
private final TextView subtitleText;
private final ImageView mainIndicator;
private final ImageView editButton;
private final ImageView deleteButton;
public RelayViewHolder(@NonNull View itemView) {
super(itemView);
titleText = itemView.findViewById(R.id.title);
subtitleText = itemView.findViewById(R.id.subtitle);
mainIndicator = itemView.findViewById(R.id.main_indicator);
editButton = itemView.findViewById(R.id.edit_button);
deleteButton = itemView.findViewById(R.id.delete_button);
}
public void bind(EnteredLoginParam relay, boolean isMain, OnRelayClickListener listener) {
@@ -84,7 +81,6 @@ public class RelayListAdapter extends RecyclerView.Adapter<RelayListAdapter.Rela
titleText.setText(parts.length == 2? parts[1] : parts[0]);
subtitleText.setText(parts.length == 2? parts[0] : "");
mainIndicator.setVisibility(isMain ? View.VISIBLE : View.INVISIBLE);
deleteButton.setVisibility(isMain ? View.GONE : View.VISIBLE);
itemView.setOnClickListener(v -> {
if (listener != null) {
@@ -92,16 +88,11 @@ public class RelayListAdapter extends RecyclerView.Adapter<RelayListAdapter.Rela
}
});
editButton.setOnClickListener(v -> {
itemView.setOnLongClickListener(v -> {
if (listener != null) {
listener.onRelayEdit(relay);
}
});
deleteButton.setOnClickListener(v -> {
if (listener != null) {
listener.onRelayDelete(relay);
listener.onRelayLongClick(v, relay, isMain);
}
return true;
});
}
}
@@ -0,0 +1,140 @@
package org.thoughtcrime.securesms.service;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.media3.common.AudioAttributes;
import androidx.media3.common.C;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.session.MediaSession;
import androidx.media3.session.MediaSessionService;
import androidx.media3.session.SessionCommand;
import androidx.media3.session.SessionCommands;
import androidx.media3.session.SessionResult;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.ConversationListActivity;
public class AudioPlaybackService extends MediaSessionService {
private static final String TAG = AudioPlaybackService.class.getSimpleName();
private ExoPlayer player;
private MediaSession session;
@Override
public void onCreate() {
super.onCreate();
AudioAttributes audioAttributes = new AudioAttributes.Builder()
.setUsage(C.USAGE_MEDIA) // USAGE_VOICE_COMMUNICATION is for VoIP calls
.setContentType(C.AUDIO_CONTENT_TYPE_SPEECH)
.build();
player = new ExoPlayer.Builder(this)
.setAudioAttributes(audioAttributes, true)
.setHandleAudioBecomingNoisy(true)
.build();
// This is for click on the notification to go back to app
Intent intent = new Intent(this, ConversationListActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent initialIntent = PendingIntent.getActivity(
this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
session = new MediaSession.Builder(this, player)
.setSessionActivity(initialIntent)
.setCallback(new MediaSession.Callback() {
@OptIn(markerClass = UnstableApi.class)
@Override
public MediaSession.ConnectionResult onConnect(
MediaSession session,
MediaSession.ControllerInfo controller
) {
SessionCommands sessionCommands = MediaSession
.ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
.add(new SessionCommand("UPDATE_ACTIVITY_CONTEXT", new Bundle()))
.build();
return new MediaSession.ConnectionResult.AcceptedResultBuilder(session)
.setAvailableSessionCommands(sessionCommands)
.build();
}
@NonNull
@Override
public ListenableFuture<SessionResult> onCustomCommand(
MediaSession session,
MediaSession.ControllerInfo controller,
SessionCommand customCommand,
Bundle args
) {
if ("UPDATE_ACTIVITY_CONTEXT".equals(customCommand.customAction)) {
updateSessionActivity(args);
}
return Futures.immediateFuture(
new SessionResult(SessionResult.RESULT_SUCCESS));
}
})
.build();
}
@OptIn(markerClass = UnstableApi.class)
private void updateSessionActivity(Bundle args) {
try {
// Put all the original extras back into the intent
if (args != null && !args.isEmpty()) {
String activityClassName = args.getString("activity_class");
args.remove("activity_class");
if (activityClassName != null) {
Class<?> activityClass = Class.forName(activityClassName);
Intent intent = new Intent(this, activityClass);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtras(args);
PendingIntent pendingIntent = PendingIntent.getActivity(
this,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
session.setSessionActivity(pendingIntent);
}
}
} catch (ClassNotFoundException e) {
Log.e(TAG, "Activity class not found", e);
}
}
@Nullable
@Override
public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
return session;
}
@Override
public void onDestroy() {
if (session != null) {
session.release();
session = null;
}
if (player != null) {
player.release();
player = null;
}
super.onDestroy();
}
}
@@ -323,7 +323,15 @@ public class ViewUtil {
* @param view The view to apply insets to
*/
public static void applyWindowInsetsAsMargin(@NonNull View view) {
applyWindowInsetsAsMargin(view, true, true, true, true);
applyWindowInsetsAsMargin(view, true, true, true, true, false);
}
public static void applyWindowInsetsAsMargin(@NonNull View view, boolean left, boolean top, boolean right, boolean bottom) {
applyWindowInsetsAsMargin(view, left, top, right, bottom, false);
}
public static void forceApplyWindowInsetsAsMargin(@NonNull View view, boolean left, boolean top, boolean right, boolean bottom) {
applyWindowInsetsAsMargin(view, left, top, right, bottom, true);
}
/**
@@ -337,8 +345,9 @@ public class ViewUtil {
* @param top Whether to apply top inset
* @param right Whether to apply right inset
* @param bottom Whether to apply bottom inset
* @param forceDispatch Whether to force application of insets
*/
public static void applyWindowInsetsAsMargin(@NonNull View view, boolean left, boolean top, boolean right, boolean bottom) {
public static void applyWindowInsetsAsMargin(@NonNull View view, boolean left, boolean top, boolean right, boolean bottom, boolean forceDispatch) {
// Only enable on API 30+ where WindowInsets APIs work correctly
if (!isEdgeToEdgeSupported()) return;
@@ -371,10 +380,10 @@ public class ViewUtil {
ViewGroup.LayoutParams layoutParams = v.getLayoutParams();
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
ViewGroup.MarginLayoutParams marginParams = (ViewGroup.MarginLayoutParams) layoutParams;
marginParams.leftMargin = baseMarginLeft + insets.left;
marginParams.topMargin = baseMarginTop + insets.top;
marginParams.rightMargin = baseMarginRight + insets.right;
marginParams.bottomMargin = baseMarginBottom + insets.bottom;
marginParams.leftMargin = left ? baseMarginLeft + insets.left : baseMarginLeft;
marginParams.topMargin = top ? baseMarginTop + insets.top : baseMarginTop;
marginParams.rightMargin = right ? baseMarginRight + insets.right : baseMarginRight;
marginParams.bottomMargin = bottom ? baseMarginBottom + insets.bottom : baseMarginBottom;
v.setLayoutParams(marginParams);
}
@@ -383,18 +392,54 @@ public class ViewUtil {
// Request the initial insets to be dispatched if the view is attached
if (view.isAttachedToWindow()) {
ViewCompat.requestApplyInsets(view);
if (forceDispatch) {
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(view);
if (insets != null) {
ViewCompat.dispatchApplyWindowInsets(view, insets);
}
} else {
ViewCompat.requestApplyInsets(view);
}
}
}
/**
* Apply window insets to a view by adding padding to avoid drawing elements behind system bars.
* Convenience method that applies insets to all sides.
* IME insets are propagated to child views.
*
* @param view The view to apply insets to
*/
public static void applyWindowInsets(@NonNull View view) {
applyWindowInsets(view, true, true, true, true);
applyWindowInsets(view, true, true, true, true, false, false);
}
/**
* Apply window insets to a view by adding padding to avoid drawing elements behind system bars.
*
* IME insets are propagated to child views.
*
* @param view The view to apply insets to
* @param left Whether to apply left inset
* @param top Whether to apply top inset
* @param right Whether to apply right inset
* @param bottom Whether to apply bottom inset
*/
public static void applyWindowInsets(@NonNull View view, boolean left, boolean top, boolean right, boolean bottom) {
applyWindowInsets(view, left, top, right, bottom, false, false);
}
/**
* Force applying window insets to a view by adding padding to avoid drawing elements behind system bars.
*
* @param view The view to apply insets to
* @param left Whether to apply left inset
* @param top Whether to apply top inset
* @param right Whether to apply right inset
* @param bottom Whether to apply bottom inset
*/
public static void forceApplyWindowInsets(@NonNull View view, boolean left, boolean top, boolean right, boolean bottom) {
applyWindowInsets(view, left, top, right, bottom, false, true);
}
/**
@@ -408,8 +453,10 @@ public class ViewUtil {
* @param top Whether to apply top inset
* @param right Whether to apply right inset
* @param bottom Whether to apply bottom inset
* @param consumeImeInsets Whether to consume IME insets so they don't propagate to child views
* @param forceDispatch Force application of Insets, regardless if system think it shall dispatch
*/
public static void applyWindowInsets(@NonNull View view, boolean left, boolean top, boolean right, boolean bottom) {
public static void applyWindowInsets(@NonNull View view, boolean left, boolean top, boolean right, boolean bottom, boolean consumeImeInsets, boolean forceDispatch) {
// Only enable on API 30+ where WindowInsets APIs work correctly
if (!isEdgeToEdgeSupported()) return;
@@ -442,12 +489,24 @@ public class ViewUtil {
bottom ? basePaddingBottom + insets.bottom : basePaddingBottom
);
if (consumeImeInsets) {
windowInsets = new WindowInsetsCompat.Builder(windowInsets)
.setInsets(WindowInsetsCompat.Type.ime(), Insets.NONE)
.build();
}
return windowInsets;
});
// Request the initial insets to be dispatched if the view is attached
if (view.isAttachedToWindow()) {
ViewCompat.requestApplyInsets(view);
if (forceDispatch) {
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(view);
if (insets != null) {
ViewCompat.dispatchApplyWindowInsets(view, insets);
}
} else {
ViewCompat.requestApplyInsets(view);
}
}
}
+19 -37
View File
@@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="org.thoughtcrime.securesms.components.AudioView">
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout android:id="@+id/audio_widget_container"
android:orientation="vertical"
@@ -12,39 +10,19 @@
android:layout_height="fill_parent"
android:orientation="horizontal">
<org.thoughtcrime.securesms.components.AnimatingToggle
android:id="@+id/control_toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center">
<ImageView android:id="@+id/play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:clickable="true"
android:visibility="gone"
android:background="@drawable/circle_touch_highlight_background"
android:src="@drawable/ic_play_circle_fill_white_48dp"
android:scaleType="centerInside"
tools:visibility="gone"
android:contentDescription="@string/menu_play"/>
<ImageView android:id="@+id/pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:clickable="true"
android:visibility="gone"
android:background="@drawable/circle_touch_highlight_background"
android:src="@drawable/ic_pause_circle_fill_white_48dp"
android:scaleType="centerInside"
android:contentDescription="@string/menu_pause"/>
</org.thoughtcrime.securesms.components.AnimatingToggle>
<ImageView android:id="@+id/play_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:focusable="true"
android:gravity="center_vertical"
android:clickable="true"
android:visibility="visible"
android:background="@drawable/ic_circle_fill_white_48dp"
android:backgroundTint="@color/audio_icon"
android:src="@drawable/play_icon"
android:scaleType="centerInside"
android:contentDescription="@string/menu_play"/>
<LinearLayout android:layout_width="fill_parent"
android:layout_height="fill_parent"
@@ -54,7 +32,11 @@
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:layout_gravity="center_vertical"/>
android:layout_gravity="center_vertical"
android:progressTint="@color/audio_icon"
android:progressTintMode="src_in"
android:thumbTint="@color/audio_icon"
android:thumbTintMode="src_in"/>
<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content"
-1
View File
@@ -44,7 +44,6 @@
android:layout_width="30dp"
android:layout_height="30dp"
android:contentDescription="@null"
android:src="@drawable/ic_videocam_white_24dp"
/>
</LinearLayout>
@@ -37,7 +37,7 @@
app:minHeight="100dp"
app:maxHeight="300dp" />
<org.thoughtcrime.securesms.components.AudioView
<org.thoughtcrime.securesms.components.audioplay.AudioView
android:id="@+id/attachment_audio"
android:layout_width="230dp"
android:layout_height="wrap_content"
@@ -27,6 +27,7 @@
/>
<View
android:id="@+id/bottom_divider"
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_gravity="bottom"
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.components.AudioView
<org.thoughtcrime.securesms.components.audioplay.AudioView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="210dp"
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.components.AudioView
<org.thoughtcrime.securesms.components.audioplay.AudioView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="210dp"
android:layout_height="wrap_content"
@@ -47,6 +47,17 @@
</LinearLayout>
<TextView
android:id="@+id/invitation_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
android:gravity="start"
android:visibility="gone"
android:textColor="@color/gray50" />
<TextView
android:id="@+id/information_label"
android:layout_width="match_parent"
@@ -17,7 +17,7 @@
android:layout_weight="1"
android:visibility="gone"/>
<org.thoughtcrime.securesms.components.AudioView
<org.thoughtcrime.securesms.components.audioplay.AudioView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/audio_view"
-26
View File
@@ -59,30 +59,4 @@
</LinearLayout>
<ImageView
android:id="@+id/delete_button"
android:layout_width="48dp"
android:layout_height="72dp"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:clickable="true"
android:focusable="true"
android:contentDescription="@string/remove_transport"
android:background="@drawable/touch_highlight_background"
android:src="@drawable/ic_delete_white_24dp"
app:tint="?attr/conversation_list_item_date_color" />
<ImageView
android:id="@+id/edit_button"
android:layout_width="48dp"
android:layout_height="72dp"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:clickable="true"
android:focusable="true"
android:contentDescription="@string/edit_transport"
android:background="@drawable/touch_highlight_background"
android:src="@drawable/ic_create_white_24dp"
app:tint="?attr/conversation_list_item_date_color" />
</LinearLayout>
+16 -3
View File
@@ -26,9 +26,22 @@
app:showAsAction="always"/>
<item android:id="@+id/menu_start_call"
android:title="@string/start_call"
android:icon="@drawable/ic_videocam_white_24dp"
app:showAsAction="always" />
android:title="@string/start_call"
app:showAsAction="always"
android:icon="@drawable/baseline_call_24">
<menu>
<item android:id="@+id/menu_start_audio_call"
android:title="@string/start_audio_call"
android:icon="@drawable/baseline_call_24"
app:iconTint="?attr/menu_icon_tint"
/>
<item android:id="@+id/menu_start_video_call"
android:title="@string/start_video_call"
android:icon="@drawable/ic_videocam_white_24dp"
app:iconTint="?attr/menu_icon_tint"
/>
</menu>
</item>
<item android:id="@+id/menu_all_media"
android:title="@string/apps_and_media"
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_edit"
android:title="@string/edit_transport"/>
<item android:id="@+id/menu_delete"
android:title="@string/remove_transport"/>
</menu>
+2
View File
@@ -384,7 +384,9 @@
<string name="answer_call">Respon</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">Rebutja</string>
<!-- deprecated, use outgoing_audio_call or outgoing_video_call instead -->
<string name="outgoing_call">Trucada sortint</string>
<!-- deprecated, use incoming_audio_call or incoming_video_call instead -->
<string name="incoming_call">Trucada entrant</string>
<string name="declined_call">Trucada rebutjada</string>
<string name="canceled_call">Trucada cancel·lada</string>
+2
View File
@@ -408,7 +408,9 @@
<string name="answer_call">Zvedni</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">Pokles</string>
<!-- deprecated, use outgoing_audio_call or outgoing_video_call instead -->
<string name="outgoing_call">Odchozí hovor</string>
<!-- deprecated, use incoming_audio_call or incoming_video_call instead -->
<string name="incoming_call">Příchozí hovor</string>
<string name="declined_call">Odmítnutý hovor</string>
<string name="canceled_call">Zrušený hovor</string>
+10 -2
View File
@@ -381,12 +381,20 @@
<!-- the action "to call someone", used as a tooltip for the "phone" icon. not: "the call" -->
<string name="start_call">Anrufen</string>
<string name="start_audio_call">Sprachanruf</string>
<string name="start_video_call">Videoanruf</string>
<!-- the action "to answer" or to "accept" or to "pick up" a call. not: "the answer" -->
<string name="answer_call">Annehmen</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">Ablehnen</string>
<!-- deprecated, use outgoing_audio_call or outgoing_video_call instead -->
<string name="outgoing_call">Ausgehender Anruf</string>
<string name="outgoing_audio_call">Ausgehender Sprachanruf</string>
<string name="outgoing_video_call">Ausgehender Videoanruf</string>
<!-- deprecated, use incoming_audio_call or incoming_video_call instead -->
<string name="incoming_call">Eingehender Anruf</string>
<string name="incoming_audio_call">Eingehender Sprachanruf</string>
<string name="incoming_video_call">Eingehender Videoanruf</string>
<string name="declined_call">Abgelehnter Anruf</string>
<string name="canceled_call">Abgebrochener Anruf</string>
<string name="missed_call">Verpasster Anruf</string>
@@ -1089,8 +1097,8 @@
<string name="name_desktop">Name</string>
<string name="select_group_image_desktop">Gruppenbild auswählen</string>
<string name="export_backup_desktop">Backup exportieren</string>
<string name="message_detail_sent_desktop">gesendet</string>
<string name="message_detail_received_desktop">empfangen</string>
<string name="message_detail_sent_desktop">Gesendet</string>
<string name="message_detail_received_desktop">Empfangen</string>
<string name="menu.view.developer.open.log.folder">Log-Ordner öffnen</string>
<string name="menu.view.developer.open.current.log.file">Aktuelle Logdatei öffnen</string>
<string name="explain_desktop_minimized_disabled_tray_pref" tools:ignore="TypographyDashes">Benachrichtigungssymbol kann nicht deaktiviert werden, da Delta Chat mit der Option --minimized gestartet wurde.</string>
+20
View File
@@ -392,12 +392,20 @@
<!-- the action "to call someone", used as a tooltip for the "phone" icon. not: "the call" -->
<string name="start_call">Llamar</string>
<string name="start_audio_call">Llamada</string>
<string name="start_video_call">Videollamada</string>
<!-- the action "to answer" or to "accept" or to "pick up" a call. not: "the answer" -->
<string name="answer_call">Contestar</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">Rechazar</string>
<!-- deprecated, use outgoing_audio_call or outgoing_video_call instead -->
<string name="outgoing_call">Llamada saliente</string>
<string name="outgoing_audio_call">Llamada saliente</string>
<string name="outgoing_video_call">Videollamada saliente</string>
<!-- deprecated, use incoming_audio_call or incoming_video_call instead -->
<string name="incoming_call">Llamada entrante</string>
<string name="incoming_audio_call">Llamada entrante</string>
<string name="incoming_video_call">Videollamada entrante</string>
<string name="declined_call">Llamada rechazada</string>
<string name="canceled_call">Llamada cancelada</string>
<string name="missed_call">Llamada perdida</string>
@@ -805,6 +813,7 @@ Desactiva este ajuste solo si has eliminado este perfil de todos tus demás disp
<string name="pref_show_emails_accepted_contacts">Para contactos aceptados</string>
<string name="pref_show_emails_all">Todos</string>
<string name="pref_experimental_features">Características experimentales</string>
<string name="pref_experimental_features_explain">Estas funciones pueden ser inestables y pueden ser modificadas o eliminadas</string>
<string name="pref_on_demand_location_streaming">Transmisión de ubicación bajo demanda</string>
<string name="pref_background_default">Fondo por defecto</string>
<string name="pref_background_default_color">Color por defecto</string>
@@ -830,6 +839,14 @@ Desactiva este ajuste solo si has eliminado este perfil de todos tus demás disp
<string name="disable_imap_idle">Deshabilitar IMAP IDLE</string>
<string name="disable_imap_idle_explain">No utilizar la extensión IMAP IDLE aunque el servidor la admita. Habilitar esta opción retrasará el recibo de mensajes, actívala solo para pruebas.</string>
<string name="send_stats_to_devs">Enviar estadísticas a los desarrolladores de Delta Chat</string>
<string name="stats_device_message">¿Quieres ayudar a mejorar Delta Chat y apoyar la investigación enviando estadísticas de uso anónimas semanales?\n\n👉 Pulsa aquí… 👈</string>
<string name="stats_confirmation_dialog">¿Quieres ayudar a mejorar Delta Chat y apoyar la investigación enviando estadísticas de uso anónimas semanales?</string>
<string name="stats_thanks">¡Gracias! Siempre puedes desactivar el envío en \"Configuración -> Avanzado\".\n\n¿Tienes 5 minutos adicionales para participar en un estudio científico sobre la seguridad de Delta Chat?</string>
<string name="stats_disable_dialog">El envío de estadísticas ya está habilitado.\n\n¿Quieres deshabilitarlo?</string>
<string name="disable">Desactivar</string>
<string name="stats_keep_sending">Seguir enviando</string>
<string name="stats_msg_body">El archivo adjunto contiene estadísticas de uso anónimas que nos ayudan a mejorar Delta Chat. Consulta https://delta.chat/help#statssending para más información. ¡Gracias!</string>
<!-- Emoji picker and categories -->
<string name="emoji_search_results">Resultados de la búsqueda</string>
<string name="emoji_not_found">No se encontraron emojis</string>
@@ -1005,10 +1022,13 @@ Desactiva este ajuste solo si has eliminado este perfil de todos tus demás disp
<string name="qrlogin_ask_login">¿Iniciar sesión en \"%1$s\"?</string>
<!-- first placeholder will be replaced by name of the inviter, second placeholder will be replaced by the name of the inviter. -->
<string name="secure_join_started">%1$s te invitó a unirte a este grupo.\n\nEsperando que el dispositivo de %2$s responda…</string>
<!-- first placeholder will be replaced by name of the inviter, second placeholder will be replaced by the name of the inviter. -->
<string name="secure_join_channel_started">%1$s te invitó a unirte a este canal.\n\nEsperando que el dispositivo de %2$s responda…</string>
<!-- placeholder will be replaced by the name of the inviter. -->
<string name="secure_join_replies">%1$s respondió, esperando ser agregado al grupo…</string>
<string name="secure_join_wait">Estableciendo cifrado de extremo a extremo garantizado, por favor, espere...</string>
<string name="contact_verified">%1$s verificado.</string>
<string name="contact_blocked">Contacto bloqueado</string>
<!-- Shown in contact profile. The placeholder will be replaced by the name of the contact that introduced the contact. -->
<string name="verified_by">Verificado por %1$s</string>
<string name="verified_by_you">Verificado por ti</string>
+8
View File
@@ -380,12 +380,20 @@
<!-- the action "to call someone", used as a tooltip for the "phone" icon. not: "the call" -->
<string name="start_call">Helista</string>
<string name="start_audio_call">Häälkõne</string>
<string name="start_video_call">Videokõne</string>
<!-- the action "to answer" or to "accept" or to "pick up" a call. not: "the answer" -->
<string name="answer_call">Vasta</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">Keeldu</string>
<!-- deprecated, use outgoing_audio_call or outgoing_video_call instead -->
<string name="outgoing_call">Väljuv kõne</string>
<string name="outgoing_audio_call">Väljuv häälkõne</string>
<string name="outgoing_video_call">Väljuv videokõne</string>
<!-- deprecated, use incoming_audio_call or incoming_video_call instead -->
<string name="incoming_call">Saabuv kõne</string>
<string name="incoming_audio_call">Saabuv häälkõne</string>
<string name="incoming_video_call">Saabuv videokõne</string>
<string name="declined_call">Keeldutud kõne</string>
<string name="canceled_call">katkestatud kõne</string>
<string name="missed_call">Märkamata kõne</string>
+565 -6
View File
@@ -58,15 +58,18 @@
<string name="save">Gorde</string>
<string name="chat">Txata</string>
<string name="media">Multimedia</string>
<string name="profile">Profila</string>
<string name="all_profiles">Profil guztiak</string>
<string name="current_profile">Oraingo profila</string>
<string name="apps_and_media">Aplikazioak eta multimedia</string>
<string name="profile">Kontua</string>
<string name="all_profiles">Kontu guztiak</string>
<string name="current_profile">Oraingo kontua</string>
<string name="main_menu">Menu nagusia</string>
<string name="start_chat">Hasi txata</string>
<string name="show_full_message">Erakutsi mezu osoa</string>
<!-- Stay short here, say ~16 characters. The source string could also be "All Read", maybe that hint can make translations easier :) -->
<string name="mark_all_as_read">Ezarri denak irakurri gisa</string>
<string name="mark_as_read">Ezarri irakurri gisa</string>
<!-- Shortest text for "Mark as being read". In english, this could be "Read" (past tense of "to read"), in german, this could be "Gelesen". -->
<string name="mark_as_read_short">Markatu irakurrita</string>
<!-- Placeholder text when something is loading -->
<string name="loading">Kargatzen...</string>
<string name="hide">Ezkutatu</string>
@@ -185,7 +188,10 @@
<string name="video">Bideoa</string>
<string name="documents">Dokumentuak</string>
<string name="contact">Kontaktua</string>
<string name="bot">Bot</string>
<string name="camera">Kamera</string>
<!-- As in "start a video recording" or "take a photo"; eg. the description of the "shutter button" in cameras -->
<string name="capture">Harrapatu</string>
<string name="switch_camera">Aldatu kamera</string>
<string name="toggle_fullscreen">Aldatu pantaila osoa modua</string>
<string name="location">Kokalekua</string>
@@ -202,6 +208,8 @@
<string name="webxdc_app">Aplikazioa</string>
<!-- plural of "App"; used to present "Webxdc App" (https://webxdc.org) in a user friendly way. Please stay close to the original term and keep it short (it is used in menus with few screen space). -->
<string name="webxdc_apps">Aplikazioak</string>
<string name="webxdc_store_url">Aplikazio-hautatzailearen URL-a</string>
<string name="webxdc_store_url_explain">Ezarriz gero, URL hori erabiliko da aplikazio-hautatzaile gisa, lehenetsitakoaren ordez.</string>
<string name="webxdc_draft_hint">Sakatu \"Bidali\" partekatzeko</string>
<string name="home">Hasiera</string>
<string name="games">Jokoak</string>
@@ -235,6 +243,7 @@
<!-- menu labels (or icon, buttons...) -->
<string name="menu_new_contact">Kontaktu berria</string>
<string name="menu_new_classic_contact">Gehitu kontaktua eskuz</string>
<string name="new_classic_contact_explain">Eskuz gehitutako kontaktuak eposta helbide klasikoetara idazteko erabil daitezke, eta ez da bermatzen muturretik muturrera zifratuta egotea.</string>
<string name="menu_new_chat">Txat berria</string>
<string name="menu_new_group">Talde berria</string>
<!-- "Chat" is a verb here, "Message to" would also fit. the string might be used in the "New Chat" screen above the contact list -->
@@ -273,32 +282,67 @@
<string name="menu_message_details">Mezuaren xehetasunak</string>
<string name="menu_copy_to_clipboard">Kopiatu arbelera</string>
<string name="share_invite_link">Partekatu gonbidapen esteka</string>
<string name="share_invite_link_explain">Esteka hau duen edonork ikus dezake zure profila eta txat bat hasi zurekin. Partekatu konfiantzazko pertsonekin bakarrik.</string>
<string name="invite_friends">Gonbidatu lagunak</string>
<!-- %1$s is replaced by the user's invitation link ("https://i.delta.chat/...") -->
<string name="invite_friends_text">Jarri nirekin harremanetan Delta Chat bidez:\n%1$s</string>
<string name="menu_copy_selection_to_clipboard">Kopiatu hautaketa</string>
<string name="menu_copy_link_to_clipboard">Kopiatu esteka</string>
<string name="menu_copy_text_to_clipboard">Kopiatu testua</string>
<string name="menu_copy_image_to_clipboard">Kopiatu irudia</string>
<string name="menu_copy_email_to_clipboard">Kopiatu eposta</string>
<string name="paste_from_clipboard">Kopiatu arbeletik</string>
<string name="ask_copy_unopenable_link_to_clipboard">\"%1$d\" esteka ezin da web arakatzailean ireki. Esteka arbelean kopiatu nahi duzu horren ordez?</string>
<string name="menu_forward">Birbidali mezua</string>
<string name="menu_reply">Erantzun mezuari</string>
<string name="menu_mute">Mututu jakinarazpenak</string>
<string name="menu_unmute">Utzi mututzeari</string>
<string name="menu_export_attachment">Esportatu eranskina</string>
<string name="menu_export_attachments">Esportatu eranskinak</string>
<string name="menu_all_media">Multimedia guztia</string>
<string name="all_apps_and_media">Aplikazio guztiak eta multimedia</string>
<!-- Command to jump to the original message corresponding to a gallery image or document -->
<string name="show_in_chat">Erakutsi txatean</string>
<string name="show_app_in_chat">Erakutsi aplikazioa txatean</string>
<string name="menu_share">Partekatu</string>
<!-- Verb "to block sth." usually a mailing list or a contact, not a noun -->
<string name="block">Bloketau</string>
<string name="menu_block_contact">Blokeatu kontaktuak</string>
<string name="menu_unblock_contact">Desblokeatu kontaktua</string>
<string name="accept">Onartu</string>
<string name="menu_play">Erreproduzitu</string>
<string name="menu_pause">Pausatu</string>
<string name="menu_scroll_to_bottom">Korritu behealdera</string>
<string name="menu_scroll_to_top">Korritu goialdera</string>
<string name="menu_help">Laguntza</string>
<!-- use the same term for "Apps" as elsewhere -->
<string name="what_is_webxdc">Aplikazioei buruz</string>
<string name="privacy_policy">Pribatutasun politika</string>
<string name="menu_select_all">Hautatu guztiak</string>
<string name="select_chat">Aukeratu txata</string>
<string name="select_more">Aukeratu gehiago</string>
<string name="menu_edit_name">Editatu izena</string>
<!-- The placeholder will be replaced by the name the contact gave themself -->
<string name="edit_name_explain">Ezarri \"%1$s\" ordez ikusiko duzun ezizena. Utzi hutsik kontaktuak aukeratutako izena erabiltzeko.</string>
<!-- The placeholder will be replaced by the name the contact gave themself -->
<string name="edit_name_placeholder">\"%1$s\"(r)en ezizena</string>
<string name="menu_settings">Ezarpenak</string>
<string name="menu_advanced">Aurreratua</string>
<string name="menu_view_profile">Ikusi profila</string>
<string name="menu_view_profile">Ikusi kontua</string>
<string name="menu_zoom_in">Hurbildu zooma</string>
<string name="menu_zoom_out">Urrundu zooma</string>
<string name="menu_save_log">Gorde egunkaria</string>
<string name="menu_more_options">Aukera gehiago</string>
<string name="menu_learn_spelling">Ikasi ortografia</string>
<string name="jump_to_message">Joan mezura</string>
<string name="jump_to_original_message">Joan jatorrizko mezura</string>
<string name="copy_json">Kopiatu JSON</string>
<string name="replace_draft">Ordeztu zirriborroa</string>
<string name="title_share_location">Partekatu kokalekua taldeko kide guztiak</string>
<string name="device_talk">Gailuaren mezuak</string>
<string name="device_talk_subtitle">Lokalki sortutako mezuak</string>
<string name="device_talk_explain">Txat honetako mezuak zure Delta Chat aplikazioak sortu ditu lokalki. Egileek aplikazioaren eguneraketei buruz eta erabileran arazoei buruz informatzeko erabiltzen dute.</string>
<string name="device_talk_welcome_message2">Jarri harremanetan!\n\n🙌 Sakatu \"QR kodea\" bi gailuen pantaila nagusian. Hautatu \"Eskaneatu QR kodea\" gailu batean eta zuzendu bestera\n\n🌍 Gela berean ez bazaudete, eskaneatu bideo-dei baten bidez edo partekatu gonbidapen-esteka bat \"Eskaneatu QR kodea\" ataletik\n\nOndoren: Gozatu mezulari deszentralizatuaren esperientziaz. Ezagunagoak diren beste aplikazio batzuek ez bezala, kontrol zentralik gabe eta zu, zure lagunak, lankideak edo familia erakunde handiei jarraitu edo saldu gabe.</string>
<string name="edit_contact">Editatu kontaktua</string>
<!-- Verb "to pin", making something sticky, not a noun or abbreviation for "pin number". -->
<string name="pin_chat">Finkatu txata</string>
@@ -308,6 +352,14 @@
<string name="pin">Finkatu</string>
<!-- Opposite of "Pin chat", removing the sticky-state from a chat. -->
<string name="unpin">Desfinkatu</string>
<string name="ConversationFragment_quoted_message_not_found">Ez da jatorrizko mezua aurkitu</string>
<string name="reply_privately">Erantzun modu pribatuan</string>
<string name="source_code">Iturburu kodea</string>
<!-- Menu item beside an app/chat that adds an icon to the system's home screen. If the user taps that icon, the app/chat is opened directly. -->
<string name="add_to_home_screen">Gehitu hasierako pantailan</string>
<string name="donate">Egin dohaintza</string>
<string name="donate_device_msg">❤️ Badirudi Delta Chat-ekin gozatzen ari zarela!\n\nMesedez, pentsatu dohaintza bat egitea Delta Chat guztiontzat doakoa izan dadin laguntzeko.\n\nDelta Chat-en erabilera doakoa eta kode irekikoa den arren, garapenak dirua balio du. Lagundu iezaguzu Delta Chat independente mantentzen eta etorkizunean are hobea egiten.\n\nhttps://delta.chat/donate</string>
<string name="mute_for_one_hour">Mututu ordubete</string>
<string name="mute_for_eight_hours">Mututu 8 ordu</string>
<string name="mute_for_one_day">Mututu egun bat</string>
@@ -320,9 +372,49 @@
<string name="share_location_for_two_hours">2 orduz</string>
<string name="share_location_for_six_hours">6 orduz</string>
<plurals name="ask_send_following_n_files_to">
<item quantity="one">Bidali hurrengo fitxategia %s-(e)ra?</item>
<item quantity="other">Bidali hurrengo %d fitxategiak %s(e)ra?</item>
</plurals>
<string name="file_saved_to">Fitxategia \"%1$s\" helbidean gordeta</string>
<!-- the action "to call someone", used as a tooltip for the "phone" icon. not: "the call" -->
<string name="start_call">Deitu</string>
<string name="start_audio_call">Audio deia</string>
<string name="start_video_call">Bideo deia</string>
<!-- the action "to answer" or to "accept" or to "pick up" a call. not: "the answer" -->
<string name="answer_call">Erantzun</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">Ukatu</string>
<!-- deprecated, use outgoing_audio_call or outgoing_video_call instead -->
<string name="outgoing_call">Irteerako deia</string>
<string name="outgoing_audio_call">Irteerako audio deia</string>
<string name="outgoing_video_call">Irteerako bideo deia</string>
<!-- deprecated, use incoming_audio_call or incoming_video_call instead -->
<string name="incoming_call">Sarrerako deia</string>
<string name="incoming_audio_call">Sarrerako audio deia</string>
<string name="incoming_video_call">Sarrerako bideo deia</string>
<string name="declined_call">Ukatutako deia</string>
<string name="canceled_call">Bertan behera utzitako deia</string>
<string name="missed_call">Galdutako deia</string>
<!-- the first placeholder will be replaced by a date, the second placeholder by duration, example: "Thu, 08:12 pm, 2 minutes" -->
<string name="call_date_and_duration">%1$s, %2$s</string>
<!-- get confirmations -->
<!-- confirmation for leaving groups or channels. If a subject is needed, "Are you sure you want to leave the chat?" would work as well -->
<string name="ask_leave_group">Ziur irten nahi duzula?</string>
<plurals name="ask_delete_chat">
<item quantity="one">Ezabatu txat %d?</item>
<item quantity="other">Ezabatu %d txat?</item>
</plurals>
<string name="ask_delete_named_chat">Ezabatu \"%1$s\" txata?</string>
<string name="ask_delete_message">Ezabatu mezu hau?</string>
<plurals name="ask_delete_messages">
<item quantity="one">Ezabatu mezu %d?</item>
<item quantity="other">Ezabatu %d mezu?</item>
</plurals>
<string name="ask_forward">Birbidali mezuak %1$s erabiltzaileari?</string>
<string name="ask_forward_multiple">Bidali mezuak %1$d txatetara?</string>
<string name="ask_export_attachment">Esportatu eranskina? Eranskinak esportatzeak zure beste gailuko beste aplikazioek atzitzea ahalbidetuko du.\n\nJarraitu?</string>
<string name="ask_block_contact">Blokeatu kontaktu hau? Ez dituzu kontaktu honen mezu gehiago jasoko.</string>
<string name="ask_unblock_contact">Desblokeatu kontaktu hau? Berriro jaso ahal izango dituzu kontaktu honen mezuak.</string>
@@ -331,6 +423,11 @@
<string name="ask_start_chat_with">Txateatu %1$s erabiltzailearekin?</string>
<!-- %1$s is replaced by a comma-separated list of names -->
<string name="ask_remove_members">Kendu %1$s taldetik?</string>
<!-- %1$s is replaced by a comma-separated list of names -->
<string name="ask_remove_from_channel">Ezabatu %1$s kanaletik?</string>
<string name="open_url_confirmation">Esteka hau ireki nahi duzu?</string>
<!-- contact list -->
<string name="contacts_title">Kontaktuak</string>
<string name="contacts_type_email_above">Idatzi e-mail helbidea goian</string>
@@ -353,6 +450,10 @@
<item quantity="one">Mezu berri %d</item>
<item quantity="other">%d mezu berri</item>
</plurals>
<!-- The placeholder will be replaced by the name of the recipient in a one-to-one chat. -->
<string name="chat_new_one_to_one_hint">Bidali mezu bat %1$s(r)i.</string>
<!-- consider keeping the term "channel" as in WhatsApp or Telegram -->
<string name="chat_new_channel_hint">Kanalak zure mezuak jende askori aldi berean helarazteko tresnak dira.</string>
<string name="chat_new_group_hint">Idatzi lehen mezua, besteek taldera erantzutea baimenduz\n\n• Ez du axola denek ez badute Delta Chat erabiltzen.\n\n• Lehen mezua bidaltzeko denbora bat behar daiteke.</string>
<string name="chat_record_slide_to_cancel">Irristatu ezeztatzeko</string>
<string name="chat_record_explain">Sakatu eta mantendu ahots-mezu bat grabatzeko, askatu bidaltzeko.</string>
@@ -361,32 +462,78 @@
<string name="chat_share_with_title">Partekatu honekin</string>
<string name="chat_input_placeholder">Mezua</string>
<string name="chat_archived_label">Artxibatuta</string>
<string name="chat_request_label">Eskaera</string>
<string name="chat_request_label_explain">Sartu eskaerak kontagailuetan eta jakinarazpenetan</string>
<string name="chat_no_messages">Mezurik ez.</string>
<string name="chat_self_talk_subtitle">Nire buruari bidalitako mezuak</string>
<string name="archive_empty_hint">Artxibatutako txatak hemen agertuko dira.</string>
<!-- Action to add a message to "Saved Messages". The longer form (instead of "Save" only) is needed esp. on desktop to make clear this is not about saving a file to disk -->
<string name="save_message">Gorde</string>
<string name="saved_messages">Gordetako mezuak</string>
<string name="saved_messages_explain">• Birbidali mezuak hona eskura izateko\n\n• Hartu oharrak edo ahots mezuak\n\n• Gehitu multimedia horietara</string>
<!-- Should match "Saved" from "Saved messages" -->
<string name="saved">Gordeta</string>
<string name="save_as">Gorde honela</string>
<string name="retry_send">Saiatu berriro mezua bidaltzen</string>
<string name="cannot_display_unsuported_file_type">Ezin da fitxategi-mota hau bistaratu: %s</string>
<string name="attachment_failed_to_load">Eranskina kargatzean huts egin du</string>
<!-- For recording Voice messages: Description for the "Lock" button allowing to lift the thumb from the record button while recording continues -->
<string name="lock_recording">Blokeatu grabazioa</string>
<!-- mailing lists -->
<string name="mailing_list">Mailin zerrenda</string>
<!-- webxdc -->
<!-- "Start..." button for an app -->
<string name="start_app">Hasi...</string>
<!-- this is a warning that is shown when one tries to send something to a chat that is not yet accepted. -->
<string name="accept_request_first">Onartu txat-eskaera lehenik, mesedez.</string>
<!-- title shown above a list of chats where one should be selected (eg. when sharing files from a webxdc). the placeholder will be replaced by a file name -->
<string name="send_file_to">Bidali \"%1$s\" honi...</string>
<!-- title shown above a list contacts where one should be selected (eg. when a webxdc attempts to send a message to a chat) -->
<string name="send_message_to">Bidali mezua honi...</string>
<string name="enable_realtime">Denbora errealeko aplikazioak</string>
<string name="enable_realtime_explain">Gaitu denbora errealeko konexioak txatei atxikitako aplikazioentzat. Gaituz gero, baliteke txat-kideek zure IP helbidea ezagutu ahal izatea aplikazio bat abiarazten duzunean.</string>
<!-- map -->
<string name="filter_map_on_time">Erakutsi kokalekuak denbora markoan</string>
<string name="show_location_traces">Erakutsi lorratzak</string>
<string name="add_poi">Bidali interesezko puntua</string>
<!-- punycode warning / labeled links -->
<!-- placeholder is domain/hostname that should be trusted -->
<string name="open_external_url_trust_domain">Ez galdetu berriro honengatik: %1$s</string>
<string name="puny_code_warning_header">Esteka susmagarria atzeman da</string>
<!-- placeholder contains the hostname converted to ascii -->
<string name="puny_code_warning_question">Ziur zaude %1$s bisitatu nahi duzula?</string>
<!-- this message is shown whenever a link with non-latin characters is clicked. first placeholder is original hostname with special chars, second placeholder is hostname encoded in ascii -->
<string name="puny_code_warning_description">Lotura honek alfabeto desberdinetako antzeko itxura duten karaktereak nahas ditzake. %1$s izeneko esteka jarraitzeak %2$s(e)ra eramango zaitu, eta hori ohikoa da ez-latindar karaktereentzat. Karaktere horiek espero ez bazenituen, esteka hau kaltegarria izan daitezke.</string>
<!-- search -->
<string name="search">Bilatu</string>
<string name="search_in_chat">Bilatu txatetan</string>
<string name="search_files">Bilatu fitxategiak</string>
<string name="search_explain">Bilatu txatak, kontaktuak, eta mezuak.</string>
<string name="search_no_result_for_x">Ez da emaitzarik aurkitu \"%s\" bilaketarentzat</string>
<!-- Adjective, as in "Show Unread Messages" -->
<string name="search_unread">Irakurri gabe</string>
<!-- create/edit groups, contact/group profile -->
<string name="group_name">Taldearen izena</string>
<string name="group_avatar">Taldearen abatarra</string>
<string name="remove_group_image">Ezabatu taldearen irudia</string>
<string name="change_group_image">Aldatu taldearen irudia</string>
<string name="group_create_button">Sortu taldea</string>
<string name="group_please_enter_group_name">Idatzi izen bat taldearentzat.</string>
<string name="group_add_members">Gehitu kideak</string>
<string name="group_self_not_in_group">Taldeko kidea izan behar zara ekintza hau burutzeko.</string>
<string name="profile_encryption">Zifratzea</string>
<string name="profile_shared_chats">Partekatutako txatak</string>
<!-- Separator between the list of actual members and past members -->
<string name="past_members">Kide izandakoak</string>
<string name="tab_contact">Kontaktua</string>
<string name="tab_group">Taldea</string>
<string name="tab_gallery">Galeria</string>
@@ -395,10 +542,99 @@
<string name="tab_map">Mapa</string>
<string name="tab_gallery_empty_hint">Txat honetan partekatutako irudi eta bideoak agertuko dira hemen</string>
<string name="tab_docs_empty_hint">Txat honetan partekatutako dokumentuak, musika, eta bestelako fitxategiak agertuko dira hemen</string>
<string name="tab_image_empty_hint">Txat honi erantsitako irudiak hemen agertuko dira.</string>
<string name="tab_video_empty_hint">Txat honi erantsitako bideoak hemen agertuko dira.</string>
<string name="tab_audio_empty_hint">Txat honi erantsitako fitxategiak eta audio mezuak hemen agertuko dira.</string>
<string name="tab_webxdc_empty_hint">Txat honi erantsitako aplikazioak hemen agertuko dira.</string>
<string name="tab_all_media_empty_hint">Edozein txati erantsitako multimedia-fitxategiak hemen agertuko dira.</string>
<string name="all_files_empty_hint">Edozein txati erantsitako dokumentuak eta bestelako fitxategiak hemen agertuko dira.</string>
<string name="all_apps_empty_hint">Edozein txati erantsitako aplikazioak hemen agertuko dira.</string>
<string name="media_preview">Multimediaren aurrebista</string>
<!-- option to show images in the gallery with the correct width/height aspect (instead of square); other gallery apps may be a source of inspiration for translation :) -->
<string name="aspect_ratio_grid">Proportzio-sareta</string>
<!-- option to show images in the gallery as square (instead of using correct width/height) -->
<string name="square_grid">Lauki-sareta</string>
<string name="send_message">Bidali mezua</string>
<!-- Multi Device -->
<!-- "Second Device" can also be translated as "Another Device", if that is catchier in the destination language. However, make sure to use the term consistently. -->
<string name="multidevice_title">Gehitu bigarren gailua</string>
<string name="multidevice_same_network_hint">Ziurtatu gailu biak Wi-Fi edo sare berera konektatuta daudela.</string>
<string name="multidevice_this_creates_a_qr_code">Honek QR kode bat sortuko du, bigarren gailuak eskaneatu eta profila kopiatu dezan.\n\nZiurtatu nahi ez duzun begiralerik edo kamerarik ezin duela ondorengo pantaila ikusi.</string>
<string name="multidevice_install_dc_on_other_device">Instalatu Delta Chat zure beste gailuan (https://get.delta.chat)</string>
<!-- "I Already Have a Profile / Add as Second Device” should be the same text as defined by the keys onboarding_alternative_logins and multidevice_receiver_title -->
<string name="multidevice_tap_scan_on_other_device">Abiarazi Delta Chat, sakatu “Badut profil bat / Gehitu bigarren gailu gisa” eta eskaneatu hemen agertzen den kodea.</string>
<!-- Shown inside a "QR code card" with very limited space; please formulate the text as short as possible therefore. The placeholder will be replaced by the profile name eg. "Scan to set up second device for Alice" -->
<string name="multidevice_qr_subtitle">Eskaneatu %1$s(r)entzat bigarren gailu bat konfiguratzeko</string>
<string name="multidevice_receiver_title">Gehitu bigarren gailu gisa</string>
<string name="multidevice_open_settings_on_other_device">Lehenengo gailuan, abiarazi Delta Chat, joan hona: “Ezarpenak / Gehitu bigarren gailua“, eta eskaneatu bertan agertzen den kodea.</string>
<string name="multidevice_receiver_scanning_ask">Kopiatu kontua beste gailutik gailu honetara?</string>
<string name="multidevice_receiver_needs_update">Inportatu nahi duzun kontua Delta Chat-en bertsio berriago batena da.\n\nBigarren gailua konfiguratzen jarraitzeko, mesedez, eguneratu gailu hau Delta Chat-en azken bertsiora.</string>
<string name="multidevice_abort">Bertan behera utzi bigarren gailuaren konfigurazioa?</string>
<string name="multidevice_abort_will_invalidate_copied_qr">Honek arbelean kopiatutako QR kodea baliogabetuko du.</string>
<string name="multidevice_transfer_done_devicemsg">️ Kontua zure bigarren gailura transferitu da.</string>
<!-- Shown beside progress bar, stay short -->
<string name="preparing_account">Kontua prestatzen...</string>
<!-- Shown beside progress bar, stay short -->
<string name="transferring">Transferitzen...</string>
<string name="troubleshooting">Arazoak konpontzea</string>
<!-- Connectivity -->
<!-- Headline for the "Inbox" eg. in the "Connectivity" view -->
<string name="incoming_messages">Sarrerako mezuak</string>
<!-- Headline for the "Outbox" eg. in the "Connectivity" view -->
<string name="outgoing_messages">Irteerako mezuak</string>
<!-- deprecated -->
<string name="storage_on_domain">%1$s(e)ko biltegiratzea</string>
<string name="connectivity">Konektagarritasuna</string>
<!-- Shown in the title bar if the app is "Not connected"; prefer short strings. -->
<string name="connectivity_not_connected">Konektatu gabe</string>
<!-- Shown in the title bar if the app is "Connecting"; prefer short strings. The ellipsis is a single character (…), not three (...) -->
<string name="connectivity_connecting">Konektatzen...</string>
<!-- Shown in the title bar if the app is "Updating" (eg. getting new/old message, sync things); prefer short strings. The ellipsis is a single character (…), not three (...) -->
<string name="connectivity_updating">Eguneratzen...</string>
<!-- Shown in the setting if the app is "Connected" -->
<string name="connectivity_connected">Konektatuta</string>
<string name="sending">Bidaltzen...</string>
<string name="last_msg_sent_successfully">Azken mezua ondo bidali da.</string>
<string name="not_supported_by_provider">Zure hornitzaileak ez du onartzen.</string>
<!-- Subtitle in quota context of "Connectivity" view. Should be be plural always, no number is prefixed. -->
<string name="messages">Mezuak</string>
<!-- Used for describing resource usage, resulting string will be eg. "1.2 GiB of 3 GiB used" -->
<string name="part_of_total_used">%2$s(e)tik %1$s erabilita</string>
<!-- welcome and login -->
<!-- Primary button on the welcome screen, allows to create an instant profile -->
<string name="onboarding_create_instant_account">Sortu kontu berria</string>
<!-- Secondary button on the welcome screen, allows to "Add as Second Device", "Restore from Backup" -->
<string name="onboarding_alternative_logins">Badut kontu bat</string>
<!-- This is a button and a title, allowing to use existing, classic email, setting ports, passwords and so on -->
<string name="manual_account_setup_option">Erabili eposta klasikoa errele gisa</string>
<!-- Instant onboarding title (there is not more to do than to set name and avatar) -->
<string name="instant_onboarding_title">Zure kontua</string>
<!-- The placeholder will be replaced by the default onboarding server -->
<string name="instant_onboarding_agree_default2">%1$s(r)en pribatutasun-politika</string>
<!-- The placeholder will be replaced by instance name, the whole text will link to the instance page -->
<string name="instant_onboarding_agree_instance">%1$s(e)ko kontuei buruz</string>
<!-- Confirmation button on the instant onboarding screen -->
<string name="instant_onboarding_create">Ados eta sortu kontua</string>
<!-- Secondary, link-like button to open a page with other possible instances -->
<string name="instant_onboarding_show_more_instances">Erabili beste zerbitzari bat</string>
<string name="instant_onboarding_other_server">Zerrendatu txatmail zerbitzariak</string>
<!-- Hint about what happens when "Create Profile" button in pressed; the placeholder will be replaced by the group name -->
<string name="instant_onboarding_group_info">Sortu kontu bat \"%1$s\" taldean sartzeko.</string>
<!-- Hint about what happens when "Create Profile" button in pressed; the placeholder will be replaced by contact name -->
<string name="instant_onboarding_contact_info">Sortu profil bat %1$s-(r)ekin txateatzeko.</string>
<!-- Question shown when another user's QR code is scanned from onboarding screen -->
<string name="instant_onboarding_confirm_contact">Sortu kontu berri bat eta hasi %1$s(r)ekin txateatzen?</string>
<!-- Question shown when group's/channel's QR code is scanned from onboarding screen -->
<string name="instant_onboarding_confirm_group">Sortu profil berri bat eta sartu \"%1$s\"(e)n?</string>
<string name="welcome_chat_over_email">Txat deszentralizatu segurua</string>
<string name="scan_invitation_code">Eskaneatu gonbidapen kodea</string>
<string name="login_title">Hasi saioa</string>
<string name="login_advanced_hint">Saio-hasiera hau erabiltzaile aurreratuentzat da:\n\n• Ez erabili beste aplikazio batean erabiltzen ari zaren helbiderik.\n\n• Posta elektronikoko zerbitzari klasikoak txatak baimentzen ditu, muturretik muturrera zifratu gabe, posta-ikono batek markatuta.</string>
<string name="login_inbox">Sarrera ontzia</string>
<string name="login_imap_login">IMAP erabiltzaile-izena</string>
<string name="login_imap_server">IMAP zerbitzaria</string>
@@ -410,6 +646,32 @@
<string name="login_smtp_server">SMTP zerbitzaria</string>
<string name="login_smtp_port">SMTP ataka</string>
<string name="login_smtp_security">SMTP segurtasuna</string>
<!-- the word "Proxy" might be left untranslated unless the destination language has a well-known term for a "Proxy Server", acting intermediary between the app and the server -->
<string name="proxy_settings">Proxy-a</string>
<string name="proxy_use_proxy">Erabili proxy-a</string>
<string name="proxy_add">Gehitu proxy-a</string>
<string name="proxy_add_explain">Onartutako proxy motak: HTTP(S), SOCKS5 eta Shadowsocks.</string>
<string name="proxy_add_url_hint">Sartu proxy-aren esteka hemen</string>
<string name="proxy_invalid">Proxy baliogabea edo ez-bateragarria</string>
<string name="proxy_list_header">Gordetako proxy-ak</string>
<string name="proxy_delete">Ezabatu proxy-a</string>
<string name="proxy_delete_explain">Ziur zaude \"%1$s\" ezabatu nahi duzula?</string>
<string name="proxy_use_proxy_confirm">\"%1$s\" proxia erabili nahi duzu?</string>
<string name="proxy_share_explain">Zure lagunek QR kodea eskaneatuta gehitu dezakete proxy hau.</string>
<string name="proxy_share_link">Partekatu esteka</string>
<string name="proxy_enabled">Proxy-a gaituta</string>
<string name="proxy_enabled_hint">Proxy bat erabiltzen ari zara. Konektatzeko arazoak badituzu, probatu beste proxy bat.</string>
<!-- "Relay" refers to the relay servers used; please chose a common term in your language, no need to be literal. If in doubt, stay with "Relay" -->
<string name="transports">Erreleak</string>
<string name="add_transport">Gehitu errelea</string>
<string name="remove_transport">Ezabatu errelea</string>
<string name="edit_transport">Editatu errelea</string>
<!-- shown if a QR code was scanned that can be used as a relay -->
<string name="confirm_add_transport">Gehitu errelea?</string>
<string name="invalid_transport_qr">Eskaneatutako QR kodeak ez dauka baliozko errelerik.</string>
<!-- placeholder will be replaced by a relay server name -->
<string name="confirm_remove_transport">\"%1$s\" errelea kendu?\n\nZure kontaktuak soilik jar daitezke zurekin harremanetan aurretik beste errele baten bidez haiekin harremanetan jarri bazara.\n\nZalantzarik baduzu, kendu errelea geroago.</string>
<string name="login_certificate_checks">Ziurtagiri-egiaztaketak</string>
<string name="login_error_mail">Sartu baliozko e-mail helbide bat</string>
<string name="login_error_server">Sartu baliozko zerbitzari / IP helbide bat</string>
@@ -424,11 +686,30 @@
<string name="accept_invalid_certificates">Onartu ziurtagiri baliogabeak</string>
<string name="switch_account">Aldatu kontua</string>
<string name="add_account">Gehitu kontua</string>
<!-- for translations, you can also think of "Profile Tag" or "Profile Description" as the source string -->
<string name="profile_tag">Kontuaren etiketa</string>
<string name="profile_tag_hint">Adib. Lana, Familia</string>
<string name="profile_tag_explain">Ezarri etiketa bat zuretzat bakarrik ikusgai egongo dena; zure kotuen artean bereizten lagunduko dizu.</string>
<!-- Menu entry to sort an item to the beginning of a list. Only "To Top" may do as well in some translations, if that helps to stay shorter. -->
<string name="move_to_top">Mugitu gora</string>
<string name="delete_account">Ezabatu kontua</string>
<string name="delete_account_ask">Ziur al zaude zure kontuaren datuak ezabatu nahi dituzula?</string>
<string name="delete_account_explain_with_name">Gailu honetako \"%s\" profilaren datu guztiak ezabatuko dira, zifratze-konfigurazioa, kontaktuak, txatak, mezuak eta multimedia barne. Ekintza hau ezin da desegin.</string>
<string name="unconfigured_account">Konfiguratu gabeko kontua</string>
<string name="unconfigured_account_hint">Ireki kontua konfiguratzeko.</string>
<string name="try_connect_now">Saiatu orain konektatzen</string>
<string name="sync_all">Sinkronizatu guztiak</string>
<!-- Translations: %1$s will be replaced by a more detailed error message -->
<string name="configuration_failed_with_error">Konfigurazioak huts egin du. Errorea: %1$s</string>
<!-- share and forward messages -->
<!-- Translators: shown above a chat/contact list when selecting recipients to forward messages -->
<string name="forward_to">Birbidali honi …</string>
<!-- first placeholder is replaced by the number of files (always 2 or more); second placeholder is replaced by a chat name -->
<string name="ask_send_files_to_chat">Bidali %1$d fitxategiak \"%2$s\"(e)ra?</string>
<string name="ask_send_files_to_selected_chats">Bidali %1$d fitxategia(k) %2$d txatetara?</string>
<string name="videos_sent_without_recoding">(Bideoak jatorrizko fitxategi handi gisa bidaltzen dira. Bideoak fitxategi txikiago gisa bidaltzeko, erantsi itzazu bereizita)</string>
<string name="share_text_multiple_chats">Bidali testua hau %1$d txatetara?\n\n\"%2$s\"</string>
<string name="share_abort">Partekatzea eten da baimenak falta direlako.</string>
@@ -438,6 +719,7 @@
<string name="pref_profile_info_headline">Zure profileko informazioa</string>
<string name="pref_profile_photo">Profileko argazkia</string>
<string name="pref_blocked_contacts">Blokeatutako kontaktuak</string>
<string name="blocked_empty_hint">Blokeatutako kontaktuak hemen agertuko dira.</string>
<string name="pref_who_can_see_profile_explain">Zure profileko argazkia eta izena zure mezuekin batera erakutsiko dira beste erabiltzaileekin komunikatzen zarenean. Informazioa behin bidalita ezin da ezabatu edo kendu.</string>
<string name="pref_your_name">Zure izena</string>
<!-- Label of the Bio/Signature/Status/Motto field -->
@@ -453,8 +735,13 @@
<string name="pref_screen_security_explain">Eskatu pantaila-argazkiak blokeatzea azkenak zerrendan eta aplikazio barruan.</string>
<string name="pref_screen_security_please_restart_hint">Pantailaren segurtasun ezarpenak aplikatzeko berrabiarazi aplikazioa.</string>
<string name="pref_notifications">Jakinarazpenak</string>
<string name="pref_mention_notifications">Aipamenak</string>
<string name="pref_mention_notifications_explain">Mutututako taldeetan, jakinarazi zuretzat diren mezuak, hala nola erantzunak edo erreakzioak</string>
<string name="pref_notifications_show">Erakutsi</string>
<string name="pref_notifications_priority">Lehentasuna</string>
<string name="pref_notifications_explain">Gaitu sistemaren jakinarazpenak mezu berrientzat</string>
<string name="pref_show_notification_content">Erakutsi mezuaren edukia jakinarazpenean</string>
<string name="pref_show_notification_content_explain">Erakutsi bidaltzailea eta mezuaren lehen hitza jakinarazpenetan</string>
<string name="pref_led_color">LED kolorea</string>
<string name="pref_sound">Soinua</string>
<string name="pref_silent">Isila</string>
@@ -468,11 +755,14 @@
<string name="pref_appearance">Itxura</string>
<string name="pref_theme">Azala</string>
<string name="pref_language">Hizkuntza</string>
<string name="pref_use_system_ui_font">Erabili sistemaren letra-mota</string>
<string name="pref_incognito_keyboard">Teklatu pribatua</string>
<!-- Translators: Must indicate that there is no guarantee as the system may not honor our request. -->
<string name="pref_incognito_keyboard_explain">Eskatu teklatuari pertsonalizatutako ikasketa desgaitzea</string>
<string name="pref_read_receipts">Irakurragiriak</string>
<string name="pref_read_receipts_explain">Irakurragiriak desgaituta badaude, ezin izango dituzu besteen irakurragiriak ikusi.</string>
<string name="pref_server">Zerbitzaria</string>
<string name="pref_encryption">Zifratua</string>
<string name="pref_manage_keys">Kudeatu gakoak</string>
<string name="pref_chats">Txatak</string>
<string name="pref_in_chat_sounds">Txat bitarteko soinuak</string>
@@ -484,6 +774,9 @@
<string name="pref_backup">Babeskopia</string>
<string name="pref_backup_explain">Egin txaten babes-kopia kanpo biltegian</string>
<string name="pref_backup_export_explain">Babes-kopia batek instalazio berri bat ezartzen laguntzen dizu, bai gailu honetan zein beste batean.\n\nBabes-kopiak mezu guztiak, kontaktuak eta txatak, eta zure Autocrypt ezarpena daramatza. Gorde babes-kopiaren fitxategia toki seguruan edo ezabatu ahalik eta lasterren.</string>
<string name="pref_backup_export_this">Esportatu kontu hau</string>
<!-- the placeholder will be replaced by the number of profiles to export; the number is always larger than 1 -->
<string name="pref_backup_export_all">Esportatu %1$d kontu guztiak</string>
<string name="pref_backup_export_start_button">Hasi babes-kopia</string>
<string name="pref_backup_written_to_x">Babes-kopia ongi idatzi da hemen: %1$s</string>
<!-- No need to translate "Wallpaper" literally. Chose what is common in your language for a "Wallpaper" or a "Background". Avoid adding the term "image" here, as the "Wallpaper" may also be just a single color. -->
@@ -491,29 +784,98 @@
<string name="pref_background_btn_default">Erabili lehenetsitako irudia</string>
<string name="pref_background_btn_gallery">Hautatu galeriatik</string>
<string name="pref_imap_folder_warn_disable_defaults">Aukera hau desaktibatuz gero, egiaztatu zure zerbitzaria eta beste bezeroen konfigurazioa bat datorrela.\n\nBestela agian gauzak ez dabiltza.</string>
<!-- No need to be literal here, you can also use "Use Multiple Devices", "Support Multiple Devices" or other fitting terms. However, it should fit to the wording or your language at https://delta.chat/help -->
<string name="pref_multidevice">Gailu anitzeko modua</string>
<string name="pref_multidevice_explain">Sinkronizatu zure mezuak zure beste gailuekin. Automatikoki gaitzen da bigarren gailu bat gehitzean.</string>
<string name="pref_multidevice_change_warn">Gailu anitzeko moduak gaituta egon behar du kontu bera hainbat gailutan erabiltzean. Desaktibatu ezarpen hau soilik kontu hau beste gailu guztietatik kendu baduzu.\n\nGailu anitzeko modua desaktibatzeak kontua hainbat gailutan erabiltzen ari den bitartean, mezuak galtzea eta bestelako arazoak eragingo ditu.</string>
<string name="pref_auto_folder_moves">Automatikoki mugitzen du DeltaChat karpetara</string>
<string name="pref_only_fetch_mvbox_title">Delta Chat karpetatik soilik berreskuratu</string>
<string name="pref_show_emails">Erakutsi ohiko e-mailak</string>
<string name="pref_show_emails_no">Ez, txatak besterik ez</string>
<string name="pref_show_emails_accepted_contacts">Onartutako kontaktuena</string>
<string name="pref_show_emails_all">Denak</string>
<string name="pref_experimental_features">Ezaugarri esperimentalak</string>
<string name="pref_on_demand_location_streaming">Eskaera bidezko kokalekuaren hedapena</string>
<string name="pref_experimental_features_explain">Ezaugarri hauek ezegonkorrak izan daitezke eta aldatu edo kendu egin daitezke.</string>
<string name="pref_on_demand_location_streaming">Eskaripeko kokapena zuzenean partekatzea</string>
<string name="pref_background_default">Lehenetsitako atzealdea</string>
<string name="pref_background_default_color">Lehenetsitako kolorea</string>
<string name="pref_background_custom_image">Irudi pertsonalizatua</string>
<string name="pref_background_custom_color">Kolore pertsonalizatua</string>
<string name="export_aborted">Esportazioa bertan behera geratu da.</string>
<string name="auto_download_messages">Deskargatu mezuak automatikoki</string>
<!-- %1$s will be replaced by a human-readable number of bytes, eg. 32 KiB, 1 MiB -->
<string name="up_to_x">%1$s(e)ra arte</string>
<string name="up_to_x_most_worse_quality_images">%1$s(e)ra arte, kalitate txarreneko irudiak</string>
<string name="up_to_x_most_balanced_quality_images">%1$s(e)ra arte, kalitate orekatueneko irudiak</string>
<string name="download_failed">Deskargak huts egin du</string>
<!-- %1$s will be replaced by a human-readable number of bytes, eg. 32 KiB, 1 MiB. Resulting string eg. "1 MiB message" -->
<string name="n_bytes_message">%1$s mezu</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Deskargatu eskuragarri dagoen gehienezkoa %1$s arte</string>
<string name="select_profile">Aukeratu kontua</string>
<string name="profile_image_select">Aukeratu abatarra</string>
<string name="select_your_new_profile_image">Aukeratu zure abatar berria</string>
<string name="profile_image_delete">Ezabatu abatarra</string>
<string name="pref_show_tray_icon">Erakutsi sistema-erretilu ikonoa</string>
<string name="pref_edit_profile">Editatu kontua</string>
<string name="disable_imap_idle">Desgaitu IMAP IDLE</string>
<string name="disable_imap_idle_explain">Ez erabili IMAP IDLE luzapena, zerbitzariak onartzen badu ere. Aukera hau gaitzeak mezuen berreskuratzea atzeratuko du, gaitu ezazu probak egiteko soilik.</string>
<string name="send_stats_to_devs">Bidali estatistikak Delta Chat-eko garatzaileei</string>
<string name="stats_device_message">Delta Chat hobetzen lagundu eta ikerketa babestu nahi duzu astero erabilera-estatistika anonimoak bidalita?\n\n👉 Sakatu hemen… 👈</string>
<string name="stats_confirmation_dialog">Delta Chat hobetzen lagundu eta ikerketa babestu nahi duzu astero erabilera-estatistika anonimoak bidalita?</string>
<string name="stats_thanks">Eskerrik asko! Bidalketa edozein unetan desgaitu dezakezu \"Ezarpenak -> Aurreratua\" atalean.\n\nBestalde, Delta Chat-en segurtasunari buruzko ikerketa zientifiko batean parte hartzeko 5 minutu dituzu?</string>
<string name="stats_disable_dialog">Estatistiken bidalketa gaituta dago jada.\n\nDesaktibatu nahi duzu?</string>
<string name="disable">Desgaitu</string>
<string name="stats_keep_sending">Bidaltzen jarraitu</string>
<string name="stats_msg_body">Eranskinak erabilera-estatistika anonimoak ditu, Delta Chat hobetzen laguntzen digutenak. Ikusi https://delta.chat/help#statssending informazio gehiago lortzeko. Eskerrik asko!</string>
<!-- Emoji picker and categories -->
<string name="emoji_search_results">Bilatu emaitzak</string>
<string name="emoji_not_found">Ez da emojirik aurkitu</string>
<string name="emoji_recent">Azkenaldian erabiliak</string>
<string name="emoji_people">Pertsonak eta gorputza</string>
<string name="emoji_nature">Animaliak eta natura</string>
<string name="emoji_foods">Janaria eta edaria</string>
<string name="emoji_activity">Aktibitatea</string>
<string name="emoji_places">Bidaiak eta lekuak</string>
<string name="emoji_objects">Objetuak</string>
<string name="emoji_symbols">Sinboloak</string>
<string name="emoji_flags">Banderak</string>
<!-- Reactions -->
<string name="reactions">Erreakzioak</string>
<!-- Verb, the action of adding a "Reaction" to a message. Used eg. in context menus, similar to "Reply" or "Forward" -->
<string name="react">Erreakzionatu</string>
<!-- %1$s will be replaced by an emoji. %2$s will be replaced by message summary (the summary is often long and whole string gets truncated; words after %2$s will often not being visible). Eg. 'You reacted 👍 to "Found my suitcase"'. Use less than 20 characters, otherwise the string will be truncated too soon and too few information are shown. Do not try to translate "reacted to" too strict. Depending on the language, "added 👍 to" or "gave 👍 to" or just "👍 to" may be shorter and/or clearer and work as well. -->
<string name="reaction_by_you">%1$s erreakzionatu zenion \"%2$s\"(e)ri.</string>
<!-- %1$s will be replaced a name. %2$s will be replaced by an emoji. %3$s will be replaced by message summary (the summary is often long and whole string gets truncated; words after %3$s will often not being visible). Eg. 'Alice reacted 👍 to "Nice photos"'. Use less than 20 characters, otherwise the string will be truncated too soon and too few information are shown. Do not try to translate "reacted to" too strict. Depending on the language, "added 👍 to" or "gave 👍 to" or just "👍 to" may be shorter and/or clearer work as well. -->
<string name="reaction_by_other">%1$s(e)k %2$s erreakzionatu zion \"%3$s\"(e)ri</string>
<!-- automatically delete message -->
<string name="delete_old_messages">Ezabatu mezu zaharrak</string>
<string name="autodel_device_title">Ezabatu mezuak gailutik</string>
<string name="autodel_server_title">Ezabatu mezuak zerbitzaritik</string>
<!-- %1$d will be replaced by the number of messages, you can assume plural/lots here. %2$s will be replaced by a timespan option. -->
<string name="autodel_device_ask">%1$d mezu ezabatu nahi dituzu orain eta \"%2$s\" etorkizunean jasotako mezuak ere?\n\n• Honek multimedia ere barne hartzen du\n\n• Mezuak ikusi ala ez ezabatuko dira\n\n• \"Gordetako mezuak\" ez dira lokalki ezabatuko</string>
<!-- %1$d will be replaced by the number of messages, you can assume plural/lots here. %2$s will be replaced by a timespan option. -->
<string name="autodel_server_ask">%1$d mezu ezabatu nahi dituzu orain eta \"%2$s\" etorkizunean jasotako mezuak ere?\n\n⚠️ Honek mezu elektronikoak, multimedia eta \"Gordetako mezuak\" barne hartzen ditu zerbitzariko karpeta guztietan\n\n⚠️ Ez erabili funtzio hau datuak zerbitzarian gorde nahi badituzu edo posta bezero klasikoak ere erabiltzen badituzu</string>
<!-- shown below enabled autodel_server-option, should be a summary of autodel_server_ask and remind about the impact -->
<string name="autodel_server_enabled_hint">Honek barne hartzen ditu mezu elektronikoak, multimedia eta \"Gordetako mezuak\" zerbitzariko karpeta guztietan. Ez erabili funtzio hau datuak zerbitzarian gorde nahi badituzu edo mezu elektronikoen bezero klasikoak ere erabiltzen badituzu.</string>
<string name="autodel_server_warn_multi_device_title">Berehalako ezabatzea aktibatu</string>
<string name="autodel_server_warn_multi_device">Berehalako ezabaketa gaitzen baduzu, ezin izango dituzu gailu anitz erabili profil honetan.</string>
<string name="autodel_confirm">Ulertzen dut, ezabatu mezuk guzti hauek</string>
<!-- "At once" in the meaning of "Immediately", without any intervening time. -->
<string name="autodel_at_once">Berehala</string>
<string name="after_5_minutes">5 minutu pasatutakoan</string>
<string name="autodel_after_1_hour">Ordubete eta gero</string>
<string name="autodel_after_1_day">Egun bat eta gero</string>
<string name="autodel_after_1_week">Aste bat eta gero</string>
<string name="after_5_weeks">5 aste pasatutakoan</string>
<string name="autodel_after_1_year">Urtebete eta gero</string>
<!-- system messages -->
<string name="systemmsg_subject_for_new_contact">%1$s(r)en mezua</string>
<string name="systemmsg_failed_sending_to">Huts egin du %1$s(r)i mezua bidaltzean.</string>
<!-- %1$s will be replaced by the old group name, %2$s will be replaced by the new group name -->
<string name="group_name_changed_by_you">Nik: Taldearen izena \"%1$s\" izatetik \"%2$s\" izatera aldatu da</string>
<!-- %1$s will be replaced by the old group name, %2$s will be replaced by the new group name, %3$s will be replaced by name and address of the contact who did the action. -->
@@ -522,6 +884,8 @@
<!-- %1$s will be replaced by name of the contact who did the action -->
<string name="group_image_changed_by_other">%1$s(e)k: Taldearen irudia aldatuta</string>
<!-- %1$s will be replaced by name of the contact added to the group -->
<string name="member_x_added">%1$s kidea gehituta.</string>
<!-- %1$s will be replaced by name of the contact added to the group -->
<string name="add_member_by_you">Nik: %1$s kidea gehituta</string>
<!-- %1$s will be replaced by name of the contact added to the group, %2$s will be replaced by name of the contact who did the action -->
<string name="add_member_by_other">%2$s(e)k: %1$s kidea gehituta</string>
@@ -529,19 +893,97 @@
<string name="remove_member_by_you">Nik: %1$s kidea kenduta</string>
<!-- %1$s will be replaced by name of the contact removed from the group, %2$s will be replaced by name of the contact who did the action -->
<string name="remove_member_by_other">%2$s(e)k: %1$s kidea kenduta</string>
<!-- %1$s will be replaced by name of the contact removed from the group; this string is used when it's unclear who did the action -->
<string name="member_x_removed">%1$s kidea kenduta.</string>
<!-- "left" in the meaning of "exited". -->
<string name="group_left_by_you">Taldetik atera zara</string>
<!-- "left" in the meaning of "exited". -->
<string name="channel_left_by_you">Kanaletik atera zara.</string>
<string name="you_joined_the_channel">Kanalera batu zara.</string>
<!-- "left" in the meaning of "exited"; %1$s will be replaced by name of the contact leaving the group -->
<string name="group_left_by_other">%1$s(e)k taldea utzi du.</string>
<string name="group_image_deleted_by_you">Nik: Taldearen irudia ezabatuta</string>
<!-- %1$s will be replaced by name of the contact -->
<string name="group_image_deleted_by_other">%1$s(e)k: Taldearen irudia ezabatuta</string>
<string name="location_enabled_by_you">Kokapena zuzenean partekatzea aktibatu duzu.</string>
<!-- %1$s will be replaced by name of the contact -->
<string name="location_enabled_by_other">%1$s(e)k kokapena zuzenean partekatzea aktibatu du.</string>
<string name="ephemeral_timer_disabled_by_you">Mezuen desagerpen-denbora desgaitu duzu.</string>
<!-- %1$s will be replaced by name of the contact -->
<string name="ephemeral_timer_disabled_by_other">%1$s(e)k mezuen desagerpen-denbora desgaitu du.</string>
<!-- %1$s will be replaced by the number of seconds (always >1) the timer is set to -->
<string name="ephemeral_timer_seconds_by_you">Mezuen desagerpen-denbora %1$s segundotan ezarri duzu.</string>
<!-- %1$s will be replaced by the number of seconds (always >1) the timer is set to, %2$s will be replaced by name of the contact -->
<string name="ephemeral_timer_seconds_by_other">%2$s(e)k mezuen desagerpen-denbora %1$s segundotan ezarri du.</string>
<!-- deprecated, usage needs to be removed in core -->
<string name="ephemeral_timer_1_minute_by_you">Mezuen desagerpen-denbora 1 minutakoa dela ezarri duzu.</string>
<!-- deprecated, usage needs to be removed in core -->
<string name="ephemeral_timer_1_minute_by_other">%1$s(e)k mezuen desagerpen-denbora 1 minutukoa dela ezarri du.</string>
<string name="ephemeral_timer_1_hour_by_you">Mezuen desagerpen-denbora 1 ordukoa dela ezarri duzu.</string>
<!-- %1$s will be replaced by name of the contact -->
<string name="ephemeral_timer_1_hour_by_other">%1$s(e)k mezuen desagerpen-denbora 1 ordukoa dela ezarri du.</string>
<string name="ephemeral_timer_1_day_by_you">Mezuen desagerpen-denbora 1 egunekoa dela ezarri duzu.</string>
<!-- %1$s will be replaced by name of the contact -->
<string name="ephemeral_timer_1_day_by_other">%1$s(e)k mezuen desagerpen-denbora 1 egunekoa dela ezarri du.</string>
<string name="ephemeral_timer_1_week_by_you">Mezuen desagerpen-denbora 1 astekoa dela ezarri duzu.</string>
<!-- %1$s will be replaced by name of the contact -->
<string name="ephemeral_timer_1_week_by_other">%1$s-(e)k mezuen desagerpen-denbora 1 astekoa dela ezarri du.</string>
<string name="ephemeral_timer_1_year_by_you">Mezuen desagerpen-denbora 1 urtekoa dela ezarri duzu.</string>
<!-- %1$s will be replaced by name of the contact -->
<string name="ephemeral_timer_1_year_by_other">%1$s(e)k mezuen desagerpen-denbora 1 urtekoa dela ezarri du.</string>
<!-- %1$s will be replaced by the number of minutes (always >1) the timer is set to -->
<string name="ephemeral_timer_minutes_by_you">Mezuen desagerpen-denbora %1$s minutukoa dela ezarri duzu.</string>
<!-- %1$s will be replaced by the number of minutes (always >1) the timer is set to, %2$s will be replaced by name of the contact -->
<string name="ephemeral_timer_minutes_by_other">%2$s(e)k mezuen desagerpen-denbora %1$s minutukoa dela ezarri du.</string>
<!-- %1$s will be replaced by the number of hours (always >1) the timer is set to -->
<string name="ephemeral_timer_hours_by_you">Mezuen desagerpen-denbora %1$s ordukoa dela ezarri duzu.</string>
<!-- %1$s will be replaced by the number of hours (always >1) the timer is set to, %2$s will be replaced by name of the contact -->
<string name="ephemeral_timer_hours_by_other">%2$s(e)k mezuen desagerpen-denbora %1$s ordukoa dela ezarri du.</string>
<!-- %1$s will be replaced by the number of days (always >1) the timer is set to -->
<string name="ephemeral_timer_days_by_you">Mezuen desagerpen-denbora %1$s egunekoa dela ezarri duzu.</string>
<!-- %1$s will be replaced by the number of days (always >1) the timer is set to, %2$s will be replaced by name of the contact -->
<string name="ephemeral_timer_days_by_other">%2$s(e)k mezuen desagerpen-denbora %1$s egunekoa dela ezarri du.</string>
<!-- %1$s will be replaced by the number of weeks (always >1) the timer is set to -->
<string name="ephemeral_timer_weeks_by_you">Mezuen desagerpen-denbora %1$s astekoa dela ezarri duzu.</string>
<!-- %1$s will be replaced by the number of weeks (always >1) the timer is set to, %2$s will be replaced by name of the contact -->
<string name="ephemeral_timer_weeks_by_other">%2$s(e)k mezuen desagerpen-denbora %1$s astekoa dela ezarri du.</string>
<string name="chat_unencrypted_explanation">Txat honetako mezuek ohiko eposta helbidea erabiltzen dute eta ez daude muturretik muturrera zifratuta.</string>
<string name="chat_protection_enabled_tap_to_learn_more">Mezuak muturretik muturrera zifratuta daude. Sakatu gehiago jakiteko.</string>
<string name="chat_protection_enabled_explanation">Txat honetako mezu guztiak muturretik muturrera zifratuta daude.\n\nMuturretik muturrera zifratzeak mezuak pribatu mantentzen ditu zure eta zure txat kideen artean. Zerbitzariek, hornitzaileek edo erreleek ere ezin dituzte irakurri.</string>
<string name="invalid_unencrypted_tap_to_learn_more">⚠️ %1$s(e)k muturretik muturrera zifratzea behar du, eta txat honetan oraindik ez dago konfiguratuta. Sakatu gehiago jakiteko.</string>
<string name="invalid_unencrypted_explanation">Muturretik muturrera zifratzeko, kontaktuekin aurrez aurre elkartu zaitezke eta haien QR kodea eskaneatu dezakezu elkar egiaztatzeko.</string>
<string name="learn_more">Ikasi gehiago</string>
<string name="devicemsg_self_deleted">\"Gordetako mezuak\" txata ezabatu duzu.\n\n️ \"Gordetako mezuak\" funtzioa berriro erabiltzeko, sortu txat berri bat zure buruarekin.</string>
<!-- %1$s will be replaced by the amount of storage already used, sth. as '500 MB'. If you want to use a percentage sign, type in two of them, eg. %1$s %% -->
<string name="devicemsg_storage_exceeding">⚠️ Zure hornitzailearen biltegiratzea agortzear dago: %%-tik %1$s erabili dituzu dagoeneko.\n\nLitekeena da mezuak jaso ezin izatea biltegiratzea beteta badago.\n\n👉 Mesedez, egiaztatu datu zaharrak hornitzailearen web-interfazean ezaba ditzakezun eta kontuan hartu \"Ezarpenak / Txatak / Ezabatu mezu zaharrak\" aukera aktibatzea. Zure uneko biltegiratze-erabilera edozein unetan egiaztatu dezakezu \"Ezarpenak / Konektibitatea\" atalean.</string>
<!-- %1%s will be replaced by date and time in some human-readable format -->
<string name="devicemsg_bad_time">⚠️ Zure gailuko data edo ordua okerra dela dirudi (%1$s).\n\nEgokitu zure erlojua ⏰🔧 zure mezuak zuzen jasotzen direla ziurtatzeko.</string>
<string name="devicemsg_update_reminder">⚠️ Zure Delta Chat bertsioa zaharkituta egon daiteke.\n\nHorrek arazoak sor ditzake zure txat-kideek bertsio berriagoak erabiltzen dituztelako - eta azken funtzioak falta dituzulako 😳\nMesedez, egiaztatu https://get.delta.chat edo zure aplikazio-denda eguneratzeak ikusteko.</string>
<!-- Some options as "Add Second Device" or "Backup" may require the system PIN/Fingerprint/Gesture/Etc. to be entered in a system dialog. This hint is added to the system dialog, below a title as "Add Second Device" or "Backup". -->
<string name="enter_system_secret_to_continue">Desblokeatu jarraitzeko</string>
<!-- qr code stuff -->
<string name="qr_code">QR kodea</string>
<string name="load_qr_code_as_image">Kargatu QR kodea irudi gisa</string>
<string name="qrscan_title">Eskaneatu QR kodea</string>
<string name="qrscan_hint">Kokatu zure kamera QR kodearen gainean</string>
<string name="qrscan_hint_desktop">Hurbildu QR kodea kamerara</string>
<string name="qrscan_failed">Ezin izan da QR kodea deszifratu</string>
<string name="qrscan_ask_join_group">\"%1$s\" taldera elkartu nahi duzu?</string>
<string name="qrscan_ask_join_channel">\"%1$s\" kanalera batu nahi duzu?</string>
<string name="qrscan_fingerprint_mismatch">Eskaneatutako hatz-marka eta %1$s erabiltzaileari ikusitako azken hatz-marka ez datoz bat.</string>
<string name="qrscan_no_addr_found">QR kode honek hatz-marka bat du baina ez e-mail helbidea.\n\nBandaz kanpoko egiaztaketa baterako, ezarri zifratutako konexio bat hartzailearekin aurretik.</string>
<string name="qrscan_contains_text">Eskaneatutako QR kodearen testua:\n\n%1$s</string>
<string name="qrscan_contains_url">Eskaneatutako QR kodearen URL-a:\n\n%1$s</string>
<string name="qrscan_fingerprint_label">Hatz-marka</string>
<string name="withdraw_verifycontact_explain">QR kode hau beste batzuek eskaneatu dezakete zurekin harremanetan jartzeko.\n\nBerrezarri egin dezakezu, lehendik dagoen QR kodeak edo gonbidapen-estekak gehiago funtziona ez dezan.</string>
<string name="withdraw_verifygroup_explain">QR kode hau beste batzuek eskaneatu dezakete \"%1$s\" taldera batzeko.\n\nBerrezarri egin dezakezu, lehendik dagoen QR kodeak edo gonbidapen-estekak gehiago funtziona ez dezan.</string>
<string name="withdraw_joinbroadcast_explain">QR kode hau beste batzuek eskaneatu dezakete \"%1$s\" kanalera batzeko.\n\nBerrezarri egin dezakezu, lehendik dagoen QR kodeak edo gonbidapen-estekak gehiago funtziona ez dezan.</string>
<string name="withdraw_qr_code">Berrezarri QR kodea</string>
<string name="revive_verifycontact_explain">QR kode hau berrezarri da eta aurrerantzean ez da aktibo egongo.</string>
<string name="revive_qr_code">Aktibatu QR kodea</string>
<string name="qrshow_title">QR gonbidapen-kodea</string>
<string name="qrshow_x_joining">%1$s elkartu da.</string>
<string name="qrshow_x_verified">%1$s egiaztatuta.</string>
@@ -549,13 +991,37 @@
<string name="qrshow_join_group_title">QR gonbidapen-kodea</string>
<!-- This text is shown inside the "QR code card" with very limited space; please formulate the text as short as possible therefore. The placeholder will be replaced by the group name, eg. "Scan to join group \"Testing group\"" -->
<string name="qrshow_join_group_hint">Eskaneatu hau \"%1$s\" taldera elkartzeko.</string>
<string name="qrshow_join_channel_hint">Eskaneatu \"%1$s\" kanalera batzeko</string>
<string name="qrshow_join_contact_title">QR gonbidapen-kodea</string>
<!-- This text is shown inside the "QR code card" with very limited space; please formulate the text as short as possible therefore. The placeholder will be replaced by the profile name eg. "Scan to chat with Alice" -->
<string name="qrshow_join_contact_hint">Eskaneatu hau %1$s erabiltzailearen kontaktua ezartzeko</string>
<string name="qrshow_join_contact_no_connection_toast">Ez dago Internet konexiorik, ezin da QR kode bidezko ezarpena aplikatu</string>
<string name="qraccount_ask_create_and_login">Sortu kontu berria \"%1$s\" zerbitzarian eta saioa han hasi?</string>
<string name="set_name_and_avatar_explain">Ezarri zure kontaktuak ezagutuko duten izen bat. Abatar bat ere ezar dezakezu.</string>
<string name="please_enter_name">Mesedez, sartu izen bat.</string>
<string name="qraccount_qr_code_cannot_be_used">Eskaneatutako QR kodea ezin da kontu berri bat ezartzeko erabili.</string>
<!-- the placeholder will be replaced by the address of the profile -->
<string name="qrlogin_ask_login">Hasi saioa \"%1$s\"(e)n?</string>
<!-- first placeholder will be replaced by name of the inviter, second placeholder will be replaced by the name of the inviter. -->
<string name="secure_join_started">%1$s(e)k talde honetara batzeko gonbidapena egin dizu. \n\n%2$s(r)en gailuak erantzun dezan itxaroten…</string>
<!-- first placeholder will be replaced by name of the inviter, second placeholder will be replaced by the name of the inviter. -->
<string name="secure_join_channel_started">%1$s(e)k kanal honetara batzeko gonbidapena egin dizu. \n\n%2$s(r)en gailuak erantzun dezan itxaroten…</string>
<!-- placeholder will be replaced by the name of the inviter. -->
<string name="secure_join_replies">%1$s(e)k erantzun du, taldera gehitzekoa zain...</string>
<string name="secure_join_wait">Konexioa ezartzen, itxaron mesedez...</string>
<string name="contact_verified">%1$s egiaztatuta.</string>
<string name="contact_blocked">Kontaktua blokeatu duzu</string>
<!-- Shown in contact profile. The placeholder will be replaced by the name of the contact that introduced the contact. -->
<string name="verified_by">%1$s(e)k egiaztatuta</string>
<string name="verified_by_you">Nik egiaztatuta</string>
<string name="verified_by_unknown">Egiaztatuta</string>
<!-- deprecated -->
<string name="verified_contact_required_explain">Muturretik muturrera zifratzea bermatzeko, marka berdea duten kontaktuak soilik gehitu ditzakezu talde honetara.\n\nKontaktuekin aurrez aurre elkartu zaitezke eta haien QR kodea eskaneatu dezakezu elkar egiaztatzeko.</string>
<string name="mailto_dialog_header_select_chat">Aukeratu txat bat mezua bidaltzeko</string>
<!-- first placeholder is the name of the chat -->
<string name="confirm_replace_draft">%1$s(e)k zirriborro bat du dagoeneko, ordezkatu nahi duzu?</string>
<string name="mailto_link_could_not_be_decoded"> mailto esteka ezin izan da deszifratu: %1$s </string>
<!-- notifications -->
<string name="notify_reply_button">Erantzun</string>
<string name="notify_new_message">Mezu berria</string>
@@ -565,6 +1031,14 @@
<string name="notify_name_and_message">Izena eta mezua</string>
<string name="notify_name_only">Izena besterik ez</string>
<string name="notify_no_name_or_message">Izenik edo mezurik ez</string>
<string name="notifications_disabled">Jakinarazpenak desgaituta</string>
<string name="unreliable_bg_notifications">Aktibatu “Behartu atzeko planoko konexioa” jakinarazpenak jasotzea ziurtatzeko.</string>
<string name="new_messages">Mezu berriak</string>
<!-- Body text for a generic "New messages" notification. Shown if we do not have more information about a new messages. Note, that the string is also referenced at https://github.com/deltachat/notifiers -->
<string name="new_messages_body">Mezu berriak dituzu</string>
<string name="n_messages_in_m_chats">%1$d mezu %2$d txatetan</string>
<!-- permissions -->
<string name="perm_required_title">Baimena behar da</string>
<string name="perm_continue">Jarraitu</string>
@@ -573,6 +1047,21 @@
<string name="perm_explain_access_to_mic_denied">Delta Chat-ek mikrofonoa erabiltzeko baimena behar du ahots-mezuak bidali ahal izateko, baina behin betiko ukatu zaio. Joan aplikazioaren ezarpenetara, hautatu \"Baimenak\" eta gaitu \"Mikrofonoa\".</string>
<string name="perm_explain_access_to_storage_denied">Delta Chat-ek biltegiratze baimena behar du argazkiak, bideoak edo audioa erantsi edo esportatzeko, baina behin betiko ukatu zaio. Joan aplikazioaren ezarpen menura, hautatu \"baimenak\", eta gaitu \"Biltegiratzea\"</string>
<string name="perm_explain_access_to_location_denied">Delta Chat-ek kokaleku aatzitzeko baimena behar du kokalekua eransteko, baina behin betiko ukatu zaio. Joan aplikazioaren ezarpen menura, hautatu \"baimenak\", eta gaitu \"Kokalekua\".</string>
<string name="perm_explain_access_to_notifications_denied">Jakinarazpenak jasotzeko, hona jo \"Sistemaren ezarpenak / Aplikazioak / Delta Chat\" eta gaitu \"Jakinarazpenak\".</string>
<!-- ImageEditorHud -->
<string name="ImageEditorHud_draw_anywhere_to_blur">Marraztu edonon lausotzeko</string>
<string name="ImageEditorHud_add_text">Gehitu testua</string>
<string name="ImageEditorHud_blur">Lausotu</string>
<string name="ImageEditorHud_brush_marker">Markatzailea</string>
<string name="ImageEditorHud_brush_highlight">Azpimarratzailea</string>
<string name="ImageEditorHud_crop">Ezabatu</string>
<string name="ImageEditorHud_flip">Irauli</string>
<string name="ImageEditorHud_rotate">Biratu</string>
<!-- strings introduced on desktop. we want to share strings between the os, in general, please do not add generic strings here -->
<string name="about_offical_app_desktop">Hau Delta Chat Desktop aplikazio ofiziala da.</string>
<string name="about_licensed_under_desktop">Software honek GNU GPL 3 bertsioaren lizentziapean dago eta iturburu-kodea GitHub-en dago eskuragarri.</string>
<string name="welcome_desktop">Ongi etorri Delta Chat-era</string>
<string name="global_menu_preferences_language_desktop">Hautatu hizkuntza...</string>
<string name="global_menu_file_desktop">Fitxategia</string>
@@ -588,8 +1077,12 @@
<string name="global_menu_view_developer_desktop">Garatzailea</string>
<string name="global_menu_view_developer_tools_desktop">Garatzaile-tresnak</string>
<string name="global_menu_help_desktop">Laguntza</string>
<string name="delta_chat_homepage">Delta Chat-en hasiera orria</string>
<string name="contribute">Lagundu</string>
<string name="global_menu_help_report_desktop">Eman arazo baten berri</string>
<string name="global_menu_help_about_desktop">Delta Chat-i buruz</string>
<string name="global_menu_file_open_desktop">Ireki Delta Chat</string>
<string name="global_menu_minimize_to_tray">Txikitu</string>
<string name="no_chat_selected_suggestion_desktop">Hautatu txat bat edo sortu txat berri bat</string>
<string name="write_message_desktop">Idatzi mezu bat</string>
<string name="encryption_info_title_desktop">Zifratzeari buruzko informazioa</string>
@@ -598,6 +1091,8 @@
<string name="timestamp_format_m_desktop">MMM D</string>
<string name="remove_desktop">Kendu</string>
<string name="save_desktop">Gorde</string>
<!-- Opposite of "Save". Undo a "Save" action. Similar to "Unmute", "Unpin". Could also be worded as "Save no longer" or so. -->
<string name="unsave">Gorde gabe utzi</string>
<string name="name_desktop">Izena</string>
<string name="select_group_image_desktop">Hautatu taldearen irudia</string>
<string name="export_backup_desktop">Esportatu babes-kopia</string>
@@ -605,6 +1100,21 @@
<string name="message_detail_received_desktop">jasota</string>
<string name="menu.view.developer.open.log.folder">Ireki egunkarien karpeta</string>
<string name="menu.view.developer.open.current.log.file">Ireki uneko egunkari-fitxategia</string>
<string name="explain_desktop_minimized_disabled_tray_pref" tools:ignore="TypographyDashes">Erretiluko ikonoa ezin da desgaitu, Delta Chat --minimized aukerarekin abiarazi baita.</string>
<string name="no_spellcheck_suggestions_found">Ez dago ortografia-iradokizunik.</string>
<string name="show_window">Erakutsi leihoa</string>
<!-- title of the "keybindings" dialog (for the keybindings names as such, where possible the normal command strings are used) -->
<string name="keybindings">Tekla-konbinazioak</string>
<string name="switch_between_chats">Aldatu txaten artean</string>
<string name="scroll_messages">Korritu mezuak</string>
<!-- command to put the cursor to the search input field -->
<string name="focus_search_input">Fokuratu bilaketa</string>
<!-- command to put the cursor to the message input field -->
<string name="focus_message_input">Fokuratu mezua idazteko eremua</string>
<string name="force_refresh_network">Behartu sarea eguneratzera</string>
<string name="insert_newline">Txertatu lerro-jauzia</string>
<!-- accessibility, the general idea is to use the normal strings for accessibility hints wherever possible -->
<string name="a11y_delivery_status_error">Bidalketa-egoera: Errorea</string>
<string name="a11y_delivery_status_sending">Bidalketa-egoera: Bidaltzen</string>
@@ -614,12 +1124,61 @@
<string name="a11y_delivery_status_invalid">Bidalketa-egoera baliogabea</string>
<string name="a11y_message_context_menu_btn_label">Mezu-ekintzak</string>
<string name="a11y_background_preview_label">Atzealdearen aurrebista</string>
<string name="a11y_disappearing_messages_activated">Desagertzen diren mezuak aktibatu dira</string>
<!-- iOS specific strings, developers: please take care to remove strings that are no longer used! -->
<string name="stop_sharing_location">Utzi kokapena partekatzeari</string>
<string name="a11y_voice_message_hint_ios">Grabatu ondoren, sakatu bi aldiz bidaltzeko. Grabazioa baztertzeko, pasatu bi hatz gainetik.</string>
<string name="a11y_connectivity_hint">Klik bikoitza konexio xehetasunak ikusteko.</string>
<string name="login_error_no_internet_connection">Ez dago Internet konexiorik, saio-hasierak huts egin du.</string>
<string name="share_account_not_configured">Kontua ez dago konfiguratuta</string>
<string name="cannot_play_audio_file">Ezin da audio-fitxategia erreproduzitu.</string>
<!-- iOS camera permission alert -->
<string name="perm_ios_explain_access_to_camera_denied">Argazkiak ateratzeko, bideoak grabatzeko edo QR kodeen eskanerra erabiltzeko, ireki sistemaren ezarpenak eta gaitu \"Kamera\".</string>
<string name="open_settings">Ireki ezarpenak</string>
<!-- iOS device contact alert -->
<string name="import_device_contacts">Inportatu gailuko kontaktuak</string>
<string name="import_device_contacts_hint">Zure gailuko kontaktuekin txateatzeko, ireki ezarpenak eta gaitu \"Kontaktuak\".</string>
<!-- iOS notification settings -->
<string name="disabled_in_dc">Desgaitu Delta Chat-en</string>
<string name="disabled_in_system_settings">Desgaitu sistemaren ezarpenetan</string>
<string name="bg_app_refresh_disabled">\"Atzeko planoko aplikazioak freskatzea\" desgaituta dago sistemaren ezarpenetan</string>
<string name="notifications_avg_minutes">Batez beste, %1$d minutuan behin</string>
<string name="notifications_avg_hours">Batez beste, %1$d orduan behin</string>
<string name="last_check_at">Noiz egiaztatua: %1$s</string>
<string name="system_settings">Sistemaren ezarpenak</string>
<!-- shown below the button "System Settings" on the notification screen. this is a hint about what can be edited on System Settings' Notification page - eg. the notification type (banner, lock screen, notification centre), sound, badges and so on. no need to be exhaustive here, it is only to give the user an idea. -->
<string name="system_settings_notify_explain_ios">Editatu mota, bereizgarriak, aurrebista eta gehiago</string>
<!-- iOS shortcut widget -->
<!-- use the same translation for "Shortcuts" as the system is using, often the term "Shortcut" stays untranslated; check eg. how the "Shortcuts" system app is called in your locale -->
<string name="shortcuts_widget_title">Lasterbideak</string>
<!-- use the same translation for "Widget" as the system is using; often the term "Widget" stays untranslated -->
<string name="shortcuts_widget_description">Erabili Delta Chat-en \"Gehitu widget-era\" elementuak gehitzeko</string>
<!-- use the same translation for "Widget" as the system is using; often the term "Widget" stays untranslated -->
<string name="remove_from_widget">Kendu widget-etik</string>
<!-- use the same translation for "Widget" as the system is using; often the term "Widget" stays untranslated -->
<string name="add_to_widget">Gehitu widget-era</string>
<!-- iOS permissions, copy from "deltachat-ios/Info.plist", which is used on missing translations in "deltachat-ios/LANG.lproj/InfoPlist.strings" -->
<string name="InfoPlist_NSCameraUsageDescription">Kamera atzitzeko baimena emateak argazkiak eta bideoak ateratzea ahalbidetzen dizu.</string>
<string name="InfoPlist_NSLocationAlwaysAndWhenInUseUsageDescription">Delta Chat-ek zure kokapena partekatzeko baimena behar du, kokapena partekatzea aktibatuta duzun denbora-tartean.</string>
<string name="InfoPlist_NSLocationWhenInUseUsageDescription">Delta Chat-ek zure kokapena partekatzeko baimena behar du, kokapena partekatzea aktibatuta duzun denbora-tartean.</string>
<string name="InfoPlist_NSMicrophoneUsageDescription">Mikrofonoa atzitzeko baimena emateak audioa grabatzea ahalbidetzen dizu.</string>
<string name="InfoPlist_NSPhotoLibraryUsageDescription">Argazkien bilduma atzitzeko baimena emateak bertatik argazkiak igotzea ahalbidetzen dizu.</string>
<string name="InfoPlist_NSPhotoLibraryAddUsageDescription">Delta Chat-ek zure argazki galerian irudiak gorde nahi ditu.</string>
<string name="InfoPlist_NSFaceIDUsageDescription">Delta Chat-ek Face ID erabil dezake zure tokiko profila, babeskopien sorkuntza eta bigarren gailu baten konfigurazioa babesteko.</string>
<!-- android specific strings, developers: please take care to remove strings that are no longer used! -->
<string name="pref_instant_delivery">Berehalako bidalketa</string>
<string name="pref_background_notifications">Erabili atzeko planoko konexioa</string>
<string name="pref_background_notifications_explain">Bateriaren optimizazioak alde batera uztea eskatzen du, erabili jakinarazpenak garaiz iristen ez badira.</string>
<string name="pref_reliable_service">Behartu atzeko planoko konexioa</string>
<string name="pref_reliable_service_explain">Jakinarazpen iraunkorra sortzen du</string>
<string name="pref_background_notifications_rationale">Konexioari eutsi eta mezuak atzeko planoan jasotzeko, baztertu bateriaren optimizazioak hurrengo urratsean.\n\nDelta Chat-ek baliabide gutxi erabiltzen ditu eta kontu handiz jokatzen du bateria ez agortzeko.</string>
<!-- disabling "Reliable service" will hide a the maybe annoying permanent-notification with the drawback that new-message-notifications get potentially unreliable -->
<string name="perm_enable_bg_reminder_title">Sakatu hemen mezuak jasotzeko Delta Chat bigarren planoan dagoenean.</string>
<string name="perm_enable_bg_already_done">Dagoeneko baimendu diozu Delta Chat aplikazioari bigarren planoan mezuak jasotzea,\n\nOraindik ere mezuak heltzen ez badira, egiaztatu ere zure sistemaren ezarpenak.</string>
</resources>
<!-- device messages for updates -->
<string name="update_2_0">Zer berri?\n\n💯 Muturretik muturrerako zifratze fidagarria eta betirakoa da orain. Sarrailak 🔒 joan dira!\n\n✉️ Muturretik muturrerako zifratzerik gabeko posta elektroniko klasikoa gutun-azal ikur batekin markatuta dago\n\n😻 Kontuaren pantaila berria zure kontaktu guztientzat\n\n🔲 Txat batean erabilitako aplikazioetara azkar sartzeko botoi berria\n\n❤️ Mesedez, egin dohaintza independente jarraitzen laguntzeko eta hobekuntzak ekartzen jarraitzeko: %1$s</string>
</resources>
+49 -5
View File
@@ -21,10 +21,10 @@
<string name="none">هیچ‌کدام</string>
<string name="automatic">خودکار</string>
<string name="strict">سخت‌گیرانه</string>
<string name="open">گشودن</string>
<string name="open">بازکردن</string>
<string name="download">بارگیری</string>
<string name="downloading">در حال بارگیری…</string>
<string name="open_attachment">گشودن پیوست</string>
<string name="open_attachment">بازکردن پیوست</string>
<string name="join">پیوستن</string>
<string name="rejoin">پیوستن مجدد</string>
<string name="delete">حذف</string>
@@ -54,7 +54,7 @@
<string name="mute">سکوت</string>
<string name="muted">بی‌صدا</string>
<string name="ephemeral_messages">پیام‌های ناپدید شونده</string>
<string name="ephemeral_messages_hint">این روی تمام عضو‌های این گپ، اگر از دلتاچت استفاده کنند، اعمال می‌شود. ولی همچنان می‌توانند پیام‌ها را رونوشت، ذخیره و هدایت کنند.</string>
<string name="ephemeral_messages_hint">شامل همه کسانی که در این گفتگو حضور دارند می‌شود. آن‌ها همچنان می‌توانند پیام‌ها را هدایت و ذخیره کرده یا از آن‌ها رونوشت بگیرند. </string>
<string name="save">ذخیره</string>
<string name="chat">گپ</string>
<string name="media">رسانه</string>
@@ -76,9 +76,9 @@
<string name="activate">فعال کردن</string>
<string name="load_remote_content">بارگیری تصاویر از بیرون</string>
<!-- Possible answers to the question are: Never, Always, Once -->
<string name="load_remote_content_ask">تصاویر مربوط به نشانی‌های اینترنتی دیگر می‌توانند برای ردیابی شما استفاده شوند. \n\n این تنظیمها همچنین برای بارگیری فونت‌ها و دیگر محتواها استفاده می‌شوند. اگر غیرفعال باشد ممکن است همچنان تصویر‌های درون پیام و تصویر‌های بارگیری شده را ببینید. \n\n آيا تصاویر خارجی بارگیری شوند؟</string>
<string name="load_remote_content_ask">تصاویر مربوط به نشانی‌های اینترنتی دیگر می‌توانند برای ردیابی شما استفاده شوند. \n\n این تنظیمات همچنین برای بارگیری فونت‌ها و دیگر محتواها استفاده می‌شود. اگر غیرفعال باشد ممکن است همچنان تصویر‌های درون پیام و تصویر‌های بارگیری شده را ببینید. \n\n آيا تصاویر خارجی بارگیری شوند؟</string>
<string name="always">همیشه</string>
<string name="always_load_remote_images">تصاویر دوردست همیشه بار شوند</string>
<string name="always_load_remote_images">تصاویر با منبع بیرونی همیشه بارگیری شوند</string>
<string name="once">همین یک بار</string>
<string name="show_warning">نمایش هشدار</string>
<string name="not_now">اکنون نه</string>
@@ -208,6 +208,8 @@
<string name="webxdc_app">برنامه</string>
<!-- plural of "App"; used to present "Webxdc App" (https://webxdc.org) in a user friendly way. Please stay close to the original term and keep it short (it is used in menus with few screen space). -->
<string name="webxdc_apps">برنامه‌ها</string>
<string name="webxdc_store_url">آدرس مخزن برنامه‌ها</string>
<string name="webxdc_store_url_explain">اگر آن را تنظیم کنید، از این آدرس برای مخزن برنامه‌ها به جای آدرس پیش‌فرض استفاده می‌شود</string>
<string name="webxdc_draft_hint">برای به اشتراک گذاری روی «ارسال» بزنید</string>
<string name="home">خانه</string>
<string name="games">بازی‌ها</string>
@@ -380,12 +382,20 @@
<!-- the action "to call someone", used as a tooltip for the "phone" icon. not: "the call" -->
<string name="start_call">تماس</string>
<string name="start_audio_call">تماس صوتی</string>
<string name="start_video_call">تماس تصویری</string>
<!-- the action "to answer" or to "accept" or to "pick up" a call. not: "the answer" -->
<string name="answer_call">جواب دادن</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">رد کردن</string>
<!-- deprecated, use outgoing_audio_call or outgoing_video_call instead -->
<string name="outgoing_call">تماس خروجی</string>
<string name="outgoing_audio_call">تماس صوتی خروجی</string>
<string name="outgoing_video_call">تماس تصویری خروجی</string>
<!-- deprecated, use incoming_audio_call or incoming_video_call instead -->
<string name="incoming_call">تماس ورودی</string>
<string name="incoming_audio_call">تماس صوتی ورودی</string>
<string name="incoming_video_call">تماس تصویری ورودی</string>
<string name="declined_call">تماس رد شده</string>
<string name="canceled_call">تماس لغو شده</string>
<string name="missed_call">تماس از دست رفته</string>
@@ -629,6 +639,7 @@
<string name="welcome_chat_over_email">گفتگو غیرمتمرکز و ایمن</string>
<string name="scan_invitation_code">اسکن کردن کد دعوت</string>
<string name="login_title">ورود</string>
<string name="login_advanced_hint">این برای کاربر‌های حرفه‌ای است:\n\n • از یک آدرس که در برنامه‌ای دیگر استفاده می‌کنید استفاده نکنید.\n\n • ارائه‌دهندهٔ رایانامه کلاسیک اجازه خواهد داد که گپ‌ها بدون رمزنگاری سراسری باشند. این گپ‌ها به وسیلهٔ یک نماد نامه نشانه‌گذاری می‌شوند.</string>
<string name="login_inbox">ورودی</string>
<string name="login_imap_login">IMAP Login Name</string>
<string name="login_imap_server">IMAP Server</string>
@@ -653,6 +664,19 @@
<string name="proxy_use_proxy_confirm">می‌خواهید از پروکسی «%1$s» استفاده کنید؟</string>
<string name="proxy_share_explain">دوست‌هایتان می‌توانند با اسکن کردن کد QR این پروکسی را اضافه کنند.</string>
<string name="proxy_share_link">اشتراک گذاشتن پیوند</string>
<string name="proxy_enabled">پروکسی فعال شده</string>
<string name="proxy_enabled_hint">شما از یک پروکسی استفاده می‌کنید. اگر مشکل وصل شدن داری، یک پروکسی دیگر استفاده کنید.</string>
<!-- "Relay" refers to the relay servers used; please chose a common term in your language, no need to be literal. If in doubt, stay with "Relay" -->
<string name="transports">رله‌ها</string>
<string name="add_transport">اضافه کردن رله</string>
<string name="remove_transport">حذف رله</string>
<string name="edit_transport">ویرایش رله</string>
<!-- shown if a QR code was scanned that can be used as a relay -->
<string name="confirm_add_transport">اضافه کردن این رله؟</string>
<string name="invalid_transport_qr">کیو‌آر کد اسکن شده دارای یک رلهٔ معتبر نیست.</string>
<!-- placeholder will be replaced by a relay server name -->
<string name="confirm_remove_transport">حذف کردن رلهٔ «%1$s»؟\n\n مخاطب‌های شما تنها در صورتی می‌توانند با شما ارتباط برقرار کنند که قبلا از طریق یک رلهٔ دیگر با آن‌ها تماس داشته‌اید.\n\n در صورت تردید، این رله را بعدا حذف کنید.</string>
<string name="login_certificate_checks">بررسی‌های گواهینامه</string>
<string name="login_error_mail">لطفاً یک نشانی رایانامهٔ معتبر وارد کنید</string>
<string name="login_error_server">لطفا یک کارساز / نشانی آی‌پی صحیح وارد کنید</string>
@@ -716,6 +740,7 @@
<string name="pref_screen_security_explain">درخواست مسدود کردن عکس گرفتن از صفحه در لیست اخیر و درون نرم‌افزار</string>
<string name="pref_screen_security_please_restart_hint">برای اعمال کردن تنظیم‌های امنیت صفحه لطفا نرم‌افزار را بسته و دوباره باز کنید.</string>
<string name="pref_notifications">اعلان‌ها</string>
<string name="pref_mention_notifications">اشاره‌ها(منشن‌ها)</string>
<string name="pref_mention_notifications_explain">در گروه‌های بی‌صدا شده، برای پیام‌های مستقیم به خودتان، مانند پاسخ‌ها یا واکنش‌ها، اعلان دریافت کنید.</string>
<string name="pref_notifications_show">نمایش</string>
<string name="pref_notifications_priority">اولویت</string>
@@ -766,6 +791,8 @@
<string name="pref_imap_folder_warn_disable_defaults">اگر این گزینه را غیر فعال می‌کنید از اینکه سرور شما و حساب‌هایتان هم بر این اساس تنظیم شده باشند.\n\n در غیر این صورت ممکن است هیچ چیز کار نکند. </string>
<!-- No need to be literal here, you can also use "Use Multiple Devices", "Support Multiple Devices" or other fitting terms. However, it should fit to the wording or your language at https://delta.chat/help -->
<string name="pref_multidevice">حالت چند دستگاهی</string>
<string name="pref_multidevice_explain">همگام‌سازی پیام‌های شما با دیگر دستگاه‌هایتان. در صورت اضافه کردن دستگاه دوم به صورت خودکار فعال می‌شود.</string>
<string name="pref_multidevice_change_warn">حالت چند دستگاهی زمانی که از یک نمایه روی چند دستگاه استفاده می‌کنید باید فعال شود. تنها در صورتی این تنظیم را غیرفعال کنید که این نمایه را از روی همهٔ دیگر دستگاه‌هایتان حذف کرده‌اید. \n\n غیرفعال کردن حالت چند دستگاهی زمانی که یک نمایه را روی چند دستگاه دارید باعث می‌شود که بعضی پیام‌ها گم شوند و همچنین مشکل‌های دیگری به وجود می‌آورد.</string>
<string name="pref_auto_folder_moves">انتقال خودکار به پوشه دلتاچت</string>
<string name="pref_only_fetch_mvbox_title">فقط پوشه دلتاچت را بررسی کن</string>
<string name="pref_show_emails">نمایش رایانامه‌های معمولی</string>
@@ -773,6 +800,7 @@
<string name="pref_show_emails_accepted_contacts">برای مخاطبین پذیرفته شده</string>
<string name="pref_show_emails_all">همه</string>
<string name="pref_experimental_features">ویژگی‌های آزمایشی</string>
<string name="pref_experimental_features_explain">این ویژگی‌ها ممکن است ناپایدار باشند و همچنین ممکن است بعدا تغییر کنند یا حذف شوند</string>
<string name="pref_on_demand_location_streaming">هم‌رسانی زندهٔ به‌فرمان مکان</string>
<string name="pref_background_default">پس‌زمینه پیش‌فرض</string>
<string name="pref_background_default_color">رنگ پیش‌فرض</string>
@@ -796,7 +824,16 @@
<string name="pref_show_tray_icon">نمایش نمادک در زیر صفحه</string>
<string name="pref_edit_profile">ویرایش نمایه</string>
<string name="disable_imap_idle">غیر فعال‌سازی IMAP IDLE</string>
<string name="disable_imap_idle_explain">استفاده نکردن از افزونهٔ IMAP IDLE حتی در صورتی که ارائه‌دهنده از آن پشتیبانی می‌کند. فعال‌سازی این گزینه باعث می‌شوند دریافت پیام‌ها به تاخیر بیوفتد. تنها برای آزمایش آن را فعال کنید.</string>
<string name="send_stats_to_devs">ارسال آمار برای توسعه‌دهندگان دلتاچت</string>
<string name="stats_device_message">آیا می‌خواهید با فرستادن آمار استفادهٔ هفتگی به بهبود دلتاچت کمک کنید؟ 👈این‌جا را بزنید...👉</string>
<string name="stats_confirmation_dialog">می‌خواهید با فرستادن گزارش‌های ناشناس استفادهٔ هفتگی به بهبود دلتاچت کمک کنید؟</string>
<string name="stats_thanks">ممنون! هر زمان که خواستید می‌توانید فرستادن آمار‌ها را در «تنظیم‌ها - پیشرفته» غیرفعال کنید\n\n علاوه بر این، تمایل دارید ۵ دقیقه در یک تحقیق علمی راجع به امنیت دلتاچت شرکت کنید؟</string>
<string name="stats_disable_dialog">ارسال آمار‌ها قبلا فعال شدن است.\n\n می‌خواهید آن را غیر فعال کنید؟</string>
<string name="disable">غیرفعال کردن</string>
<string name="stats_keep_sending">ادامه‌دادن به ارسال</string>
<string name="stats_msg_body">این پیوست شامل آمار استفادهٔ ناشناس است که به ما کمک می‌کند دلتاچت را بهبود دهیم. برای اطلاعات بیش‌تر https://delta.chat/help#statssending را ببینید. متشکریم!</string>
<!-- Emoji picker and categories -->
<string name="emoji_search_results">نتایج جستجو</string>
<string name="emoji_not_found">شکلکی پیدا نشد</string>
@@ -829,6 +866,8 @@
<string name="autodel_server_ask">آیا می‌خواهید %1$d پیام را اکنون و تمام پیام‌های آیندهٔ «%2$s» را پاک کنید؟/n/n⚠️ این شامل رایانامه‌ها، رسانه و «پیام‌های ذخیره شده» در همه پوشه‌های کارساز(سرور) می‌شود/n/n⚠️ اگر می‌خواهید داده‌ها در کارساز باقی بمانند از این قابلیت استفاده نکنید/n/n⚠️ اگر به‌جز دلتاچت از دیگر نرم‌افزارهای رایانامه هم استفاده می‌کنید این قابلیت را به کار نگیرید.</string>
<!-- shown below enabled autodel_server-option, should be a summary of autodel_server_ask and remind about the impact -->
<string name="autodel_server_enabled_hint">این شامل رایانامه‌ها، رسانه‌ها و «پیام‌های ذخیره شده» در تمام پوشه‌های کارساز(سرور) می‌شود. اگر می‌خواهید داده‌ها را در کارساز نگه دارید از این قابلیت استفاده نکنید. اگر از دیگر نرم‌افزارهای رایانامه به‌جز دلتاچت استفاده می‌کنید هم از این قابلیت استفاده نکنید.</string>
<string name="autodel_server_warn_multi_device_title">فعال‌سازی حذف بلافاصله</string>
<string name="autodel_server_warn_multi_device">اگر حذف بلافاصله را فعال کنید، نمی‌توانید این نمایه را روی چند دستگاه داشته باشید.</string>
<string name="autodel_confirm">متوجه هستم، همه پیام‌ها حذف شوند</string>
<!-- "At once" in the meaning of "Immediately", without any intervening time. -->
<string name="autodel_at_once">یک بار بعد از بارگیری</string>
@@ -949,6 +988,7 @@
<string name="qrscan_fingerprint_label">اثر انگشت</string>
<string name="withdraw_verifycontact_explain">دیگران برای ارتباط با شما می‌توانند این کد کیوآر را اسکن کنند. \n\n می‌توان کد کیوآر را در اینجا غیر فعال کنید و با اسکن کردن مجدد، آن را فعال کنید. </string>
<string name="withdraw_verifygroup_explain">این کد کیوآر می‌تواند توسط دیگران و برای پیوستن به گروه «%1$s» اسکن شود.\n\n شما می‌تواند این کد کیوآر را در اینجا غیرفعال کنید و با اسکن کردن مجدد، آن را فعال نمایید. </string>
<string name="withdraw_joinbroadcast_explain">این کد کیوآر می‌تواند توسط بقیه برای پیوستن به کانال «%1$s» اسکن شود. \n\n می‌توانید آن را بازنشانی کنید تا کد یا پیوند دعوت فعلی از کار بیوفتد.</string>
<string name="withdraw_qr_code">غیرفعال کردن کد کیوآر</string>
<string name="revive_verifycontact_explain">این کد کیو‌آر بازنشانی شده است و دیگر فعال نیست.</string>
<string name="revive_qr_code">فعال کردن کد کیوآر</string>
@@ -959,6 +999,7 @@
<string name="qrshow_join_group_title">کد دعوت کیوآر</string>
<!-- This text is shown inside the "QR code card" with very limited space; please formulate the text as short as possible therefore. The placeholder will be replaced by the group name, eg. "Scan to join group \"Testing group\"" -->
<string name="qrshow_join_group_hint">برای عضو شدن در گروه\"%1$s\" این را اسکن کنید.</string>
<string name="qrshow_join_channel_hint">برای ملحق شدن به کانال «%1$s» اسکن کنید</string>
<string name="qrshow_join_contact_title">کد دعوت کیوآر</string>
<!-- This text is shown inside the "QR code card" with very limited space; please formulate the text as short as possible therefore. The placeholder will be replaced by the profile name eg. "Scan to chat with Alice" -->
<string name="qrshow_join_contact_hint">برای ارتباط با %1$s این را اسکن کنید.</string>
@@ -972,10 +1013,13 @@
<!-- first placeholder will be replaced by name of the inviter, second placeholder will be replaced by the name of the inviter. -->
<string name="secure_join_started">کاربر %1$s شما را دعوت کرده است تا عضو این گروه شوید.
در انتظار دستگاه %2$s برای پاسخگویی...</string>
<!-- first placeholder will be replaced by name of the inviter, second placeholder will be replaced by the name of the inviter. -->
<string name="secure_join_channel_started">%1$sشما را به این کانال دعوت کرده است.\n\n منتظر دستگاه %2$s برای پاسخ‌دادن هستیم...</string>
<!-- placeholder will be replaced by the name of the inviter. -->
<string name="secure_join_replies">کاربر %1$s پاسخ داد، در انتظار اضافه شدن به گروه...</string>
<string name="secure_join_wait">در حال برقراری ارتباط، لطفا صبر کنید...</string>
<string name="contact_verified">%1$s تأیید شد.</string>
<string name="contact_blocked">شما این مخاطب را مسدود کردید.</string>
<!-- Shown in contact profile. The placeholder will be replaced by the name of the contact that introduced the contact. -->
<string name="verified_by">احراز هویت شده توسط %1$s</string>
<string name="verified_by_you">تأیید شده توسط من</string>
+28 -6
View File
@@ -199,6 +199,7 @@
<string name="video">Vidéo</string>
<string name="documents">Documents</string>
<string name="contact">Contact</string>
<string name="bot">Robot</string>
<string name="camera">Caméra</string>
<!-- As in "start a video recording" or "take a photo"; eg. the description of the "shutter button" in cameras -->
<string name="capture">Enregistrer</string>
@@ -218,6 +219,8 @@
<string name="webxdc_app">Appli</string>
<!-- plural of "App"; used to present "Webxdc App" (https://webxdc.org) in a user friendly way. Please stay close to the original term and keep it short (it is used in menus with few screen space). -->
<string name="webxdc_apps">Applis</string>
<string name="webxdc_store_url">URL du catalogue des apps </string>
<string name="webxdc_store_url_explain">Si elle est définit l\'URL sera utilisée à la place de l\'URL par défaut.</string>
<string name="webxdc_draft_hint">Appuyez \"Envoyer\" pour partager</string>
<string name="home">Accueil</string>
<string name="games">Jeux</string>
@@ -350,7 +353,7 @@
<string name="device_talk">Messages de l\'appareil</string>
<string name="device_talk_subtitle">Messages générés localement</string>
<string name="device_talk_explain">Les messages dans cette discussion sont générés localement par votre application Delta Chat. Cela vous permet de recevoir des informations concernant les mises à jour et problèmes rencontrés à l\'utilisation.</string>
<string name="device_talk_welcome_message2">Gardez contact !\n\n🙌 Cliquez sur « code QR » sur la page d\'accueil des deux appareils. Sélectionnez « Scanner le code QR » sur un des appareils et positionnez les deux appareils l\'un vers l\'autre\n\n🌍 Si vous n\'êtes pas dans la même pièce, vous pouvez scanner le code QR pendant un appel vidéo ou partager un lien d\'invitation à partir de « Scanner le code QR »\n\n Profitez ensuite de votre expérience de messagerie sur le plus grand réseau décentralisé du monde : les emails. Contrairement aux autres applications populaires, tout cela se fait sans contrôle centralisé, suivi ou revente de vos données, celles de vos amis, collègues ou famille à de grandes entreprises.</string>
<string name="device_talk_welcome_message2">Entrez en contact !\n\n🙌 Cliquez sur « code QR » sur la page d\'accueil des deux appareils. Sélectionnez « Scanner le code QR » sur un des appareils et positionnez les deux appareils l\'un vers l\'autre\n\n🌍 Si vous n\'êtes pas dans la même pièce, vous pouvez scanner le code QR pendant un appel vidéo ou partager un lien d\'invitation à partir de « Scanner le code QR »\n\n Profitez ensuite de votre expérience de messagerie sur le plus grand réseau décentralisé du monde : les emails. Contrairement aux autres applications populaires, tout cela se fait sans contrôle centralisé, suivi ou revente de vos données, celles de vos amis, collègues ou famille à de grandes entreprises.</string>
<string name="edit_contact">Mettre à jour le contact</string>
<!-- Verb "to pin", making something sticky, not a noun or abbreviation for "pin number". -->
<string name="pin_chat">Épingler la discussion</string>
@@ -366,7 +369,7 @@
<!-- Menu item beside an app/chat that adds an icon to the system's home screen. If the user taps that icon, the app/chat is opened directly. -->
<string name="add_to_home_screen">Ajouter à l\'écran d\'accueil</string>
<string name="donate">Faire un don</string>
<string name="donate_device_msg">❤️ On dirait que vous aimez bien Delta Chat!\n\nMerci de considérer une donation à Delta Chat afin d\'assurer qu\'il reste libre et gratuit pour tout le monde.\n\nBien que Delta Chat est gratuit à l\'utilisation et un logiciel libre, le développer coûte de l\'argent. Aidez-nous à garder Delta Chat indépendent et le rendre encore plus incroyable dans le future.\n\nhttps://delta.chat/donate</string>
<string name="donate_device_msg">❤️ On dirait que vous aimez bien Delta Chat!\n\nMerci de considérer une donation à Delta Chat afin d\'assurer qu\'il reste libre et gratuit pour tout le monde.\n\nBien que Delta Chat soit gratuit à l\'utilisation et un logiciel libre, le développer coûte de l\'argent. Aidez-nous à garder Delta Chat indépendant et le rendre encore plus incroyable dans le futur.\n\nhttps://delta.chat/donate</string>
<string name="mute_for_one_hour">Désactiver pour 1 heure</string>
<string name="mute_for_eight_hours">Désactiver pour 8 heures</string>
@@ -394,11 +397,16 @@
<string name="answer_call">Décrocher</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">Raccrocher</string>
<!-- deprecated, use outgoing_audio_call or outgoing_video_call instead -->
<string name="outgoing_call">Appel sortant</string>
<!-- deprecated, use incoming_audio_call or incoming_video_call instead -->
<string name="incoming_call">Appel entrant</string>
<string name="declined_call">Appel rejeté</string>
<string name="canceled_call">Appel annulé</string>
<string name="missed_call">Appel manqué</string>
<!-- the first placeholder will be replaced by a date, the second placeholder by duration, example: "Thu, 08:12 pm, 2 minutes" -->
<string name="call_date_and_duration">%1$s, %2$s</string>
<!-- get confirmations -->
<!-- confirmation for leaving groups or channels. If a subject is needed, "Are you sure you want to leave the chat?" would work as well -->
<string name="ask_leave_group">Êtes-vous sûr de vouloir quitter la discussion ?</string>
@@ -658,7 +666,7 @@
<string name="proxy_add_url_hint">Entrer le lien du proxy ici</string>
<string name="proxy_invalid">Proxy invalide ou non supporté</string>
<string name="proxy_list_header">Sauver les proxy</string>
<string name="proxy_delete">Effacer le proxy</string>
<string name="proxy_delete">Supprimer le proxy</string>
<string name="proxy_delete_explain">Voulez-vous vraiment supprimer « %1$s » ?</string>
<string name="proxy_use_proxy_confirm">Voulez-vous utilser le proxy \"%1$s\" ?</string>
<string name="proxy_share_explain">Vos amis peuvent ajouter ce proxy en scannant le code QR.</string>
@@ -672,6 +680,10 @@
<string name="edit_transport">Modifier le relais</string>
<!-- shown if a QR code was scanned that can be used as a relay -->
<string name="confirm_add_transport">Ajouter le relais ?</string>
<string name="invalid_transport_qr">Le QR code scanné ne renvoie pas vers un relai valide.</string>
<!-- placeholder will be replaced by a relay server name -->
<string name="confirm_remove_transport">Supprimer le relai \"%1$s\"\n\nVos contacts ne peuvent vous contacter que si vous les avez déjà contactés depuis un autre relai.\n\nDans le doute, supprimez le relai plus tard.</string>
<string name="login_certificate_checks">Vérification du certificat</string>
<string name="login_error_mail">Entrez une adresse email valide</string>
<string name="login_error_server">Entre un serveur / adresse IP valide</string>
@@ -947,7 +959,7 @@
<string name="ephemeral_timer_weeks_by_you">Vous avez réglé le temps d\'affichage des messages éphémères à %1$s semaines.</string>
<!-- %1$s will be replaced by the number of weeks (always >1) the timer is set to, %2$s will be replaced by name of the contact -->
<string name="ephemeral_timer_weeks_by_other">Minuterie des messages éphémères réglée à %1$s semaines par %2$s.</string>
<string name="chat_unencrypted_explanation">Les messages de cette conversation utilise le mail classique et ne sont pas chiffrés de bout-en-bout.</string>
<string name="chat_unencrypted_explanation">Les messages de cette conversation utilisent le mail classique et ne sont pas chiffrés de bout-en-bout.</string>
<string name="chat_protection_enabled_tap_to_learn_more">Les messages sont désormais garantis chiffrés de bout en bout. Appuyer pour en savoir plus.</string>
<string name="chat_protection_enabled_explanation">Il n\'est pas garanti que tous les messages de cette discussion sont chiffrés de bout en bout.\n\nLe chiffrement de bout en bout assure la confidentialité des messages échangés par les membres d\'une discussion. Même votre fournisseur de courriel ne peut pas les lire. </string>
<string name="invalid_unencrypted_tap_to_learn_more">⚠️ %1$s nécessite un chiffrement de bout en bout qui n\'est pas encore configuré sur cette discussion. Appuyez pour en savoir plus.</string>
@@ -997,7 +1009,7 @@
<string name="qrshow_join_contact_hint">Scanner pour discuter avec %1$s</string>
<string name="qrshow_join_contact_no_connection_toast">Pas de connexion Internet, ne peut pas effectuer la configuration du QR code.</string>
<string name="qraccount_ask_create_and_login">Créer une nouvelle adresse de courriel sur « %1$s » et se connecter ici ?</string>
<string name="set_name_and_avatar_explain">Choisissez un nom que vos contacts reconnaîtrons. Vous pouvez également définir une image pour votre profil.</string>
<string name="set_name_and_avatar_explain">Choisissez un nom que vos contacts reconnaîtront. Vous pouvez également définir une image pour votre profil.</string>
<string name="please_enter_name">Veuillez saisir un nom.</string>
<string name="qraccount_qr_code_cannot_be_used">Le code QR scanné ne peut pas être utilisé pour ouvrir un nouveau compte.</string>
<!-- the placeholder will be replaced by the address of the profile -->
@@ -1147,9 +1159,17 @@
<string name="notifications_avg_hours">Toutes les %1$d heures en moyenne</string>
<string name="last_check_at">Dernièrement vu à %1$s</string>
<string name="system_settings">Paramètres du système</string>
<!-- shown below the button "System Settings" on the notification screen. this is a hint about what can be edited on System Settings' Notification page - eg. the notification type (banner, lock screen, notification centre), sound, badges and so on. no need to be exhaustive here, it is only to give the user an idea. -->
<string name="system_settings_notify_explain_ios">Éditer le type, les badges, les prévisualisation et plus encore.</string>
<!-- iOS shortcut widget -->
<!-- use the same translation for "Shortcuts" as the system is using, often the term "Shortcut" stays untranslated; check eg. how the "Shortcuts" system app is called in your locale -->
<string name="shortcuts_widget_title">Raccourcis</string>
<!-- use the same translation for "Widget" as the system is using; often the term "Widget" stays untranslated -->
<string name="shortcuts_widget_description">Utiliser la fonction \"Ajouter au Widget\" de Delta Chat pour ajouter un élément.</string>
<!-- use the same translation for "Widget" as the system is using; often the term "Widget" stays untranslated -->
<string name="remove_from_widget">Retirer du Widget</string>
<!-- use the same translation for "Widget" as the system is using; often the term "Widget" stays untranslated -->
<string name="add_to_widget">Ajouter au Widget</string>
<!-- iOS permissions, copy from "deltachat-ios/Info.plist", which is used on missing translations in "deltachat-ios/LANG.lproj/InfoPlist.strings" -->
<string name="InfoPlist_NSCameraUsageDescription">Delta Chat utilise votre appareil photo pour prendre des photos et des vidéos et les envoyer, ainsi que pour scanner des QR codes.</string>
<string name="InfoPlist_NSLocationAlwaysAndWhenInUseUsageDescription">Delta Chat a besoin de votre autorisation pour partager votre position pour la durée d\'activation du partage de position.</string>
@@ -1164,11 +1184,13 @@
<string name="pref_background_notifications">Utiliser une connexion en arrière plan</string>
<string name="pref_background_notifications_explain">Requiert la désactivation des optimisations de battterie, utilisez cette option si les notifications sont en retard.</string>
<string name="pref_reliable_service">Forcer une connexion en arrière plan</string>
<string name="pref_reliable_service_explain">Entraîne une notification permanente.</string>
<string name="pref_background_notifications_rationale">Pour maintenir une connexion à votre serveur de courriel et recevoir les messages en arrière-plan, ignorez l\'optimisation de la batterie à l\'étape suivante.\n\nDelta Chat utilise peu de ressources et prend garde à ne pas vider votre batterie.</string>
<!-- disabling "Reliable service" will hide a the maybe annoying permanent-notification with the drawback that new-message-notifications get potentially unreliable -->
<string name="perm_enable_bg_reminder_title">Touchez ici pour recevoir des messages pendant que DeltaChat est en arrière-plan.</string>
<string name="perm_enable_bg_already_done">Vous avez déjà autorisé Delta Chat à recevoir des messages en arrière plan.\n\nSi vous ne les recevez toujours pas en arrière plan, vérifiez les paramètres de votre système.</string>
<!-- device messages for updates -->
<string name="update_2_0">Quoi de neuf?\n\n💯 Le chiffrement de bout-en-bout est maintenant fiable et péreint. 🔒Fini les cadenas !\n\n✉️ Les mails sans chiffrement de bout-en-bout sont maintenant marqués par une icône de mail \n\n😻 Nouvelle fenêtre de profil améliorée pour tous les contactsn\n🔲 Un nouveau button pour l\'accès rapide aux applis utilisés dans une discussion\n\n❤️ Merci de considérer à faire une donation afin que nous restions indépendents et continuons à améliorer Delta Chat: %1$s</string>
<string name="update_2_0">Quoi de neuf?\n\n💯 Le chiffrement de bout-en-bout est maintenant fiable et pérenne. 🔒Fini les cadenas !\n\n✉️ Les mails sans chiffrement de bout-en-bout sont maintenant marqués par une icône de mail \n\n😻 Nouvelle fenêtre de profil améliorée pour tous les contacts \n\n🔲 Nouveau bouton pour l\'accès rapide aux applis utilisées dans une discussion\n\n❤️ Merci d\'envisager de faire une donation afin que nous restions indépendants et continuions à améliorer Delta Chat: %1$s</string>
</resources>
+2
View File
@@ -384,7 +384,9 @@
<string name="answer_call">Fogadás</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">Elutasítás</string>
<!-- deprecated, use outgoing_audio_call or outgoing_video_call instead -->
<string name="outgoing_call">Kimenő hívás</string>
<!-- deprecated, use incoming_audio_call or incoming_video_call instead -->
<string name="incoming_call">Bejövő hívás</string>
<string name="declined_call">Elutasított hívás</string>
<string name="canceled_call">Visszavont hívás</string>
+9
View File
@@ -393,12 +393,20 @@
<!-- the action "to call someone", used as a tooltip for the "phone" icon. not: "the call" -->
<string name="start_call">Chiama</string>
<string name="start_audio_call">Chiamata Audio</string>
<string name="start_video_call">Chiamata Video</string>
<!-- the action "to answer" or to "accept" or to "pick up" a call. not: "the answer" -->
<string name="answer_call">Rispondi</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">Rifiuta</string>
<!-- deprecated, use outgoing_audio_call or outgoing_video_call instead -->
<string name="outgoing_call">Chiamata in uscita</string>
<string name="outgoing_audio_call">Chiamata in uscita</string>
<string name="outgoing_video_call">Videochiamata in uscita</string>
<!-- deprecated, use incoming_audio_call or incoming_video_call instead -->
<string name="incoming_call">Chiamata in arrivo</string>
<string name="incoming_audio_call">Chiamata in arrivo</string>
<string name="incoming_video_call">Videochiamata in arrivo</string>
<string name="declined_call">Chiamata rifiutata</string>
<string name="canceled_call">Chiamata annullata</string>
<string name="missed_call">Chiamata persa</string>
@@ -1020,6 +1028,7 @@
<string name="secure_join_replies">%1$s ha risposto, in attesa di essere aggiunto al gruppo…</string>
<string name="secure_join_wait">Impostazione della crittografia end-to-end, attendere...</string>
<string name="contact_verified">%1$s introdotto.</string>
<string name="contact_blocked">Hai bloccato il contatto</string>
<!-- Shown in contact profile. The placeholder will be replaced by the name of the contact that introduced the contact. -->
<string name="verified_by">Introdotto da %1$s</string>
<string name="verified_by_you">Introdotto da me</string>
+2
View File
@@ -383,7 +383,9 @@
<string name="answer_call">Err</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">Agi</string>
<!-- deprecated, use outgoing_audio_call or outgoing_video_call instead -->
<string name="outgoing_call">Tiɣri tuffiɣt</string>
<!-- deprecated, use incoming_audio_call or incoming_video_call instead -->
<string name="incoming_call">Tiɣri i d-ikecmen</string>
<string name="declined_call">Tiɣri tettwagi</string>
<string name="canceled_call">Tiɣri tettwasefsex</string>
+134 -9
View File
@@ -5,6 +5,10 @@
<string name="ok">Gerai</string>
<string name="cancel">Atsisakyti</string>
<string name="clear_search">Išvalyti paiešką</string>
<!-- a noun, used on a button, short for "show link" -->
<string name="link">Nuoroda</string>
<!-- "scan" in the meaning of "scan QR code" -->
<string name="scan">Skenuoti</string>
<string name="yes">Taip</string>
<string name="no">Ne</string>
<string name="on">Įjungta</string>
@@ -27,6 +31,7 @@
<string name="delete_for_everyone">Ištrinti visiems</string>
<string name="info">Informacija</string>
<string name="update">Atnaujinti</string>
<string name="emoji">Šypsniukas</string>
<string name="attachment">Priedas</string>
<!-- the placeholder will be replaced by the name of the image or file to attach -->
<string name="ask_attach">Pridėti „%1$s“?</string>
@@ -39,6 +44,8 @@
<string name="later">Vėliau</string>
<!-- "Resend" means "Sending the selected message(s) again to the same chat". The string is used in a menu and should be as short as possible. Resending may be needed after failures or to repost old messages to new members. -->
<string name="resend">Siųsti iš naujo</string>
<string name="edited">Taisyta</string>
<string name="edit_message">Taisyti žinutę</string>
<!-- Verb "to archive", as in "put a chat in the archive", not a noun "The Archive". -->
<string name="archive">Archyvuoti</string>
<!-- Verb "to unarchive", as in "remove a chat from the archive", opposite of the previous string -->
@@ -47,6 +54,7 @@
<string name="ephemeral_messages_hint">Šie nustatymai taikomi visiems Delta Chat besinaudojantiems dalyviams. Vis dėlto, jie gali kopijuoti, įrašyti ir persiųsti žinutes arba naudoti kitas el. pašto kliento programas.</string>
<string name="save">Įrašyti</string>
<string name="media">Medija</string>
<string name="apps_and_media">Programėlės ir medija</string>
<string name="profile">Profilis</string>
<string name="all_profiles">Visi profiliai</string>
<string name="current_profile">Dabartinis profilis</string>
@@ -83,7 +91,7 @@
<string name="email_address">El. pašto adresas</string>
<string name="bad_email_address">Blogas el. pašto adresas.</string>
<string name="password">Slaptažodis</string>
<string name="existing_password">Esamas slaptažodis</string>
<string name="existing_password">Dabartinis slaptažodis</string>
<string name="now">Dabar</string>
<!-- Headline for destructive actions with no undo. Could also be "Caution" or "Warning". -->
<string name="danger">Dėmesio</string>
@@ -93,6 +101,25 @@
<string name="this_month">Šį mėnesį</string>
<string name="last_week">Praeitą savaitę</string>
<string name="last_month">Praeitą mėnesį</string>
<!-- Refers to the time a contact was last seen. Shown below contact name in the profile. The placeholder will be replaced by date or time, resulting in "Last seen at 12:13 AM" or "Last seen Nov 12" -->
<string name="last_seen_at">Adresatas paskutinį kartą matytas %1$s</string>
<!-- Refers to the time a contact was last seen. Shown below contact name in the profile. The placeholder will be replaced by a relative point in time as "3 minutes ago" (see https://momentjs.com for more examples and languages)-->
<string name="last_seen_relative">Adresatas paskutinį kartą matytas %1$s</string>
<string name="last_seen_unknown">Adresatas paskutinį kartą matytas: Nežinoma</string>
<!-- Shown in call duration. Avoid abbreviations, prefer full words ex. "N seconds". -->
<plurals name="n_seconds_ext">
<item quantity="one">%d sekundė</item>
<item quantity="few">%d sekundės</item>
<item quantity="many">%d sekundžių</item>
<item quantity="other">%d sekundė</item>
</plurals>
<!-- Shown in call duration. Avoid abbreviations, prefer full words ex. "N minutes". -->
<plurals name="n_minutes_ext">
<item quantity="one">%d minutė</item>
<item quantity="few">%d minutės</item>
<item quantity="many">%d minučių</item>
<item quantity="other">%d minutė</item>
</plurals>
<!-- Shown beside messages that are "N minutes old". Prefer short strings, or well-known abbreviations. -->
<plurals name="n_minutes">
<item quantity="one">%d min.</item>
@@ -158,6 +185,7 @@
<string name="images">Paveikslai</string>
<string name="audio">Garso įrašai</string>
<string name="voice_message">Balso žinutė</string>
<string name="forwarded">Persiųsta</string>
<string name="forwarded_message">Persiųsta žinutė</string>
<!-- %1$s will be replaced by the name of the person who forwarded the message -->
<string name="forwarded_by">Persiuntė %1$s</string>
@@ -176,9 +204,16 @@
<!-- "App" is used to present "Webxdc App" (https://webxdc.org) in a user friendly way. Please stay close to the original term and keep it short (it is used in menus with few screen space). -->
<string name="webxdc_app">Privati programėlė</string>
<!-- plural of "App"; used to present "Webxdc App" (https://webxdc.org) in a user friendly way. Please stay close to the original term and keep it short (it is used in menus with few screen space). -->
<string name="webxdc_apps">Privačios programėlės</string>
<string name="webxdc_apps">Programėlės</string>
<string name="webxdc_store_url">Programėlių parinkiklio URL adresas</string>
<string name="webxdc_store_url_explain">Jei nustatytas, šis URL adresas bus naudojamas kaip programėlių parinkiklis, vietoj numatytojo</string>
<string name="webxdc_draft_hint">Norėdami bendrinti bakstelėkite „Siųsti“</string>
<string name="home">Pradžia</string>
<string name="games">Žaidimai</string>
<string name="tools">Įrankiai</string>
<string name="app_size">Dydis</string>
<string name="app_date_published">Paskelbta</string>
<string name="add_to_chat">Pridėti į pokalbį</string>
<!-- short for "Browse through the App Picker/Store/Catalogue"; could also be translated as "Discover" or "Search" -->
<string name="browse">Naršyti</string>
<string name="unknown">Nežinoma</string>
@@ -207,8 +242,22 @@
<string name="menu_new_classic_contact">Pridėti adresatą rankiniu būdu</string>
<string name="menu_new_chat">Naujas pokalbis</string>
<string name="menu_new_group">Nauja grupė</string>
<!-- "Chat" is a verb here, "Message to" would also fit. the string might be used in the "New Chat" screen above the contact list -->
<string name="chat_with">Pradėti pokalbį su…</string>
<string name="clone_chat">Dubliuoti pokalbį</string>
<!-- consider keeping the term "channel" as in WhatsApp or Telegram -->
<string name="channel">Kanalas</string>
<!-- consider keeping the term "channel" as in WhatsApp or Telegram -->
<string name="channels">Kanalai</string>
<!-- consider keeping the term "channel" as in WhatsApp or Telegram -->
<string name="new_channel">Naujas kanalas</string>
<!-- deprecated -->
<string name="add_recipients">Pridėti gavėjus</string>
<!-- consider keeping the term "channel" as in WhatsApp or Telegram -->
<string name="channel_name">Kanalo pavadinimas</string>
<string name="email">El. paštas</string>
<!-- "New" as in "Create New Email"; shown together with "New Group" and "New Channel" -->
<string name="new_email">Naujas el. laiškas</string>
<!-- the "Subject" of an email, use the term common in classic email apps -->
<string name="subject">Tema</string>
<string name="menu_send">Siųsti</string>
@@ -220,6 +269,7 @@
<string name="menu_unarchive_chat">Išarchyvuoti pokalbį</string>
<string name="menu_add_attachment">Pridėti priedą</string>
<string name="menu_leave_group">Išeiti iš grupės</string>
<string name="menu_leave_channel">Išeiti iš kanalo</string>
<string name="menu_delete_chat">Ištrinti pokalbį</string>
<!-- Command to delete all messages in a chat. The chat itself will not be deleted but will be empty afterwards, so make sure to be different from "Delete Chat" here. "Clear" is a verb here, "Empty Chat" would also be fine (eg. in German "Chat leeren") -->
<string name="clear_chat">Išvalyti pokalbį</string>
@@ -227,6 +277,11 @@
<string name="menu_delete_location">Ištrinti šią vietą?</string>
<string name="menu_message_details">Išsamesnė žinutės informacija</string>
<string name="menu_copy_to_clipboard">Kopijuoti į iškarpinę</string>
<string name="share_invite_link">Bendrinti pakvietimo nuorodą</string>
<string name="share_invite_link_explain">Kiekvienas, turintis šią nuorodą, gali peržiūrėti jūsų profilį ar pradėti su jumis pokalbį. Bendrinkite ją tik su žmonėmis, kuriais pasitikite.</string>
<string name="invite_friends">Pakviesti draugus</string>
<!-- %1$s is replaced by the user's invitation link ("https://i.delta.chat/...") -->
<string name="invite_friends_text">Susisiekite su manimi Delta Chat programoje:\n%1$s</string>
<string name="menu_copy_selection_to_clipboard">Kopijuoti žymėjimą</string>
<string name="menu_copy_link_to_clipboard">Kopijuoti nuorodą</string>
<string name="menu_copy_text_to_clipboard">Kopijuoti tekstą</string>
@@ -241,6 +296,7 @@
<string name="menu_export_attachment">Eksportuoti priedą</string>
<string name="menu_export_attachments">Eksportuoti priedus</string>
<string name="menu_all_media">Visa medija</string>
<string name="all_apps_and_media">Visos programėlės ir medija</string>
<!-- Command to jump to the original message corresponding to a gallery image or document -->
<string name="show_in_chat">Rodyti pokalbyje</string>
<string name="menu_share">Bendrinti</string>
@@ -255,10 +311,14 @@
<string name="menu_scroll_to_top">Slinkti į viršų</string>
<string name="menu_help">Žinynas</string>
<!-- use the same term for "Apps" as elsewhere -->
<string name="what_is_webxdc">Kas yra „Webxdc“?</string>
<string name="what_is_webxdc">Apie programėles</string>
<string name="privacy_policy">Privatumo politika</string>
<string name="menu_select_all">Žymėti viską</string>
<string name="menu_edit_name">Taisyti vardą</string>
<!-- The placeholder will be replaced by the name the contact gave themself -->
<string name="edit_name_explain">Nustatykite slapyvardį, kuris jums bus rodomas vietoj „%1$s“. Palikite tuščią norėdami naudoti adresato pasirinktą vardą.</string>
<!-- The placeholder will be replaced by the name the contact gave themself -->
<string name="edit_name_placeholder">Slapyvardis, skirta „%1$s“</string>
<string name="menu_settings">Nustatymai</string>
<string name="menu_advanced">Išplėstiniai</string>
<string name="menu_view_profile">Rodyti profilį</string>
@@ -301,10 +361,13 @@
<!-- the action "to call someone", used as a tooltip for the "phone" icon. not: "the call" -->
<string name="start_call">Skambinti</string>
<string name="start_audio_call">Garso skambutis</string>
<string name="start_video_call">Vaizdo skambutis</string>
<!-- the action "to answer" or to "accept" or to "pick up" a call. not: "the answer" -->
<string name="answer_call">Atsiliepti</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">Atmesti</string>
<string name="missed_call">Praleistas skambutis</string>
<!-- the first placeholder will be replaced by a date, the second placeholder by duration, example: "Thu, 08:12 pm, 2 minutes" -->
<string name="call_date_and_duration">%1$s, %2$s</string>
@@ -360,6 +423,7 @@
<string name="chat_share_with_title">Bendrinti su</string>
<string name="chat_input_placeholder">Žinutė</string>
<string name="chat_archived_label">Archyvuotas</string>
<string name="chat_request_label">Užklausa</string>
<string name="chat_no_messages">Žinučių nėra.</string>
<string name="chat_self_talk_subtitle">Žinutės, kurias siunčiu asmeniškai sau</string>
<string name="archive_empty_hint">Jei archyvuosite pokalbius, jie bus rodomi čia.</string>
@@ -406,7 +470,6 @@
<string name="tab_image_empty_hint">Čia bus rodomi šiame pokalbyje bendrinti paveikslai.</string>
<string name="tab_video_empty_hint">Čia bus rodomi šiame pokalbyje bendrinti vaizdo įrašai.</string>
<string name="tab_audio_empty_hint">Čia bus rodomi šiame pokalbyje bendrinti garso įrašų failai ir balso žinutės.</string>
<string name="tab_webxdc_empty_hint">Čia bus rodomos šiame pokalbyje bendrintos privačios programėlės.</string>
<string name="tab_all_media_empty_hint">Čia bus rodoma bet kuriame pokalbyje bendrinama medija.</string>
<string name="media_preview">Medijos peržiūra</string>
<string name="send_message">Siųsti žinutę</string>
@@ -415,8 +478,15 @@
<!-- "Second Device" can also be translated as "Another Device", if that is catchier in the destination language. However, make sure to use the term consistently. -->
<string name="multidevice_title">Pridėti antrąjį įrenginį</string>
<string name="multidevice_same_network_hint">Įsitikinkite, kad abu įrenginiai yra prisijungę prie to pačio belaidžio (Wi-Fi) arba laidinio tinklo</string>
<string name="multidevice_this_creates_a_qr_code">Tai sukuria QR kodą, kurį antrasis įrenginys gali nuskenuoti, kad nukopijuotų profilį.\n\nĮsitikinkite, kad joks pašalinis stebėtojas ar kamera nematys lango, kuris bus parodytas paspaudus mygtuką.</string>
<string name="multidevice_install_dc_on_other_device">Įsidiekite Delta Chat kitame savo įrenginyje (https://get.delta.chat)</string>
<!-- "I Already Have a Profile / Add as Second Device” should be the same text as defined by the keys onboarding_alternative_logins and multidevice_receiver_title -->
<string name="multidevice_tap_scan_on_other_device">Paleiskite Delta Chat, bakstelėkite „Aš jau turiu profilį / Pridėti kaip antrąjį įrenginį“ ir nuskenuokite čia rodomą kodą</string>
<!-- Shown inside a "QR code card" with very limited space; please formulate the text as short as possible therefore. The placeholder will be replaced by the profile name eg. "Scan to set up second device for Alice" -->
<string name="multidevice_qr_subtitle">Nuskenuokite norėdami nustatyti antrąjį įrenginį, skirtą %1$s</string>
<string name="multidevice_receiver_title">Pridėti kaip antrąjį įrenginį</string>
<string name="multidevice_open_settings_on_other_device">Pirmajame įrenginyje pereikite į „Nustatymai / Pridėti antrąjį įrenginį“ ir nuskenuokite ten rodomą kodą</string>
<string name="multidevice_abort">Nutraukti antrojo įrenginio nustatymą?</string>
<!-- Shown beside progress bar, stay short -->
<string name="preparing_account">Ruošiama paskyra…</string>
<string name="troubleshooting">Nesklandumų šalinimas</string>
@@ -426,6 +496,8 @@
<string name="incoming_messages">Gaunamosios žinutės</string>
<!-- Headline for the "Outbox" eg. in the "Connectivity" view -->
<string name="outgoing_messages">Siunčiamosios žinutės</string>
<!-- deprecated -->
<string name="storage_on_domain">Saugykla ties %1$s</string>
<string name="connectivity">Jungiamumas</string>
<!-- Shown in the title bar if the app is "Not connected"; prefer short strings. -->
<string name="connectivity_not_connected">Neprisijungta</string>
@@ -437,6 +509,7 @@
<string name="connectivity_connected">Prisijungta</string>
<string name="sending">Siunčiama…</string>
<string name="last_msg_sent_successfully">Paskutinė žinutė sėkmingai išsiųsta.</string>
<string name="not_supported_by_provider">Jūsų teikėjas nepalaiko šios ypatybės.</string>
<!-- Subtitle in quota context of "Connectivity" view. Should be be plural always, no number is prefixed. -->
<string name="messages">Žinutės</string>
<!-- welcome and login -->
@@ -444,14 +517,26 @@
<string name="onboarding_create_instant_account">Sukurti naują profilį</string>
<!-- Secondary button on the welcome screen, allows to "Add as Second Device", "Restore from Backup" -->
<string name="onboarding_alternative_logins">Aš jau turiu profilį</string>
<!-- This is a button and a title, allowing to use existing, classic email, setting ports, passwords and so on -->
<string name="manual_account_setup_option">Naudoti klasikinį el. paštą kaip retransliavimą</string>
<!-- Instant onboarding title (there is not more to do than to set name and avatar) -->
<string name="instant_onboarding_title">Jūsų profilis</string>
<!-- The placeholder will be replaced by the default onboarding server -->
<string name="instant_onboarding_agree_default2">%1$s privatumo politika</string>
<!-- Confirmation button on the instant onboarding screen -->
<string name="instant_onboarding_create">Sutinku ir sukurti profilį</string>
<!-- Secondary, link-like button to open a page with other possible instances -->
<string name="instant_onboarding_show_more_instances">Naudoti kitą serverį</string>
<string name="instant_onboarding_other_server">Išvardyti retransliavimo serverius</string>
<!-- Question shown when another user's QR code is scanned from onboarding screen -->
<string name="instant_onboarding_confirm_contact">Sukurti naują profilį ir pradėti pokalbį su %1$s?</string>
<!-- Question shown when group's/channel's QR code is scanned from onboarding screen -->
<string name="instant_onboarding_confirm_group">Sukurti naują profilį ir prisijungti prie „%1$s“?</string>
<string name="welcome_chat_over_email">Saugūs decentralizuoti pokalbiai</string>
<string name="scan_invitation_code">Skenuoti pakvietimo kodą</string>
<string name="login_title">Prisijungti</string>
<string name="login_advanced_hint">Šis prisijungimas skirtas patyrusiems naudotojams:\n\n• Nenaudokite adreso, kurį naudojate kitoje programoje.\n\n• Klasikinis el. pašto serveris leidžia pokalbius be ištisinio šifravimo, pažymėtus voko piktograma.</string>
<string name="login_inbox">Gauta</string>
<string name="login_imap_login">IMAP prisijungimo vardas</string>
<string name="login_imap_server">IMAP serveris</string>
@@ -468,10 +553,13 @@
<string name="proxy_use_proxy">Naudoti įgaliotąjį serverį</string>
<string name="proxy_add">Pridėti įgaliotąjį serverį</string>
<string name="proxy_add_explain">Palaikomi įgaliotųjų serverių tipai: HTTP(S), SOCKS5 ir Shadowsocks.</string>
<string name="proxy_add_url_hint">Įveskite čia įgaliotojo serverio nuorodą</string>
<string name="proxy_list_header">Įrašyti įgaliotieji serveriai</string>
<string name="proxy_delete">Ištrinti įgaliotąjį serverį</string>
<string name="proxy_delete_explain">Ar tikrai norite ištrinti „%1$s“?</string>
<string name="proxy_use_proxy_confirm">Ar norite naudoti įgaliotąjį serverį „%1$s“?</string>
<!-- "Relay" refers to the relay servers used; please chose a common term in your language, no need to be literal. If in doubt, stay with "Relay" -->
<string name="transports">Retransliavimai</string>
<string name="login_certificate_checks">Liudijimų patikrinimai</string>
<string name="login_error_mail">Įveskite teisingą el. pašto adresą.</string>
<string name="login_error_server">Įveskite teisingą serverį / IP adresą</string>
@@ -523,6 +611,8 @@
<string name="pref_screen_security_explain">Prašyti blokuoti ekrano kopijas paskiausiųjų sąraše ir programėlės viduje</string>
<string name="pref_screen_security_please_restart_hint">Norėdami pritaikyti ekrano saugumo nustatymą, paleiskite programėlę iš naujo.</string>
<string name="pref_notifications">Pranešimai</string>
<string name="pref_mention_notifications">Paminėjimai</string>
<string name="pref_mention_notifications_explain">Grupėse, kuriose išjungti pranešimai, pranešti apie jums adresuotas žinutes, tokias kaip atsakymai ar reakcijos</string>
<string name="pref_notifications_show">Rodyti</string>
<string name="pref_notifications_priority">Pirmenybė</string>
<string name="pref_notifications_explain">Įjungti sistemos pranešimus apie naujas žinutes</string>
@@ -558,7 +648,8 @@
<string name="pref_other">Kita</string>
<string name="pref_backup">Atsarginė kopija</string>
<string name="pref_backup_explain">Padaro atsarginę pokalbių kopiją į išorinę saugyklą</string>
<string name="pref_backup_export_explain">Atsarginė kopija padeda jums nusistatyti naują diegimą šiame ar kitame įrenginyje.\n\nAtsarginėje kopijoje bus visos žinutės, adresatai ir pokalbiai bei jūsų ištisinė Autocrypt sąranka. Atsarginės kopijos failą laikykite saugioje vietoje arba kaip įmanoma greičiau jį ištrinkite.</string>
<string name="pref_backup_export_explain">Atsarginė kopija padeda jums nusistatyti naują diegimą šiame ar kitame įrenginyje.\n\nAtsarginėje kopijoje bus visos žinutės, adresatai ir pokalbiai bei jūsų ištisinio perdavimo „Autocrypt sąranka. Atsarginės kopijos failą laikykite saugioje vietoje arba kaip įmanoma greičiau jį ištrinkite.</string>
<string name="pref_backup_export_this">Eksportuoti šį profilį</string>
<!-- the placeholder will be replaced by the number of profiles to export; the number is always larger than 1 -->
<string name="pref_backup_export_all">Eksportuoti visas %1$d paskyras</string>
<string name="pref_backup_export_start_button">Pradėti daryti atsarginę kopiją</string>
@@ -568,6 +659,8 @@
<string name="pref_background_btn_default">Naudoti numatytąjį paveikslą</string>
<string name="pref_background_btn_gallery">Pasirinkti iš galerijos</string>
<string name="pref_imap_folder_warn_disable_defaults">Jeigu išjungiate šį parametrą, įsitikinkite, kad jūsų serveris ir kiti klientai yra sukonfigūruoti atitinkamai.\n\nPriešingu atveju, tai gali iš viso neveikti.</string>
<!-- No need to be literal here, you can also use "Use Multiple Devices", "Support Multiple Devices" or other fitting terms. However, it should fit to the wording or your language at https://delta.chat/help -->
<string name="pref_multidevice">Kelių įrenginių veiksena</string>
<string name="pref_auto_folder_moves">Automatiniai perkėlimai į DeltaChat aplanką</string>
<string name="pref_show_emails">Rodyti klasikinius el. laiškus</string>
<string name="pref_show_emails_no">Ne, tik pokalbius</string>
@@ -579,7 +672,7 @@
<string name="pref_background_custom_image">Tinkintas paveikslas</string>
<string name="pref_background_custom_color">Tinkinta spalva</string>
<string name="export_aborted">Eksportavimas nutrauktas.</string>
<string name="auto_download_messages">Automatinis žinučių atsisiuntimas</string>
<string name="auto_download_messages">Automatinis žinučių atsiuntimas</string>
<!-- %1$s will be replaced by a human-readable number of bytes, eg. 32 KiB, 1 MiB -->
<string name="up_to_x">Iki %1$s</string>
<string name="up_to_x_most_worse_quality_images">Iki %1$s, iš esmės, blogos kokybės paveikslus</string>
@@ -587,9 +680,12 @@
<string name="download_failed">Atsiuntimas nepavyko</string>
<!-- %1$s will be replaced by a human-readable number of bytes, eg. 32 KiB, 1 MiB. Resulting string eg. "1 MiB message" -->
<string name="n_bytes_message">%1$s žinutė</string>
<string name="profile_image_delete">Ištrinti profilio paveikslą</string>
<string name="profile_image_select">Pasirinkti profilio nuotrauką</string>
<string name="select_your_new_profile_image">Pasirinkite naują profilio nuotrauką</string>
<string name="profile_image_delete">Ištrinti profilio nuotrauką</string>
<string name="pref_show_tray_icon">Rodyti dėklo piktogramą</string>
<string name="pref_edit_profile">Taisyti profilį</string>
<string name="disable_imap_idle">Išjungti IMAP IDLE</string>
<string name="send_stats_to_devs">Siųsti statistiką Delta Chat plėtotojams</string>
<!-- Emoji picker and categories -->
<string name="emoji_search_results">Paieškos rezultatai</string>
@@ -608,6 +704,11 @@
<string name="reactions">Reakcijos</string>
<!-- Verb, the action of adding a "Reaction" to a message. Used eg. in context menus, similar to "Reply" or "Forward" -->
<string name="react">Reaguoti</string>
<!-- %1$s will be replaced by an emoji. %2$s will be replaced by message summary (the summary is often long and whole string gets truncated; words after %2$s will often not being visible). Eg. 'You reacted 👍 to "Found my suitcase"'. Use less than 20 characters, otherwise the string will be truncated too soon and too few information are shown. Do not try to translate "reacted to" too strict. Depending on the language, "added 👍 to" or "gave 👍 to" or just "👍 to" may be shorter and/or clearer and work as well. -->
<string name="reaction_by_you">Jūs sureagavote %1$s į „%2$s“</string>
<!-- %1$s will be replaced a name. %2$s will be replaced by an emoji. %3$s will be replaced by message summary (the summary is often long and whole string gets truncated; words after %3$s will often not being visible). Eg. 'Alice reacted 👍 to "Nice photos"'. Use less than 20 characters, otherwise the string will be truncated too soon and too few information are shown. Do not try to translate "reacted to" too strict. Depending on the language, "added 👍 to" or "gave 👍 to" or just "👍 to" may be shorter and/or clearer work as well. -->
<string name="reaction_by_other">%1$s sureagavo %2$s į „%3$s“</string>
<!-- automatically delete message -->
<string name="delete_old_messages">Ištrinti senas žinutes</string>
<string name="autodel_device_title">Ištrinti žinutes iš įrenginio</string>
@@ -628,8 +729,16 @@
<string name="group_name_changed_by_you">Jūs pakeitėte grupės pavadinimą iš „%1$s“ į „%2$s“.</string>
<!-- %1$s will be replaced by the old group name, %2$s will be replaced by the new group name, %3$s will be replaced by name and address of the contact who did the action. -->
<string name="group_name_changed_by_other">%3$s pakeitė grupės pavadinimą iš „%1$s“ į „%2$s“.</string>
<string name="group_image_changed_by_you">Jūs pakeitėte grupės paveikslą.</string>
<!-- %1$s will be replaced by name of the contact who did the action -->
<string name="group_image_changed_by_other">%1$s pakeitė grupės paveikslą.</string>
<!-- %1$s will be replaced by name of the contact removed from the group; this string is used when it's unclear who did the action -->
<string name="member_x_removed">Dalyvis %1$s pašalintas.</string>
<!-- "left" in the meaning of "exited". -->
<string name="group_left_by_you">Jūs išėjote iš grupės.</string>
<!-- "left" in the meaning of "exited". -->
<string name="channel_left_by_you">Jūs išėjote iš kanalo.</string>
<string name="you_joined_the_channel">Jūs prisijungėte prie kanalo.</string>
<!-- "left" in the meaning of "exited"; %1$s will be replaced by name of the contact leaving the group -->
<string name="group_left_by_other">%1$s išėjo iš grupės.</string>
<string name="group_image_deleted_by_you">Jūs ištrynėte grupės paveikslą.</string>
@@ -655,6 +764,9 @@
<string name="ephemeral_timer_1_week_by_you">Jūs nustatėte išnykstančių žinučių laikmatį į 1 savaitę.</string>
<!-- %1$s will be replaced by name of the contact -->
<string name="ephemeral_timer_1_week_by_other">%1$s nustatė išnykstančių žinučių laikmatį į 1 savaitę.</string>
<string name="ephemeral_timer_1_year_by_you">Jūs nustatėte išnykstančių žinučių laikmatį į 1 metus.</string>
<!-- %1$s will be replaced by name of the contact -->
<string name="ephemeral_timer_1_year_by_other">%1$s nustatė išnykstančių žinučių laikmatį į 1 metus.</string>
<!-- %1$s will be replaced by the number of minutes (always >1) the timer is set to -->
<string name="ephemeral_timer_minutes_by_you">Jūs nustatėte išnykstančių žinučių laikmatį į %1$s min.</string>
<!-- %1$s will be replaced by the number of minutes (always >1) the timer is set to, %2$s will be replaced by name of the contact -->
@@ -671,6 +783,9 @@
<string name="ephemeral_timer_weeks_by_you">Jūs nustatėte išnykstančių žinučių laikmatį į %1$s sav.</string>
<!-- %1$s will be replaced by the number of weeks (always >1) the timer is set to, %2$s will be replaced by name of the contact -->
<string name="ephemeral_timer_weeks_by_other">%2$s nustatė išnykstančių žinučių laikmatį į %1$s sav. </string>
<string name="chat_unencrypted_explanation">Žinutės šiame pokalbyje naudoja klasikinius el. laiškus ir nėra šifruotos ištisinio perdavimo būdu.</string>
<string name="chat_protection_enabled_tap_to_learn_more">Žinutės yra šifruotos ištisinio perdavimo būdu. Bakstelėkite norėdami sužinoti daugiau.</string>
<string name="chat_protection_enabled_explanation">Visos žinutės šiame pokalbyje yra šifruotos ištisinio perdavimo būdu.\n\nIštisinis šifravimas išlaiko žinutes tarp jūsų ir jūsų pokalbio partnerių privačias. Netgi serveriai, teikėjai ar retransliavimai negali jų perskaityti.</string>
<string name="learn_more">Sužinoti daugiau</string>
<!-- %1%s will be replaced by date and time in some human-readable format -->
@@ -685,6 +800,7 @@
<string name="qrscan_hint">Laikykite savo kamerą virš QR kodo</string>
<string name="qrscan_failed">Nepavyko dekoduoti QR kodo</string>
<string name="qrscan_ask_join_group">Ar norite prisijungti prie grupės \"%1$s\"?</string>
<string name="qrscan_ask_join_channel">Ar norite prisijungti prie kanalo „%1$s“?</string>
<string name="qrscan_contains_text">Nuskenuoto QR kodo tekstas:\n\n%1$s</string>
<string name="qrscan_contains_url">Nuskenuoto QR kodo URL:\n\n%1$s</string>
<string name="qrscan_fingerprint_label">Kontrolinis kodas</string>
@@ -698,14 +814,19 @@
<string name="qrshow_join_group_hint">Nuskenuokite tai, norėdami prisijungti prie \"%1$s\" grupės.</string>
<string name="qrshow_join_contact_title">QR pakvietimo kodas</string>
<!-- This text is shown inside the "QR code card" with very limited space; please formulate the text as short as possible therefore. The placeholder will be replaced by the profile name eg. "Scan to chat with Alice" -->
<string name="qrshow_join_contact_hint">Nuskenuokite tai, norėdami užmegzti kontaktą su %1$s</string>
<string name="qrshow_join_contact_hint">Nuskenuokite norėdami pradėti pokalbį su %1$s</string>
<string name="qrshow_join_contact_no_connection_toast">Nėra interneto ryšio, nepavyksta atlikti QR kodo sąrankos.</string>
<string name="qraccount_ask_create_and_login">Sukurti naują el. pašto adresą ties „%1$s“ ir prisijungti ten?</string>
<string name="set_name_and_avatar_explain">Nusistatykite vardą, kurį jūsų adresatai atpažins. Taip pat galite nusistatyti profilio nuotrauką.</string>
<string name="please_enter_name">Įrašykite vardą.</string>
<string name="qraccount_qr_code_cannot_be_used">Nuskenuotas QR kodas negali būti naudojamas naujos paskyros nustatymui.</string>
<!-- the placeholder will be replaced by the address of the profile -->
<string name="qrlogin_ask_login">Prisijungti prie „%1$s“?</string>
<!-- placeholder will be replaced by the name of the inviter. -->
<string name="secure_join_replies">%1$s atsakė, laukiama, kol būsite pridėti į grupę…</string>
<string name="secure_join_wait">Užmezgiamas ryšys, palaukite…</string>
<string name="contact_verified">%1$s patvirtintas.</string>
<string name="contact_blocked">Jūs užblokavote adresatą</string>
<string name="mailto_dialog_header_select_chat">Pasirinkite pokalbį, į kurį siųsti žinutę</string>
<string name="mailto_link_could_not_be_decoded">nepavyko dekoduoti mailto nuorodos: %1$s</string>
@@ -738,7 +859,7 @@
<string name="global_menu_preferences_language_desktop">Pasirinkti kalbą...</string>
<string name="global_menu_file_desktop">Failas</string>
<string name="global_menu_file_quit_desktop">Išeiti</string>
<string name="global_menu_edit_desktop">Taisa</string>
<string name="global_menu_edit_desktop">Taisyti</string>
<string name="global_menu_edit_undo_desktop">Atšaukti</string>
<string name="global_menu_edit_redo_desktop">Grąžinti</string>
<string name="global_menu_edit_cut_desktop">Iškirpti</string>
@@ -780,6 +901,7 @@
<string name="a11y_delivery_status_delivered">Pristatymo būsena: Pristatyta</string>
<string name="a11y_delivery_status_read">Pristatymo būsena: Perskaityta</string>
<string name="a11y_delivery_status_invalid">Neteisinga pristatymo būsena</string>
<string name="a11y_message_context_menu_btn_label">Veiksmai su žinute</string>
<string name="a11y_background_preview_label">Fono peržiūra</string>
<string name="a11y_disappearing_messages_activated">Išnykstančios žinutės aktyvuotos</string>
@@ -793,10 +915,13 @@
<!-- iOS notification settings -->
<string name="disabled_in_dc">Išjungta Delta Chat programėlėje</string>
<string name="disabled_in_system_settings">Išjungta sistemos nustatymuose</string>
<string name="notifications_avg_minutes">Vidutiniškai kas %1$d min.</string>
<string name="notifications_avg_hours">Vidutiniškai kas %1$d val.</string>
<string name="system_settings">Sistemos nustatymai</string>
<string name="InfoPlist_NSMicrophoneUsageDescription">Delta Chat naudoja mikrofoną, kad įrašytų ir siųstų balso žinutes bei vaizdo įrašus su garsu.</string>
<string name="InfoPlist_NSPhotoLibraryUsageDescription">Delta Chat leis jums pasirinkti, kurias nuotraukas iš savo kolekcijos išsiųsti.</string>
<string name="InfoPlist_NSPhotoLibraryAddUsageDescription">Delta Chat nori įrašyti paveikslus į jūsų nuotraukų kolekciją.</string>
<string name="pref_background_notifications">Naudoti foninį ryšį</string>
<string name="pref_background_notifications_rationale">Norėdami palaikyti ryšį su savo el. pašto serveriu ir gauti žinutes fone, kitame žingsnyje nepaisykite akumuliatoriaus optimizavimų (leiskite vykdyti programą fone).\n\nDelta Chat naudoja nedaug išteklių ir rūpinasi, kad neeikvotų jūsų akumuliatoriaus energijos.</string>
<!-- disabling "Reliable service" will hide a the maybe annoying permanent-notification with the drawback that new-message-notifications get potentially unreliable -->
<string name="perm_enable_bg_reminder_title">Bakstelėkite čia, norėdami gauti žinutes, kol Delta Chat veikia fone.</string>
+2
View File
@@ -384,7 +384,9 @@
<string name="answer_call">Svar</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">Avvis</string>
<!-- deprecated, use outgoing_audio_call or outgoing_video_call instead -->
<string name="outgoing_call">Utgående anrop</string>
<!-- deprecated, use incoming_audio_call or incoming_video_call instead -->
<string name="incoming_call">Innkommende anrop</string>
<string name="declined_call">Avvist anrop</string>
<string name="canceled_call">Avbrutt anrop</string>
+10 -2
View File
@@ -380,12 +380,20 @@
<!-- the action "to call someone", used as a tooltip for the "phone" icon. not: "the call" -->
<string name="start_call">Bellen</string>
<string name="start_audio_call">Audiogesprek</string>
<string name="start_video_call">Videogesprek</string>
<!-- the action "to answer" or to "accept" or to "pick up" a call. not: "the answer" -->
<string name="answer_call">Aanvaarden</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">Weigeren</string>
<!-- deprecated, use outgoing_audio_call or outgoing_video_call instead -->
<string name="outgoing_call">Uitgaande oproep</string>
<string name="outgoing_audio_call">Uitgaand audiogesprek</string>
<string name="outgoing_video_call">Uitgaand videogesprek</string>
<!-- deprecated, use incoming_audio_call or incoming_video_call instead -->
<string name="incoming_call">Inkomende oproep</string>
<string name="incoming_audio_call">Inkomend audiogesprek</string>
<string name="incoming_video_call">Inkomend videogesprek</string>
<string name="declined_call">Geweigerde oproep</string>
<string name="canceled_call">Afgebroken oproep</string>
<string name="missed_call">Gemiste oproep</string>
@@ -1088,8 +1096,8 @@
<string name="name_desktop">Naam</string>
<string name="select_group_image_desktop">Kies een groepsafbeelding</string>
<string name="export_backup_desktop">Reservekopie exporteren</string>
<string name="message_detail_sent_desktop">verstuurd</string>
<string name="message_detail_received_desktop">ontvangen</string>
<string name="message_detail_sent_desktop">Verstuurd</string>
<string name="message_detail_received_desktop">Ontvangen</string>
<string name="menu.view.developer.open.log.folder">Map met logbestanden openen</string>
<string name="menu.view.developer.open.current.log.file">Huidig logbestand openen</string>
<string name="explain_desktop_minimized_disabled_tray_pref" tools:ignore="TypographyDashes">Het systeemvakpictogram is uitgeschakeld omdat Delta Chat is gestart met de optie --minimized</string>
+11 -2
View File
@@ -404,12 +404,20 @@
<!-- the action "to call someone", used as a tooltip for the "phone" icon. not: "the call" -->
<string name="start_call">Zadzwoń</string>
<string name="start_audio_call">Połączenie audio</string>
<string name="start_video_call">Połączenie wideo</string>
<!-- the action "to answer" or to "accept" or to "pick up" a call. not: "the answer" -->
<string name="answer_call">Odbierz</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">Odrzuć</string>
<!-- deprecated, use outgoing_audio_call or outgoing_video_call instead -->
<string name="outgoing_call">Połączenie wychodzące</string>
<string name="outgoing_audio_call">Wychodzące połączenie audio</string>
<string name="outgoing_video_call">Wychodzące połączenie wideo</string>
<!-- deprecated, use incoming_audio_call or incoming_video_call instead -->
<string name="incoming_call">Połączenie przychodzące</string>
<string name="incoming_audio_call">Przychodzące połączenie audio</string>
<string name="incoming_video_call">Przychodzące połączenie wideo</string>
<string name="declined_call">Połączenie odrzucone</string>
<string name="canceled_call">Połączenie anulowane</string>
<string name="missed_call">Połączenie anulowane</string>
@@ -1036,6 +1044,7 @@
<string name="secure_join_replies">Użytkownik %1$s odpowiedział, czekając na dodanie do grupy…</string>
<string name="secure_join_wait">Ustanawiam połączenie, proszę czekać…</string>
<string name="contact_verified">Kontakt %1$s zweryfikowany.</string>
<string name="contact_blocked">Zablokowano kontakt</string>
<!-- Shown in contact profile. The placeholder will be replaced by the name of the contact that introduced the contact. -->
<string name="verified_by">Kontakt zweryfikowany przez %1$s</string>
<string name="verified_by_you">Kontakt zweryfikowany przez ciebie</string>
@@ -1121,8 +1130,8 @@
<string name="name_desktop">Nazwa</string>
<string name="select_group_image_desktop">Wybierz obraz grupy</string>
<string name="export_backup_desktop">Eksport kopii zapasowej</string>
<string name="message_detail_sent_desktop">wysłane</string>
<string name="message_detail_received_desktop">odebrane</string>
<string name="message_detail_sent_desktop">Wysłana</string>
<string name="message_detail_received_desktop">Odebrana</string>
<string name="menu.view.developer.open.log.folder">Otwórz folder dziennika</string>
<string name="menu.view.developer.open.current.log.file">Otwórz bieżący plik dziennika</string>
<string name="explain_desktop_minimized_disabled_tray_pref" tools:ignore="TypographyDashes">Ikona w zasobniku nie może być wyłączona, ponieważ Delta Chat został uruchomiony z opcją --minimized.</string>
+8
View File
@@ -404,12 +404,20 @@
<!-- the action "to call someone", used as a tooltip for the "phone" icon. not: "the call" -->
<string name="start_call">Позвонить</string>
<string name="start_audio_call">Аудиозвонок</string>
<string name="start_video_call">Видеозвонок</string>
<!-- the action "to answer" or to "accept" or to "pick up" a call. not: "the answer" -->
<string name="answer_call">Ответить</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">Отклонить</string>
<!-- deprecated, use outgoing_audio_call or outgoing_video_call instead -->
<string name="outgoing_call">Исходящий звонок</string>
<string name="outgoing_audio_call">Исходящий аудиозвонок</string>
<string name="outgoing_video_call">Исходящий видеозвонок</string>
<!-- deprecated, use incoming_audio_call or incoming_video_call instead -->
<string name="incoming_call">Входящий звонок</string>
<string name="incoming_audio_call">Входящий аудиозвонок</string>
<string name="incoming_video_call">Входящий видеозвонок</string>
<string name="declined_call">Отклонённый звонок</string>
<string name="canceled_call">Отменённый звонок</string>
<string name="missed_call">Пропущенный звонок</string>

Some files were not shown because too many files have changed in this diff Show More