Compare commits

..

89 Commits

Author SHA1 Message Date
copilot-swe-agent[bot] 1d2b379e5a Document ProGuard retrace usage in BUILDING.md
Agent-Logs-Url: https://github.com/ArcaneChat/android/sessions/9b58d3e8-6163-4dac-8ca0-d6e7fa2df3a8

Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-04-28 13:52:01 +00:00
copilot-swe-agent[bot] d2c3ed8be3 Include ProGuard mapping.txt in release symbols zip
Agent-Logs-Url: https://github.com/ArcaneChat/android/sessions/5f6429cb-14d1-4558-863e-ea68cf744422

Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-04-28 13:45:52 +00:00
adbenitez f870e9c8fd Merge remote-tracking branch 'upstream/main' 2026-04-22 21:48:58 +02:00
wchen342 d5982ba09f Merge pull request #4361 from deltachat/wch423/location-streaming
Add foreground service for location streaming
2026-04-18 17:30:56 -04:00
adbenitez 22d70d7bd4 Merge remote-tracking branch 'upstream/main' 2026-04-18 22:15:03 +02:00
adb 09bdc32a9c Merge pull request #4315 from deltachat/r10s/markfresh
feat: mark messages as "unread"
2026-04-18 21:09:18 +02:00
adb 7763ba17bf Merge branch 'main' into r10s/markfresh 2026-04-18 20:15:57 +02:00
adb 965204c46f Merge branch 'main' into wch423/location-streaming 2026-04-18 19:56:00 +02:00
adbenitez 815e4def6f Merge branch 'wch423/location-streaming' of https://github.com/deltachat/deltachat-android into wch423/location-streaming 2026-04-18 19:54:17 +02:00
adbenitez dc3ca8ff70 use "Location Streaming" for notification title and channel name 2026-04-18 19:48:39 +02:00
adb 3bccb3fb84 Apply suggestion from @adbenitez 2026-04-18 19:44:39 +02:00
adb 402d93b5a4 Merge pull request #4363 from deltachat/adb/allow-autoplay
allow webxdc to auto-play audio without requiring user gesture/touch
2026-04-18 17:10:48 +02:00
adbenitez 927dc46431 update changelog 2026-04-18 00:02:19 +02:00
adbenitez 87c32b1904 allow webxdc to auto-play audio without requiring user gesture/touch 2026-04-17 23:59:51 +02:00
adbenitez bb97213460 apply spotless 2026-04-17 23:52:22 +02:00
adbenitez 9ca9bf1acd remove unnecessary entry in src/main/res/xml/file_provider_paths.xml 2026-04-17 23:45:36 +02:00
wch423 885c902b14 Allow fallback to different providers 2026-04-17 21:11:56 +02:00
adbenitez dc4ee3f686 Merge remote-tracking branch 'upstream/main' 2026-04-17 20:52:30 +02:00
wch423 61bf5aaad4 Change how location sources work 2026-04-17 20:30:57 +02:00
link2xt 18595cbee1 Run nix flake update
This fixes some deprecation warnings.
I used it to build the app after update,
it still works.
2026-04-17 18:08:53 +00:00
wch423 5d76586ac1 Add foreground service for location streaming 2026-04-15 19:07:34 +02:00
adb dc78307df7 Merge pull request #4335 from deltachat/adb/re-enable-full-screen-intent-perm
get calls out of experimental
2026-04-14 18:23:39 +02:00
adbenitez 86a9cbfb45 update changelog 2026-04-14 18:19:52 +02:00
adb 623b20f713 Merge branch 'main' into adb/re-enable-full-screen-intent-perm 2026-04-14 18:12:05 +02:00
adb 5f4eae798f Merge pull request #4359 from deltachat/prep-2.49.0
prepare 2.49.0
2026-04-13 16:39:18 +02:00
adbenitez 4a8609822d prepare 2.49.0 2026-04-13 16:23:27 +02:00
adb 89ddc1e01f Merge pull request #4358 from deltachat/update-core-and-stuff-2026-04-13
Update core to 2.49.0
2026-04-13 16:20:44 +02:00
adbenitez 0ce42578fa update changelog 2026-04-13 15:44:42 +02:00
adbenitez faa7ad0a35 update translations 2026-04-13 15:44:33 +02:00
adbenitez 38e8ceb253 update RPC 2026-04-13 15:33:58 +02:00
adbenitez a32460f253 update deltachat-core-rust to 'chore(release): prepare for 2.49.0' of 'v2.49.0' 2026-04-13 15:29:06 +02:00
adbenitez 198268a4c3 Merge remote-tracking branch 'upstream/main' 2026-04-11 14:35:01 +02:00
adb 71158970ae Merge pull request #4357 from deltachat/wch423/external-cache-fix
Add fallback for getExternalCacheDir() in case primary storage is SD
2026-04-10 18:40:34 +02:00
wch423 1383b06e86 Add fallback for getExternalCacheDir() in case primary storage is SD card 2026-04-10 15:36:56 +02:00
B. Petersen 24165e311b deprecate sticker string, pointing to deeply nested profile folder 2026-04-09 17:34:14 +02:00
adb 5ec892db34 Merge pull request #4352 from deltachat/wch423/call-microphone-permission
Refine call permission checks and gates
2026-04-08 20:46:21 +02:00
wch423 caef7eda29 Add fixes for PiP caused bug and potential Android 16 problem with permission popups 2026-04-08 18:21:49 +02:00
wch423 cf53af4778 Refine permission checks and gates 2026-04-08 16:08:14 +02:00
adb 415e0c2b5f Merge branch 'main' into adb/re-enable-full-screen-intent-perm 2026-04-03 17:55:46 +02:00
adbenitez 9a22597473 delete zapstore 2026-04-03 17:18:08 +02:00
B. Petersen beb45af440 prefer 'mark noticed' over 'mark fresh' 2026-04-02 15:50:40 +02:00
adbenitez 87a21eb0f2 apply spotless 2026-04-02 15:41:41 +02:00
adbenitez dea51cd356 use RPC API instead of C API 2026-04-02 15:41:41 +02:00
B. Petersen ed540e5584 update CHANGLOG 2026-04-02 15:41:38 +02:00
B. Petersen 9f80c9f35f make spotless formatter happy 2026-04-02 15:40:32 +02:00
B. Petersen 5dec1b24cd option to mark chat fresh 2026-04-02 15:40:32 +02:00
adb acb4eb2ae1 Merge pull request #4334 from deltachat/adb/issue-4316
remove proxy switch from EditRelayActivity
2026-04-01 00:14:06 +02:00
wchen342 20c0354938 Merge pull request #4336 from deltachat/wch423/call-multidevice-incoming
Fix wrong states when an incoming call is answered on a second device
2026-03-31 17:36:30 -04:00
adbenitez eac112d602 update changelog 2026-03-31 23:09:29 +02:00
wch423 9b4f659f67 Fix crash when an incoming call is answered on a second device 2026-03-31 17:17:11 +02:00
adbenitez 0166d4e656 remove "Debug Calls" 2026-03-31 16:20:16 +02:00
adbenitez 92b3761d2d re-enable full intent permission 2026-03-31 15:59:22 +02:00
adbenitez 484cee21c6 apply Spotless 2026-03-31 15:24:03 +02:00
adbenitez 0e40318050 rmeove proxy switch from EditRelayActivity 2026-03-31 15:15:02 +02:00
wchen342 b2cc76ff2e Merge pull request #4333 from deltachat/wch423/call-mirror-self
Mirror self video during call
2026-03-30 13:24:01 -04:00
adb 96acaaf000 Merge pull request #4332 from deltachat/adb/remove-deprecated-stock-str-2026-03-30
remove deprecated stock strings 99 and 100
2026-03-30 18:44:31 +02:00
adbenitez 400e5ea671 Merge branch 'adb/remove-deprecated-stock-str-2026-03-30' of https://github.com/deltachat/deltachat-android into adb/remove-deprecated-stock-str-2026-03-30 2026-03-30 18:41:03 +02:00
adbenitez 4fac460926 remove unused strings 2026-03-30 18:40:44 +02:00
wch423 94a5631566 Fix camera iterating through multiple back cameras; Formatting code 2026-03-30 18:00:39 +02:00
wch423 ea91075107 Mirror self video during calls; Minor thread-safe fixes 2026-03-30 17:29:43 +02:00
adbenitez 0c7b82b9e4 Merge remote-tracking branch 'upstream/main' 2026-03-30 16:27:17 +02:00
adb d765d3ddeb Merge branch 'main' into adb/remove-deprecated-stock-str-2026-03-30 2026-03-30 16:01:06 +02:00
adbenitez 094fb1e2a4 remove deprecated stock strings 99 and 100 2026-03-30 15:47:35 +02:00
wchen342 f7244c2152 Merge pull request #4323 from deltachat/wch423/fix-file-sharing
Fix file sharing to certain apps
2026-03-30 09:38:02 -04:00
wch423 2b5d1005e3 Fix missing query method in AttachmentsContentProvider 2026-03-30 15:32:50 +02:00
adb b1eadf0716 Merge pull request #4331 from deltachat/prep-2.48.0
prepare 2.48.0
2026-03-30 15:27:07 +02:00
adbenitez c001c13053 prepare 2.48.0 2026-03-30 15:15:25 +02:00
adb ea4ec343bc Merge pull request #4330 from deltachat/update-core-and-stuff-2026-03-30
update core to 2.48.0
2026-03-30 15:10:46 +02:00
adb 5642e86f6a Merge branch 'main' into update-core-and-stuff-2026-03-30 2026-03-30 14:56:05 +02:00
adbenitez 46db14fc3e update changelog 2026-03-30 14:54:53 +02:00
adbenitez 0f6d9670ff update RPC 2026-03-30 14:45:47 +02:00
adbenitez 4c3c24ae5a update translations 2026-03-30 14:45:16 +02:00
B. Petersen eeb558d94d sticker alerts: do not repeat text in title and message
moreover, the "Delete" button is shown as being destructive.
this removes noise and is more consistent with other confirmations.
2026-03-30 14:33:50 +02:00
adbenitez 5e08f56dd3 update deltachat-core-rust to 'chore(release): prepare for 2.48.0' of 'v2.48.0' 2026-03-30 14:11:27 +02:00
biörn d99f150dd2 relay update warning (#4325)
* add a warning to the existing hint; desktop and iOS will pick that up without code changes

* add hint to the relay list

* add padding

* move relay up

* update CHANGELOG

* make spotless happy
2026-03-28 19:36:28 +01:00
adbenitez 82b3100570 Merge remote-tracking branch 'upstream/main' 2026-03-26 19:37:59 +01:00
B. Petersen 348b6fd3c1 update store descriptions 2026-03-26 15:38:57 +01:00
adb c2f492463f Merge pull request #4321 from deltachat/prep-2.47.0
prepare 2.47.0
2026-03-24 17:35:18 +01:00
adbenitez 38239b2644 prepare 2.47.0 2026-03-24 16:37:41 +01:00
adb 4b996c95de Merge pull request #4320 from deltachat/update-core-and-stuff-2026-03-24
Update core 2.47.0
2026-03-24 16:35:18 +01:00
adbenitez 89d90efcef update changelog 2026-03-24 16:05:36 +01:00
adb d5b4bae502 Merge pull request #4319 from deltachat/adb/issue-4318
only pass URL domain to IDN.toASCII()
2026-03-24 15:53:49 +01:00
adbenitez c3ec163e1a update translations 2026-03-24 15:51:25 +01:00
adbenitez a927a32909 update deltachat-core-rust to 'chore(release): prepare for 2.47.0' of 'v2.47.0' 2026-03-24 15:49:02 +01:00
adbenitez 9aab4517ef only pass URL domain to IDN.toASCII()
Avoid crashes due to IllegalArgumentException launched by
IDN.toASCII() if URL is too long
2026-03-23 22:58:01 +01:00
adb 727e68edc7 Merge pull request #4317 from deltachat/adb/disable-full-screen-intent-for-gplay
temporarily don't require android.permission.USE_FULL_SCREEN_INTENT for gplay
2026-03-23 12:46:20 +01:00
adbenitez 9047de85c2 update version 2026-03-23 12:35:42 +01:00
adbenitez 29c313ba58 Merge remote-tracking branch 'upstream/adb/disable-full-screen-intent-for-gplay' 2026-03-23 12:35:00 +01:00
adbenitez 487f601c09 temporarily don't require android.permission.USE_FULL_SCREEN_INTENT for gplay 2026-03-23 12:15:47 +01:00
107 changed files with 3587 additions and 1097 deletions
-11
View File
@@ -72,14 +72,3 @@ jobs:
files: |
build/outputs/apk/foss/release/*.apk
build/outputs/mapping/fossRelease/mapping-*.txt
- name: Release on ZapStore
run: |
export CHECKSUM=6e2c7cf6da53c3f1a78b523a6aacd6316dce3d74ace6f859c2676729ee439990
curl -sL https://cdn.zapstore.dev/$CHECKSUM -o zapstore
if echo "$CHECKSUM zapstore" | sha256sum -c --status; then
chmod +x zapstore
SIGN_WITH=${{ secrets.NOSTR_KEY }} ./zapstore publish --indexer-mode
else
echo "ERROR: checksum doesn't match!"
fi
+11
View File
@@ -243,3 +243,14 @@ $ANDROID_NDK_ROOT/ndk-stack --sym obj/local/armeabi-v7a --dump crash.txt > decod
`obj/local/armeabi-v7a` is the extracted path from `deltachat-gplay-release-X.X.X.apk-symbols.zip` file from https://download.delta.chat/android/symbols/
Replace `armeabi-v7a` by the correct architecture the logs come from (can be guessed by trial and error)
### Deobfuscating Java/Kotlin Stack Traces
Because the app uses code minification (ProGuard/R8), Java stack traces in crash reports are obfuscated.
To decode them, use `retrace` with the `mapping.txt` file that is included in the symbols zip:
```
retrace mapping.txt crash.txt > decoded-crash.txt
```
`mapping.txt` is extracted from the same `deltachat-gplay-release-X.X.X.apk-symbols.zip` file available at https://download.delta.chat/android/symbols/
+30 -2
View File
@@ -1,6 +1,34 @@
# Delta Chat Android Changelog
## v2.46.0
## Unreleased
* Better incoming call system integration
* Calls are not experimental anymore and don't need to be manually enabled
* Display a permanent notification when doing location streaming and get rid of dangerous "Access Location in Background" permission
* Allow mini-apps to play audio without user interaction
* Mark chats as unread (long tap a chat and select the corresponding option from the three-dot-menu)
## v2.49.0
2026-04
* Fix file sharing to certain apps (e.g. Material Files, etc.)
* Fix problem with calls when microphone permission is not granted
* Fix taking pictures and videos in devices with SD cards
* Fix flipped orientation for some images
* Fix: avoid empty contact request chats when using invite links in a multi-device setup
* Remove proxy toggle from profile editing to avoid confusion
* Updated translations
* Update to core 2.49.0
## v2.48.0
2026-03
* Add a warning when editing relays
* Fix message reordering problems in multi-relay setups
* Some more bug fixes and updated translations
* Update to core 2.48.0
## v2.47.0
2026-03
* Allow to set chat description
@@ -19,7 +47,7 @@
* Fix: avoid "reply privately" not quoting the selected message sometimes
* Fix: properly hide the calls button
* Some more bug fixes and updated translations
* Update to core 2.46.0
* Update to core 2.47.0
## v2.43.0
2026-02
+4 -3
View File
@@ -34,8 +34,8 @@ android {
useLibrary 'org.apache.http.legacy'
defaultConfig {
versionCode 30000738
versionName "2.46.0"
versionCode 30000742
versionName "2.49.0"
applicationId "chat.delta.lite"
multiDexEnabled true
@@ -223,7 +223,7 @@ spotless {
}
format 'xml', {
target 'src/*/res/**/*.xml', 'src/*/AndroidManifest.xml'
targetExclude 'src/*/res/values*/strings.xml' // line-break changes invalidate translations
targetExclude 'src/*/res/values*/strings*.xml' // line-break changes invalidate translations
eclipseWtp('xml').configFile('spotless/eclipse-wtp-xml.prefs')
trimTrailingWhitespace()
endWithNewline()
@@ -307,6 +307,7 @@ dependencies {
exclude group: 'com.google.firebase', module: 'firebase-analytics'
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
}
gplayImplementation 'com.google.android.gms:play-services-location:21.3.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.assertj:assertj-core:3.27.3'
@@ -1,6 +1,8 @@
ArcaneChat is a decentralized and secure instant messenger that is easy to use for friends and family.
Anonymous. Instant onboarding without a phone number, e-mail or other private data.
Private. Instant on-boarding without a phone number or other private data.
• Friends & Family: Only chat with people you know. Unknown users cannot message or follow you without mutual consent.
• Flexible. Supports multiple chat profiles and is easy to setup on multiple devices.
@@ -23,7 +25,7 @@ ArcaneChat is a Delta Chat client and was created with a focus on usability, goo
<li>Multiple color themes/skins</li>
<li>It is possible to disable profiles to completely disconnect them saving data/bandwidth</li>
<li>You can easily see the connection status of all your profiles in the profile switcher</li>
<li>Extra option to share location for 12 hours</li>
<li>Extra option to share location for 24 hours</li>
<li>Clicking on a message with a POI location, will open the POI on the map</li>
<li>Last-seen status of contacts is shown in your contact list, like in WhatsApp, Telegram, etc.</li>
<li>Videos are played in loop, useful for short GIF videos</li>
Generated
+15 -15
View File
@@ -7,11 +7,11 @@
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1756239746,
"narHash": "sha256-0ibN685tT+u/Nbmbrrq9G3mRUzct2Votyv/a7Wwv26s=",
"lastModified": 1775939535,
"narHash": "sha256-Zq1U7Vhw5ctvhJQy+3ShqIv7Mplf3XkgJY+QJnhfUGQ=",
"owner": "tadfisher",
"repo": "android-nixpkgs",
"rev": "256631d162ec883b2341ee59621516e1f65f0f6b",
"rev": "d2e16192309bf3101e4998ffa62e914819dee077",
"type": "github"
},
"original": {
@@ -28,11 +28,11 @@
]
},
"locked": {
"lastModified": 1741473158,
"narHash": "sha256-kWNaq6wQUbUMlPgw8Y+9/9wP0F8SHkjy24/mN3UAppg=",
"lastModified": 1768818222,
"narHash": "sha256-460jc0+CZfyaO8+w8JNtlClB2n4ui1RbHfPTLkpwhU8=",
"owner": "numtide",
"repo": "devshell",
"rev": "7c9e793ebe66bcba8292989a68c0419b737a22a0",
"rev": "255a2b1725a20d060f566e4755dbf571bbbb5f76",
"type": "github"
},
"original": {
@@ -79,11 +79,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1756125398,
"narHash": "sha256-XexyKZpf46cMiO5Vbj+dWSAXOnr285GHsMch8FBoHbc=",
"lastModified": 1775710090,
"narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "3b9f00d7a7bf68acd4c4abb9d43695afb04e03a5",
"rev": "4c1018dae018162ec878d42fec712642d214fdfa",
"type": "github"
},
"original": {
@@ -95,11 +95,11 @@
},
"nixpkgs_2": {
"locked": {
"lastModified": 1756159630,
"narHash": "sha256-ohMvsjtSVdT/bruXf5ClBh8ZYXRmD4krmjKrXhEvwMg=",
"lastModified": 1775823930,
"narHash": "sha256-ALT447J7FcxP/97J01A/gp/hgdO5lXRsm+zLMt+gIjc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "84c256e42600cb0fdf25763b48d28df2f25a0c8b",
"rev": "8c11f88bb9573a10a7d6bf87161ef08455ac70b9",
"type": "github"
},
"original": {
@@ -138,11 +138,11 @@
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1763347184,
"narHash": "sha256-6QH8hpCYJxifvyHEYg+Da0BotUn03BwLIvYo3JAxuqQ=",
"lastModified": 1775877051,
"narHash": "sha256-wpSQm2PD/w4uRo2wb8utk0b5hOBkkg/CZ1xICY+qB7M=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "08895cce80433978d5bfd668efa41c5e24578cbd",
"rev": "08b4f3633471874c8894632ade1b78d75dbda002",
"type": "github"
},
"original": {
+1
View File
@@ -1393,6 +1393,7 @@ JNIEXPORT jint Java_com_b44t_messenger_DcMsg_getId(JNIEnv *env, jobject obj)
return dc_msg_get_id(get_dc_msg(env, obj));
}
JNIEXPORT jstring Java_com_b44t_messenger_DcMsg_getText(JNIEnv *env, jobject obj)
{
char* temp = dc_msg_get_text(get_dc_msg(env, obj));
+1
View File
@@ -34,6 +34,7 @@ cd ../..
SYMBOLS_ZIP="$APK-symbols.zip"
rm $SYMBOLS_ZIP
zip -r $SYMBOLS_ZIP obj
zip $SYMBOLS_ZIP build/outputs/mapping/gplayRelease/mapping.txt
ls -l $SYMBOLS_ZIP
rsync --progress $SYMBOLS_ZIP jekyll@download.delta.chat:/var/www/html/download/android/symbols/
@@ -0,0 +1,17 @@
package org.thoughtcrime.securesms.geolocation;
import android.content.Context;
import android.util.Log;
/** Non-GMS, always uses the platform LocationManager. */
public final class LocationSourceFactory {
private static final String TAG = LocationSourceFactory.class.getSimpleName();
private LocationSourceFactory() {}
public static LocationSource create(Context context) {
Log.i(TAG, "Non-GMS build, Using platform LocationManager");
return new PlatformLocationSource();
}
}
@@ -0,0 +1,61 @@
package org.thoughtcrime.securesms.geolocation;
import android.content.Context;
import android.location.Location;
import android.os.Looper;
import android.util.Log;
import androidx.annotation.NonNull;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.Priority;
public class GmsLocationSource implements LocationSource {
private static final String TAG = GmsLocationSource.class.getSimpleName();
private static final long UPDATE_INTERVAL_MS = 3_000;
private static final long FASTEST_INTERVAL_MS = 1_000;
private FusedLocationProviderClient client;
private LocationCallback locationCallback;
@Override
public void startUpdates(@NonNull Context context, @NonNull Callback callback) {
client = LocationServices.getFusedLocationProviderClient(context);
LocationRequest request =
new LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, UPDATE_INTERVAL_MS)
.setMinUpdateIntervalMillis(FASTEST_INTERVAL_MS)
.setMinUpdateDistanceMeters(0)
.setWaitForAccurateLocation(false)
.build();
locationCallback =
new LocationCallback() {
@Override
public void onLocationResult(@NonNull LocationResult result) {
Location loc = result.getLastLocation();
if (loc != null) {
callback.onLocationUpdate(loc);
}
}
};
try {
client.requestLocationUpdates(request, locationCallback, Looper.getMainLooper());
} catch (SecurityException e) {
Log.e(TAG, "Missing location permission", e);
}
}
@Override
public void stopUpdates() {
if (client != null && locationCallback != null) {
client.removeLocationUpdates(locationCallback);
client = null;
locationCallback = null;
}
}
}
@@ -0,0 +1,35 @@
package org.thoughtcrime.securesms.geolocation;
import android.content.Context;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
/**
* Prefers FusedLocationProviderClient, falls back to platform LocationManager if Play Services are
* somehow unavailable.
*/
public final class LocationSourceFactory {
private static final String TAG = LocationSourceFactory.class.getSimpleName();
private LocationSourceFactory() {}
public static LocationSource create(Context context) {
if (isGmsAvailable(context)) {
Log.i(TAG, "Using FusedLocationProviderClient");
return new GmsLocationSource();
}
Log.i(TAG, "GMS unavailable, falling back to LocationManager");
return new PlatformLocationSource();
}
private static boolean isGmsAvailable(Context context) {
try {
return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context)
== ConnectionResult.SUCCESS;
} catch (Exception e) {
return false;
}
}
}
+7 -6
View File
@@ -37,7 +37,6 @@
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" />
@@ -50,6 +49,7 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<!-- normal/instant permissions - adding them here is enough -->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
@@ -71,6 +71,7 @@
android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<!-- force compiling libs on older sdk than supported; runtime checks are required -->
@@ -81,7 +82,6 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
tools:replace="android:allowBackup"
android:allowBackup="false"
android:theme="@style/TextSecure.LightTheme"
android:largeHeap="true"
@@ -464,10 +464,6 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
</activity>
<service
android:name=".geolocation.LocationBackgroundService"
android:foregroundServiceType="location" />
<service
android:name=".service.GenericForegroundService"
android:foregroundServiceType="dataSync" />
@@ -476,6 +472,11 @@
android:name=".service.FetchForegroundService"
android:foregroundServiceType="dataSync" />
<service
android:name=".geolocation.LocationStreamingService"
android:foregroundServiceType="location"
android:exported="false" />
<service
android:name=".service.AudioPlaybackService"
android:foregroundServiceType="mediaPlayback"
+12 -8
View File
@@ -1043,22 +1043,26 @@ Relays are operated by different groups and people.</p>
<p>By default, after installation, a relay is <strong>automatically set up</strong>,
so you do not need to care about that.
However, if you want to,
you can configure relays at At <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at <strong>Settings → Advanced → Relays</strong>:</p>
<ul>
<li>
<p>You can <strong>add</strong> a relay by scanning its QR code;
<a href="https://chatmail.at/relays">https://chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.</p>
<a href="https://chatmail.at/relays">chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.
Contacts learn your current relays automatically when you message them.</p>
</li>
<li>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</p>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
</li>
<li>
<p>If you <strong>remove</strong> a relay,
make sure another default relay was used for a sufficient amount of time.
Otherwise, messages from your chat partners wont reach you.
If in doubt, remove later.</p>
contacts who only know this relay may not reach you until you message them again.
To stay reachable in the meantime, choose <strong>Hide from Contacts</strong> in the confirmation dialog
instead of removing it right away.</p>
</li>
<li>
<p>To <strong>show</strong> a hidden relay again, tap on it.</p>
</li>
</ul>
@@ -1574,7 +1578,7 @@ See <a href="https://delta.chat/en/2023-05-22-webxdc-security">here for the full
<li>
<p>2023 March, <a href="https://cure53.de">Cure53</a> analyzed both the transport encryption of
Delta Chats network connections and a reproducible mail server setup as
<a href="https://delta.chat/cs/serverguide">recommended on this site</a>.
<a href="https://delta.chat/serverguide">recommended on this site</a>.
You can read more about the audit <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">on our blog</a>
or read the <a href="https://delta.chat/assets/blog/MER-01-report.pdf">full report here</a>.</p>
</li>
+7 -9
View File
@@ -970,18 +970,16 @@ kannst du jedoch unter <strong>Einstellungen → Erweitert → Relays</strong>
<ul>
<li>
<p>Du kannst ein Relay <strong>hinzufügen</strong>, indem du einen QR-Code scannst,
z.B. von <a href="https://chatmail.at/relays">https://chatmail.at/relays</a>.
Bei mehreren Relays, empfängst du die Nachrichten von allen Relays.</p>
<p>Du kannst ein Relay <strong>hinzufügen</strong>, indem du einen QR-Code scannst, z.B. von <a href="https://chatmail.at/relays">chatmail.at/relays</a>. Bei mehreren Relays, empfängst du die Nachrichten von allen Relays. Deine Kontakte lernen deine Relays automatisch, sobald du ihnen schreibst.</p>
</li>
<li>
<p><strong>Standard</strong> legt das Relay fest, an das deine Chatpartner zukünftig Nachrichten senden.</p>
<p>Tippe ein Relay an, um es <strong>Zum Senden zu verwenden</strong></p>
</li>
<li>
<p>Wenn du ein Relay <strong>entfernst</strong>,
stelle sicher, dass ein anderes Standard-Relay ausreichend lange verwendet wurde.
Andernfalls erreichen dich keine Nachrichten von deinen Kontakten.
Im Zweifelsfall entferne das Relay später.</p>
<p>Wenn du ein Relay <strong>entfernst</strong>, können Kontakte, die nur dieses Relay kennen, dich nicht erreichen, bis du ihnen wieder schreibst. Um erreichbar zu bleiben, wähle <strong>Vor Kontakten verstecken</strong> im Bestätigungsdialog anstelle das Relay direkt zu löschen.</p>
</li>
<li>
<p>Um ein verstecktes Relay wieder <strong>anzuzeigen</strong> tippe es an.</p>
</li>
</ul>
@@ -1470,7 +1468,7 @@ Weitere Informationen findest du in unserem Blogbeitrag über <a href="https://d
<p>Im April 2023 haben wir Sicherheits- und Datenschutzprobleme mit dem “In Chats geteilten Apps”-Feature behoben, die mit Fehlern beim Sandboxing, insbesondere mit Chromium zusammenhängen. Wir haben daraufhin eine unabhängige Sicherheitsprüfung von Cure53 durchführen lassen, und alle gefundenen Probleme wurden mit den im April 2023 veröffentlichten 1.36 Releases behoben. Siehe <a href="https://delta.chat/en/2023-05-22-webxdc-security">hier für die vollständige Hintergrundgeschichte</a>.</p>
</li>
<li>
<p>Im März 2023 analysierte <a href="https://cure53.de">Cure53</a> sowohl die Transportverschlüsselung von Delta Chats Netzwerkverbindungen als auch das reproduzierbare Mailserver-Setup wie <a href="https://delta.chat/de/serverguide">auf dieser Seite empfohlen</a>. Du kannst mehr über das Audit <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">in unserem Blog</a> lesen oder du liest den <a href="https://delta.chat/assets/blog/MER-01-report.pdf">vollständigen Bericht hier</a>.</p>
<p>Im März 2023 analysierte <a href="https://cure53.de">Cure53</a> sowohl die Transportverschlüsselung von Delta Chats Netzwerkverbindungen als auch das reproduzierbare Mailserver-Setup wie <a href="https://delta.chat/serverguide">auf dieser Seite empfohlen</a>. Du kannst mehr über das Audit <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">in unserem Blog</a> lesen oder du liest den <a href="https://delta.chat/assets/blog/MER-01-report.pdf">vollständigen Bericht hier</a>.</p>
</li>
<li>
<p>Im Jahr 2020 analysierte <a href="https://includesecurity.com">Include Security</a> Delta Chats Rust <a href="https://github.com/deltachat/deltachat-core-rust/">core</a>, <a href="https://github.com/async-email/async-imap">IMAP</a>,<a href="https://github.com/async-email/async-smtp">SMTP</a>, und <a href="https://github.com/async-email/async-native-tls">TLS</a> Bibliotheken.
+12 -8
View File
@@ -1043,22 +1043,26 @@ Relays are operated by different groups and people.</p>
<p>By default, after installation, a relay is <strong>automatically set up</strong>,
so you do not need to care about that.
However, if you want to,
you can configure relays at At <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at <strong>Settings → Advanced → Relays</strong>:</p>
<ul>
<li>
<p>You can <strong>add</strong> a relay by scanning its QR code;
<a href="https://chatmail.at/relays">https://chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.</p>
<a href="https://chatmail.at/relays">chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.
Contacts learn your current relays automatically when you message them.</p>
</li>
<li>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</p>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
</li>
<li>
<p>If you <strong>remove</strong> a relay,
make sure another default relay was used for a sufficient amount of time.
Otherwise, messages from your chat partners wont reach you.
If in doubt, remove later.</p>
contacts who only know this relay may not reach you until you message them again.
To stay reachable in the meantime, choose <strong>Hide from Contacts</strong> in the confirmation dialog
instead of removing it right away.</p>
</li>
<li>
<p>To <strong>show</strong> a hidden relay again, tap on it.</p>
</li>
</ul>
@@ -1574,7 +1578,7 @@ See <a href="https://delta.chat/en/2023-05-22-webxdc-security">here for the full
<li>
<p>2023 March, <a href="https://cure53.de">Cure53</a> analyzed both the transport encryption of
Delta Chats network connections and a reproducible mail server setup as
<a href="https://delta.chat/en/serverguide">recommended on this site</a>.
<a href="https://delta.chat/serverguide">recommended on this site</a>.
You can read more about the audit <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">on our blog</a>
or read the <a href="https://delta.chat/assets/blog/MER-01-report.pdf">full report here</a>.</p>
</li>
+13 -10
View File
@@ -105,8 +105,7 @@
</h2>
<p>Delta Chat is a reliable, decentralized and secure instant messaging app,
available for mobile and desktop platforms.</p>
<p>Delta Chat es una aplicación de mensajería instantánea confiable, descentralizada y segura, disponible para dispositivos móviles y computadoras de escritorio.</p>
<ul>
<li>
@@ -1039,22 +1038,26 @@ Relays are operated by different groups and people.</p>
<p>By default, after installation, a relay is <strong>automatically set up</strong>,
so you do not need to care about that.
However, if you want to,
you can configure relays at At <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at <strong>Settings → Advanced → Relays</strong>:</p>
<ul>
<li>
<p>You can <strong>add</strong> a relay by scanning its QR code;
<a href="https://chatmail.at/relays">https://chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.</p>
<a href="https://chatmail.at/relays">chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.
Contacts learn your current relays automatically when you message them.</p>
</li>
<li>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</p>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
</li>
<li>
<p>If you <strong>remove</strong> a relay,
make sure another default relay was used for a sufficient amount of time.
Otherwise, messages from your chat partners wont reach you.
If in doubt, remove later.</p>
contacts who only know this relay may not reach you until you message them again.
To stay reachable in the meantime, choose <strong>Hide from Contacts</strong> in the confirmation dialog
instead of removing it right away.</p>
</li>
<li>
<p>To <strong>show</strong> a hidden relay again, tap on it.</p>
</li>
</ul>
@@ -1563,7 +1566,7 @@ See <a href="https://delta.chat/en/2023-05-22-webxdc-security">here for the full
<li>
<p>2023 March, <a href="https://cure53.de">Cure53</a> analyzed both the transport encryption of
Delta Chats network connections and a reproducible mail server setup as
<a href="https://delta.chat/es/serverguide">recommended on this site</a>.
<a href="https://delta.chat/serverguide">recommended on this site</a>.
You can read more about the audit <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">on our blog</a>
or read the <a href="https://delta.chat/assets/blog/MER-01-report.pdf">full report here</a>.</p>
</li>
File diff suppressed because it is too large Load Diff
+12 -8
View File
@@ -957,22 +957,26 @@ Relays are operated by different groups and people.</p>
<p>By default, after installation, a relay is <strong>automatically set up</strong>,
so you do not need to care about that.
However, if you want to,
you can configure relays at At <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at <strong>Settings → Advanced → Relays</strong>:</p>
<ul>
<li>
<p>You can <strong>add</strong> a relay by scanning its QR code;
<a href="https://chatmail.at/relays">https://chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.</p>
<a href="https://chatmail.at/relays">chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.
Contacts learn your current relays automatically when you message them.</p>
</li>
<li>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</p>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
</li>
<li>
<p>If you <strong>remove</strong> a relay,
make sure another default relay was used for a sufficient amount of time.
Otherwise, messages from your chat partners wont reach you.
If in doubt, remove later.</p>
contacts who only know this relay may not reach you until you message them again.
To stay reachable in the meantime, choose <strong>Hide from Contacts</strong> in the confirmation dialog
instead of removing it right away.</p>
</li>
<li>
<p>To <strong>show</strong> a hidden relay again, tap on it.</p>
</li>
</ul>
@@ -1479,7 +1483,7 @@ research paper published afterwards.</p>
Vous trouverez <a href="https://delta.chat/en/2023-05-22-webxdc-security">ici un article de fond complet à propos de la sécurité du chiffrement de bout-en-bout sur internet</a>.</p>
</li>
<li>
<p>Début 2023, <a href="https://cure53.de">Cure53</a> a analysé le chiffrement dacheminement des connexions réseau de Delta Chat et testé une configuration de serveur de courriel reproductible, telle que <a href="https://delta.chat/fr/serverguide">recommandée sur ce site</a>.
<p>Début 2023, <a href="https://cure53.de">Cure53</a> a analysé le chiffrement dacheminement des connexions réseau de Delta Chat et testé une configuration de serveur de courriel reproductible, telle que <a href="https://delta.chat/serverguide">recommandée sur ce site</a>.
Vous trouverez plus dinformations sur cet audit <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">sur notre blog</a> ou dans <a href="https://delta.chat/assets/blog/MER-01-report.pdf">le rapport complet ici</a>.</p>
</li>
<li>
+12 -8
View File
@@ -1043,22 +1043,26 @@ Relays are operated by different groups and people.</p>
<p>By default, after installation, a relay is <strong>automatically set up</strong>,
so you do not need to care about that.
However, if you want to,
you can configure relays at At <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at <strong>Settings → Advanced → Relays</strong>:</p>
<ul>
<li>
<p>You can <strong>add</strong> a relay by scanning its QR code;
<a href="https://chatmail.at/relays">https://chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.</p>
<a href="https://chatmail.at/relays">chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.
Contacts learn your current relays automatically when you message them.</p>
</li>
<li>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</p>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
</li>
<li>
<p>If you <strong>remove</strong> a relay,
make sure another default relay was used for a sufficient amount of time.
Otherwise, messages from your chat partners wont reach you.
If in doubt, remove later.</p>
contacts who only know this relay may not reach you until you message them again.
To stay reachable in the meantime, choose <strong>Hide from Contacts</strong> in the confirmation dialog
instead of removing it right away.</p>
</li>
<li>
<p>To <strong>show</strong> a hidden relay again, tap on it.</p>
</li>
</ul>
@@ -1574,7 +1578,7 @@ See <a href="https://delta.chat/en/2023-05-22-webxdc-security">here for the full
<li>
<p>2023 March, <a href="https://cure53.de">Cure53</a> analyzed both the transport encryption of
Delta Chats network connections and a reproducible mail server setup as
<a href="https://delta.chat/id/serverguide">recommended on this site</a>.
<a href="https://delta.chat/serverguide">recommended on this site</a>.
You can read more about the audit <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">on our blog</a>
or read the <a href="https://delta.chat/assets/blog/MER-01-report.pdf">full report here</a>.</p>
</li>
+20 -14
View File
@@ -13,7 +13,7 @@
<li><a href="#cosa-significa-il-punto-verde">Cosa significa il punto verde?</a></li>
<li><a href="#cosa-significano-i-segni-di-spunta-visualizzati-accanto-ai-messaggi-in-uscita">Cosa significano i segni di spunta visualizzati accanto ai messaggi in uscita?</a></li>
<li><a href="#edit">Correggi gli errori e cancella i messaggi dopo averli inviati</a></li>
<li><a href="#mediaquality">How is media quality handled?</a></li>
<li><a href="#mediaquality">Come viene gestita la qualità dei media?</a></li>
<li><a href="#ephemeralmsgs">Come funzionano i messaggi a scomparsa?</a></li>
<li><a href="#delold">Cosa succede se attivo “Elimina Messaggi dal Dispositivo”?</a></li>
<li><a href="#remove-account">Come posso eliminare il mio profilo chat?</a></li>
@@ -399,7 +399,7 @@ che avrebbero già potuto rispondere, inoltrare, salvare, scattare una schermata
<h3 id="mediaquality">
How is media quality handled? <a href="#mediaquality" class="anchor"></a>
Come viene gestita la qualità dei media? <a href="#mediaquality" class="anchor"></a>
</h3>
@@ -409,16 +409,16 @@ o <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" a
<ul>
<li>
<p>By default, compression ensures <strong>fast, efficient delivery</strong> that respects everyones data limits and storage.
This is ideal for everyday communication.</p>
<p>Per impostazione predefinita, la compressione garantisce una <strong>consegna rapida ed efficiente</strong> che rispetta i limiti di dati e di spazio di archiviazione di tutti.
Questa soluzione è ideale per la comunicazione quotidiana.</p>
</li>
<li>
<p>In regions with worse connectivity,
you can choose higher compression at <strong>Settings → ChatsOutgoing Media Quality</strong>.</p>
<p>Nelle regioni con connettività peggiore,
è possibile scegliere una compressione più elevata in <strong>Impostazioni → Chat → Qualità Media in Uscita</strong>.</p>
</li>
<li>
<p>If you specifically need to send media in its <strong>original quality</strong>, use <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attach → File</strong> in the chat.
Please use this method sparingly, as sending original files will significantly increase data usage for you and all recipients in the chat.</p>
<p>Se hai bisogno di inviare i file multimediali nella loro <strong>qualità originale</strong>, usa <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Allega → File</strong> nella chat.
Si prega di utilizzare questo metodo con parsimonia, poiché linvio di file originali aumenterà significativamente il consumo di dati per te e per tutti i destinatari della chat.</p>
</li>
</ul>
@@ -1034,16 +1034,22 @@ Tuttavia, se lo si desidera,
<ul>
<li>
<p>Puoi <strong>aggiungere</strong> un ripetitore scansionando il suo codice QR;<a href="https://chatmail.at/relays">https://chatmail.at/relays</a> ne mostra alcuni noti.
Se hai più ripetitori, riceverai messaggi su tutti.</p>
<p>Puoi <strong>aggiungere</strong> un ripetitore scansionando il suo codice QR;
<a href="https://chatmail.at/relays">chatmail.at/relays</a> mostra alcuni ripetitori noti.
Se hai più ripetitori, riceverai i messaggi su tutti.
I contatti vengono a conoscenza automaticamente dei tuoi ripetitori attuali quando invii loro un messaggio.</p>
</li>
<li>
<p>Lopzione <strong>predefinita</strong> definisce quella a cui i tuoi partner di chat invieranno messaggi futuri.</p>
<p>Tocca un ripetitore per impostarlo come <strong>utilizzato per linvio</strong>.</p>
</li>
<li>
<p>Se <strong>rimuovi</strong> un ripetitore,
assicurati che un altro ripetitore predefinito sia stato utilizzato per un periodo di tempo sufficiente.
In caso contrario, i messaggi dei tuoi interlocutori di chat non ti arriveranno.In caso di dubbio, rimuovilo in seguito.</p>
i contatti che conoscono solo quel ripetitore potrebbero non raggiungerti finché non invierai loro un nuovo messaggio.
Per rimanere raggiungibile nel frattempo, seleziona <strong>Nascondi dai Contatti</strong> nella finestra di dialogo di conferma
invece di rimuoverlo immediatamente.</p>
</li>
<li>
<p>Per <strong>visualizzare</strong> nuovamente un ripetitore nascosto, toccalo.</p>
</li>
</ul>
@@ -1560,7 +1566,7 @@ Vedi <a href="https://delta.chat/en/2023-05-22-webxdc-security">qui per la stori
<li>
<p>A partire dal 2023, <a href="https://cure53.de">Cure53</a> ha analizzato sia la crittografia del trasporto delle
Connessioni di rete di Delta Chat e una configurazione del server di posta riproducibile come
<a href="https://delta.chat/it/serverguide">consigliato su questo sito</a>.
<a href="https://delta.chat/serverguide">consigliato su questo sito</a>.
Puoi leggere ulteriori informazioni sullaudit <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">sul nostro blog</a>
o leggere il <a href="https://delta.chat/assets/blog/MER-01-report.pdf">rapporto completo qui</a>.</p>
</li>
+12 -8
View File
@@ -1037,22 +1037,26 @@ Relays are operated by different groups and people.</p>
<p>By default, after installation, a relay is <strong>automatically set up</strong>,
so you do not need to care about that.
However, if you want to,
you can configure relays at At <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at <strong>Settings → Advanced → Relays</strong>:</p>
<ul>
<li>
<p>You can <strong>add</strong> a relay by scanning its QR code;
<a href="https://chatmail.at/relays">https://chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.</p>
<a href="https://chatmail.at/relays">chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.
Contacts learn your current relays automatically when you message them.</p>
</li>
<li>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</p>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
</li>
<li>
<p>If you <strong>remove</strong> a relay,
make sure another default relay was used for a sufficient amount of time.
Otherwise, messages from your chat partners wont reach you.
If in doubt, remove later.</p>
contacts who only know this relay may not reach you until you message them again.
To stay reachable in the meantime, choose <strong>Hide from Contacts</strong> in the confirmation dialog
instead of removing it right away.</p>
</li>
<li>
<p>To <strong>show</strong> a hidden relay again, tap on it.</p>
</li>
</ul>
@@ -1568,7 +1572,7 @@ Lees <a href="https://delta.chat/en/2023-05-22-webxdc-security">hier het volledi
<li>
<p>Aan het begin van 2023 heeft <a href="https://cure53.de">Cure53</a> de transportversleuteling van
Delta Chats netwerkverbindingen getest, evenals de e-mailserveropzet zoals
<a href="https://delta.chat/nl/serverguide">beschreven op onze site</a>.
<a href="https://delta.chat/serverguide">beschreven op onze site</a>.
Meer informatie over deze test is te lezen <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">op ons blog</a>
of in het <a href="https://delta.chat/assets/blog/MER-01-report.pdf">volledige verslag</a>.</p>
</li>
+12 -8
View File
@@ -959,22 +959,26 @@ Relays are operated by different groups and people.</p>
<p>By default, after installation, a relay is <strong>automatically set up</strong>,
so you do not need to care about that.
However, if you want to,
you can configure relays at At <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at <strong>Settings → Advanced → Relays</strong>:</p>
<ul>
<li>
<p>You can <strong>add</strong> a relay by scanning its QR code;
<a href="https://chatmail.at/relays">https://chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.</p>
<a href="https://chatmail.at/relays">chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.
Contacts learn your current relays automatically when you message them.</p>
</li>
<li>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</p>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
</li>
<li>
<p>If you <strong>remove</strong> a relay,
make sure another default relay was used for a sufficient amount of time.
Otherwise, messages from your chat partners wont reach you.
If in doubt, remove later.</p>
contacts who only know this relay may not reach you until you message them again.
To stay reachable in the meantime, choose <strong>Hide from Contacts</strong> in the confirmation dialog
instead of removing it right away.</p>
</li>
<li>
<p>To <strong>show</strong> a hidden relay again, tap on it.</p>
</li>
</ul>
@@ -1409,7 +1413,7 @@ od najnowszych do najstarszych:</p>
<p>W kwietniu 2023 r. naprawiliśmy problemy z bezpieczeństwem i prywatnością w funkcji „aplikacje internetowe udostępniane na czacie”, związane z awariami piaskownicy, szczególnie w przypadku Chromium. Następnie przeprowadziliśmy niezależny audyt bezpieczeństwa od Cure53 i wszystkie wykryte problemy zostały naprawione w aplikacji z serii 1.36 wydanej w kwietniu 2023 r. <a href="https://delta.chat/en/2023-05-22-webxdc-security">Pełną historię bezpieczeństwa end-to-end w sieci można znaleźć tutaj</a>.</p>
</li>
<li>
<p>W marcu 2023 r. firma <a href="https://cure53.de">Cure53</a> przeanalizowała zarówno szyfrowanie transportu połączeń sieciowych Delta Chat, jak i powtarzalną konfigurację serwera pocztowego zgodnie z <a href="https://delta.chat/pl/serverguide">zaleceniami na tej stronie</a>. Możesz przeczytać więcej o audycie <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">na naszym blogu</a> lub przeczytać pełny raport <a href="https://delta.chat/assets/blog/MER-01-report.pdf">tutaj</a>.</p>
<p>W marcu 2023 r. firma <a href="https://cure53.de">Cure53</a> przeanalizowała zarówno szyfrowanie transportu połączeń sieciowych Delta Chat, jak i powtarzalną konfigurację serwera pocztowego zgodnie z <a href="https://delta.chat/serverguide">zaleceniami na tej stronie</a>. Możesz przeczytać więcej o audycie <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">na naszym blogu</a> lub przeczytać pełny raport <a href="https://delta.chat/assets/blog/MER-01-report.pdf">tutaj</a>.</p>
</li>
<li>
<p>W 2020 r. firma <a href="https://includesecurity.com">Include Security</a> przeanalizowała biblioteki Rust <a href="https://github.com/deltachat/deltachat-core-rust/">core</a>, <a href="https://github.com/async-email/async-imap">IMAP</a>, <a href="https://github.com/async-email/async-smtp">SMTP</a> i <a href="https://github.com/async-email/async-native-tls">TLS</a> Delta Chat. Nie znalazła żadnych problemów krytycznych ani poważnych. W raporcie zwrócono uwagę na kilka słabych punktów o średniej wadze same w sobie nie stanowią zagrożenia dla użytkowników Delta Chat, ponieważ zależą od środowiska, w którym używany jest Delta Chat. Ze względu na użyteczność i kompatybilność nie możemy złagodzić wszystkich z nich i zdecydowaliśmy się przedstawić zalecenia dotyczące bezpieczeństwa zagrożonym użytkownikom. Pełny raport można przeczytać <a href="https://delta.chat/assets/blog/2020-second-security-review.pdf">tutaj</a>.</p>
+12 -8
View File
@@ -1038,22 +1038,26 @@ Relays are operated by different groups and people.</p>
<p>By default, after installation, a relay is <strong>automatically set up</strong>,
so you do not need to care about that.
However, if you want to,
you can configure relays at At <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at <strong>Settings → Advanced → Relays</strong>:</p>
<ul>
<li>
<p>You can <strong>add</strong> a relay by scanning its QR code;
<a href="https://chatmail.at/relays">https://chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.</p>
<a href="https://chatmail.at/relays">chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.
Contacts learn your current relays automatically when you message them.</p>
</li>
<li>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</p>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
</li>
<li>
<p>If you <strong>remove</strong> a relay,
make sure another default relay was used for a sufficient amount of time.
Otherwise, messages from your chat partners wont reach you.
If in doubt, remove later.</p>
contacts who only know this relay may not reach you until you message them again.
To stay reachable in the meantime, choose <strong>Hide from Contacts</strong> in the confirmation dialog
instead of removing it right away.</p>
</li>
<li>
<p>To <strong>show</strong> a hidden relay again, tap on it.</p>
</li>
</ul>
@@ -1569,7 +1573,7 @@ See <a href="https://delta.chat/en/2023-05-22-webxdc-security">here for the full
<li>
<p>2023 March, <a href="https://cure53.de">Cure53</a> analyzed both the transport encryption of
Delta Chats network connections and a reproducible mail server setup as
<a href="https://delta.chat/pt/serverguide">recommended on this site</a>.
<a href="https://delta.chat/serverguide">recommended on this site</a>.
You can read more about the audit <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">on our blog</a>
or read the <a href="https://delta.chat/assets/blog/MER-01-report.pdf">full report here</a>.</p>
</li>
+16 -13
View File
@@ -1037,22 +1037,26 @@ PIN-код разблокировки экрана, графический кл
<p>По умолчанию, после установки, релей <strong>настраивается автоматически</strong>,
поэтому вам не нужно об этом волноваться.
Однако, если хотите,
вы можете настроить релеи в <strong>Настройки → Дополнительно → Релеи</strong>:</p>
вы можете настроить релеи в меню <strong>Настройки → Дополнительно → Релеи</strong>:</p>
<ul>
<li>
<p>Вы можете <strong>добавить</strong> релей, отсканировав его QR-код;
<a href="https://chatmail.at/relays">https://chatmail.at/relays</a> содержит список известных релеев.
Если у вас несколько релеев, вы будете получать сообщения на все из них.</p>
на сайте <a href="https://chatmail.at/relays">chatmail.at/relays</a> представлен список известных.
Если у вас настроено несколько релеев, сообщения будут доставляться через все из них.
Ваши контакты автоматически узнают о текущих используемых вами релеях при обмене сообщениями.</p>
</li>
<li>
<p><strong>По умолчанию</strong> определяет релей, на который ваши собеседники будут отправлять будущие сообщения.</p>
<p>Нажмите на релей, чтобы установить его как <strong>используемый для отправки</strong>.</p>
</li>
<li>
<p>Если вы <strong>удаляете</strong> релей,
убедитесь, что другой релей по умолчанию использовался в течение достаточного количества времени.
В противном случае сообщения от ваших собеседников не будут доходить до вас.
Если сомневаетесь, удалите его позже.</p>
<p>Если вы <strong>удалите</strong> релей,
контакты, которые знают только этот релей, не смогут связаться с вами до тех пор, пока вы снова не напишите им.
Чтобы оставаться на связи в это время, выберите опцию <strong>Скрыть из контактов</strong> во всплывающем окне
вместо того, чтобы удалять его сразу.</p>
</li>
<li>
<p>Чтобы снова <strong>показать</strong> скрытый релей, нажмите на него.</p>
</li>
</ul>
@@ -1567,11 +1571,10 @@ Applied Cryptography в ETH Цюрихе и устранили все выявл
См. здесь <a href="https://delta.chat/en/2023-05-22-webxdc-security">полную информацию о безопасности сквозного шифрования в Интернете</a>.</p>
</li>
<li>
<p>В Марте 2023 года, <a href="https://cure53.de">Cure53</a> проанализировал как протокол защиты транспортного уровня
сетевого соединения Delta Chat, так и воспроизводимую установку почтового сервера,
<a href="https://delta.chat/ru/serverguide">рекомендуемую на этом сайте</a>.
Подробнее об аудите можно узнать <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">в нашем блоге</a>
или прочитать <a href="https://delta.chat/assets/blog/MER-01-report.pdf">полный отчёт здесь</a>.</p>
<p>В марте 2023 года компания <a href="https://cure53.de">Cure53</a> провела анализ шифрования транспортного уровня сетевых соединений Delta Chat, а также проверку воспроизводимой конфигурации почтового сервера,
<a href="https://delta.chat/serverguide">рекомендованной на этом сайте</a>.
Подробнее об аудите можно узнать <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">в нашем блоге</a>,
а <a href="https://delta.chat/assets/blog/MER-01-report.pdf">полный отчет доступен по этой ссылке</a>.</p>
</li>
<li>
<p>2020 год, <a href="https://includesecurity.com">Include Security</a> проанализировала Delta
+12 -8
View File
@@ -1043,22 +1043,26 @@ Relays are operated by different groups and people.</p>
<p>By default, after installation, a relay is <strong>automatically set up</strong>,
so you do not need to care about that.
However, if you want to,
you can configure relays at At <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at <strong>Settings → Advanced → Relays</strong>:</p>
<ul>
<li>
<p>You can <strong>add</strong> a relay by scanning its QR code;
<a href="https://chatmail.at/relays">https://chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.</p>
<a href="https://chatmail.at/relays">chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.
Contacts learn your current relays automatically when you message them.</p>
</li>
<li>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</p>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
</li>
<li>
<p>If you <strong>remove</strong> a relay,
make sure another default relay was used for a sufficient amount of time.
Otherwise, messages from your chat partners wont reach you.
If in doubt, remove later.</p>
contacts who only know this relay may not reach you until you message them again.
To stay reachable in the meantime, choose <strong>Hide from Contacts</strong> in the confirmation dialog
instead of removing it right away.</p>
</li>
<li>
<p>To <strong>show</strong> a hidden relay again, tap on it.</p>
</li>
</ul>
@@ -1574,7 +1578,7 @@ See <a href="https://delta.chat/en/2023-05-22-webxdc-security">here for the full
<li>
<p>2023 March, <a href="https://cure53.de">Cure53</a> analyzed both the transport encryption of
Delta Chats network connections and a reproducible mail server setup as
<a href="https://delta.chat/sk/serverguide">recommended on this site</a>.
<a href="https://delta.chat/serverguide">recommended on this site</a>.
You can read more about the audit <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">on our blog</a>
or read the <a href="https://delta.chat/assets/blog/MER-01-report.pdf">full report here</a>.</p>
</li>
+12 -8
View File
@@ -1045,22 +1045,26 @@ Relays are operated by different groups and people.</p>
<p>By default, after installation, a relay is <strong>automatically set up</strong>,
so you do not need to care about that.
However, if you want to,
you can configure relays at At <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at <strong>Settings → Advanced → Relays</strong>:</p>
<ul>
<li>
<p>You can <strong>add</strong> a relay by scanning its QR code;
<a href="https://chatmail.at/relays">https://chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.</p>
<a href="https://chatmail.at/relays">chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.
Contacts learn your current relays automatically when you message them.</p>
</li>
<li>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</p>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
</li>
<li>
<p>If you <strong>remove</strong> a relay,
make sure another default relay was used for a sufficient amount of time.
Otherwise, messages from your chat partners wont reach you.
If in doubt, remove later.</p>
contacts who only know this relay may not reach you until you message them again.
To stay reachable in the meantime, choose <strong>Hide from Contacts</strong> in the confirmation dialog
instead of removing it right away.</p>
</li>
<li>
<p>To <strong>show</strong> a hidden relay again, tap on it.</p>
</li>
</ul>
@@ -1577,7 +1581,7 @@ Shihni <a href="https://delta.chat/en/2023-05-22-webxdc-security">këtu, për sh
<li>
<p>Në fillim të 2023-it, <a href="https://cure53.de">Cure53</a> analizoi qoftë fshehtëzimin
e transporteve për lidhje rrjeti të Delta Chat-it, qoftë një formësim të riprodhueshëm
shërbyesi poste si <a href="https://delta.chat/sq/serverguide">të rekomanduarin në këtë sajt</a>.
shërbyesi poste si <a href="https://delta.chat/serverguide">të rekomanduarin në këtë sajt</a>.
Mund të lexoni më tepër rreth auditimit <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">në blogun tonë</a>,
ose të lexoni <a href="https://delta.chat/assets/blog/MER-01-report.pdf">raportin e plotë këtu</a>.</p>
</li>
+12 -8
View File
@@ -945,22 +945,26 @@ Relays are operated by different groups and people.</p>
<p>By default, after installation, a relay is <strong>automatically set up</strong>,
so you do not need to care about that.
However, if you want to,
you can configure relays at At <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at <strong>Settings → Advanced → Relays</strong>:</p>
<ul>
<li>
<p>You can <strong>add</strong> a relay by scanning its QR code;
<a href="https://chatmail.at/relays">https://chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.</p>
<a href="https://chatmail.at/relays">chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.
Contacts learn your current relays automatically when you message them.</p>
</li>
<li>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</p>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
</li>
<li>
<p>If you <strong>remove</strong> a relay,
make sure another default relay was used for a sufficient amount of time.
Otherwise, messages from your chat partners wont reach you.
If in doubt, remove later.</p>
contacts who only know this relay may not reach you until you message them again.
To stay reachable in the meantime, choose <strong>Hide from Contacts</strong> in the confirmation dialog
instead of removing it right away.</p>
</li>
<li>
<p>To <strong>show</strong> a hidden relay again, tap on it.</p>
</li>
</ul>
@@ -1387,7 +1391,7 @@ you need to look it up in the “keypairs” SQLite table of a profile backup ta
<p>Починаючи з 2023 року, ми виправили проблеми з безпекою та конфіденційністю у функції “веб застосунків, що поширені у чаті”, пов’язані зі збоями в роботі пісочниці особливо в Chromium. Згодом ми отримали незалежний аудит безпеки аудит безпеки від Cure53, і всі знайдені проблеми були виправлені в серії додатків 1.36, випущених у квітні 2023 року. Повну історію про наскрізну безпеку в Інтернеті дивіться <a href="https://delta.chat/en/2023-05-22-webxdc-security">тут</a>.</p>
</li>
<li>
<p>Починаючи з 2023 року <a href="https://cure53.de">Cure53</a> проаналізував транспортне шифрування мережевих з’єднань Delta Chat і відтворюване налаштування поштового сервера як <a href="https://delta.chat/uk/serverguide">рекомендовано на цьому сайті</a>. Ви можете прочитати більше про аудит <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">у нашому блозі</a> або прочитайте <a href="https://delta.chat/assets/blog/MER-01-report.pdf">повний звіт тут</a>.</p>
<p>Починаючи з 2023 року <a href="https://cure53.de">Cure53</a> проаналізував транспортне шифрування мережевих з’єднань Delta Chat і відтворюване налаштування поштового сервера як <a href="https://delta.chat/serverguide">рекомендовано на цьому сайті</a>. Ви можете прочитати більше про аудит <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">у нашому блозі</a> або прочитайте <a href="https://delta.chat/assets/blog/MER-01-report.pdf">повний звіт тут</a>.</p>
</li>
<li>
<p>У 2020 році <a href="https://includesecurity.com">Include Security</a> проаналізувала Rust-<a href="https://github.com/deltachat/deltachat-core-rust/">ядро</a> Delta Chat і бібліотеки <a href="https://github.com/async-email/async-imap">IMAP</a>, <a href="https://github.com/async-email/async-smtp">SMTP</a> та <a href="https://github.com/async-email/async-native-tls">TLS</a>. Він не виявив критичних або серйозних проблем. У звіті виявлено кілька слабких місць середнього ступеня тяжкості – вони самі по собі не становлять загрози для користувачів Delta Chat оскільки вони залежать від середовища, у якому використовується Delta Chat. З міркувань зручності використання та сумісності ми не можемо пом’якшити їх усі тому вирішили надати рекомендації щодо безпеки користувачам, яким загрожує небезпека. Ви можете прочитати <a href="https://delta.chat/assets/blog/2020-second-security-review.pdf">повний звіт тут</a>.</p>
+15 -17
View File
@@ -1032,29 +1032,27 @@ Please use this method sparingly, as sending original files will significantly i
<p>可以在“设置 → 高级 → 中继服务器”中配置:</p>
<ul>
<li>您可以通过扫描二维码来<strong>添加</strong>中继;</li>
</ul>
<p><a href="https://chatmail.at/relays">https://chatmail.at/relays</a> 显示了一些已知的中继服务器。</p>
<p>如果您添加了多个中继,您将在所有中继上收到消息。</p>
<ul>
<li>
<p><strong>默认</strong>地址确定了聊天伙伴今后发送消息的目标地址。</p>
<p>You can <strong>add</strong> a relay by scanning its QR code;
<a href="https://chatmail.at/relays">chatmail.at/relays</a> shows some known ones.
If you have multiple relays, you will receive messages on all of them.
Contacts learn your current relays automatically when you message them.</p>
</li>
<li>
<p>如果您<strong>移除</strong>某个中继服务器,</p>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
</li>
<li>
<p>If you <strong>remove</strong> a relay,
contacts who only know this relay may not reach you until you message them again.
To stay reachable in the meantime, choose <strong>Hide from Contacts</strong> in the confirmation dialog
instead of removing it right away.</p>
</li>
<li>
<p>To <strong>show</strong> a hidden relay again, tap on it.</p>
</li>
</ul>
<p>请确保已使用另一个默认中继服务器足够长的时间。</p>
<p>否则,您将无法收到聊天伙伴的消息。</p>
<p>如有问题,请稍后再移除。</p>
<p>有关中继服务器的更多细节和未来可能性、
您可以关注 <a href="https://support.delta.chat">论坛</a> 中的讨论。</p>
@@ -1559,7 +1557,7 @@ Delta Chat 长期接受第三方独立机构的安全审计与漏洞分析,
</li>
<li>
<p>2023 年 3 月,<a href="https://cure53.de">Cure53</a> 分析了 Delta Chat 网络连接的传输加密和一个可重现的邮件服务器设置,如
<a href="https://delta.chat/zh_CN/serverguide">本网站</a> 推荐的那样。
<a href="https://delta.chat/serverguide">本网站</a> 推荐的那样。
你可以在 <a href="https://delta.chat/en/2023-03-27-third-independent-security-audit">我们的博客</a> 上阅读有关审计的更多信息
,或在此处阅读 <a href="https://delta.chat/assets/blog/MER-01-report.pdf">完整报告</a></p>
</li>
+26 -2
View File
@@ -396,7 +396,7 @@ public class Rpc {
}
/**
* Gets messages to be processed by the bot and returns their IDs.
* (deprecated) Gets messages to be processed by the bot and returns their IDs.
* <p>
* Only messages with database ID higher than `last_msg_id` config value
* are returned. After processing the messages, the bot should
@@ -404,6 +404,13 @@ public class Rpc {
* or manually updating the value to avoid getting already
* processed messages.
* <p>
* Deprecated 2026-04: This returns the message's id as soon as the first part arrives,
* even if it is not fully downloaded yet.
* The bot needs to wait for the message to be fully downloaded.
* Since this is usually not the desired behavior,
* bots should instead use the #DC_EVENT_INCOMING_MSG / [`types::events::EventType::IncomingMsg`]
* event for getting notified about new messages.
* <p>
* [`markseen_msgs`]: Self::markseen_msgs
*/
public java.util.List<Integer> getNextMsgs(Integer accountId) throws RpcException {
@@ -411,7 +418,7 @@ public class Rpc {
}
/**
* Waits for messages to be processed by the bot and returns their IDs.
* (deprecated) Waits for messages to be processed by the bot and returns their IDs.
* <p>
* This function is similar to [`get_next_msgs`],
* but waits for internal new message notification before returning.
@@ -422,6 +429,13 @@ public class Rpc {
* To shutdown the bot, stopping I/O can be used to interrupt
* pending or next `wait_next_msgs` call.
* <p>
* Deprecated 2026-04: This returns the message's id as soon as the first part arrives,
* even if it is not fully downloaded yet.
* The bot needs to wait for the message to be fully downloaded.
* Since this is usually not the desired behavior,
* bots should instead use the #DC_EVENT_INCOMING_MSG / [`types::events::EventType::IncomingMsg`]
* event for getting notified about new messages.
* <p>
* [`get_next_msgs`]: Self::get_next_msgs
*/
public java.util.List<Integer> waitNextMsgs(Integer accountId) throws RpcException {
@@ -820,6 +834,16 @@ public class Rpc {
transport.call("marknoticed_chat", mapper.valueToTree(accountId), mapper.valueToTree(chatId));
}
/**
* Marks the last incoming message in the chat as _fresh_.
* <p>
* UI can use this to offer a "mark unread" option,
* so that already noticed chats get a badge counter again.
*/
public void markfreshChat(Integer accountId, Integer chatId) throws RpcException {
transport.call("markfresh_chat", mapper.valueToTree(accountId), mapper.valueToTree(chatId));
}
/**
* Returns the message that is immediately followed by the last seen
* message.
@@ -40,7 +40,6 @@ import org.thoughtcrime.securesms.connect.KeepAliveService;
import org.thoughtcrime.securesms.connect.NetworkStateReceiver;
import org.thoughtcrime.securesms.crypto.DatabaseSecret;
import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider;
import org.thoughtcrime.securesms.geolocation.DcLocationManager;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.notifications.FcmReceiveService;
import org.thoughtcrime.securesms.notifications.InChatSounds;
@@ -61,7 +60,6 @@ public class ApplicationContext extends MultiDexApplication {
private Rpc rpc;
private DcContext dcContext;
private DcLocationManager dcLocationManager;
private DcEventCenter eventCenter;
private NotificationCenter notificationCenter;
private JobManager jobManager;
@@ -126,15 +124,6 @@ public class ApplicationContext extends MultiDexApplication {
}
}
/**
* Get DcLocationManager instance, waiting for initialization if necessary. This method is
* thread-safe and will block until initialization is complete.
*/
public DcLocationManager getLocationManager() {
ensureInitialized();
return dcLocationManager;
}
/**
* Get DcEventCenter instance, waiting for initialization if necessary. This method is thread-safe
* and will block until initialization is complete.
@@ -282,7 +271,6 @@ public class ApplicationContext extends MultiDexApplication {
}
dcContext = dcAccounts.getSelectedAccount();
notificationCenter = new NotificationCenter(this);
dcLocationManager = new DcLocationManager(this, dcContext);
isInitialized = true;
initLock.notifyAll();
@@ -13,6 +13,7 @@ import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -26,6 +27,8 @@ import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.core.graphics.drawable.IconCompat;
import androidx.fragment.app.Fragment;
import chat.delta.rpc.Rpc;
import chat.delta.rpc.RpcException;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContact;
import com.b44t.messenger.DcContext;
@@ -44,6 +47,7 @@ import org.thoughtcrime.securesms.util.task.SnackbarAsyncTask;
import org.thoughtcrime.securesms.util.views.ProgressDialog;
public abstract class BaseConversationListFragment extends Fragment implements ActionMode.Callback {
private static final String TAG = BaseConversationListFragment.class.getSimpleName();
protected ActionMode actionMode;
protected PulsingFloatingActionButton fab;
@@ -170,6 +174,17 @@ public abstract class BaseConversationListFragment extends Fragment implements A
return false;
}
private boolean areSomeSelectedChatsFresh() {
DcContext dcContext = DcHelper.getContext(requireActivity());
final Set<Long> selectedChats = getListAdapter().getBatchSelections();
for (long chatId : selectedChats) {
if (dcContext.getFreshMsgCount((int) chatId) > 0) {
return true;
}
}
return false;
}
private void handlePinAllSelected() {
final DcContext dcContext = DcHelper.getContext(requireActivity());
final Set<Long> selectedConversations =
@@ -229,6 +244,26 @@ public abstract class BaseConversationListFragment extends Fragment implements A
}
}
private void handleMarkfreshSelected() {
final Set<Long> selectedConversations =
new HashSet<Long>(getListAdapter().getBatchSelections());
final Rpc rpc = DcHelper.getRpc(requireActivity());
try {
int accId = rpc.getSelectedAccountId();
for (long chatId : selectedConversations) {
rpc.markfreshChat(accId, (int) chatId);
}
} catch (RpcException e) {
Log.e(TAG, "RPC error", e);
}
if (actionMode != null) {
actionMode.finish();
actionMode = null;
}
}
@SuppressLint("StaticFieldLeak")
private void handleArchiveAllSelected() {
final DcContext dcContext = DcHelper.getContext(requireActivity());
@@ -409,6 +444,7 @@ public abstract class BaseConversationListFragment extends Fragment implements A
if (!isRelayingMessageContent(requireActivity())) {
final int selectedCount = getListAdapter().getBatchSelections().size();
menu.findItem(R.id.menu_add_to_home_screen).setVisible(selectedCount == 1);
MenuItem archiveItem = menu.findItem(R.id.menu_archive_selected);
if (offerToArchive()) {
archiveItem.setIcon(R.drawable.ic_archive_white_24dp);
@@ -417,6 +453,7 @@ public abstract class BaseConversationListFragment extends Fragment implements A
archiveItem.setIcon(R.drawable.ic_unarchive_white_24dp);
archiveItem.setTitle(R.string.menu_unarchive_chat);
}
MenuItem pinItem = menu.findItem(R.id.menu_pin_selected);
if (areSomeSelectedChatsUnpinned()) {
pinItem.setIcon(R.drawable.ic_pin_white);
@@ -425,12 +462,17 @@ public abstract class BaseConversationListFragment extends Fragment implements A
pinItem.setIcon(R.drawable.ic_unpin_white);
pinItem.setTitle(R.string.unpin_chat);
}
MenuItem muteItem = menu.findItem(R.id.menu_mute_selected);
if (areSomeSelectedChatsUnmuted()) {
muteItem.setTitle(R.string.menu_mute);
} else {
muteItem.setTitle(R.string.menu_unmute);
}
final boolean someAreFresh = areSomeSelectedChatsFresh();
menu.findItem(R.id.menu_marknoticed_selected).setVisible(someAreFresh);
menu.findItem(R.id.menu_markfresh_selected).setVisible(!someAreFresh);
}
}
@@ -484,6 +526,9 @@ public abstract class BaseConversationListFragment extends Fragment implements A
} else if (itemId == R.id.menu_marknoticed_selected) {
handleMarknoticedSelected();
return true;
} else if (itemId == R.id.menu_markfresh_selected) {
handleMarkfreshSelected();
return true;
} else if (itemId == R.id.menu_add_to_home_screen) {
handleAddToHomeScreen();
return true;
@@ -536,8 +536,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
menu.findItem(R.id.menu_start_call)
.setVisible(
Prefs.isCallsEnabled(this)
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
&& dcChat.canSend()
&& dcChat.isEncrypted()
&& !dcChat.isSelfTalk()
@@ -1059,7 +1059,6 @@ public class ConversationFragment extends MessageSelectorFragment {
@Override
public void onStickerClicked(DcMsg messageRecord) {
new AlertDialog.Builder(getContext())
.setTitle(R.string.add_to_sticker_collection)
.setMessage(R.string.ask_add_sticker_to_collection)
.setPositiveButton(
R.string.ok,
@@ -69,6 +69,7 @@ import org.thoughtcrime.securesms.components.SearchToolbar;
import org.thoughtcrime.securesms.connect.AccountManager;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.connect.DirectShareUtil;
import org.thoughtcrime.securesms.geolocation.LocationStreamingService;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
@@ -449,6 +450,9 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
refreshTitle();
invalidateOptionsMenu();
DirectShareUtil.triggerRefreshDirectShare(this);
if (DcHelper.getContext(this).isSendingLocationsToChat(0)) {
LocationStreamingService.ensureRunning(this);
}
}
@Override
@@ -688,7 +692,11 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
// Util.copy(inputStream, new FileOutputStream(outputFile));
// msg.setFile(outputFile, "image/jpeg");
msg.setText(getString(R.string.update_2_46ac, "https://arcanechat.me/#contribute", "https://i.delta.chat/#0A45953086F0C166D3BAF1D4BB2025496E4C2704&x=MVPi07rQBEmHO4FRb3brpwDe&j=n8mkKqu42WAKKUCx1bQOVh23&s=AM1YCd_2hKuiSiTEb-2177On&a=arcanechat%40arcanechat.me&n=ArcaneChat&b=ArcaneChat+Channel"));
msg.setText(
getString(
R.string.update_2_46ac,
"https://arcanechat.me/#contribute",
"https://i.delta.chat/#0A45953086F0C166D3BAF1D4BB2025496E4C2704&x=MVPi07rQBEmHO4FRb3brpwDe&j=n8mkKqu42WAKKUCx1bQOVh23&s=AM1YCd_2hKuiSiTEb-2177On&a=arcanechat%40arcanechat.me&n=ArcaneChat&b=ArcaneChat+Channel"));
}
dcContext.addDeviceMsg(deviceMsgLabel, msg);
@@ -33,7 +33,7 @@ public class ShareLocationDialog {
shareLocationUnit = 6 * 60 * 60;
break;
case 5:
shareLocationUnit = 12 * 60 * 60;
shareLocationUnit = 24 * 60 * 60;
break;
}
@@ -22,6 +22,8 @@ import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;
import chat.delta.rpc.types.SecurejoinSource;
import java.net.IDN;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.thoughtcrime.securesms.qr.QrCodeHandler;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.IntentUtils;
@@ -31,6 +33,9 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class WebViewActivity extends PassphraseRequiredActionBarActivity
implements SearchView.OnQueryTextListener, WebView.FindListener {
private static final String TAG = WebViewActivity.class.getSimpleName();
// Regex to extract the host from a URL for IDN conversion.
private static final Pattern URL_PATTERN =
Pattern.compile("^((?:[a-zA-Z0-9]+://)?)([^/?#]+)(.*)$");
protected WebView webView;
@@ -320,9 +325,17 @@ public class WebViewActivity extends PassphraseRequiredActionBarActivity
}
if (shouldAskToOpenLink()) {
String displayUrl;
try {
Matcher m = URL_PATTERN.matcher(url);
displayUrl = m.find() ? m.group(1) + IDN.toASCII(m.group(2)) + m.group(3) : url;
} catch (IllegalArgumentException e) {
// IDN.toASCII() failed (e.g. string too long), fall back to raw url
displayUrl = url;
}
new AlertDialog.Builder(this)
.setTitle(R.string.open_url_confirmation)
.setMessage(IDN.toASCII(url))
.setMessage(displayUrl)
.setNeutralButton(R.string.cancel, null)
.setPositiveButton(R.string.open, (d, w) -> IntentUtils.showInBrowser(this, url))
.setNegativeButton(
@@ -240,6 +240,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setMediaPlaybackRequiresUserGesture(false);
webSettings.setAllowFileAccess(false);
webSettings.setBlockNetworkLoads(!internetAccess);
webSettings.setAllowContentAccess(false);
@@ -52,7 +52,8 @@ import org.webrtc.VideoTrack;
public class CallActivity extends AppCompatActivity {
private static final String TAG = CallActivity.class.getSimpleName();
private static final int PERMISSION_REQUEST_CODE = 1001;
private static final int MIC_PERMISSION_REQUEST_CODE = 1001;
private static final int CAMERA_PERMISSION_REQUEST_CODE = 1002;
public static final String ACTION_ANSWER_CALL = BuildConfig.APPLICATION_ID + ".ANSWER_CALL";
public static final String ACTION_DECLINE_CALL = BuildConfig.APPLICATION_ID + ".DECLINE_CALL";
@@ -94,10 +95,30 @@ public class CallActivity extends AppCompatActivity {
private PowerManager.WakeLock proximityWakeLock;
// States
private boolean awaitingPermissionResult = false;
private boolean pausedWhileAwaitingPermission = false;
private boolean intentHandled = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Destructive actions need nothing
String action = getIntent() != null ? getIntent().getAction() : null;
if (ACTION_DECLINE_CALL.equals(action)) {
Log.d(TAG, "Handling DECLINE_CALL action from notification");
CallCoordinator.getInstance(getApplication()).declineCall();
finish();
return;
}
if (ACTION_HANGUP_CALL.equals(action)) {
Log.d(TAG, "Handling HANGUP_CALL action from notification");
CallCoordinator.getInstance(getApplication()).hangUp();
finish();
return;
}
setContentView(R.layout.activity_call);
setupWindowFlags();
@@ -108,11 +129,6 @@ public class CallActivity extends AppCompatActivity {
initializeProximityWakeLock();
if (!hasRequiredPermissions()) {
requestRequiredPermissions();
return;
}
// PiP listener
addOnPictureInPictureModeChangedListener(
pipModeInfo -> {
@@ -139,47 +155,74 @@ public class CallActivity extends AppCompatActivity {
initializeViewModel();
// Intent handling needs permissions
if (!hasMicrophonePermission()) {
awaitingPermissionResult = true;
ActivityCompat.requestPermissions(
this, new String[] {Manifest.permission.RECORD_AUDIO}, MIC_PERMISSION_REQUEST_CODE);
return;
}
if (shouldRequestCameraPermission()) {
awaitingPermissionResult = true;
ActivityCompat.requestPermissions(
this, new String[] {Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE);
return;
}
handleIntents(getIntent());
intentHandled = true;
}
private void handleIntents(Intent intent) {
if (intent == null || viewModel == null) {
if (intent == null) {
return;
}
CallCoordinator coordinator = CallCoordinator.getInstance(getApplication());
if (!coordinator.hasActiveCall()) {
Log.e(TAG, "No active call exists, cannot proceed");
Toast.makeText(this, "No active call", Toast.LENGTH_SHORT).show();
finish();
return;
}
String action = intent.getAction();
Log.d(TAG, "handleIntents: action=" + action);
// Handle notification actions
if (ACTION_ANSWER_CALL.equals(action)) {
Log.d(TAG, "Handling ANSWER_CALL action from notification");
viewModel.handleNotificationAnswer();
return;
}
// Destructive actions without ViewModel
if (ACTION_DECLINE_CALL.equals(action)) {
Log.d(TAG, "Handling DECLINE_CALL action from notification");
viewModel.handleNotificationDecline();
Log.d(TAG, "Handling DECLINE_CALL action");
if (viewModel != null) {
viewModel.handleNotificationDecline();
} else {
coordinator.declineCall();
}
finish();
return;
}
if (ACTION_HANGUP_CALL.equals(action)) {
Log.d(TAG, "Handling HANGUP_CALL action from notification");
viewModel.handleNotificationHangup();
Log.d(TAG, "Handling HANGUP_CALL action");
if (viewModel != null) {
viewModel.handleNotificationHangup();
} else {
coordinator.hangUp();
}
finish();
return;
}
if (viewModel == null) {
return;
}
if (!coordinator.hasActiveCall()) {
Log.e(TAG, "No active call exists, cannot proceed");
finish();
return;
}
if (ACTION_ANSWER_CALL.equals(action)) {
Log.d(TAG, "Handling ANSWER_CALL action from notification");
viewModel.handleNotificationAnswer();
return;
}
if (coordinator.hasOngoingCall()) {
Log.d(TAG, "Resuming existing call");
} else if (!coordinator.isIncomingCall()) {
@@ -373,6 +416,7 @@ public class CallActivity extends AppCompatActivity {
case INITIALIZING:
case PROMPTING_USER_ACCEPT:
case ENDED:
case ANSWERED_ELSEWHERE:
case ERROR:
default:
finish();
@@ -504,6 +548,8 @@ public class CallActivity extends AppCompatActivity {
viewModel.getVideoEnabled(), v -> videoConfigChanged.setValue(true));
videoConfigChanged.addSource(
viewModel.getRemoteVideoEnabled(), v -> videoConfigChanged.setValue(true));
videoConfigChanged.addSource(
viewModel.getIsFrontCamera(), v -> videoConfigChanged.setValue(true));
// Video layout
videoConfigChanged.observe(
@@ -578,6 +624,23 @@ public class CallActivity extends AppCompatActivity {
statusText.setText(R.string.call_reconnecting);
break;
case ANSWERED_ELSEWHERE:
statusText.setText(R.string.call_answered_elsewhere);
incomingCallPrompt.setVisibility(View.GONE);
bottomLayoutContainer.setVisibility(View.GONE);
callerIconContainer.setVisibility(View.GONE);
answerModeSelector.setVisibility(View.GONE);
new Handler(Looper.getMainLooper())
.postDelayed(
() -> {
if (!isFinishing()) {
finish();
}
},
1500);
break;
case ENDED:
statusText.setText(R.string.call_ended);
finish();
@@ -705,17 +768,21 @@ public class CallActivity extends AppCompatActivity {
VideoTrack localTrack = viewModel.getLocalVideoTrack().getValue();
VideoTrack remoteTrack = viewModel.getRemoteVideoTrack().getValue();
boolean isFront = Boolean.TRUE.equals(viewModel.getIsFrontCamera().getValue());
boolean showFullScreen = false;
if (state == CallViewModel.CallState.CONNECTED
&& remoteTrack != null
&& Boolean.TRUE.equals(remoteVideoEnabled)) {
remoteVideoView.setMirror(false);
remoteTrack.addSink(remoteVideoView);
showFullScreen = true;
} else if (!coordinator.isIncomingCall()
&& (state == CallViewModel.CallState.RINGING || state == CallViewModel.CallState.CONNECTING)
&& localTrack != null
&& Boolean.TRUE.equals(videoEnabled)) {
remoteVideoView.setMirror(isFront);
localTrack.addSink(remoteVideoView);
showFullScreen = true;
}
@@ -729,6 +796,7 @@ public class CallActivity extends AppCompatActivity {
&& !isInPictureInPictureMode();
if (showCorner) {
localVideoView.setMirror(isFront);
localTrack.addSink(localVideoView);
}
@@ -758,18 +826,46 @@ public class CallActivity extends AppCompatActivity {
// Permissions
private boolean hasRequiredPermissions() {
return ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED
&& ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
== PackageManager.PERMISSION_GRANTED;
private boolean hasMicrophonePermission() {
return ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
== PackageManager.PERMISSION_GRANTED;
}
private void requestRequiredPermissions() {
ActivityCompat.requestPermissions(
this,
new String[] {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO},
PERMISSION_REQUEST_CODE);
private boolean hasCameraPermission() {
return ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED;
}
private boolean shouldRequestCameraPermission() {
CallCoordinator coordinator = CallCoordinator.getInstance(getApplication());
return coordinator.isStartsWithVideo() && !hasCameraPermission();
}
private void handleMicPermissionDenied() {
Toast.makeText(this, R.string.call_requires_mic_permission, Toast.LENGTH_LONG).show();
CallCoordinator coordinator = CallCoordinator.getInstance(getApplication());
if (coordinator.hasActiveCall()
&& coordinator.isIncomingCall()
&& !coordinator.hasOngoingCall()) {
coordinator.declineCall();
}
finish();
}
private void proceedAfterPermissions() {
if (intentHandled) return;
if (shouldRequestCameraPermission()) {
awaitingPermissionResult = true;
ActivityCompat.requestPermissions(
this, new String[] {Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE);
return;
}
handleIntents(getIntent());
intentHandled = true;
}
@Override
@@ -777,38 +873,34 @@ public class CallActivity extends AppCompatActivity {
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) {
boolean microphoneGranted = false;
boolean cameraGranted = false;
awaitingPermissionResult = false;
pausedWhileAwaitingPermission = false;
for (int i = 0; i < permissions.length; i++) {
if (permissions[i].equals(Manifest.permission.RECORD_AUDIO)) {
microphoneGranted = (grantResults[i] == PackageManager.PERMISSION_GRANTED);
} else if (permissions[i].equals(Manifest.permission.CAMERA)) {
cameraGranted = (grantResults[i] == PackageManager.PERMISSION_GRANTED);
}
}
CallCoordinator coordinator = CallCoordinator.getInstance(getApplication());
if (!microphoneGranted) {
Toast.makeText(this, "Microphone permission is required for calls", Toast.LENGTH_LONG)
.show();
finish();
if (requestCode == MIC_PERMISSION_REQUEST_CODE) {
boolean micGranted =
grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
if (!micGranted) {
handleMicPermissionDenied();
return;
}
CallCoordinator coordinator = CallCoordinator.getInstance(getApplication());
} else if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) {
boolean cameraGranted =
grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
if (!cameraGranted && coordinator.isStartsWithVideo()) {
if (!cameraGranted) {
Log.w(TAG, "Camera permission denied, switching to audio-only");
Toast.makeText(
this, "Starting audio-only call (camera permission denied)", Toast.LENGTH_SHORT)
.show();
coordinator.setStartsWithVideo(false);
}
initializeViewModel();
handleIntents(getIntent());
}
proceedAfterPermissions();
}
// Picture-in-Picture
@@ -817,6 +909,11 @@ public class CallActivity extends AppCompatActivity {
public void onUserLeaveHint() {
super.onUserLeaveHint();
// Do not finish activity when a permission request is pending
if (awaitingPermissionResult) {
return;
}
// Enter PiP mode when user presses home button during active call
if (viewModel != null) {
CallViewModel.CallState state = viewModel.getCallState().getValue();
@@ -833,6 +930,7 @@ public class CallActivity extends AppCompatActivity {
case INITIALIZING:
case PROMPTING_USER_ACCEPT:
case ENDED:
case ANSWERED_ELSEWHERE:
case ERROR:
default:
finish();
@@ -873,17 +971,51 @@ public class CallActivity extends AppCompatActivity {
protected void onPause() {
super.onPause();
if (awaitingPermissionResult) {
pausedWhileAwaitingPermission = true;
}
if (proximityWakeLock != null && proximityWakeLock.isHeld()) {
proximityWakeLock.release();
Log.d(TAG, "Proximity wake lock released in onDestroy");
}
}
@Override
protected void onResume() {
super.onResume();
// Fallback for Android 16 bug: onRequestPermissionsResult not called
if (awaitingPermissionResult && pausedWhileAwaitingPermission) {
Log.w(TAG, "Permission result callback not received, handling in onResume");
awaitingPermissionResult = false;
pausedWhileAwaitingPermission = false;
if (!hasMicrophonePermission()) {
handleMicPermissionDenied();
return;
}
// Mic was granted without callback
if (shouldRequestCameraPermission()) {
awaitingPermissionResult = true;
ActivityCompat.requestPermissions(
this, new String[] {Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE);
return;
}
proceedAfterPermissions();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
detachAllTracks();
if (viewModel != null) {
detachAllTracks();
}
// Release video renderers
if (localVideoView != null) {
@@ -95,6 +95,8 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
private final MutableLiveData<String> displayName = new MutableLiveData<>();
private final MutableLiveData<Icon> displayIcon = new MutableLiveData<>();
private final MutableLiveData<Boolean> outgoingCallPlaced = new MutableLiveData<>(false);
private final MutableLiveData<Boolean> answeredElsewhere = new MutableLiveData<>(false);
private final MutableLiveData<Boolean> isFrontCamera = new MutableLiveData<>(true);
// Audio Routing Support
private final MediatorLiveData<CallEndpointCompat> currentAudioEndpoint =
@@ -317,6 +319,10 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
return outgoingCallPlaced;
}
public LiveData<Boolean> getAnsweredElsewhere() {
return answeredElsewhere;
}
public LiveData<CallEndpointCompat> getCurrentAudioEndpoint() {
return currentAudioEndpoint;
}
@@ -325,6 +331,10 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
return availableAudioEndpoints;
}
public LiveData<Boolean> getIsFrontCamera() {
return isFrontCamera;
}
// State Update Methods (CallService)
public void updateConnectionState(PeerConnection.PeerConnectionState state) {
@@ -359,6 +369,11 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
isRelayUsed.postValue(isRelay);
}
public void updateFrontCamera(boolean front) {
Log.d(TAG, "updateFrontCamera: " + front);
isFrontCamera.postValue(front);
}
public void reportError(String error) {
Log.e(TAG, "reportError: " + error);
errorMessage.postValue(error);
@@ -366,7 +381,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
// Delayed Media Initialization Support
public void startMediaCapture() {
public synchronized void startMediaCapture() {
Log.d(TAG, "startMediaCapture");
if (callService != null) {
callService.startMediaCapture();
@@ -375,7 +390,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
}
}
public void handleCallControlScopeAnswer() {
public synchronized void handleCallControlScopeAnswer() {
Log.d(TAG, "handleCallControlScopeAnswer");
if (!isIncomingCall) {
@@ -512,22 +527,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
return;
}
// Check microphone and camera permissions
if (!hasMicrophonePermission()) {
Log.e(TAG, "Microphone permission not granted");
Intent intent = new Intent(appContext, CallActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
appContext.startActivity(intent);
notificationManager.cancel(NOTIFICATION_ID_CALL);
return;
}
if (startsWithVideo && !hasCameraPermission()) {
Log.w(TAG, "Camera permission not granted");
startsWithVideo = false;
}
// Launch CallActivity with answer action
// Launch CallActivity
Intent intent = new Intent(appContext, CallActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
appContext.startActivity(intent);
@@ -628,7 +628,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
cleanupCall(activeAccId, activeCallId);
}
public void setAudioEnabled(boolean enabled) {
public synchronized void setAudioEnabled(boolean enabled) {
Log.d(TAG, "setAudioEnabled: " + enabled);
localAudioEnabled.postValue(enabled);
@@ -640,7 +640,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
}
}
public void setVideoEnabled(boolean enabled) {
public synchronized void setVideoEnabled(boolean enabled) {
Log.d(TAG, "setVideoEnabled: " + enabled);
localVideoEnabled.postValue(enabled);
@@ -652,14 +652,14 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
}
}
public void switchCamera() {
public synchronized void switchCamera() {
Log.d(TAG, "switchCamera");
if (callService != null) {
callService.switchCamera();
}
}
public void startOutgoingCall() {
public synchronized void startOutgoingCall() {
Log.d(TAG, "startOutgoingCall");
if (callService != null) {
callService.startOutgoingCall();
@@ -846,7 +846,8 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
onIncomingCall(accId, callId, event.getData2Str(), hasVideo);
break;
case DcContext.DC_EVENT_INCOMING_CALL_ACCEPTED:
onIncomingCallAccepted(callId);
boolean fromThisDevice = event.getData2Int() != 0; // Data2 is from_this_device
onIncomingCallAccepted(callId, fromThisDevice);
break;
case DcContext.DC_EVENT_OUTGOING_CALL_ACCEPTED:
String answerSDP = event.getData2Str();
@@ -902,8 +903,13 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
startAndBindService();
}
private synchronized void onIncomingCallAccepted(int callId) {
Log.d(TAG, "onIncomingCallAccepted: callId=" + callId);
private synchronized void onIncomingCallAccepted(int callId, boolean fromThisDevice) {
Log.d(TAG, "onIncomingCallAccepted: callId=" + callId + ", fromThisDevice=" + fromThisDevice);
if (!fromThisDevice) {
onCallAnsweredOnOtherDevice();
return;
}
if (activeCallId == null || !activeCallId.equals(callId)) {
Log.w(TAG, "Accepted call ID doesn't match active call");
@@ -918,6 +924,52 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
showOrUpdateOngoingNotification("Call with " + callerName);
}
private synchronized void onCallAnsweredOnOtherDevice() {
Log.d(TAG, "Call was answered on another device");
if (!hasActiveCall()) {
Log.d(TAG, "No active call, ignoring");
return;
}
// Prevent notifyBackendCallEnded() from firing during WebRTC teardown.
// The call is still active on the other device.
hasNotifiedBackend = true;
if (callService != null) {
callService.stopRingtone();
}
notificationManager.cancel(NOTIFICATION_ID_CALL);
answeredElsewhere.postValue(true);
// Disconnect from Telecom CallControlScope
CallControlScope scope = activeCallControlScope;
if (scope != null) {
scope.disconnect(
new DisconnectCause(DisconnectCause.REMOTE),
new Continuation<CallControlResult>() {
@NonNull
@Override
public CoroutineContext getContext() {
return EmptyCoroutineContext.INSTANCE;
}
@Override
public void resumeWith(@NonNull Object result) {
Log.d(TAG, "Disconnect (answered elsewhere) completed");
}
});
}
if (callService != null) {
callService.endCall();
}
cleanupCall(activeAccId, activeCallId);
}
private void onOutgoingCallAccepted(int callId, String answerSdp) {
Log.d(TAG, "onOutgoingCallAccepted: callId=" + callId + ", got answer SDP");
@@ -1125,6 +1177,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
private void resetLiveDataForNewCall() {
connectionState.postValue(PeerConnection.PeerConnectionState.NEW);
answeredElsewhere.postValue(false); // clearLiveData() must not reset answeredElsewhere
clearLiveData();
}
@@ -1150,7 +1203,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
return;
}
// Check camera and microphone permissions
// Check microphone permission
if (!hasMicrophonePermission()) {
Log.e(TAG, "Microphone permission not granted");
@@ -1160,11 +1213,6 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
return;
}
if (startsWithVideo && !hasCameraPermission()) {
Log.w(TAG, "Camera permission not granted, will start audio-only");
startsWithVideo = false;
}
resetLiveDataForNewCall();
this.activeCallId = -1; // Placeholder call ID for Intent
@@ -1310,10 +1358,8 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
}
// Check full screen intent permission on Android 14+
boolean canUseFullScreen;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
canUseFullScreen = canUseFullScreenIntent();
if (!canUseFullScreen) {
if (!canUseFullScreenIntent()) {
Log.w(TAG, "Full screen intent permission not granted, notification will appear normally");
}
}
@@ -142,17 +142,13 @@ public class CallService extends Service implements WebRTCClient.Callbacks {
return;
}
// Check permissions
boolean hasMicrophone = callCoordinator.hasMicrophonePermission();
boolean hasCamera = callCoordinator.hasCameraPermission();
if (!hasMicrophone) {
if (!callCoordinator.hasMicrophonePermission()) {
Log.e(TAG, "Microphone permission not granted, cannot start call");
callCoordinator.reportError("Microphone permission is required for calls");
return;
}
boolean startsWithVideo = callCoordinator.isStartsWithVideo() && hasCamera;
boolean startsWithVideo = callCoordinator.isStartsWithVideo();
Log.d(TAG, "Creating media stream with video: " + startsWithVideo);
@@ -164,14 +160,18 @@ public class CallService extends Service implements WebRTCClient.Callbacks {
webRTCClient.setLocalMediaStream(stream);
callCoordinator.updateFrontCamera(mediaStreamManager.isFrontCamera());
callCoordinator.setVideoEnabled(startsWithVideo);
if (!stream.videoTracks.isEmpty()) {
VideoTrack localTrack = stream.videoTracks.get(0);
callCoordinator.updateLocalVideoTrack(localTrack);
} else {
Log.w(TAG, "Camera unavailable, call will be audio-only");
callCoordinator.reportError("Camera unavailable, using audio only");
Log.w(TAG, "No video track in stream, call will be audio-only");
if (startsWithVideo) {
callCoordinator.reportError("Camera unavailable, using audio only");
}
callCoordinator.setVideoEnabled(false);
}
@@ -181,7 +181,9 @@ public class CallService extends Service implements WebRTCClient.Callbacks {
@Override
public void onError(String error) {
Log.e(TAG, "Failed to setup media: " + error);
callCoordinator.reportError("Camera/microphone error: " + error);
if (startsWithVideo) {
callCoordinator.reportError("Camera/microphone error: " + error);
}
callCoordinator.setVideoEnabled(false);
}
});
@@ -410,7 +412,18 @@ public class CallService extends Service implements WebRTCClient.Callbacks {
Log.d(TAG, "switchCamera");
if (mediaStreamManager != null) {
mediaStreamManager.switchCamera();
mediaStreamManager.switchCamera(
new MediaStreamManager.CameraSwitchCallback() {
@Override
public void onCameraSwitch(boolean isFrontCamera) {
callCoordinator.updateFrontCamera(isFrontCamera);
}
@Override
public void onError(String error) {
Log.e(TAG, "Camera switch failed: " + error);
}
});
}
}
@@ -13,6 +13,7 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
import androidx.lifecycle.Observer;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.webrtc.PeerConnection;
import org.webrtc.VideoTrack;
@@ -36,8 +37,10 @@ public class CallViewModel extends AndroidViewModel {
private final LiveData<String> displayName;
private final LiveData<Icon> displayIcon;
private final LiveData<Boolean> outgoingCallPlaced;
private final LiveData<Boolean> answeredElsewhere;
private final LiveData<CallEndpointCompat> currentAudioEndpoint;
private final LiveData<List<CallEndpointCompat>> availableAudioEndpoints;
private final LiveData<Boolean> isFrontCamera;
// Translated from coordinator's connectionState
private final MediatorLiveData<CallState> callState;
@@ -46,7 +49,7 @@ public class CallViewModel extends AndroidViewModel {
private Observer<VideoTrack> answerCallObserver;
private Observer<VideoTrack> startOutgoingCallObserver;
private boolean hasCallEnded = false;
private final AtomicBoolean hasCallEnded = new AtomicBoolean(false);
// User-facing call states
public enum CallState {
@@ -56,6 +59,7 @@ public class CallViewModel extends AndroidViewModel {
CONNECTING,
CONNECTED,
RECONNECTING,
ANSWERED_ELSEWHERE,
ENDED,
ERROR
}
@@ -77,8 +81,10 @@ public class CallViewModel extends AndroidViewModel {
this.displayName = callCoordinator.getDisplayName();
this.displayIcon = callCoordinator.getDisplayIcon();
this.outgoingCallPlaced = callCoordinator.getOutgoingCallPlaced();
this.answeredElsewhere = callCoordinator.getAnsweredElsewhere();
this.currentAudioEndpoint = callCoordinator.getCurrentAudioEndpoint();
this.availableAudioEndpoints = callCoordinator.getAvailableAudioEndpoints();
this.isFrontCamera = callCoordinator.getIsFrontCamera();
this.callState = new MediatorLiveData<>(CallState.INITIALIZING);
@@ -105,6 +111,10 @@ public class CallViewModel extends AndroidViewModel {
callState.addSource(
callCoordinator.getConnectionState(),
state -> {
if (callState.getValue() == CallState.ANSWERED_ELSEWHERE) {
return;
}
CallState newState = translateConnectionState(state);
if (callState.getValue() != newState) {
@@ -113,9 +123,7 @@ public class CallViewModel extends AndroidViewModel {
if (state == PeerConnection.PeerConnectionState.FAILED
|| state == PeerConnection.PeerConnectionState.CLOSED) {
if (!hasCallEnded) {
hasCallEnded = true;
}
hasCallEnded.set(true);
}
});
@@ -128,6 +136,15 @@ public class CallViewModel extends AndroidViewModel {
}
}
});
callState.addSource(
answeredElsewhere,
value -> {
if (Boolean.TRUE.equals(value)) {
hasCallEnded.set(true);
callState.setValue(CallState.ANSWERED_ELSEWHERE);
}
});
}
private CallState translateConnectionState(PeerConnection.PeerConnectionState state) {
@@ -257,11 +274,10 @@ public class CallViewModel extends AndroidViewModel {
public void declineCall() {
Log.d(TAG, "declineCall");
if (hasCallEnded) {
if (!hasCallEnded.compareAndSet(false, true)) {
Log.w(TAG, "Call already ended");
return;
}
hasCallEnded = true;
callCoordinator.declineCall();
}
@@ -269,11 +285,10 @@ public class CallViewModel extends AndroidViewModel {
public void hangUp() {
Log.d(TAG, "hangUp");
if (hasCallEnded) {
if (!hasCallEnded.compareAndSet(false, true)) {
Log.w(TAG, "Call already ended");
return;
}
hasCallEnded = true;
callCoordinator.hangUp();
}
@@ -354,8 +369,7 @@ public class CallViewModel extends AndroidViewModel {
public void onCallDisconnected(DisconnectCause disconnectCause) {
Log.d(TAG, "onCallDisconnected callback from CallControlScope, cause: " + disconnectCause);
if (!hasCallEnded) {
hasCallEnded = true;
if (hasCallEnded.compareAndSet(false, true)) {
callState.postValue(CallState.ENDED);
}
}
@@ -414,6 +428,10 @@ public class CallViewModel extends AndroidViewModel {
return availableAudioEndpoints;
}
public LiveData<Boolean> getIsFrontCamera() {
return isFrontCamera;
}
// Notification Action Handlers
public void handleNotificationAnswer() {
@@ -1,9 +1,14 @@
package org.thoughtcrime.securesms.calls;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.content.ContextCompat;
import org.thoughtcrime.securesms.EglUtils;
import org.webrtc.AudioSource;
import org.webrtc.AudioTrack;
@@ -31,6 +36,7 @@ public class MediaStreamManager {
private VideoSource videoSource;
private AudioSource audioSource;
private SurfaceTextureHelper surfaceTextureHelper;
private volatile boolean isFrontCamera = true;
public interface Callback {
void onMediaStreamReady(MediaStream stream);
@@ -38,6 +44,12 @@ public class MediaStreamManager {
void onError(String error);
}
public interface CameraSwitchCallback {
void onCameraSwitch(boolean isFrontCamera);
void onError(String error);
}
public MediaStreamManager(@NonNull Context context, PeerConnectionFactory peerConnectionFactory) {
this.context = context.getApplicationContext();
@@ -45,6 +57,7 @@ public class MediaStreamManager {
}
/** Create media stream with audio and optionally video */
@RequiresApi(api = Build.VERSION_CODES.M)
public void createMediaStream(Callback callback) {
try {
MediaStream mediaStream = peerConnectionFactory.createLocalMediaStream(STREAM_ID);
@@ -83,6 +96,12 @@ public class MediaStreamManager {
@Nullable
private VideoCapturer createVideoCapturer() {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
Log.w(TAG, "Camera permission not granted");
return null;
}
Camera2Enumerator enumerator = new Camera2Enumerator(context);
// Try front camera first
@@ -91,6 +110,7 @@ public class MediaStreamManager {
if (enumerator.isFrontFacing(deviceName)) {
VideoCapturer capturer = enumerator.createCapturer(deviceName, null);
if (capturer != null) {
isFrontCamera = true;
return capturer;
}
}
@@ -100,6 +120,7 @@ public class MediaStreamManager {
for (String deviceName : deviceNames) {
VideoCapturer capturer = enumerator.createCapturer(deviceName, null);
if (capturer != null) {
isFrontCamera = enumerator.isFrontFacing(deviceName);
return capturer;
}
}
@@ -107,12 +128,61 @@ public class MediaStreamManager {
return null;
}
public void switchCamera() {
if (videoCapturer instanceof CameraVideoCapturer) {
CameraVideoCapturer cameraVideoCapturer = (CameraVideoCapturer) videoCapturer;
cameraVideoCapturer.switchCamera(null);
Log.d(TAG, "Camera switched");
public void switchCamera(@Nullable CameraSwitchCallback callback) {
if (!(videoCapturer instanceof CameraVideoCapturer)) {
Log.e(TAG, "switchCamera called but videoCapturer is not a CameraVideoCapturer");
return;
}
CameraVideoCapturer cameraVideoCapturer = (CameraVideoCapturer) videoCapturer;
// Find the opposite-facing camera
Camera2Enumerator enumerator = new Camera2Enumerator(context);
String[] deviceNames = enumerator.getDeviceNames();
String targetCameraName = null;
for (String deviceName : deviceNames) {
boolean isTargetFront = !isFrontCamera;
boolean deviceIsFront = enumerator.isFrontFacing(deviceName);
if (deviceIsFront == isTargetFront) {
targetCameraName = deviceName;
break; // Take the first match
}
}
if (targetCameraName == null) {
Log.e(TAG, "No camera found with opposite facing direction");
if (callback != null) {
callback.onError("No opposite camera available");
}
return;
}
final String finalTargetCameraName = targetCameraName;
Log.d(TAG, "Switching to camera: " + finalTargetCameraName);
// Call with explicit camera name
cameraVideoCapturer.switchCamera(
new CameraVideoCapturer.CameraSwitchHandler() {
@Override
public void onCameraSwitchDone(boolean isFront) {
Log.d(TAG, "switchCamera SUCCESS, isFront=" + isFront);
isFrontCamera = isFront;
if (callback != null) callback.onCameraSwitch(isFront);
}
@Override
public void onCameraSwitchError(String errorDescription) {
Log.e(TAG, "switchCamera FAILED: " + errorDescription);
if (callback != null) callback.onError(errorDescription);
}
},
finalTargetCameraName);
}
public boolean isFrontCamera() {
return isFrontCamera;
}
/** Cleanup resources */
@@ -26,6 +26,7 @@ import androidx.loader.app.LoaderManager;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.ViewUtil;
public class AttachmentTypeSelector extends PopupWindow {
@@ -45,7 +46,7 @@ public class AttachmentTypeSelector extends PopupWindow {
private final @NonNull ImageView imageButton;
private final @NonNull ImageView documentButton;
private final @NonNull ImageView contactButton;
// private final @NonNull ImageView cameraButton;
// private final @NonNull ImageView cameraButton;
private final @NonNull ImageView videoButton;
private final @NonNull ImageView locationButton;
private final @NonNull ImageView webxdcButton;
@@ -87,11 +88,9 @@ public class AttachmentTypeSelector extends PopupWindow {
this.webxdcButton.setOnClickListener(new PropagatingClickListener(ADD_WEBXDC));
this.recentRail.setListener(new RecentPhotoSelectedListener());
// disable location streaming button for now
// if (!Prefs.isLocationStreamingEnabled(context)) {
this.locationButton.setVisibility(View.GONE);
ViewUtil.findById(layout, R.id.location_button_label).setVisibility(View.GONE);
// }
if (!Prefs.isLocationStreamingEnabled(context)) {
ViewUtil.findById(layout, R.id.location_linear_layout).setVisibility(View.GONE);
}
setLocationButtonImage(context);
@@ -8,6 +8,7 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
@@ -18,6 +19,7 @@ import java.util.List;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.util.Util;
public class StickerPickerView extends RecyclerView {
@@ -130,25 +132,26 @@ public class StickerPickerView extends RecyclerView {
private void deleteSticker(File stickerFile) {
if (stickerFile != null && stickerFile.exists()) {
new androidx.appcompat.app.AlertDialog.Builder(context)
.setTitle(R.string.delete)
.setMessage(R.string.ask_delete_sticker)
.setPositiveButton(
R.string.delete,
(dialog, which) -> {
if (stickerFile.delete()) {
int position = stickerFiles.indexOf(stickerFile);
if (position >= 0) {
stickerFiles.remove(position);
notifyItemRemoved(position);
}
if (listener != null) {
listener.onStickerDeleted(stickerFile);
}
}
})
.setNegativeButton(R.string.cancel, null)
.show();
AlertDialog dialog =
new AlertDialog.Builder(context)
.setMessage(R.string.ask_delete_sticker)
.setPositiveButton(
R.string.delete,
(d, which) -> {
if (stickerFile.delete()) {
int position = stickerFiles.indexOf(stickerFile);
if (position >= 0) {
stickerFiles.remove(position);
notifyItemRemoved(position);
}
if (listener != null) {
listener.onStickerDeleted(stickerFile);
}
}
})
.setNegativeButton(R.string.cancel, null)
.show();
Util.redPositiveButton(dialog);
}
}
@@ -3,18 +3,24 @@ package org.thoughtcrime.securesms.connect;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.OpenableColumns;
import android.webkit.MimeTypeMap;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.b44t.messenger.DcContext;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Arrays;
import java.util.Objects;
import org.thoughtcrime.securesms.util.MediaUtil;
public class AttachmentsContentProvider extends ContentProvider {
private static final String[] ALL_COLUMNS = {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
/* We save all attachments in our private files-directory
that cannot be read by other apps.
@@ -46,7 +52,8 @@ public class AttachmentsContentProvider extends ContentProvider {
}
@Override
public int delete(@NonNull Uri arg0, String arg1, String[] arg2) {
public int delete(
@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@@ -55,7 +62,7 @@ public class AttachmentsContentProvider extends ContentProvider {
String file = uri.getPathSegments().get(0);
String mimeType = DcHelper.sharedFiles.get(file);
return DcHelper.checkMime(uri.toString(), mimeType);
return DcHelper.checkMime(uri.getPathSegments().get(1), mimeType);
}
@Override
@@ -65,22 +72,69 @@ public class AttachmentsContentProvider extends ContentProvider {
}
@Override
public Uri insert(@NonNull Uri arg0, ContentValues arg1) {
public Uri insert(@NonNull Uri arg0, ContentValues values) {
return null;
}
@Override
public boolean onCreate() {
return false;
return true;
}
@Override
public Cursor query(@NonNull Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4) {
return null;
public Cursor query(
@NonNull Uri uri,
@Nullable String[] projection,
@Nullable String selection,
@Nullable String[] selectionArgs,
@Nullable String sortOrder) {
// Segment [0] is the blob file key ("ef39a39"), same as in openFile() and getType()
// Segment [1] is the original display name ("text.txt")
String file = uri.getPathSegments().get(0);
// Same guard used in openFile()
if (!DcHelper.sharedFiles.containsKey(file)) {
return null;
}
// Resolve the actual File
DcContext dcContext = DcHelper.getContext(Objects.requireNonNull(getContext()));
File privateFile = new File(dcContext.getBlobdir(), file);
// Default to all supported columns
if (projection == null) {
projection = ALL_COLUMNS;
}
// Build column-name and value arrays
String[] cols = new String[projection.length];
Object[] values = new Object[projection.length];
int i = 0;
for (String col : projection) {
if (OpenableColumns.DISPLAY_NAME.equals(col)) {
cols[i] = OpenableColumns.DISPLAY_NAME;
values[i++] = uri.getPathSegments().get(1);
} else if (OpenableColumns.SIZE.equals(col)) {
cols[i] = OpenableColumns.SIZE;
values[i++] = privateFile.length();
}
}
// Set arrays to only matched columns.
cols = Arrays.copyOf(cols, i);
values = Arrays.copyOf(values, i);
MatrixCursor cursor = new MatrixCursor(cols, 1);
cursor.addRow(values);
return cursor;
}
@Override
public int update(@NonNull Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
public int update(
@NonNull Uri uri,
@Nullable ContentValues values,
@Nullable String selection,
@Nullable String[] selectionArgs) {
return 0;
}
}
@@ -145,8 +145,6 @@ public class DcHelper {
dcContext.setStockTranslation(91, context.getString(R.string.devicemsg_self_deleted));
dcContext.setStockTranslation(97, context.getString(R.string.forwarded));
dcContext.setStockTranslation(98, context.getString(R.string.devicemsg_storage_exceeding));
dcContext.setStockTranslation(99, context.getString(R.string.n_bytes_message));
dcContext.setStockTranslation(100, context.getString(R.string.download_max_available_until));
dcContext.setStockTranslation(103, context.getString(R.string.incoming_messages));
dcContext.setStockTranslation(104, context.getString(R.string.outgoing_messages));
dcContext.setStockTranslation(107, context.getString(R.string.connectivity_connected));
@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.connect;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -11,6 +10,7 @@ import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import org.thoughtcrime.securesms.ConversationListActivity;
@@ -119,7 +119,7 @@ public class KeepAliveService extends Service {
private static boolean ch_created = false;
@TargetApi(Build.VERSION_CODES.O)
@RequiresApi(Build.VERSION_CODES.O)
private static void createFgNotificationChannel(Context context) {
if (!ch_created) {
ch_created = true;
@@ -0,0 +1,53 @@
package org.thoughtcrime.securesms.geolocation;
import android.content.Context;
import android.content.SharedPreferences;
import java.util.HashSet;
import java.util.Set;
public final class ActiveLocationChats {
private static final String PREFS_NAME = "location_streaming";
private static final String KEY_ACTIVE = "active_chat_ids";
private ActiveLocationChats() {}
private static SharedPreferences prefs(Context context) {
return context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
}
/**
* Add a chat. Uses commit() to guarantee the write reaches disk before the process can die to
* preserve the superset invariant.
*/
static void add(Context context, int chatId) {
Set<String> current = new HashSet<>(getAll(context));
current.add(String.valueOf(chatId));
prefs(context).edit().putStringSet(KEY_ACTIVE, current).commit();
}
public static void remove(Context context, int chatId) {
Set<String> current = new HashSet<>(getAll(context));
current.remove(String.valueOf(chatId));
prefs(context).edit().putStringSet(KEY_ACTIVE, current).apply();
}
static void clear(Context context) {
prefs(context).edit().remove(KEY_ACTIVE).apply();
}
static Set<Integer> getAllIds(Context context) {
Set<Integer> ids = new HashSet<>();
for (String s : getAll(context)) {
try {
ids.add(Integer.parseInt(s));
} catch (NumberFormatException ignored) {
}
}
return ids;
}
private static Set<String> getAll(Context context) {
return prefs(context).getStringSet(KEY_ACTIVE, new HashSet<>());
}
}
@@ -1,128 +0,0 @@
package org.thoughtcrime.securesms.geolocation;
import android.location.Location;
import android.util.Log;
import java.util.Observable;
public class DcLocation extends Observable {
private static final String TAG = DcLocation.class.getSimpleName();
private Location lastLocation;
private static DcLocation instance;
private static final int TIMEOUT = 1000 * 15;
private static final int EARTH_RADIUS = 6371;
private DcLocation() {
lastLocation = getDefault();
}
public static DcLocation getInstance() {
if (instance == null) {
instance = new DcLocation();
}
return instance;
}
public Location getLastLocation() {
return lastLocation;
}
public boolean isValid() {
return !"?".equals(lastLocation.getProvider());
}
void updateLocation(Location location) {
if (isBetterLocation(location, lastLocation)) {
lastLocation = location;
instance.setChanged();
instance.notifyObservers();
}
}
void reset() {
updateLocation(getDefault());
}
private Location getDefault() {
return new Location("?");
}
/**
* https://developer.android.com/guide/topics/location/strategies Determines whether one Location
* reading is better than the current Location fix
*
* @param location The new Location that you want to evaluate
* @param currentBestLocation The current Location fix, to which you want to compare the new one
*/
private boolean isBetterLocation(Location location, Location currentBestLocation) {
if (currentBestLocation == null) {
// A new location is always better than no location
return true;
}
// Check whether the new location fix is newer or older
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyOlder = timeDelta < -TIMEOUT;
if (isSignificantlyOlder) {
return false;
}
// Check whether the new location fix is more or less accurate
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
Log.d(TAG, "accuracyDelta: " + accuracyDelta);
boolean isSignificantlyMoreAccurate = accuracyDelta > 50;
boolean isSameProvider =
isSameProvider(location.getProvider(), currentBestLocation.getProvider());
if (isSignificantlyMoreAccurate && isSameProvider) {
return true;
}
boolean isMoreAccurate = accuracyDelta > 0;
double distance = distance(location, currentBestLocation);
return hasLocationChanged(distance) && isMoreAccurate
|| hasLocationSignificantlyChanged(distance);
}
private boolean hasLocationSignificantlyChanged(double distance) {
return distance > 30D;
}
private boolean hasLocationChanged(double distance) {
return distance > 10D;
}
private double distance(Location location, Location currentBestLocation) {
double startLat = location.getLatitude();
double startLong = location.getLongitude();
double endLat = currentBestLocation.getLatitude();
double endLong = currentBestLocation.getLongitude();
double dLat = Math.toRadians(endLat - startLat);
double dLong = Math.toRadians(endLong - startLong);
startLat = Math.toRadians(startLat);
endLat = Math.toRadians(endLat);
double a = haversin(dLat) + Math.cos(startLat) * Math.cos(endLat) * haversin(dLong);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double distance = EARTH_RADIUS * c * 1000;
Log.d(TAG, "Distance between location updates: " + distance);
return distance;
}
private double haversin(double val) {
return Math.pow(Math.sin(val / 2), 2);
}
/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
}
@@ -1,125 +0,0 @@
package org.thoughtcrime.securesms.geolocation;
import static android.content.Context.BIND_AUTO_CREATE;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.location.Location;
import android.os.IBinder;
import android.util.Log;
import com.b44t.messenger.DcContext;
import java.util.LinkedList;
import java.util.Observable;
import java.util.Observer;
import org.thoughtcrime.securesms.connect.DcHelper;
public class DcLocationManager implements Observer {
private static final String TAG = DcLocationManager.class.getSimpleName();
private LocationBackgroundService.LocationBackgroundServiceBinder serviceBinder;
private final Context context;
private DcLocation dcLocation = DcLocation.getInstance();
private final LinkedList<Integer> pendingShareLastLocation = new LinkedList<>();
private final ServiceConnection serviceConnection =
new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "background service connected");
serviceBinder = (LocationBackgroundService.LocationBackgroundServiceBinder) service;
while (!pendingShareLastLocation.isEmpty()) {
shareLastLocation(pendingShareLastLocation.pop());
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "background service disconnected");
serviceBinder = null;
}
};
public DcLocationManager(Context context, DcContext dcContext) {
this.context = context.getApplicationContext();
DcLocation.getInstance().addObserver(this);
if (dcContext.isSendingLocationsToChat(0)) {
startLocationEngine();
}
}
public void startLocationEngine() {
if (serviceBinder == null) {
Intent intent = new Intent(context.getApplicationContext(), LocationBackgroundService.class);
context.bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
}
public void stopLocationEngine() {
if (serviceBinder == null) {
return;
}
context.unbindService(serviceConnection);
serviceBinder.stop();
serviceBinder = null;
}
public void stopSharingLocation(int chatId) {
DcHelper.getContext(context).sendLocationsToChat(chatId, 0);
if (!DcHelper.getContext(context).isSendingLocationsToChat(0)) {
stopLocationEngine();
}
}
public void shareLocation(int duration, int chatId) {
startLocationEngine();
Log.d(TAG, String.format("Share location in chat %d for %d seconds", chatId, duration));
DcHelper.getContext(context).sendLocationsToChat(chatId, duration);
if (dcLocation.isValid()) {
writeDcLocationUpdateMessage();
}
}
public void shareLastLocation(int chatId) {
if (serviceBinder == null) {
pendingShareLastLocation.push(chatId);
startLocationEngine();
return;
}
if (dcLocation.isValid()) {
DcHelper.getContext(context).sendLocationsToChat(chatId, 1);
writeDcLocationUpdateMessage();
}
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof DcLocation) {
dcLocation = (DcLocation) o;
if (dcLocation.isValid()) {
writeDcLocationUpdateMessage();
}
}
}
private void writeDcLocationUpdateMessage() {
Log.d(
TAG,
"Share location: "
+ dcLocation.getLastLocation().getLatitude()
+ ", "
+ dcLocation.getLastLocation().getLongitude());
Location lastLocation = dcLocation.getLastLocation();
boolean continueLocationStreaming =
DcHelper.getContext(context)
.setLocation(
(float) lastLocation.getLatitude(),
(float) lastLocation.getLongitude(),
lastLocation.getAccuracy());
if (!continueLocationStreaming) {
stopLocationEngine();
}
}
}
@@ -1,142 +0,0 @@
package org.thoughtcrime.securesms.geolocation;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.NonNull;
public class LocationBackgroundService extends Service {
private static final int INITIAL_TIMEOUT = 1000 * 60 * 2;
private static final String TAG = LocationBackgroundService.class.getSimpleName();
private LocationManager locationManager = null;
private static final int LOCATION_INTERVAL = 1000;
private static final float LOCATION_DISTANCE = 25F;
ServiceLocationListener locationListener;
private final IBinder mBinder = new LocationBackgroundServiceBinder();
@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
return super.bindService(service, conn, flags);
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onCreate() {
locationManager =
(LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
if (locationManager == null) {
Log.e(TAG, "Unable to initialize location service");
return;
}
locationListener = new ServiceLocationListener();
Location lastLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (lastLocation != null) {
long locationAge = System.currentTimeMillis() - lastLocation.getTime();
if (locationAge <= 600 * 1000) { // not older than 10 minutes
DcLocation.getInstance().updateLocation(lastLocation);
}
}
// requestLocationUpdate(LocationManager.NETWORK_PROVIDER);
requestLocationUpdate(LocationManager.GPS_PROVIDER);
initialLocationUpdate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
if (locationManager == null) {
return;
}
try {
locationManager.removeUpdates(locationListener);
} catch (Exception ex) {
Log.i(TAG, "fail to remove location listeners, ignore", ex);
}
}
private void requestLocationUpdate(String provider) {
try {
locationManager.requestLocationUpdates(
provider, LOCATION_INTERVAL, LOCATION_DISTANCE, locationListener);
} catch (SecurityException | IllegalArgumentException ex) {
Log.e(
TAG,
String.format("Unable to request %s provider based location updates.", provider),
ex);
}
}
private void initialLocationUpdate() {
try {
Location gpsLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (gpsLocation != null
&& System.currentTimeMillis() - gpsLocation.getTime() < INITIAL_TIMEOUT) {
locationListener.onLocationChanged(gpsLocation);
}
} catch (NullPointerException | SecurityException e) {
e.printStackTrace();
}
}
class LocationBackgroundServiceBinder extends Binder {
LocationBackgroundServiceBinder getService() {
return LocationBackgroundServiceBinder.this;
}
void stop() {
DcLocation.getInstance().reset();
stopSelf();
}
}
private class ServiceLocationListener implements LocationListener {
@Override
public void onLocationChanged(@NonNull Location location) {
Log.d(TAG, "onLocationChanged: " + location);
if (location == null) {
return;
}
DcLocation.getInstance().updateLocation(location);
}
@Override
public void onProviderDisabled(@NonNull String provider) {
Log.e(TAG, "onProviderDisabled: " + provider);
}
@Override
public void onProviderEnabled(@NonNull String provider) {
Log.e(TAG, "onProviderEnabled: " + provider);
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.e(TAG, "onStatusChanged: " + provider + " status: " + status);
}
}
}
@@ -0,0 +1,40 @@
package org.thoughtcrime.securesms.geolocation;
import android.location.Location;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
/**
* Process-wide holder for the current streamed location. Foreground service writes, UI observes.
*/
public final class LocationData {
private static final LocationData INSTANCE = new LocationData();
private final MutableLiveData<Location> liveLocation = new MutableLiveData<>();
private LocationData() {}
public static LocationData getInstance() {
return INSTANCE;
}
public LiveData<Location> observable() {
return liveLocation;
}
@Nullable
public Location current() {
return liveLocation.getValue();
}
void post(@NonNull Location location) {
liveLocation.postValue(location);
}
void clear() {
liveLocation.postValue(null);
}
}
@@ -0,0 +1,17 @@
package org.thoughtcrime.securesms.geolocation;
import android.content.Context;
import android.location.Location;
import androidx.annotation.NonNull;
/** Abstraction over platform LocationManager and GMS FusedLocationProviderClient. */
public interface LocationSource {
void startUpdates(@NonNull Context context, @NonNull Callback callback);
void stopUpdates();
interface Callback {
void onLocationUpdate(@NonNull Location location);
}
}
@@ -0,0 +1,231 @@
package org.thoughtcrime.securesms.geolocation;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.location.Location;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.core.app.ServiceCompat;
import androidx.core.content.ContextCompat;
import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.connect.DcHelper;
public class LocationStreamingService extends Service {
private static final String TAG = LocationStreamingService.class.getSimpleName();
private static final String ACTION_STOP = "org.thoughtcrime.securesms.geolocation.STOP_STREAMING";
private static final int NOTIFICATION_ID = 8801;
private static final String CHANNEL_ID = "location_streaming";
private static volatile boolean running = false;
private LocationSource source;
private Location lastPublished;
// static API
/** Register a chat for location updates, then ensure the service is running. */
public static void startSharing(Context context, int chatId, int durationSeconds) {
ActiveLocationChats.add(context, chatId);
DcHelper.getContext(context).sendLocationsToChat(chatId, durationSeconds);
ContextCompat.startForegroundService(
context, new Intent(context, LocationStreamingService.class));
}
/** Unregister a chat. If no chats remain, stop the service. */
public static void stopSharing(Context context, int chatId) {
ActiveLocationChats.remove(context, chatId);
DcHelper.getContext(context).sendLocationsToChat(chatId, 0);
if (!DcHelper.getContext(context).isSendingLocationsToChat(0)) {
context.stopService(new Intent(context, LocationStreamingService.class));
}
}
public static void ensureRunning(Context context) {
if (!hasLocationPermission(context)) {
for (int chatId : ActiveLocationChats.getAllIds(context)) {
DcHelper.getContext(context).sendLocationsToChat(chatId, 0);
}
ActiveLocationChats.clear(context);
return;
}
ContextCompat.startForegroundService(
context, new Intent(context, LocationStreamingService.class));
}
public static boolean isRunning() {
return running;
}
// lifecycle
@Override
public void onCreate() {
super.onCreate();
if (!hasLocationPermission(this)) {
Log.w(TAG, "Location permission not granted, stopping");
stopSelf();
return;
}
running = true;
promoteToForeground();
beginLocationUpdates();
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
if (intent != null && ACTION_STOP.equals(intent.getAction())) {
stopAllSharing();
stopSelf();
return START_NOT_STICKY;
}
// If the service is already running, and we already have a fix,
// push it immediately.
if (lastPublished != null) {
publishAndWrite(lastPublished);
}
return START_STICKY;
}
private void stopAllSharing() {
for (int chatId : ActiveLocationChats.getAllIds(this)) {
DcHelper.getContext(this).sendLocationsToChat(chatId, 0);
}
ActiveLocationChats.clear(this);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
running = false;
if (source != null) {
source.stopUpdates();
source = null;
}
LocationData.getInstance().clear();
super.onDestroy();
}
@Override
public void onTimeout(int startId) {
stopSelf();
}
@Override
public void onTimeout(int startId, int fgsType) {
stopSelf();
}
// location
private void beginLocationUpdates() {
source = LocationSourceFactory.create(this);
source.startUpdates(this, this::onNewLocation);
}
private void onNewLocation(Location location) {
Log.d(TAG, "onNewLocation raw: " + location);
publishAndWrite(location);
lastPublished = location;
}
private void publishAndWrite(Location location) {
LocationData.getInstance().post(location);
boolean keepGoing =
DcHelper.getContext(this)
.setLocation(
(float) location.getLatitude(),
(float) location.getLongitude(),
location.getAccuracy());
Log.d(TAG, "keepGoing: " + keepGoing);
if (!keepGoing) {
stopAllSharing();
stopSelf();
}
}
// foreground / notification
private void promoteToForeground() {
ensureNotificationChannel();
Notification notification = buildNotification();
try {
int type =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
? ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION
: 0;
ServiceCompat.startForeground(this, NOTIFICATION_ID, notification, type);
} catch (Exception e) {
// SecurityException on API 34+ if permission missing,
// ForegroundServiceStartNotAllowedException on API 31+ if in background.
Log.e(TAG, "Cannot promote to foreground", e);
stopSelf();
}
}
private void ensureNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel =
new NotificationChannel(
CHANNEL_ID,
getString(R.string.location_streaming_notification_title),
NotificationManager.IMPORTANCE_LOW);
channel.setDescription(getString(R.string.location_streaming_channel_desc));
channel.setShowBadge(false);
NotificationManager nm = getSystemService(NotificationManager.class);
if (nm != null) nm.createNotificationChannel(channel);
}
}
private Notification buildNotification() {
Intent tapIntent = new Intent(this, ConversationListActivity.class);
PendingIntent contentPendingIntent =
PendingIntent.getActivity(
this, 0, tapIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
Intent stopIntent = new Intent(this, LocationStreamingService.class);
stopIntent.setAction(ACTION_STOP);
PendingIntent stopPendingIntent =
PendingIntent.getService(this, 1, stopIntent, PendingIntent.FLAG_IMMUTABLE);
return new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(getString(R.string.location_streaming_notification_title))
.setContentText(getString(R.string.location_streaming_notification_text))
.setSmallIcon(R.drawable.ic_location_on_white_24dp)
.setOngoing(true)
.setContentIntent(contentPendingIntent)
.addAction(
R.drawable.ic_stop_circle, getString(R.string.stop_sharing_location), stopPendingIntent)
.setPriority(NotificationCompat.PRIORITY_LOW)
.build();
}
private static boolean hasLocationPermission(Context context) {
return ContextCompat.checkSelfPermission(
context, android.Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(
context, android.Manifest.permission.ACCESS_COARSE_LOCATION)
== PackageManager.PERMISSION_GRANTED;
}
}
@@ -0,0 +1,88 @@
package org.thoughtcrime.securesms.geolocation;
import android.content.Context;
import android.location.LocationManager;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.core.location.LocationListenerCompat;
import androidx.core.location.LocationManagerCompat;
import androidx.core.location.LocationRequestCompat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
public class PlatformLocationSource implements LocationSource {
private static final String TAG = PlatformLocationSource.class.getSimpleName();
private static final long UPDATE_INTERVAL_MS = 0;
private LocationManager locationManager;
private final List<LocationListenerCompat> activeListeners = new ArrayList<>();
@Override
public void startUpdates(@NonNull Context context, @NonNull Callback callback) {
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
if (locationManager == null) {
Log.e(TAG, "LocationManager unavailable");
return;
}
boolean registered = false;
// API 31+: try the platform fused provider first
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
registered = requestProvider(context, LocationManager.FUSED_PROVIDER, callback);
}
// Fall back (or complement) with individual providers
if (!registered) {
requestProvider(context, LocationManager.GPS_PROVIDER, callback);
requestProvider(context, LocationManager.NETWORK_PROVIDER, callback);
}
}
private boolean requestProvider(Context context, String provider, Callback callback) {
if (locationManager == null) return false;
if (!locationManager.isProviderEnabled(provider)) {
Log.d(TAG, "Provider " + provider + " not enabled, skipping");
return false;
}
try {
LocationRequestCompat request =
new LocationRequestCompat.Builder(UPDATE_INTERVAL_MS)
.setMinUpdateDistanceMeters(0)
.setQuality(LocationRequestCompat.QUALITY_HIGH_ACCURACY)
.build();
LocationListenerCompat listener = callback::onLocationUpdate;
Executor mainExecutor = ContextCompat.getMainExecutor(context);
LocationManagerCompat.requestLocationUpdates(
locationManager, provider, request, mainExecutor, listener);
activeListeners.add(listener);
Log.d(TAG, "Registered on provider: " + provider);
return true;
} catch (SecurityException | IllegalArgumentException e) {
Log.e(TAG, "Cannot request " + provider + " updates", e);
return false;
}
}
@Override
public void stopUpdates() {
if (locationManager != null) {
for (LocationListenerCompat listener : activeListeners) {
try {
locationManager.removeUpdates(listener);
} catch (Exception e) {
Log.w(TAG, "Error removing listener", e);
}
}
activeListeners.clear();
}
}
}
@@ -47,7 +47,6 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.MediaPreviewActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.ShareLocationDialog;
@@ -64,7 +63,8 @@ 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;
import org.thoughtcrime.securesms.geolocation.ActiveLocationChats;
import org.thoughtcrime.securesms.geolocation.LocationStreamingService;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
import org.thoughtcrime.securesms.scribbles.ScribbleActivity;
@@ -490,47 +490,36 @@ public class AttachmentManager {
}
public static void selectLocation(Activity activity, int chatId) {
ApplicationContext applicationContext = ApplicationContext.getInstance(activity);
DcLocationManager dcLocationManager = applicationContext.getLocationManager();
Context appContext = activity.getApplicationContext();
if (DcHelper.getContext(applicationContext).isSendingLocationsToChat(chatId)) {
dcLocationManager.stopSharingLocation(chatId);
return;
if (DcHelper.getContext(appContext).isSendingLocationsToChat(chatId)) {
if (LocationStreamingService.isRunning()) {
LocationStreamingService.stopSharing(appContext, chatId);
return;
}
// Stale service is dead but chat layer still thinks it's sharing.
// Clean up this chat and fall through to the fresh start flow.
ActiveLocationChats.remove(appContext, chatId);
DcHelper.getContext(appContext).sendLocationsToChat(chatId, 0);
}
// see
// https://support.google.com/googleplay/android-developer/answer/9799150#zippy=%2Cstep-provide-prominent-in-app-disclosure
// for rationale dialog requirements
Permissions.PermissionsBuilder permissionsBuilder =
Permissions.with(activity)
.ifNecessary()
.withRationaleDialog(
"To share your live location with chat members, allow ArcaneChat to use your location data.\n\nTo make live location work gaplessly, location data is used even when the app is closed or not in use.",
R.drawable.ic_location_on_white_24dp)
.withPermanentDenialDialog(
activity.getString(R.string.perm_explain_access_to_location_denied))
.onAllGranted(
() -> {
ShareLocationDialog.show(
activity,
durationInSeconds -> {
if (durationInSeconds == 1) {
dcLocationManager.shareLastLocation(chatId);
} else {
dcLocationManager.shareLocation(durationInSeconds, chatId);
}
});
});
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
permissionsBuilder.request(
Manifest.permission.ACCESS_BACKGROUND_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION);
} else {
permissionsBuilder.request(
Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION);
}
permissionsBuilder.execute();
Permissions.with(activity)
.ifNecessary()
.withRationaleDialog(
activity.getString(R.string.location_rationale), R.drawable.ic_location_on_white_24dp)
.withPermanentDenialDialog(
activity.getString(R.string.perm_explain_access_to_location_denied))
.onAllGranted(
() -> {
ShareLocationDialog.show(
activity,
durationInSeconds ->
LocationStreamingService.startSharing(appContext, chatId, durationInSeconds));
})
.request(
android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.ACCESS_COARSE_LOCATION)
.execute();
}
private @Nullable Uri getSlideUri() {
@@ -208,7 +208,6 @@ public class PersistentBlobProvider {
return getFile(context, ContentUris.parseId(uri)).delete();
}
//noinspection SimplifiableIfStatement
if (isExternalBlobUri(context, uri)) {
return new File(uri.getPath()).delete();
}
@@ -287,6 +286,20 @@ public class PersistentBlobProvider {
private static @NonNull File getExternalDir(Context context) throws IOException {
File externalDir = context.getExternalCacheDir();
if (externalDir != null) {
try {
FileProviderUtil.getUriFor(context, new File(externalDir, "test"));
} catch (IllegalArgumentException e) {
Log.w(
TAG,
"External cache dir not resolvable by FileProvider, "
+ "falling back to internal cache",
e);
externalDir = null;
}
}
if (externalDir == null) {
externalDir = context.getCacheDir();
}
@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.relay;
import static org.thoughtcrime.securesms.connect.DcHelper.CONFIG_PROXY_ENABLED;
import static org.thoughtcrime.securesms.connect.DcHelper.getContext;
import android.content.DialogInterface;
@@ -24,7 +23,6 @@ import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.SwitchCompat;
import androidx.constraintlayout.widget.Group;
import chat.delta.rpc.Rpc;
import chat.delta.rpc.RpcException;
@@ -44,7 +42,6 @@ import org.thoughtcrime.securesms.WelcomeActivity;
import org.thoughtcrime.securesms.connect.DcEventCenter;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.proxy.ProxySettingsActivity;
import org.thoughtcrime.securesms.util.IntentUtils;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
@@ -79,8 +76,6 @@ public class EditRelayActivity extends BaseActionBarActivity
Spinner smtpSecurity;
Spinner certCheck;
private SwitchCompat proxySwitch;
Rpc rpc;
int accId;
@@ -116,13 +111,6 @@ public class EditRelayActivity extends BaseActionBarActivity
smtpSecurity = findViewById(R.id.smtp_security);
certCheck = findViewById(R.id.cert_check);
proxySwitch = findViewById(R.id.proxy_settings);
proxySwitch.setOnClickListener(
l -> {
proxySwitch.setChecked(!proxySwitch.isChecked()); // revert toggle
startActivity(new Intent(this, ProxySettingsActivity.class));
});
String addr = getIntent().getStringExtra(EXTRA_ADDR);
EnteredLoginParam config = null;
try {
@@ -185,10 +173,6 @@ public class EditRelayActivity extends BaseActionBarActivity
boolean expandAdvanced = false;
int intVal;
intVal = DcHelper.getInt(this, CONFIG_PROXY_ENABLED);
proxySwitch.setChecked(intVal == 1);
expandAdvanced = expandAdvanced || intVal == 1;
if (config != null) { // configured
emailInput.setText(config.addr);
if (!TextUtils.isEmpty(config.addr)) {
@@ -246,7 +230,6 @@ public class EditRelayActivity extends BaseActionBarActivity
@Override
public void onResume() {
super.onResume();
proxySwitch.setChecked(DcHelper.getInt(this, CONFIG_PROXY_ENABLED) == 1);
}
private void showLog() {
@@ -6,6 +6,7 @@ import android.util.Log;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ScrollView;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
@@ -99,7 +100,8 @@ public class RelayListActivity extends BaseActionBarActivity
PulsingFloatingActionButton fabAdd = findViewById(R.id.fab_add_relay);
// add padding to avoid content hidden behind system bars
ViewUtil.applyWindowInsets(recyclerView);
ScrollView scrollView = findViewById(R.id.relay_scroll_view);
ViewUtil.applyWindowInsets(scrollView);
// Apply insets to prevent fab from being covered by system bars
ViewUtil.applyWindowInsetsAsMargin(fabAdd);
@@ -198,10 +198,6 @@ public class Prefs {
return true;
}
public static boolean isCallsEnabled(Context context) {
return true;
}
// ringtone
public static @NonNull Uri getNotificationRingtone(Context context) {
@@ -15,6 +15,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -57,34 +58,34 @@ public class WebRTCClient {
private final Context context;
private final Handler mainHandler;
private PeerConnectionFactory peerConnectionFactory;
private PeerConnection peerConnection;
private List<PeerConnection.IceServer> iceServers;
private volatile PeerConnection peerConnection;
private volatile List<PeerConnection.IceServer> iceServers;
private DataChannel iceTricklingDataChannel;
private DataChannel mutedStateDataChannel;
// ICE candidate
private final List<IceCandidate> iceCandidateBuffer;
private boolean iceTricklingChannelOpen;
private boolean mutedStateChannelOpen;
private boolean enableIceTrickling;
private boolean isEnded = false;
private volatile boolean iceTricklingChannelOpen;
private volatile boolean mutedStateChannelOpen;
private volatile boolean enableIceTrickling;
private final AtomicBoolean isEnded = new AtomicBoolean(false);
// ICE gathering
private volatile boolean isIceGatheringComplete;
private volatile boolean hasRelayCandidate;
private volatile boolean hasSrflxCandidate;
private volatile boolean hasHostCandidate;
private CountDownLatch iceGatheringLatch;
private CountDownLatch relayCandidateLatch;
private CountDownLatch srflxCandidateLatch;
private volatile CountDownLatch iceGatheringLatch;
private volatile CountDownLatch relayCandidateLatch;
private volatile CountDownLatch srflxCandidateLatch;
// Media
private MediaStream localStream;
private VideoTrack localVideoTrack;
private AudioTrack localAudioTrack;
private VideoTrack remoteVideoTrack;
private AudioTrack remoteAudioTrack;
private volatile VideoTrack localVideoTrack;
private volatile AudioTrack localAudioTrack;
private volatile VideoTrack remoteVideoTrack;
private volatile AudioTrack remoteAudioTrack;
// Callbacks to ViewModel
private final Callbacks callbacks;
@@ -644,7 +645,7 @@ public class WebRTCClient {
boolean gotIce = waitForEnoughIce();
synchronized (WebRTCClient.this) {
if (isEnded || peerConnection == null) {
if (isEnded.get() || peerConnection == null) {
Log.d(TAG, "Call ended during ICE gathering, aborting");
return;
}
@@ -803,7 +804,7 @@ public class WebRTCClient {
boolean gotIce = waitForEnoughIce();
synchronized (WebRTCClient.this) {
if (isEnded || peerConnection == null) {
if (isEnded.get() || peerConnection == null) {
Log.d(TAG, "Call ended during ICE gathering, aborting");
return;
}
@@ -939,12 +940,10 @@ public class WebRTCClient {
// Cleanup
public synchronized void endCall() {
if (isEnded) {
if (!isEnded.compareAndSet(false, true)) {
Log.d(TAG, "endCall() already called, skipping");
return;
}
isEnded = true;
Log.d(TAG, "Ending call");
// Unblock any thread waiting in waitForEnoughIce()
+11
View File
@@ -0,0 +1,11 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M320,640L640,640L640,320L320,320L320,640ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z" />
</vector>
+2 -13
View File
@@ -129,7 +129,7 @@
android:visibility="gone"
tools:visibility="visible"
app:constraint_referenced_ids="inbox, imap_login, imap_server, imap_port, imap_security_label, imap_security, outbox_view_spacer_top,
outbox, smtp_login, smtp_password, smtp_server, smtp_port, smtp_security_label, smtp_security, cert_check_label, cert_check, view_log_button, proxy_settings" />
outbox, smtp_login, smtp_password, smtp_server, smtp_port, smtp_security_label, smtp_security, cert_check_label, cert_check, view_log_button" />
<ImageView
android:id="@+id/advanced_icon"
@@ -157,17 +157,6 @@
app:layout_constraintStart_toEndOf="@id/advanced_icon"
app:layout_constraintTop_toBottomOf="@id/no_servers_hint" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/proxy_settings"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:text="@string/proxy_use_proxy"
app:layout_constraintEnd_toEndOf="@id/guideline_root_end"
app:layout_constraintStart_toStartOf="@id/guideline_root_start"
app:layout_constraintTop_toBottomOf="@id/advanced_text" />
<TextView
android:id="@+id/inbox"
android:layout_width="wrap_content"
@@ -176,7 +165,7 @@
android:layout_marginTop="8dp"
android:text="@string/login_inbox"
app:layout_constraintStart_toStartOf="@id/guideline_root_start"
app:layout_constraintTop_toBottomOf="@id/proxy_settings" />
app:layout_constraintTop_toBottomOf="@id/advanced_text" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/imap_login"
+32 -8
View File
@@ -1,21 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/status_bar_background" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/relay_list"
<ScrollView
android:id="@+id/relay_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="88dp"
android:scrollbars="vertical"
tools:listitem="@layout/relay_list_item" />
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/relay_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
android:scrollbars="none"
tools:listitem="@layout/relay_list_item" />
<TextView
android:id="@+id/transport_list_hint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:text="@string/transport_list_hint"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary" />
</LinearLayout>
</ScrollView>
<org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton
android:id="@+id/fab_add_relay"
@@ -28,4 +52,4 @@
android:focusable="true"
android:contentDescription="@string/add_transport" />
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
+5
View File
@@ -31,6 +31,11 @@
android:id="@+id/menu_marknoticed_selected"
app:showAsAction="never" />
<item
android:title="@string/mark_as_unread"
android:id="@+id/menu_markfresh_selected"
app:showAsAction="never" />
<item
android:title="@string/add_to_home_screen"
android:id="@+id/menu_add_to_home_screen"
-4
View File
@@ -615,10 +615,6 @@
<string name="up_to_x_most_worse_quality_images">До %1$s, повечето от които изображения с по-лошо качество</string>
<string name="up_to_x_most_balanced_quality_images">До %1$s, повечето от които изображения с балансирано качество</string>
<string name="download_failed">Неуспешно сваляне</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 съобщение</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Сваляне на максимално наличното до %1$s</string>
<string name="profile_image_select">Избор на изображение за профила</string>
<string name="select_your_new_profile_image">Изберете новото изображение за профила си</string>
<string name="profile_image_delete">Изтриване на изображението на профила</string>
-2
View File
@@ -397,8 +397,6 @@
<string name="pref_show_emails_all">پوی</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</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</string>
<string name="profile_image_select">پسند شؽوات پوروفایل</string>
<string name="profile_image_delete">پاک کردن شؽوات پوروفایل</string>
<string name="pref_edit_profile">آلشت پوروفایل</string>
+17 -16
View File
@@ -69,6 +69,8 @@
<string name="mark_as_read">Marca-ho com a llegit</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">Llegit</string>
<!-- Shortest text for "Mark as being unread". In english, this could be "Unread" (past tense of "to unread"), in german, this could be "Ungelesen". -->
<string name="mark_as_unread_short">No llegit</string>
<!-- Placeholder text when something is loading -->
<string name="loading">S\'està carregant...</string>
<string name="hide">Amaga</string>
@@ -100,7 +102,7 @@
<string name="email_address">Adreça de correu electrònic</string>
<string name="bad_email_address">Adreça de correu electrònic errònia.</string>
<string name="password">Contrasenya</string>
<string name="existing_password">Ja existeix aquesta contrasenya</string>
<string name="existing_password">Contrasenya existent</string>
<string name="now">Ara</string>
<!-- Headline for destructive actions with no undo. Could also be "Caution" or "Warning". -->
<string name="danger">Perill</string>
@@ -413,6 +415,8 @@
<string name="declined_call">Trucada rebutjada</string>
<string name="canceled_call">Trucada cancel·lada</string>
<string name="missed_call">Trucada perduda</string>
<string name="already_in_call">En una trucada</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">Segur que vols sortir?</string>
@@ -606,7 +610,7 @@
<!-- Secondary button on the welcome screen, allows to "Add as Second Device", "Restore from Backup" -->
<string name="onboarding_alternative_logins">Ja tinc un perfil</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">Inici de sessió correu-e clàssic</string>
<string name="manual_account_setup_option">Utilitza correu-e clàssic com a retransmissor</string>
<!-- Instant onboarding title (there is not more to do than to set name and avatar) -->
<string name="instant_onboarding_title">El vostre perfil</string>
<!-- The placeholder will be replaced by the default onboarding server -->
@@ -658,20 +662,20 @@
<string name="proxy_enabled">Servidor intermediari activat</string>
<string name="proxy_enabled_hint">Esteu fent servir un servidor intermediari. Si teniu problemes per a connectar-vos, proveu un servidor intermediari diferent.</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">Res</string>
<string name="add_transport">Afegir Re</string>
<string name="remove_transport">Elimina Re</string>
<string name="edit_transport">Edita Re</string>
<string name="transports">Retransmissors</string>
<string name="add_transport">Afegir Retransmissor</string>
<string name="remove_transport">Elimina Retransmissors</string>
<string name="edit_transport">Edita Retransmissor</string>
<string name="used_for_sending">Utilitzat per enviar</string>
<string name="hide_from_contacts">Amagar als contactes</string>
<string name="hidden_from_contacts">Amagat als contactes</string>
<!-- Hint for the list of relays -->
<string name="transport_list_hint">Missatges rebuts en tots els relés</string>
<string name="transport_list_hint">Els missatges es reben en tots els retransmissors. ⚠️ Si canvies qualsevol cosa aquí, assegura\'t que tots els teus dispositius funcionin com a mínim amb la versió 2.47.0. Sinó, els dispositius més antics podrien no rebre els missatges.</string>
<!-- shown if a QR code was scanned that can be used as a relay -->
<string name="confirm_add_transport">Afegeix aquest re?</string>
<string name="invalid_transport_qr">El codi QR escanejat no conté un re vàlid.</string>
<string name="confirm_add_transport">Afegir aquest retransmissor?</string>
<string name="invalid_transport_qr">El codi QR escanejat no conté un retransmissor vàlid.</string>
<!-- placeholder will be replaced by a relay server name -->
<string name="confirm_remove_or_hide_transport_x">Esborrar el re \"%1$s\"?\n\nEls contactes que només coneixen aquest re no et podran contactar fins que els escriguis de nou.\n\nSi no ho tens clar, amaga\'l enlloc d\'esborrar-lo.</string>
<string name="confirm_remove_or_hide_transport_x">Esborrar el retransmissor \"%1$s\"?\n\nEls contactes que només coneixen aquest retransmissor no et podran contactar fins que els escriguis de nou.\n\nSi no ho tens clar, amaga\'l enlloc d\'esborrar-lo.</string>
<string name="login_certificate_checks">El certificat coincideix</string>
<string name="login_error_mail">Si us plau poseu una adreça d\'email vàlida</string>
<string name="login_error_server">Si us plau poseu un servidor / adreça IP vàlida</string>
@@ -810,10 +814,6 @@
<string name="up_to_x_most_worse_quality_images">Fins a %1$s, les imatges de pitjor qualitat</string>
<string name="up_to_x_most_balanced_quality_images">Fins a %1$s, imatges de qualitat la majoria de vegades.</string>
<string name="download_failed">La baixada ha fallat</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 missatge</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Baixa el màxim disponible fins a %1$s</string>
<string name="profile_image_select">Trieu la imatge de perfil</string>
<string name="select_your_new_profile_image">Trieu la imatge nova de perfil</string>
<string name="profile_image_delete">Esborra la imatge del perfil</string>
@@ -946,10 +946,11 @@
<!-- %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 ha definit el temporitzador dels missatges efímers a %1$s setmanes.</string>
<string name="chat_unencrypted_explanation">Els missatges en aquest xat fan servir el correu electrònic tradicional i no estan xifrats d\'extrem a extrem.</string>
<string name="messages_are_e2ee">Els missatges són xifrats d\'extrem a extrem</string>
<string name="chat_protection_enabled_tap_to_learn_more">Us garantim que a partir d\'ara els missatges són xifrats d\'extrem a extrem. Toqueu per a obtenir més informació.</string>
<string name="chat_protection_enabled_explanation">Ara es garanteix que tots els missatges d\'aquest xat n xifrats d\'extrem a extrem.\n\nEl xifratge d\'extrem a extrem manté privats els missatges entre vós i els companys de xat. Ni tan sols el vostre proveïdor de correu electrònic pot llegir-los.</string>
<string name="chat_protection_enabled_explanation">Tots els missatges en aquest xat estan xifrats d\'extrem a extrem. El xifrat d\'extrem a extrem manté els missatges privats entre tu i els destinataris. Ni tan sols els servidors, els proveïdors o els retransmissors els poden llegir.</string>
<string name="invalid_unencrypted_tap_to_learn_more">%1$srequereix xifratge d\'extrem a extrem, que encara no s\'ha configurat per a aquest xat. Toqueu per a obtenir més informació.</string>
<string name="invalid_unencrypted_explanation">Per a establir xifratge d\'extrem a extrem, heu de trobar-vos amb els contactes en persona i escanejar el seu codi QR per a poder afegir-los.</string>
<string name="invalid_unencrypted_explanation">Per a establir xifratge d\'extrem a extrem, heu de trobar-vos amb els contactes en persona i escanejar el seu codi QR per a afegir-los.</string>
<string name="learn_more">Més informació</string>
<string name="devicemsg_self_deleted">Heu esborrat el xat «Missatges desats».\n\n️ Per a usar altre cop la funcionalitat de «Missatges desats», creeu un xat nou amb vós mateix.</string>
-4
View File
@@ -788,10 +788,6 @@
<string name="up_to_x_most_worse_quality_images">Do %1$s, většina obrázků horší kvality</string>
<string name="up_to_x_most_balanced_quality_images">Do %1$s, většina obrázků vyvážené kvality</string>
<string name="download_failed">Stahování selhalo</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 zpráva</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Stahovat dostupné maximum do %1$s</string>
<string name="profile_image_select">Vybrat profilový obrázek</string>
<string name="select_your_new_profile_image">Vyberte si svůj nový profilový obrázek</string>
<string name="profile_image_delete">Smazat profilový obrázek</string>
+2 -6
View File
@@ -377,7 +377,6 @@
<string name="share_location_for_one_hour">für 1 Stunde</string>
<string name="share_location_for_two_hours">für 2 Stunden</string>
<string name="share_location_for_six_hours">für 6 Stunden</string>
<string name="share_location_for_12_hours">für 12 Stunden</string>
<plurals name="ask_send_following_n_files_to">
<item quantity="one">Die folgende Datei an %s senden?</item>
@@ -671,7 +670,7 @@
<string name="hide_from_contacts">Vor Kontakten verstecken</string>
<string name="hidden_from_contacts">Vor Kontakten versteckt</string>
<!-- Hint for the list of relays -->
<string name="transport_list_hint">Nachrichten werden auf allen Relays empfangen</string>
<string name="transport_list_hint">Nachrichten werden auf allen Relays empfangen.\n\n⚠️ Wenn Sie hier Änderungen vornehmen, stellen Sie sicher, dass auf allen Ihren Geräten mindestens Version 2.47.0 installiert ist. Andernfalls könnten auf älteren Versionen Nachrichten verloren gehen.</string>
<!-- shown if a QR code was scanned that can be used as a relay -->
<string name="confirm_add_transport">Relay hinzufügen?</string>
<string name="invalid_transport_qr">Der gescannte QR-Code enthält keine gültiges Relay.</string>
@@ -815,10 +814,6 @@
<string name="up_to_x_most_worse_quality_images">Bis zu %1$s, die meisten Bilder schlechter Qualität</string>
<string name="up_to_x_most_balanced_quality_images">Bis zu %1$s, die meisten Bilder ausgewogener Qualität</string>
<string name="download_failed">Download gescheitert</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 Nachricht</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Download max. verfügbar bis %1$s</string>
<string name="profile_image_select">Profilbild auswählen</string>
<string name="select_your_new_profile_image">Wählen dein Profilbild aus</string>
<string name="profile_image_delete">Profilbild löschen</string>
@@ -951,6 +946,7 @@
<!-- %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">Ablaufzeit verschwindender Nachrichten auf %1$s Wochen gesetzt von %2$s.</string>
<string name="chat_unencrypted_explanation">Dieser Chat verwendet klassische E-Mails und ist nicht Ende-zu-Ende-verschlüsselt.</string>
<string name="messages_are_e2ee">Nachrichten sind Ende-zu-Ende-verschlüsselt.</string>
<string name="chat_protection_enabled_tap_to_learn_more">Nachrichten sind Ende-zu-Ende-verschlüsselt. Tippen, um mehr zu erfahren.</string>
<string name="chat_protection_enabled_explanation">Alle Nachrichten in diesem Chat sind Ende-zu-Ende verschlüsselt.\n\nDurch die Ende-zu-Ende-Verschlüsselung bleiben die Nachrichten zwischen dir und deinen Chat-Partnern privat. Nicht einmal Server, Provider oder Relays können sie lesen.</string>
<string name="invalid_unencrypted_tap_to_learn_more">⚠️ %1$s verlangt eine Ende-zu-Ende-Verschlüsselung, die für diesen Chat noch nicht hergestellt wurde. Tippen, um mehr zu erfahren.</string>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingTranslation">
<string name="or_separator">oder</string>
<string name="online">online</string>
<string name="pink">Rosa</string>
<string name="gray">Grau</string>
<string name="share_location_for_24_hours">für 24 Stunden</string>
<!-- device messages for updates -->
<string name="device_welcome_message">🔮 Willkommen bei ArcaneChat✨\n\nUm mit Freunden in Kontakt zu treten:\n\n🤳🏻Tippe auf dem Startbildschirm beider Geräte auf „QR-Code“. Wähle auf einem Gerät „QR-Code scannen“ und richte die Kamera auf das andere.\n\n🔗 Falls ihr euch nicht im selben Raum befindet, teilt euren Einladungslink über „Freunde einladen“ im Menü.\n\n📢Tritt dem [Offiziellen ArcaneChat-Kanal](%1$s) (auf Englisch) bei, um über die App-Entwicklung auf dem Laufenden zu bleiben.</string>
</resources>
-4
View File
@@ -526,10 +526,6 @@
<string name="up_to_x_most_worse_quality_images">Έως και %1$s, τις περισσότερες εικόνες χειρότερης ποιότητας</string>
<string name="up_to_x_most_balanced_quality_images">Έως %1$s, εικόνες πιο ισορροπημένης ποιότητας</string>
<string name="download_failed">Αποτυχία Λήψης</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 μήνυμα</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Μέγιστη διαθέσιμη λήψη μέχρι %1$s</string>
<string name="profile_image_select">Επιλογή Εικόνας Προφίλ</string>
<string name="select_your_new_profile_image">Επιλογή Νέας Εικόνας Προφίλ</string>
<string name="profile_image_delete">Διαγραφή Εικόνας Προφίλ</string>
+10 -13
View File
@@ -627,7 +627,7 @@
<!-- Secondary button on the welcome screen, allows to "Add as Second Device", "Restore from Backup" -->
<string name="onboarding_alternative_logins">Ya tengo un perfil</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">Iniciar sesión con correo electrónico</string>
<string name="manual_account_setup_option">Usar correo electrónico como Retransmisor</string>
<!-- Instant onboarding title (there is not more to do than to set name and avatar) -->
<string name="instant_onboarding_title">Tu perfil</string>
<!-- The placeholder will be replaced by the default onboarding server -->
@@ -679,20 +679,20 @@
<string name="proxy_enabled">Proxy activado</string>
<string name="proxy_enabled_hint">Estás utilizando un proxy. Si tienes problemas para conectarte, prueba con otro proxy.</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">Transportes</string>
<string name="add_transport">Añadir transporte</string>
<string name="remove_transport">Eliminar transporte</string>
<string name="edit_transport">Editar transporte</string>
<string name="transports">Retransmisores</string>
<string name="add_transport">Añadir Retransmisor</string>
<string name="remove_transport">Eliminar Retransmisor</string>
<string name="edit_transport">Editar Retransmisor</string>
<string name="used_for_sending">Usado para enviar</string>
<string name="hide_from_contacts">Ocultar de los contactos</string>
<string name="hidden_from_contacts">Ocultado de los contactos</string>
<!-- Hint for the list of relays -->
<string name="transport_list_hint">Los mensajes se reciben en todos los relays.</string>
<string name="transport_list_hint">Los mensajes se reciben en todos los retransmisores.\n\n⚠️ Si cambias algo aquí, asegúrese de que todos sus dispositivos ejecutan al menos la versión 2.47.0. De lo contrario, los dispositivos más antiguos pueden perder algunos mensajes.</string>
<!-- shown if a QR code was scanned that can be used as a relay -->
<string name="confirm_add_transport">¿Añadir este transporte?</string>
<string name="invalid_transport_qr">El código QR escaneado no contiene un transporte válido.</string>
<string name="confirm_add_transport">¿Añadir este retransmisor?</string>
<string name="invalid_transport_qr">El código QR escaneado no contiene un retransmisor válido.</string>
<!-- placeholder will be replaced by a relay server name -->
<string name="confirm_remove_or_hide_transport_x">¿Eliminar el relay \"%1$s\"?\n\nLos contactos que solo conocen este relay podrían no comunicarse contigo hasta que les envíes otro mensaje.\n\nEn caso de duda, es mejor ocultarlo que eliminarlo.</string>
<string name="confirm_remove_or_hide_transport_x">¿Eliminar el retransmisor \"%1$s\"?\n\nLos contactos que solo conocen este retransmisor podrían no comunicarse contigo hasta que les envíes otro mensaje.\n\nEn caso de duda, es mejor ocultarlo que eliminarlo.</string>
<string name="login_certificate_checks">Comprobaciones de certificados</string>
<string name="login_error_mail">Por favor, introduzca una dirección de correo válida</string>
<string name="login_error_server">Por favor, introduzca un servidor / dirección IP válido</string>
@@ -831,10 +831,6 @@
<string name="up_to_x_most_worse_quality_images">Hasta %1$s, la mayoría de imágenes de peor calidad</string>
<string name="up_to_x_most_balanced_quality_images">Hasta %1$s, la mayoría de imágenes de calidad balanceada</string>
<string name="download_failed">Descarga fallida</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">Mensaje de %1$s</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Descarga disponible hasta %1$s</string>
<string name="profile_image_select">Seleccionar imagen de perfil</string>
<string name="select_your_new_profile_image">Seleccionar tu nueva imagen de perfil</string>
<string name="profile_image_delete">Borrar imagen de perfil</string>
@@ -967,6 +963,7 @@
<!-- %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">Temporizador de la desaparición de mensajes establecido en %1$s semanas por %2$s.</string>
<string name="chat_unencrypted_explanation">Los mensajes en este chat usan el correo electrónico clásico y no están cifrados de extremo a extremo.</string>
<string name="messages_are_e2ee">Los mensajes están cifrados de extremo a extremo.</string>
<string name="chat_protection_enabled_tap_to_learn_more">A partir de ahora se garantiza que los mensajes están cifrados de extremo a extremo . Toca para obtener más información.</string>
<string name="chat_protection_enabled_explanation">Ahora se garantiza que todos los mensajes en este chat están cifrados de extremo a extremo.\n\nEl cifrado de extremo a extremo mantiene los mensajes privados entre tú y tus compañeros de chat. Ni siquiera tu proveedor de correo electrónico puede leerlos.</string>
<string name="invalid_unencrypted_tap_to_learn_more">⚠️ %1$s requiere cifrado de extremo a extremo que aún no está configurado para este chat. Toca para obtener más información.</string>
@@ -2,20 +2,14 @@
<resources
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingTranslation">
<string name="description">Descripción</string>
<string name="or_separator">o</string>
<string name="online">en línea</string>
<string name="pink">Rosa</string>
<string name="gray">Gris</string>
<string name="share_location_for_12_hours">Por 12 horas</string>
<string name="share_location_for_24_hours">Por 24 horas</string>
<!-- device messages for updates -->
<string name="device_welcome_message">🔮 Bienvenido a ArcaneChat✨\n\nPara ponerte en contacto con amigos:\n\n🤳🏻
Toca \"Código QR\" en la pantalla principal de ambos dispositivos. Elije \"Escanear código
QR\" en un dispositivo y apunta la cámara hacia el otro\n\n🔗 Si no estás en la misma
habitación, comparte tu enlace de invitación usando \"Invitar amigos\" en el menú\n\n📢
Únete al [Canal Oficial de ArcaneChat](%1$s) (en inglés) para noticias sobre el desarrollo
de la aplicación.</string>
<string name="device_welcome_message">🔮 Bienvenido a ArcaneChat✨\n\nPara ponerte en contacto con amigos:\n\n🤳🏻 Toca \"Código QR\" en la pantalla principal de ambos dispositivos. Elije \"Escanear código QR\" en un dispositivo y apunta la cámara hacia el otro\n\n🔗 Si no estás en la misma habitación, comparte tu enlace de invitación usando \"Invitar amigos\" en el menú\n\n📢 Únete al [Canal Oficial de ArcaneChat](%1$s) (en inglés) para noticias sobre el desarrollo de la aplicación.</string>
<string name="update_2_46ac">🔮 ¿Qué hay de nuevo?\n\n✦ Llamadas de audio/video experimentales mejoradas con una mejor integración con el sistema\n\n✦ Se agregó un selector de stickers básico\n\n✦ Se mejoró el selector de archivos adjuntos\n\n✦ Se minimizó aún más los metadatos en los mensajes\n\n✦ Mayor resiliencia con el uso de múltiples relays: si un relay falla, los mensajes se siguen recibiendo en los demás\n\n✦ Mejor sincronización del estado de las notificaciones en varios dispositivos\n\n✦ Enlace de invitación corregido para unirse al 📢 [Canal oficial de ArcaneChat](%2$s) (en inglés)\n\n✦ Varias correcciones de errores y traducciones actualizadas\n\n\n💜 Por favor, dona para ayudarnos a mantenernos independientes y continuar brindando mejoras: %1$s</string>
</resources>
+21 -6
View File
@@ -69,6 +69,8 @@
<string name="mark_as_read">Märgi loetuks</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">Märgi loetuks</string>
<!-- Shortest text for "Mark as being unread". In english, this could be "Unread" (past tense of "to unread"), in german, this could be "Ungelesen". -->
<string name="mark_as_unread_short">Märgi mitteloetuks</string>
<!-- Placeholder text when something is loading -->
<string name="loading">Laadimisel…</string>
<string name="hide">Peida</string>
@@ -163,6 +165,7 @@
<item quantity="other">Valitud: %d</item>
</plurals>
<string name="selected_colon">Valitud:</string>
<string name="selected">Valitud</string>
<string name="self">Mina</string>
<string name="draft">Kavand</string>
<string name="image">Polt</string>
@@ -395,6 +398,12 @@
<string name="audio_call">Häälkõne</string>
<!-- Used in call bubbles, summaries, notifications -->
<string name="video_call">Videokõne</string>
<string name="call_initializing">Valmendan…</string>
<string name="call_incoming">Saabuv kõne</string>
<string name="call_ringing">Heliseb…</string>
<string name="call_reconnecting">Ühendan uuesti…</string>
<string name="call_ended">Kõne on lõppenud</string>
<string name="call_failed">Kõne ühendamine ei õnnestunud</string>
<!-- deprecated, use "Audio call" instead -->
<string name="outgoing_audio_call">Väljuv häälkõne</string>
<!-- deprecated, use "Video call" instead -->
@@ -406,6 +415,8 @@
<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>
<string name="already_in_call">Kõne juba on pooleli</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">Kas oled kindel, et soovid lahkuda?</string>
@@ -655,9 +666,16 @@
<string name="add_transport">Lisa alusühendus</string>
<string name="remove_transport">Eemalda alusühendus</string>
<string name="edit_transport">Muuda alusühendust</string>
<string name="used_for_sending">Kasutatud saatmiseks</string>
<string name="hide_from_contacts">Peida kontaktidest</string>
<string name="hidden_from_contacts">Peidetud kontaktidest</string>
<!-- Hint for the list of relays -->
<string name="transport_list_hint">Sõnumid saavad kõik alusühendused (edastusserverid).\n\n⚠️ Kui muudad siin midagi, siis palun kontrolli, et kõikides sinu seadmetes on kasutusel vähemalt versioon 2.47.0. Vastasel juhul võivad vanema tarkvaraga seadmetes jääta sõnumid saabumata.</string>
<!-- shown if a QR code was scanned that can be used as a relay -->
<string name="confirm_add_transport">Kas lisad selle alusühenduse?</string>
<string name="invalid_transport_qr">Skaneeritud QR-koodis pole korrektset alusühendust</string>
<string name="confirm_add_transport">Kas lisad selle alusühenduse (edastusserveri)?</string>
<string name="invalid_transport_qr">Skaneeritud QR-koodis pole korrektset alusühendust (edastusserverid)</string>
<!-- placeholder will be replaced by a relay server name -->
<string name="confirm_remove_or_hide_transport_x">Kas eemaldad alusühenduse (edastusserveri) „%1$s“?\n\nKui mõni kontakt teab vaid seda ühendusviisi, siis ta ei pruugi sind kätte saada enne, kui oled talle uuesti kirjutanud.\n\nKui kahtled, siis eemakdamise asemel pigem peida see alusühendus (edastusserver).</string>
<string name="login_certificate_checks">Sertifikaadikontrollid</string>
<string name="login_error_mail">Palun sisesta korrektne e-posti aadress</string>
<string name="login_error_server">Palun sisesta korrektne serveri täisnimi või ip-aadress</string>
@@ -796,10 +814,6 @@
<string name="up_to_x_most_worse_quality_images">Suurusega kuni %1$s, üldjuhul kehva kvaliteediga pildid</string>
<string name="up_to_x_most_balanced_quality_images">Suurusega kuni %1$s, üldjuhul mõistliku kvaliteediga pildid</string>
<string name="download_failed">Allalaadimine ei õnnestunud</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">Sõnum suurusega %1$s</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Allalaadimise ülempiir on saadaval kuni %1$s</string>
<string name="profile_image_select">Vali profiilipilt</string>
<string name="select_your_new_profile_image">Vali oma uus profiilipilt</string>
<string name="profile_image_delete">Kustuta profiilipilt</string>
@@ -932,6 +946,7 @@
<!-- %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 määras isekustuvate sõnumite kehtivuseks %1$s nädalat.</string>
<string name="chat_unencrypted_explanation">Sõnumid siin vestluses kasutavad klassikalist e-postiliidestust ja pole läbivalt krüptitud.</string>
<string name="messages_are_e2ee">Sõnumid on läbivalt krüptitud.</string>
<string name="chat_protection_enabled_tap_to_learn_more">Sõnumid on läbivalt krüptitud. Lisateabe ligemiseks klõpsi.</string>
<string name="chat_protection_enabled_explanation">Kõik selle vestluse sõnumid on läbivalt krüptitud.\n\nSee tagab, et sõnumid on loetavad vaid sinu ja vestluse teis(t)e osapool(t)e poolt. Ei teenusepakkujad, serverihaldajad, ega põrgatajad ei saa neid sõnumeid lugeda.</string>
<string name="invalid_unencrypted_tap_to_learn_more">⚠️ %1$s eeldab läbiva krüptimise kasutamist, aga see pole veel antud vestluse jaoks seadistatud. Lisateabe ligemiseks klõpsi.</string>
+46 -6
View File
@@ -69,6 +69,8 @@
<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>
<!-- Shortest text for "Mark as being unread". In english, this could be "Unread" (past tense of "to unread"), in german, this could be "Ungelesen". -->
<string name="mark_as_unread_short">Irakurri gabe</string>
<!-- Placeholder text when something is loading -->
<string name="loading">Kargatzen...</string>
<string name="hide">Ezkutatu</string>
@@ -111,10 +113,17 @@
<string name="last_week">Azken astea</string>
<string name="last_month">Azken hilabetea</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">%1$s-n ikusi da azken</string>
<string name="last_seen_at">Azken aldiz begiratua: %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">Azken aldiz begiratua %1$s</string>
<string name="last_seen_unknown">Azken aldiz begiratzea: ezezaguna</string>
<!-- Call duration as shown in the call bubbles. There is some risk to mix with the sending time, so the word "Duration" may be helpful. Might also be "Duration: %d minutes", "%d-minute duration", "%d-minute length" or "%d-minute call" -->
<plurals name="call_duration_minutes">
<item quantity="one">minutu %d</item>
<item quantity="other">%d minutu</item>
</plurals>
<!-- Call duration as shown in the call bubbles in case the call is less than one minute. If the context is not clear enough, one may want to add the word "Duration". -->
<string name="call_duration_less_than_a_minute">Minutu 1 baino gutxiago</string>
<!-- Shown beside messages that are "N minutes old". Prefer short strings, or well-known abbreviations. -->
<plurals name="n_minutes">
<item quantity="one">min %d</item>
@@ -156,6 +165,7 @@
<item quantity="other">%d hautatu dira</item>
</plurals>
<string name="selected_colon">Hautaketa:</string>
<string name="selected">Hautatua</string>
<string name="self">Ni</string>
<string name="draft">Zirriborroa</string>
<string name="image">Irudia</string>
@@ -167,6 +177,9 @@
<string name="add_to_sticker_collection">Gehitu Sticker bildumara</string>
<string name="add_stickers_instructions">Stickerretara gehitzeko, klik egin \"Ireki sticker karpeta\" aukeran, sortu azpikarpeta bat zure sticker multzoarentzat eta arrastatu irudi eta sticker fitxategiak bertara.</string>
<string name="open_sticker_folder">Ireki sticker karpeta</string>
<string name="ask_add_sticker_to_collection">Gehitu eranskailu hau zure bildumara?</string>
<string name="ask_delete_sticker">Ezabatu eranskailu hau?</string>
<string name="sticker_picker_empty_hint">Sakatu eranskailu bat hemen gehitzeko</string>
<string name="images">Irudiak</string>
<!-- a noun, used for "Music" and other "Audio" files -->
<string name="audio">Audioa</string>
@@ -248,6 +261,11 @@
<string name="new_channel">Kanal berria</string>
<!-- consider keeping the term "channel" as in WhatsApp or Telegram -->
<string name="channel_name">Kanalaren izena</string>
<!-- The number of times a message is shown in channels. Use what other messengers are using here, smth. as "N-times seen" or "1 Aufruf" (german) work as well, where "1 impression" or "1 hit" may sound too formal and may be more marketing slang. -->
<plurals name="n_views">
<item quantity="one">Ikustaldi %d</item>
<item quantity="other">%d ikustaldi</item>
</plurals>
<string name="email">Posta elektronikoa</string>
<!-- "New" as in "Create New Email"; shown together with "New Group" and "New Channel" -->
<string name="new_email">Posta elektroniko berria</string>
@@ -261,8 +279,9 @@
<string name="menu_archive_chat">Artxibatu txata</string>
<string name="menu_unarchive_chat">Berreskuratu txata artxibotik</string>
<string name="menu_add_attachment">Gehitu eranskina</string>
<string name="menu_leave_group">Atera taldetik</string>
<string name="menu_leave_group">Utzi taldea</string>
<string name="menu_leave_channel">Utzi kanala</string>
<string name="menu_leave_and_delete">Utzi eta ezabatu niretzat</string>
<string name="menu_delete_chat">Ezabatu txata</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">Garbitu txata</string>
@@ -375,6 +394,16 @@
<string name="answer_call">Erantzun</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">Ukatu</string>
<!-- Used in call bubbles, summaries, notifications -->
<string name="audio_call">Audio deia</string>
<!-- Used in call bubbles, summaries, notifications -->
<string name="video_call">Bideo deia</string>
<string name="call_initializing">Abiarazten…</string>
<string name="call_incoming">Sarrerako deia</string>
<string name="call_ringing">Jotzen...</string>
<string name="call_reconnecting">Berriz konektatzen...</string>
<string name="call_ended">Deia amaitu da</string>
<string name="call_failed">Deiak huts egin du</string>
<!-- deprecated, use "Audio call" instead -->
<string name="outgoing_audio_call">Irteerako audio deia</string>
<!-- deprecated, use "Video call" instead -->
@@ -386,6 +415,8 @@
<string name="declined_call">Ukatutako deia</string>
<string name="canceled_call">Bertan behera utzitako deia</string>
<string name="missed_call">Galdutako deia</string>
<string name="already_in_call">Dei batean dagoeneko</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>
@@ -635,9 +666,16 @@
<string name="add_transport">Gehitu errelea</string>
<string name="remove_transport">Ezabatu errelea</string>
<string name="edit_transport">Editatu errelea</string>
<string name="used_for_sending">Bidaltzeko erabilita</string>
<string name="hide_from_contacts">Ezkutatu kontaktuetatik</string>
<string name="hidden_from_contacts">Kontaktuetatik ezkutatuta</string>
<!-- Hint for the list of relays -->
<string name="transport_list_hint">Mezuak errele guztietan jasotzen dira.\n\n⚠️ Hemen zerbait aldatzen baduzu, ziurtatu zure gailu guztiek gutxienez 2.47.0 bertsioa dutela. Bestela, gailu zaharragoek mezuak gal ditzakete.</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_or_hide_transport_x">Ezabatu \"%1$s\" errelea?\n\nErrele hau bakarrik ezagutzen duten kontaktuak ezin izango dira zurekin harremanetan jarri zuk mezu bat berriro bidali arte.\n\nZalantzarik baduzu, hobe da ezkutatzea ezabatzea baino.</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>
@@ -692,6 +730,7 @@
<string name="pref_enter_sends">Enter teklarekin bidali</string>
<string name="pref_enter_sends_explain">Enter tekla sakatzeak mezua bidaliko du</string>
<string name="pref_outgoing_media_quality">Bidalitako multimediaren kalitatea</string>
<string name="pref_outgoing_media_quality_hint">Jatorrizko kalitatean bidaltzeko, erantsi multimedia \"Fitxategi\" gisa. Honek datu askoz gehiago kontsumitzen du zuretzat eta jasotzaileentzat.</string>
<string name="pref_outgoing_balanced">Orekatua</string>
<string name="pref_outgoing_worse">Kalitate txarragoa, tamaina txikia</string>
<string name="pref_vibrate">Bibratu</string>
@@ -702,6 +741,10 @@
<string name="pref_notifications">Jakinarazpenak</string>
<string name="pref_mention_notifications">Aipamenak</string>
<string name="pref_mention_notifications_explain">Mutututako taldeetan, jaso zuretzat diren mezuen jakinarazpenak, hala nola erantzunak edo erreakzioak</string>
<!-- Title for the "Calls" notification setting. This is a plural noun, not a verb. -->
<string name="pref_calls">Deiak</string>
<!-- Details about what the "Calls" notification setting does -->
<string name="pref_calls_explain">Erakutsi dei-pantaila deiak jasotzean</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>
@@ -771,10 +814,6 @@
<string name="up_to_x_most_worse_quality_images">%1$s arte: kalitate txarreneko irudiak</string>
<string name="up_to_x_most_balanced_quality_images">%1$s 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="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>
@@ -907,6 +946,7 @@
<!-- %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="messages_are_e2ee">Mezuak muturretik muturrera zifratuta daude.</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>
@@ -2,15 +2,13 @@
<resources
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingTranslation">
<string name="description">Deskribapena</string>
<string name="could_not_open_file">Ezin izan da fitxategia ireki</string>
<string name="menu_export_image">Esportatu irudia</string>
<!-- the word "or" to separate blocks in the user interface that are mutually exclusive -->
<string name="or_separator">edo</string>
<string name="online">online</string>
<string name="pink">Arrosa</string>
<string name="gray">Grisa</string>
<string name="share_location_for_12_hours">12 orduz</string>
<string name="share_location_for_24_hours">24 orduz</string>
<!-- device messages for updates -->
<!-- when translating the sentence about the ArcaneChat channel, clarify that the channel is in English -->
-4
View File
@@ -770,10 +770,6 @@
<string name="up_to_x_most_worse_quality_images">تا سقف %1$s، بدترین کیفیت تصویر</string>
<string name="up_to_x_most_balanced_quality_images">تا سقف %1$s، متعادل‌ترین کیفیت تصویر</string>
<string name="download_failed">بارگیری ناموفق</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 </string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">سقف دانلود تا %1$s فعال خواهد بود</string>
<string name="profile_image_select">انتخاب تصویر نمایه</string>
<string name="select_your_new_profile_image">تصویر نمایهٔ جدیدتان را برگزینید</string>
<string name="profile_image_delete">حذف تصویر نمایه</string>
-4
View File
@@ -619,10 +619,6 @@
<string name="up_to_x_most_worse_quality_images">Korkeintaan %1$s, suurin osa alhaisen laadun kuvista</string>
<string name="up_to_x_most_balanced_quality_images">Korkeintaan %1$s, suurin osa tasapainoisen laadun kuvista</string>
<string name="download_failed">Lataus epäonnistui</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 viesti</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Latauksen yläraja saatavilla %1$s asti</string>
<string name="profile_image_select">Valitse profiilikuva</string>
<string name="select_your_new_profile_image">Valitse uusi profiilikuvasi</string>
<string name="profile_image_delete">Poista profiilikuva</string>
+45 -12
View File
@@ -69,6 +69,8 @@
<string name="mark_as_read">Marquer comme lu</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">Marquer comme lu</string>
<!-- Shortest text for "Mark as being unread". In english, this could be "Unread" (past tense of "to unread"), in german, this could be "Ungelesen". -->
<string name="mark_as_unread_short">Non lus</string>
<!-- Placeholder text when something is loading -->
<string name="loading">Chargement...</string>
<string name="hide">Cacher</string>
@@ -115,6 +117,14 @@
<!-- 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">Dernière activité %1$s</string>
<string name="last_seen_unknown">Vu·e pour la dernière fois : inconnu</string>
<!-- Call duration as shown in the call bubbles. There is some risk to mix with the sending time, so the word "Duration" may be helpful. Might also be "Duration: %d minutes", "%d-minute duration", "%d-minute length" or "%d-minute call" -->
<plurals name="call_duration_minutes">
<item quantity="one">Durée : %d minute</item>
<item quantity="many">Durée : %d minutes</item>
<item quantity="other">Durée : %d minutes</item>
</plurals>
<!-- Call duration as shown in the call bubbles in case the call is less than one minute. If the context is not clear enough, one may want to add the word "Duration". -->
<string name="call_duration_less_than_a_minute">Durée : moins de 1 minute</string>
<!-- 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>
@@ -165,6 +175,7 @@
<item quantity="other">%d Sélectionnés</item>
</plurals>
<string name="selected_colon">Sélection :</string>
<string name="selected">Sélectionné</string>
<string name="self">Moi</string>
<string name="draft">Brouillon</string>
<string name="image">Image</string>
@@ -174,8 +185,11 @@
<!-- "Stickers" as known from other messengers; in some languages, the English "Sticker" is fine. -->
<string name="sticker">Vignette</string>
<string name="add_to_sticker_collection">Ajouter à la collection de vignettes</string>
<string name="add_stickers_instructions">Pour ajouter des vignettes, appuyez sur \"Ouvrir le dossier Vignettes\", créez un sous-dossier pour votre paquet de vignettes et faites-y glisser vos fichiers image ou vignette </string>
<string name="add_stickers_instructions">Pour ajouter des vignettes, appuyez sur « Ouvrir le dossier Vignettes », créez un sous-dossier pour votre paquet de vignettes et faites-y glisser vos fichiers image ou vignette </string>
<string name="open_sticker_folder">Ouvrir le dossier Vignettes</string>
<string name="ask_add_sticker_to_collection">Ajouter cette vignette à votre collection ?</string>
<string name="ask_delete_sticker">Supprimer cette vignette ?</string>
<string name="sticker_picker_empty_hint">Touchez une vignette pour l\'ajouter ici</string>
<string name="images">Images</string>
<!-- a noun, used for "Music" and other "Audio" files -->
<string name="audio">Audio</string>
@@ -278,6 +292,7 @@
<string name="menu_add_attachment">Ajouter une pièce jointe</string>
<string name="menu_leave_group">Quitter le groupe</string>
<string name="menu_leave_channel">Quitter le canal</string>
<string name="menu_leave_and_delete">Quitter et supprimer pour moi</string>
<string name="menu_delete_chat">Supprimer la discussion</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">Vider la discussion</string>
@@ -296,7 +311,7 @@
<string name="menu_copy_image_to_clipboard">Copier l\'image</string>
<string name="menu_copy_email_to_clipboard">Copier l\'adresse courriel</string>
<string name="paste_from_clipboard">Coller depuis le presse-papiers</string>
<string name="ask_copy_unopenable_link_to_clipboard">Le lien \"%1$d\" ne peut pas être ouvert dans le navigateur, souhaitez-vous le copier dans le presse-papier à la place ?</string>
<string name="ask_copy_unopenable_link_to_clipboard">Le lien « %1$d » ne peut pas être ouvert dans le navigateur, souhaitez-vous le copier dans le presse-papier à la place ?</string>
<string name="menu_forward">Faire suivre le message</string>
<string name="menu_reply">Répondre au message</string>
<string name="menu_mute">Mettre en sourdine</string>
@@ -360,7 +375,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\nNous vous serions reconnaissants d\'envisager de nous faire un don pour aider Delta Chat à rester libre et gratuit pour tout le monde.\n\nBien que Delta Chat soit gratuit et open source, son développement coûte de l\'argent. Aidez-nous à garder Delta Chat indépendant et à le rendre encore plus génial à l\'avenir.\n\nhttps://delta.chat/fr/donate</string>
<string name="donate_device_msg">❤️ On dirait que Delta Chat vous plaît !\n\nNous vous serions reconnaissants d\'envisager de nous faire un don pour aider Delta Chat à rester libre et gratuit pour tout le monde.\n\nBien que Delta Chat soit gratuit et open source, son développement coûte de l\'argent. Aidez-nous à garder Delta Chat indépendant et à le rendre encore plus génial à l\'avenir.\n\nhttps://delta.chat/fr/donate</string>
<string name="mute_for_one_hour">Sourdine pendant 1 heure</string>
<string name="mute_for_eight_hours">Sourdine pendant 8 heures</string>
@@ -373,7 +388,6 @@
<string name="share_location_for_one_hour">pour 1 heure</string>
<string name="share_location_for_two_hours">pour 2 heures</string>
<string name="share_location_for_six_hours">pour 6 heures</string>
<string name="share_location_for_12_hours">pour 12 heures</string>
<plurals name="ask_send_following_n_files_to">
<item quantity="one">Envoyer le fichier suivant à %s ?</item>
@@ -392,6 +406,16 @@
<string name="answer_call">Décrocher</string>
<!-- the action "to decline" a call, not: "the decline" -->
<string name="end_call">Refuser</string>
<!-- Used in call bubbles, summaries, notifications -->
<string name="audio_call">Appel audio</string>
<!-- Used in call bubbles, summaries, notifications -->
<string name="video_call">Appel vidéo</string>
<string name="call_initializing">Initialisation en cours</string>
<string name="call_incoming">Appel entrant</string>
<string name="call_ringing">Sonnerie en cours</string>
<string name="call_reconnecting">Reconnexion en cours</string>
<string name="call_ended">Appel terminé</string>
<string name="call_failed">L\'appel a échoué</string>
<!-- deprecated, use "Audio call" instead -->
<string name="outgoing_audio_call">Appel audio sortant</string>
<!-- deprecated, use "Video call" instead -->
@@ -403,6 +427,8 @@
<string name="declined_call">Appel refusé</string>
<string name="canceled_call">Appel annulé</string>
<string name="missed_call">Appel manqué</string>
<string name="already_in_call">Déjà en appel</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">Voulez-vous vraiment quitter la discussion ?</string>
@@ -476,7 +502,7 @@
<!-- 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">Enregistrer le message</string>
<string name="saved_messages">Messages enregistrés</string>
<string name="saved_messages_explain">• Transférez les messages ici pour les retrouver facilement\n\n• Prenez des notes ou enregistrez des vocaux\n\n• Ajoutez des fichiers multimédias en pièces jointes pour les sauvegarder</string>
<string name="saved_messages_explain">• Transférez les messages ici pour les retrouver facilement\n\n• Prenez des notes ou enregistrez des vocaux\n\n• Ajoutez des fichiers multimédias en pièces jointes pour les enregistrer</string>
<!-- Should match "Saved" from "Saved messages" -->
<string name="saved">Enregistrés</string>
<string name="save_as">Enregistrer sous</string>
@@ -625,7 +651,7 @@
<string name="welcome_chat_over_email">Messagerie décentralisée sécurisée</string>
<string name="scan_invitation_code">Scanner le code d\'invitation</string>
<string name="login_title">Connexion</string>
<string name="login_advanced_hint">Ce type de connexion est réservé aux utilisateurs avancés.\n\n• N\'utilisez pas une adresse déjà employée dans une autre appli.\n\n• Les serveurs de courriel classiques permettent les discussions non chiffrées de bout en bout, lesquelles sont représentées par une icône enveloppe.</string>
<string name="login_advanced_hint">Ce type de connexion est réservé aux utilisateurs avancés.\n\n• N\'utilisez pas une adresse déjà employée dans une autre appli.\n\n• Les serveurs de courriel classiques autorisent les discussions non chiffrées de bout en bout, lesquelles sont représentées par une icône enveloppe.</string>
<string name="login_inbox">Boîte de réception</string>
<string name="login_imap_login">Utilisateur IMAP</string>
<string name="login_imap_server">Serveur IMAP</string>
@@ -657,9 +683,14 @@
<string name="add_transport">Ajouter un relais</string>
<string name="remove_transport">Supprimer un relais</string>
<string name="edit_transport">Modifier le relais</string>
<string name="used_for_sending">Utilisé pour les envois</string>
<string name="hide_from_contacts">Masquer pour les contacts</string>
<string name="hidden_from_contacts">Masqué pour les contacts</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 code QR scanné ne renvoie pas vers un relais valide.</string>
<!-- placeholder will be replaced by a relay server name -->
<string name="confirm_remove_or_hide_transport_x">Supprimer le relai %1$s ? Les contacts qui connaissent uniquement ce relai risquent de ne plus pouvoir vous joindre jusqu\'à ce que vous leur envoyiez un nouveau message. En cas de doute, mieux vaut le masquer que le supprimer.</string>
<string name="login_certificate_checks">Vérification du certificat</string>
<string name="login_error_mail">Entrez une adresse courriel valide</string>
<string name="login_error_server">Entre un serveur / adresse IP valide</string>
@@ -714,6 +745,7 @@
<string name="pref_enter_sends">Touche Entrée pour envoyer</string>
<string name="pref_enter_sends_explain">Appuyez sur la touche Entrée pour envoyer des messages texte</string>
<string name="pref_outgoing_media_quality">Qualité du média en sortie</string>
<string name="pref_outgoing_media_quality_hint">Pour envoyer un fichier multimédia dans sa qualité d\'origine, joignez-le au message en tant que « Fichier ». Cette méthode utilise beaucoup plus de données, aussi bien pour vous que pour vos destinataires.</string>
<string name="pref_outgoing_balanced">Équilibré</string>
<string name="pref_outgoing_worse">Qualité la plus basse, taille réduite</string>
<string name="pref_vibrate">Vibrer</string>
@@ -724,6 +756,10 @@
<string name="pref_notifications">Notifications</string>
<string name="pref_mention_notifications">Mentions</string>
<string name="pref_mention_notifications_explain">Dans les groupes mis en sourdine, notifier les messages qui vous sont adressés, tels que les réponses ou les réactions</string>
<!-- Title for the "Calls" notification setting. This is a plural noun, not a verb. -->
<string name="pref_calls">Appels</string>
<!-- Details about what the "Calls" notification setting does -->
<string name="pref_calls_explain">Afficher l\'écran d\'appel pour tous les appels entrants</string>
<string name="pref_notifications_show">Afficher</string>
<string name="pref_notifications_priority">Priorité</string>
<string name="pref_notifications_explain">Activer les notifications système pour les nouveaux messages</string>
@@ -772,7 +808,7 @@
<!-- 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">Mode appareils multiples</string>
<string name="pref_multidevice_explain">Synchronise vos messages avec vos autres appareils. Activé automatiquement lorsque vous ajoutez un deuxième appareil</string>
<string name="pref_multidevice_change_warn">Le mode appareils multiples doit être activé quand vous utilisez le même profil sur plusieurs appareils. Désactivez cette option seulement si vous avez supprimé ce profil de vos autres appareils.\n\nDésactiver le mode alors que vous utilisez un profil sur plusieurs appareils causera un certain nombre de problèmes, dont des messages perdus.</string>
<string name="pref_multidevice_change_warn">Le mode appareils multiples doit être activé quand vous utilisez le même profil sur plusieurs appareils. Désactivez cette option seulement si vous avez supprimé ce profil de tous vos autres appareils.\n\nDésactiver ce mode alors que vous utilisez un profil sur plusieurs appareils causera un certain nombre de problèmes, dont des messages perdus.</string>
<string name="pref_auto_folder_moves">Déplacer automatiquement vers le dossier DeltaChat</string>
<string name="pref_only_fetch_mvbox_title">Ne consulter que le dossier DeltaChat</string>
<string name="pref_show_emails">Voir les courriels classiques</string>
@@ -793,10 +829,6 @@
<string name="up_to_x_most_worse_quality_images">Jusqu\'à %1$s, plus basse qualité d\'image</string>
<string name="up_to_x_most_balanced_quality_images">Jusqu\'à %1$s, qualité d\'image la plus équilibrée </string>
<string name="download_failed">Échec du téléchargement</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 message</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Télécharger le plus possible jusqu\'à %1$s</string>
<string name="profile_image_select">Sélectionnez l\'image de profil</string>
<string name="select_your_new_profile_image">Sélectionnez votre nouvelle image de profil</string>
<string name="profile_image_delete">Supprimer l\'image de profil</string>
@@ -805,7 +837,7 @@
<string name="send_stats_to_devs">Envoyer les statistiques aux développeurs de Delta Chat</string>
<string name="stats_device_message">Voulez-vous participer à l\'amélioration de Delta Chat et soutenir nos recherches en envoyant des statistiques d\'utilisation hebdomadaires de manière anonyme ?\n\n👉 Appuyez ici… 👈</string>
<string name="stats_confirmation_dialog">Voulez-vous participer à l\'amélioration de Delta Chat et soutenir nos recherches en envoyant des statistiques d\'utilisation hebdomadaires de manière anonyme ?</string>
<string name="stats_thanks">Merci ! Vous pouvez toujours désactiver l\'envoi dans \"Paramètres -> Avancé\".\n\nAuriez-vous 5 minutes de plus pour participer à une étude scientifique sur la sécurité de Delta Chat ?</string>
<string name="stats_thanks">Merci ! Vous pouvez toujours désactiver l\'envoi dans « Paramètres -> Avancé ».\n\nAuriez-vous 5 minutes de plus pour participer à une étude scientifique sur la sécurité de Delta Chat ?</string>
<string name="stats_disable_dialog">L\'envoi de statistiques est déjà activé.\n\nSouhaitez-vous le désactiver ?</string>
<string name="disable">Désactiver</string>
<string name="stats_keep_sending">Maintenir l\'envoi de statistiques</string>
@@ -929,6 +961,7 @@
<!-- %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">Délai avant disparition des messages éphémères défini à %1$s semaines par %2$s.</string>
<string name="chat_unencrypted_explanation">Les messages de cette discussion utilisent le courriel classique et ne sont pas chiffrés de bout en bout.</string>
<string name="messages_are_e2ee">Les messages sont chiffrés de bout en bout.</string>
<string name="chat_protection_enabled_tap_to_learn_more">Les messages sont chiffrés de bout en bout. Toucher pour en savoir plus.</string>
<string name="chat_protection_enabled_explanation">Tous les messages de cette discussion sont chiffrés de bout-en-bout.\n\nLe chiffrement de bout-en-bout garantit la confidentialité des messages échangés par les membres d\'une discussion. Même les serveurs, les fournisseurs de courriel et les relais ne peuvent 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>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingTranslation">
<string name="share_location_for_24_hours">pour 24 heures</string>
<!-- device messages for updates -->
<string name="device_welcome_message">🔮 Bienvenue sur ArcaneChat ✨\n\nPour vous connecter avec vos amis:\n\n🤳🏻Appuyez sur «Code QR» sur l’écran daccueil des deux appareils. Sélectionnez «Scanner le code QR» sur un appareil et pointez lappareil photo vers lautre.\n\n🔗 Si vous n’êtes pas dans la même pièce, partagez votre lien dinvitation via «Inviter des amis» dans le menu.\n\n📢Rejoignez le [Chaîne officielle ArcaneChat](%1$s) (en anglais) pour suivre lactualité du développement de lapplication.</string>
</resources>
-4
View File
@@ -713,10 +713,6 @@
<string name="up_to_x_most_worse_quality_images">Ata %1$s, a maioría de imaxes da menor calidade</string>
<string name="up_to_x_most_balanced_quality_images">Ata %1$s, a maioría de imaxes de calidade media</string>
<string name="download_failed">Fallou a descarga</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 mensaxe</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Descarga o máximo dispoñible ata %1$s</string>
<string name="profile_image_select">Elixe imaxe de perfil</string>
<string name="select_your_new_profile_image">Elixe a túa nova imaxe de perfil</string>
<string name="profile_image_delete">Elimina a imaxe de perfil</string>
-4
View File
@@ -744,10 +744,6 @@
<string name="up_to_x_most_worse_quality_images">Legfeljebb %1$s, leginkább rosszabb minőségű kép</string>
<string name="up_to_x_most_balanced_quality_images">Legfeljebb %1$s, leginkább kiegyensúlyozatabb minőségű kép</string>
<string name="download_failed">Sikertelen letöltés</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 üzenet</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Letöltés maximum ennyi ideig visszamenőleg: %1$s </string>
<string name="profile_image_select">Profilkép kiválasztása</string>
<string name="select_your_new_profile_image">Válassza ki az új profilképet</string>
<string name="profile_image_delete">Profilkép törlése</string>
+20 -5
View File
@@ -69,6 +69,8 @@
<string name="mark_as_read">Segna come Letto</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">Segna Letto</string>
<!-- Shortest text for "Mark as being unread". In english, this could be "Unread" (past tense of "to unread"), in german, this could be "Ungelesen". -->
<string name="mark_as_unread_short">Non Letto</string>
<!-- Placeholder text when something is loading -->
<string name="loading">Caricamento…</string>
<string name="hide">Nascondi</string>
@@ -173,6 +175,7 @@
<item quantity="other">%d selezionati</item>
</plurals>
<string name="selected_colon">Selezionato:</string>
<string name="selected">Selezionato</string>
<string name="self">Io</string>
<string name="draft">Bozza</string>
<string name="image">Immagine</string>
@@ -385,7 +388,6 @@
<string name="share_location_for_one_hour">Per 1 ora</string>
<string name="share_location_for_two_hours">Per 2 ore</string>
<string name="share_location_for_six_hours">Per 6 ore</string>
<string name="share_location_for_12_hours">Per 12 ore</string>
<plurals name="ask_send_following_n_files_to">
<item quantity="one">Invia il seguente file a %s?</item>
@@ -408,6 +410,12 @@
<string name="audio_call">Chiamata audio</string>
<!-- Used in call bubbles, summaries, notifications -->
<string name="video_call">Videochiamata</string>
<string name="call_initializing">Inizializzazione...</string>
<string name="call_incoming">Chiamata in arrivo</string>
<string name="call_ringing">Squilla…</string>
<string name="call_reconnecting">Riconnessione…</string>
<string name="call_ended">Chiamata terminata</string>
<string name="call_failed">Chiamata non riuscita</string>
<!-- deprecated, use "Audio call" instead -->
<string name="outgoing_audio_call">Chiamata in uscita</string>
<!-- deprecated, use "Video call" instead -->
@@ -419,6 +427,8 @@
<string name="declined_call">Chiamata rifiutata</string>
<string name="canceled_call">Chiamata annullata</string>
<string name="missed_call">Chiamata persa</string>
<string name="already_in_call">Già in chiamata</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">Sei sicuro di voler abbandonare?</string>
@@ -673,9 +683,16 @@
<string name="add_transport">Aggiungi Trasporto</string>
<string name="remove_transport">Rimuovi Trasporto</string>
<string name="edit_transport">Modifica Trasporto</string>
<string name="used_for_sending">Utilizzato per l\'invio</string>
<string name="hide_from_contacts">Nascondi dai Contatti</string>
<string name="hidden_from_contacts">Nascosto dai contatti</string>
<!-- Hint for the list of relays -->
<string name="transport_list_hint">I messaggi vengono ricevuti su tutti i ripetitori.\n\n⚠️ Se modifichi qualcosa qui, assicurati che tutti i tuoi dispositivi abbiano almeno la versione 2.47.0. Altrimenti i dispositivi più vecchi potrebbero non ricevere i messaggi.</string>
<!-- shown if a QR code was scanned that can be used as a relay -->
<string name="confirm_add_transport">Aggiungere questo trasporto?</string>
<string name="invalid_transport_qr">Il codice QR scansionato non contiene un mezzo di trasporto valido.</string>
<!-- placeholder will be replaced by a relay server name -->
<string name="confirm_remove_or_hide_transport_x">Rimuovo ripetitore \"%1$s\"?\n\nI contatti che conoscono solo questo relay potrebbero non raggiungerti finché non invierai loro un altro messaggio. In caso di dubbio, nascondilo invece di rimuoverlo.</string>
<string name="login_certificate_checks">Controlli Certificato</string>
<string name="login_error_mail">Per piacere Inserisci un indirizzo e-mail valido</string>
<string name="login_error_server">Per piacere inserisci un server / indirizzo IP valido</string>
@@ -730,6 +747,7 @@
<string name="pref_enter_sends">Spedisci con Invio</string>
<string name="pref_enter_sends_explain">Premendo il tasto Invio invierà i messaggi</string>
<string name="pref_outgoing_media_quality">Qualità Media in Uscita</string>
<string name="pref_outgoing_media_quality_hint">Per inviare contenuti in qualità originale, allega i file multimediali come \"File\". Questo comporta un consumo di dati significativamente maggiore sia per te che per i destinatari.</string>
<string name="pref_outgoing_balanced">Bilanciata</string>
<string name="pref_outgoing_worse">Qualità peggiore, dimensione ridotta</string>
<string name="pref_vibrate">Vibra</string>
@@ -813,10 +831,6 @@
<string name="up_to_x_most_worse_quality_images">Fino a %1$s, immagini di qualità peggiore</string>
<string name="up_to_x_most_balanced_quality_images">Fino a %1$s, immagini di qualità più equilibrata</string>
<string name="download_failed">Scaricamento fallito</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 messaggio</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Scaricamento disponibile massimo fino a %1$s</string>
<string name="profile_image_select">Seleziona Immagine Profilo</string>
<string name="select_your_new_profile_image">Seleziona la tua nuova immagine profilo</string>
<string name="profile_image_delete">Elimina Immagine Profilo</string>
@@ -949,6 +963,7 @@
<!-- %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">Timer messaggi a scomparsa impostato su %1$s settimane da %2$s.</string>
<string name="chat_unencrypted_explanation">I messaggi in questa chat utilizzano l\' e-mail classica e non sono crittografati end-to-end.</string>
<string name="messages_are_e2ee">I messaggi sono crittografati end-to-end.</string>
<string name="chat_protection_enabled_tap_to_learn_more">I messaggi sono crittografati end-to-end. Tocca per saperne di più.</string>
<string name="chat_protection_enabled_explanation">Tutti i messaggi in questa chat sono crittografati end-to-end. La crittografia end-to-end mantiene privati i messaggi tra te e i tuoi interlocutori. Nemmeno servers, providers o relays possono leggerli.</string>
<string name="invalid_unencrypted_tap_to_learn_more">⚠️ %1$s richiede la crittografia end-to-end che non è ancora configurata per questa chat. Tocca per saperne di più.</string>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingTranslation">
<string name="share_location_for_24_hours">Per 24 ore</string>
<!-- device messages for updates -->
<string name="device_welcome_message">🔮 Benvenuti su ArcaneChat✨\n\nPer connettervi con gli amici:\n\n🤳🏻 Tocca "Codice QR" sulla schermata iniziale di entrambi i dispositivi. Seleziona \"Scansiona codice QR\" su un dispositivo e punta la fotocamera verso l\'altro.\n\n🔗 Se non siete nella stessa stanza, condividi il tuo link di invito utilizzando \"Invita amici\" nel menu.\n\n📢 Unisciti al [Canale ufficiale di ArcaneChat](%1$s) (in inglese) per gli aggiornamenti sullo sviluppo dell\'app.</string>
</resources>
+6 -6
View File
@@ -69,6 +69,8 @@
<string name="mark_as_read">Creḍ am akken yettwaɣra</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">Creḍ yettwaɣra</string>
<!-- Shortest text for "Mark as being unread". In english, this could be "Unread" (past tense of "to unread"), in german, this could be "Ungelesen". -->
<string name="mark_as_unread_short">Ur ittwaɣṛi ara</string>
<!-- Placeholder text when something is loading -->
<string name="loading"> Asali...</string>
<string name="hide">Ffer</string>
@@ -411,6 +413,8 @@
<string name="declined_call">Tiɣri tettwagi</string>
<string name="canceled_call">Tiɣri tettwasefsex</string>
<string name="missed_call">Tiɣri ittwazeglen</string>
<string name="already_in_call">Aqli deg usiwel yakan</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">Tetḥeqqeḍ tebɣiḍ ad truḥeḍ? </string>
@@ -663,8 +667,6 @@
<string name="used_for_sending">Yettwaseqdec i tuzzna</string>
<string name="hide_from_contacts">Ffer-it ɣef inermisen</string>
<string name="hidden_from_contacts">Uffir ɣef inermisen</string>
<!-- Hint for the list of relays -->
<string name="transport_list_hint">Iznan ttwaremsen ɣef meṛṛa imedwal</string>
<!-- shown if a QR code was scanned that can be used as a relay -->
<string name="confirm_add_transport">Rnu amedwel-agi?</string>
<string name="invalid_transport_qr">Tangalt QR ittusemḍen ur tesɛi ara amedwel ameɣtu</string>
@@ -724,6 +726,7 @@
<string name="pref_enter_sends">Kcem tisura i umazan</string>
<string name="pref_enter_sends_explain">Asiti ɣef tanast Kcem ad yazen aḍris n yiznan</string>
<string name="pref_outgoing_media_quality">Taɣara n umidya</string>
<string name="pref_outgoing_media_quality_hint">Akken ad tazneḍ afaylu s taɣara-ines taɣbalut, mudd-d amidya s wudem n \"Afaylu\". Ayagi ad iseqdac ugar n yisefka i kečč akked iɣerwaḍen-ik·im.</string>
<string name="pref_outgoing_balanced">Imnekni</string>
<string name="pref_outgoing_worse">Taɣara n diri, tiddi tameẓyant</string>
<string name="pref_vibrate">Argagi</string>
@@ -807,10 +810,6 @@
<string name="up_to_x_most_worse_quality_images">Arma d %1$s, tugniwin n tɣara n diri maḍi</string>
<string name="up_to_x_most_balanced_quality_images">Arma d %1$s, tugniwin n tɣara yemneknayen</string>
<string name="download_failed">Asader ur yeddi ara</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">Izen %1$s</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Sader-d afellay i yellan alamma d %1$s</string>
<string name="profile_image_select">Fren tugna n umaɣnu</string>
<string name="select_your_new_profile_image">Fren tugna-ik·im tamaynut n umaɣnu</string>
<string name="profile_image_delete">Kkes tugna n umaɣnu</string>
@@ -943,6 +942,7 @@
<!-- %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">Amkud n yiznan ur nettdum ara yettwasbed ar %1$s n yimalas sɣur %2$s.</string>
<string name="chat_unencrypted_explanation">Iznan deg udiwenni-a sseqdacen imayl aklasiki, ur ttwawgelhen ara seg yixef ɣer yixef.</string>
<string name="messages_are_e2ee">Iznan tteddun s wudem n uwgelhen seg yixef ɣer yixef.</string>
<string name="chat_protection_enabled_tap_to_learn_more">Iznan ttwawgelhen seg yixef ɣer yixef. Sit akken ad tissineḍ ugar.</string>
<string name="chat_protection_enabled_explanation">Iznan akk yellan deg udiwenni-a ttwawgelhen seg yixef ɣer yixef.\n\nAwgelhen n yixef ɣer yixef yettaǧǧa iznan d usligen gar-ak·am d yecriken n udiwenni. Ula d iqeddacen, isaǧǧawen neɣ imsawaḍen ur zemren ad ten-ɣren.</string>
<string name="invalid_unencrypted_tap_to_learn_more">⚠️%1$s Yesra awgelhen n yixef ɣer yixef ur nelli ara yakan i udiwenni-a. Sit akken ad tissineḍ ugar.</string>
-4
View File
@@ -502,10 +502,6 @@
<!-- %1$s will be replaced by a human-readable number of bytes, eg. 32 KiB, 1 MiB -->
<string name="up_to_x">%1$s까지 </string>
<string name="download_failed">다운로드 실패</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 메시지</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">%1$s까지 최대 다운로드 가능</string>
<string name="profile_image_select">프로필 이미지 선택</string>
<string name="select_your_new_profile_image">새로운 프로필 이미지 선택</string>
<string name="profile_image_delete">프로필 이미지 삭제</string>
-2
View File
@@ -720,8 +720,6 @@
<string name="up_to_x_most_worse_quality_images">Iki %1$s, iš esmės, blogos kokybės paveikslus</string>
<string name="up_to_x_most_balanced_quality_images">Iki %1$s, iš esmės, subalansuotos kokybės paveikslus</string>
<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_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>
+2 -5
View File
@@ -670,7 +670,7 @@
<string name="hide_from_contacts">Verbergen op contactpersonenlijst</string>
<string name="hidden_from_contacts">Verborgen op contactpersonenlijst</string>
<!-- Hint for the list of relays -->
<string name="transport_list_hint">Berichten worden op alle relays ontvangen.</string>
<string name="transport_list_hint">Berichten worden op alle relays ontvangen.\n\n⚠️ Als je hier iets wijzigt, doe dat dan alléén als ál je apparaten gebruikmaken van versie 2.47.0, anders kunnen er berichten ontbreken.</string>
<!-- shown if a QR code was scanned that can be used as a relay -->
<string name="confirm_add_transport">Wil je deze server toevoegen?</string>
<string name="invalid_transport_qr">De gescande QR-code bevat geen geldige overdrachtsserver.</string>
@@ -814,10 +814,6 @@
<string name="up_to_x_most_worse_quality_images">Tot %1$s - lage afbeeldingskwaliteit</string>
<string name="up_to_x_most_balanced_quality_images">Tot %1$s - redelijke afbeeldingskwaliteit</string>
<string name="download_failed">Het downloaden is mislukt</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 bericht</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Maximaal downloaden tot en met een aantal van %1$s</string>
<string name="profile_image_select">Profielfoto kiezen</string>
<string name="select_your_new_profile_image">Kies een nieuwe profielfoto</string>
<string name="profile_image_delete">Profielfoto verwijderen</string>
@@ -950,6 +946,7 @@
<!-- %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">De tijdklok van verdwijnende berichten is door %2$s ingesteld op %1$s weken.</string>
<string name="chat_unencrypted_explanation">Berichten in dit gesprek maken gebruik van traditionele e-mailcommunicatie - eind-tot-endversleuteling is daarom niet gegarandeerd.</string>
<string name="messages_are_e2ee">Berichten zijn van eind-tot-eindversleuteling voorzien.</string>
<string name="chat_protection_enabled_tap_to_learn_more">Berichten zijn gegarandeerd van eind-tot-eindversleuteling voorzien. Druk hier voor meer informatie.</string>
<string name="chat_protection_enabled_explanation">Alle berichten in dit gesprek zijn gegarandeerd van eind-tot-eindversleuteling voorzien.\n\nMet behulp van deze versleuteling blijven berichten tussen jou en je gesprekspartners geheim - zelfs je e-mailprovider kan ze niet uitlezen.</string>
<string name="invalid_unencrypted_tap_to_learn_more">⚠️ Voor %1$s is eind-tot-eindversleuteling vereist, maar dit is nog niet ingesteld in het huidige gesprek. Druk voor meer informatie.</string>
-4
View File
@@ -800,10 +800,6 @@
<string name="up_to_x_most_worse_quality_images">Do %1$s, większ. obrazów gorszej jakości</string>
<string name="up_to_x_most_balanced_quality_images">Do %1$s, obrazy zrównoważonej jakości</string>
<string name="download_failed">Pobieranie nie udane</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">Wiadomość %1$s</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Maksymalna ilość do pobrania dostępna do %1$s</string>
<string name="profile_image_select">Wybierz zdjęcie profilowe</string>
<string name="select_your_new_profile_image">Wybierz nowe zdjęcie profilowe</string>
<string name="profile_image_delete">Usuń zdjęcie profilowe</string>
-4
View File
@@ -585,10 +585,6 @@
<string name="up_to_x_most_worse_quality_images">Até %1$s, imagens de pior qualidade</string>
<string name="up_to_x_most_balanced_quality_images">Até %1$s, imagens de qualidade equilibrada</string>
<string name="download_failed">Falha no Download</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">Mensagem de %1$s</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Descarregamento máximo disponível até %1$s</string>
<string name="profile_image_select">Selecione a imagem do perfil</string>
<string name="select_your_new_profile_image">Selecione sua nova imagem do perfil</string>
<string name="profile_image_delete">Excluir imagem do perfil</string>
+67 -53
View File
@@ -69,13 +69,15 @@
<string name="mark_as_read">Отметить как прочитанное</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">Прочитано</string>
<!-- Shortest text for "Mark as being unread". In english, this could be "Unread" (past tense of "to unread"), in german, this could be "Ungelesen". -->
<string name="mark_as_unread_short">Не прочитано</string>
<!-- Placeholder text when something is loading -->
<string name="loading">Загрузка…</string>
<string name="hide">Скрыть</string>
<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="once">Только сейчас</string>
@@ -183,6 +185,7 @@
<item quantity="other">%d выбрано</item>
</plurals>
<string name="selected_colon">Выбрано:</string>
<string name="selected">Выбрано</string>
<string name="self">Я</string>
<string name="draft">Черновик</string>
<string name="image">Изображение</string>
@@ -192,7 +195,7 @@
<!-- "Stickers" as known from other messengers; in some languages, the English "Sticker" is fine. -->
<string name="sticker">Стикер</string>
<string name="add_to_sticker_collection">Добавить в коллекцию стикеров</string>
<string name="add_stickers_instructions">Для добавления стикеров, нажмите \"Открыть папку стикеров\", создайте подпапку для набора ваших стикеров и перетащите туда изображения и файлы стикеров</string>
<string name="add_stickers_instructions">Для добавления стикеров, нажмите \"Открыть папку стикеров\", создайте в ней подпапку для вашего набора стикеров и перетащите туда файлы изображений и стикеров</string>
<string name="open_sticker_folder">Открыть папку стикеров</string>
<string name="ask_add_sticker_to_collection">Добавить этот стикер в коллекцию?</string>
<string name="ask_delete_sticker">Удалить этот стикер?</string>
@@ -366,8 +369,8 @@
<string name="title_share_location">Делиться местоположением со всеми участниками группы</string>
<string name="device_talk">Сообщения устройства</string>
<string name="device_talk_subtitle">Сообщения, созданные устройством</string>
<string name="device_talk_explain">Сообщения в этом чате создаются локально на вашем устройстве для уведомления о обновлениях и проблемах при использовании приложения.</string>
<string name="device_talk_welcome_message2">Оставайтесь на связи!\n\n🙌 Нажмите \"QR-код\" на главном экране обоих устройств. Выберите \"Сканировать QR-код\" на одном устройстве и покажите его на другом.\n\n🌍 Если вы не находитесь в одном помещении, отсканируйте код с помощью видеозвонка или поделитесь ссылкой-приглашением из раздела \"Сканировать QR-код\"\n\nЗатем: Наслаждайтесь децентрализованным мессенджером. В отличие от других популярных приложений, он работает без центрального управления, отслеживания и продажи ваших данных, а также данных ваших друзей, коллег или семьи крупным организациям.</string>
<string name="device_talk_explain">Сообщения в этом чате генерируются локально на вашем устройстве чтобы информировать об обновлениях и проблемах при использовании приложения.</string>
<string name="device_talk_welcome_message2">Оставайтесь на связи!\n\n🙌 Нажмите \"QR-код\" на главном экране обоих устройств. На одном устройстве выберите \"Сканировать QR-код\" и направьте камеру на другое устройство.\n\n🌍 Если устройства находятся в разных местах, отсканируйте код с помощью видеозвонка или поделитесь ссылкой-приглашением из раздела \"Сканировать QR-код\"\n\nЗатем: Наслаждайтесь децентрализованным мессенджером. В отличие от других популярных приложений, он не имеет центра управления и не отслеживает ваши действия, а также не продаёт данные о вас, ваших друзьях, коллегах или членах семьи крупным организациям.</string>
<string name="edit_contact">Редактировать контакт</string>
<!-- Verb "to pin", making something sticky, not a noun or abbreviation for "pin number". -->
<string name="pin_chat">Закрепить чат</string>
@@ -383,7 +386,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">Добавить на главный экран</string>
<string name="donate">Пожертвовать</string>
<string name="donate_device_msg">❤️ Кажется, вам нравится Delta Chat!\n\nРассмотрите возможность сделать пожертвование, чтобы Delta Chat и дальше оставался бесплатным для всех пользователей.\n\nDelta Chat — это бесплатное приложение с открытым исходным кодом, но его разработка требует денег. Поддержите нас, чтобы сохранить независимость Delta Chat и сделать его ещё более крутым в будущем.\n\nhttps://delta.chat/donate</string>
<string name="donate_device_msg">❤️ Кажется, вам нравится Delta Chat!\n\nРассмотрите возможность сделать пожертвование, чтобы помочь нам сохранить Delta Chat бесплатным для всех пользователей.\n\nХотя использование Delta Chat бесплатно и исходный код открыт, разработка требует финансовых затрат. Помогите нам сохранить независимость Delta Chat и сделать его еще лучше в будущем.\n\nhttps://delta.chat/donate</string>
<string name="mute_for_one_hour">На 1 час</string>
<string name="mute_for_eight_hours">На 8 часов</string>
@@ -419,7 +422,12 @@
<string name="audio_call">Аудиозвонок</string>
<!-- Used in call bubbles, summaries, notifications -->
<string name="video_call">Видеозвонок</string>
<string name="call_initializing">Инициализация...</string>
<string name="call_incoming">Входящий звонок</string>
<string name="call_ringing">Звонок...</string>
<string name="call_reconnecting">Переподключение...</string>
<string name="call_ended">Звонок завершен</string>
<string name="call_failed">Звонок не удался</string>
<!-- deprecated, use "Audio call" instead -->
<string name="outgoing_audio_call">Исходящий аудиозвонок</string>
<!-- deprecated, use "Video call" instead -->
@@ -431,6 +439,8 @@
<string name="declined_call">Отклонённый звонок</string>
<string name="canceled_call">Отменённый звонок</string>
<string name="missed_call">Пропущенный звонок</string>
<string name="already_in_call">Вызов уже идёт</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">Вы действительно хотите выйти?</string>
@@ -450,11 +460,11 @@
</plurals>
<string name="ask_forward">Переслать сообщения для %1$s?</string>
<string name="ask_forward_multiple">Переслать сообщения в %1$d чата(ов)?</string>
<string name="ask_export_attachment">Экспорт вложений позволит любому приложению на вашем устройстве получить к ним доступ.\n\nПродолжить?</string>
<string name="ask_block_contact">Заблокировать этот контакт?\n\nЗаблокированные контакты не будут показаны в личных сообщениях или группах, созданных ими.\n\nВ других группах сообщения заблокированных контактов будут по-прежнему доступны.</string>
<string name="ask_export_attachment">Экспорт вложений позволит другим приложениям на вашем устройстве получить к ним доступ.\n\nПродолжить?</string>
<string name="ask_block_contact">Заблокировать этот контакт?\n\nЗаблокированные контакты не будут показаны в личных сообщениях или группах, созданных ими.\n\nВ других группах сообщения заблокированных контактов, по-прежнему будут видны.</string>
<string name="ask_unblock_contact">Разблокировать этот контакт?</string>
<string name="ask_delete_contacts">Удалить контакты?\n\nКонтакты из действующих чатов не могут быть удалены навсегда.</string>
<string name="ask_delete_contact">Удалить контакт %1$s?\n\nКонтакты из действующих чатов не могут быть удалены навсегда.</string>
<string name="ask_delete_contacts">Удалить контакты?\n\nКонтакты из действующих чатов нельзя удалить безвозвратно.</string>
<string name="ask_delete_contact">Удалить контакт %1$s?\n\nКонтакты из действующих чатов нельзя удалить безвозвратно.</string>
<string name="ask_start_chat_with">Создать чат с %1$s?</string>
<!-- %1$s is replaced by a comma-separated list of names -->
<string name="ask_remove_members">Удалить %1$s из группы?</string>
@@ -509,7 +519,7 @@
<!-- 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">Сохранить сообщение</string>
<string name="saved_messages">Сохранённые сообщения</string>
<string name="saved_messages_explain">• Пересылайте сообщения сюда для быстрого доступа\n\n• Создайте текстовые или голосовые заметки\n\n• Прикрепляйте медиафайлы, чтобы сохранить их</string>
<string name="saved_messages_explain">• Пересылайте сообщения сюда для быстрого доступа\n\n• Делайте заметки или записывайте голосовые сообщения\n\n• Прикрепляйте медиафайлы, чтобы сохранить их</string>
<!-- Should match "Saved" from "Saved messages" -->
<string name="saved">Сохранено</string>
<string name="save_as">Сохранить как…</string>
@@ -537,7 +547,7 @@
<!-- placeholder contains the hostname converted to ascii -->
<string name="puny_code_warning_question">Вы уверены, что хотите посетить %1$s?</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">Эта ссылка может искажать символы, представляя их похожими на символы из других алфавитов. Переход по ссылке отмеченной %1$s приведет к %2$s, что является нормальным для нелатинских символов. Если вы не ожидали увидеть такие символы, эта ссылка может быть опасной.</string>
<string name="puny_code_warning_description">Эта ссылка может искажать символы, заменяя их похожими символами из других алфавитов. Переход по ссылке отмеченной %1$s приведет к %2$s, что является нормальным для нелатинских символов. Если вы не ожидали увидеть такие символы, эта ссылка может быть опасной.</string>
<!-- search -->
<string name="search">Поиск</string>
@@ -587,16 +597,16 @@
<!-- "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">Добавить второе устройство</string>
<string name="multidevice_same_network_hint">Убедитесь, что оба устройства подключены к одной и той же локальной или Wi-Fi сети.</string>
<string name="multidevice_this_creates_a_qr_code">Это позволяет создать QR-код, который можно отсканировать на втором устройстве, чтобы скопировать профиль.\n\nУбедитесь, что посторонние люди или камера не видят следующий экран.</string>
<string name="multidevice_this_creates_a_qr_code">Это позволяет создать QR-код, который можно отсканировать на втором устройстве, чтобы скопировать профиль.\n\nУбедитесь, что посторонние лица или камеры не могут видеть следующий экран.</string>
<string name="multidevice_install_dc_on_other_device">Установите Delta Chat на другом устройстве (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">Запустите Delta Chat, выберите \"У меня уже есть профиль / Добавить как второе устройство\" и отсканируйте код, показанный здесь</string>
<string name="multidevice_tap_scan_on_other_device">Запустите Delta Chat, нажмите \"У меня уже есть профиль / Добавить как второе устройство\" и отсканируйте показанный здесь код</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">Отсканируйте, чтобы настроить второе устройство для %1$s</string>
<string name="multidevice_receiver_title">Добавить как второе устройство</string>
<string name="multidevice_open_settings_on_other_device">На первом устройстве запустите Delta Chat, откройте \"Настройки / Добавить второе устройство\" и отсканируйте показанный там код</string>
<string name="multidevice_open_settings_on_other_device">На первом устройстве запустите Delta Chat, перейдите в \"Настройки / Добавить второе устройство\" и отсканируйте показанный там код</string>
<string name="multidevice_receiver_scanning_ask">Скопировать профиль с другого устройства на это устройство?</string>
<string name="multidevice_receiver_needs_update">Профиль, который вы хотите импортировать, создан в более новой версии Delta Chat. Чтобы продолжить настройку второго устройства, пожалуйста, обновите это устройство до последней версии Delta Chat.</string>
<string name="multidevice_receiver_needs_update">Профиль, который вы хотите импортировать, создан в более новой версии Delta Chat.\n\nЧтобы продолжить настройку второго устройства, пожалуйста, обновите это устройство до последней версии Delta Chat.</string>
<string name="multidevice_abort">Прервать настройку второго устройства?</string>
<string name="multidevice_abort_will_invalidate_copied_qr">Это сделает недействительным QR-код, скопированный в буфер обмена.</string>
<string name="multidevice_transfer_done_devicemsg">ℹ️ Профиль перенесен на ваше второе устройство.</string>
@@ -658,7 +668,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_advanced_hint">Этот способ входа предназначен для опытных пользователей:\n\n• Не используйте адрес, который вы уже используете в другом приложении.\n\n• Обычный почтовый сервер позволяет вести чаты без сквозного шифрования, (такие чаты помечаются значком конверта).</string>
<string name="login_inbox">Входящие</string>
<string name="login_imap_login">Имя пользователя IMAP</string>
<string name="login_imap_server">Сервер IMAP</string>
@@ -684,15 +694,22 @@
<string name="proxy_share_explain">Ваши друзья могут добавить этот прокси, отсканировав QR-код.</string>
<string name="proxy_share_link">Поделиться ссылкой</string>
<string name="proxy_enabled">Прокси включен</string>
<string name="proxy_enabled_hint">Вы используете прокси. При проблемах с подключением, попробуйте другой прокси.</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>
<string name="used_for_sending">Используется для отправки</string>
<string name="hide_from_contacts">Скрыть из контактов</string>
<string name="hidden_from_contacts">Скрыт из контактов</string>
<!-- Hint for the list of relays -->
<string name="transport_list_hint">Сообщения поступают на все релеи.\n\n⚠️ Если вы измените какие-либо настройки в этом разделе, убедитесь, что на всех ваших устройствах установлена версия Delta Chat не ниже 2.47.0. В противном случае старые версии могут не получать сообщения.</string>
<!-- shown if a QR code was scanned that can be used as a relay -->
<string name="confirm_add_transport">Добавить этот релей?</string>
<string name="invalid_transport_qr">Отсканированный QR-код не содержит корректный релей.</string>
<!-- placeholder will be replaced by a relay server name -->
<string name="confirm_remove_or_hide_transport_x">Удалить релей \"%1$s\"?\n\nКонтакты, знающие только этот релей, могут потерять связь с вами, пока вы не напишете им снова.\n\nЕсли сомневаетесь, лучше скрыть его, а не удалять.</string>
<string name="login_certificate_checks">Проверки сертификатов</string>
<string name="login_error_mail">Введите корректный адрес электронной почты</string>
<string name="login_error_server">Введите корректный адрес сервера / IP-адрес</string>
@@ -729,7 +746,7 @@
<!-- 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">Отправить %1$d файла(ов) в \"%2$s\"?</string>
<string name="ask_send_files_to_selected_chats">Отправить %1$d файл(ов) в %2$d чата(ов)?</string>
<string name="videos_sent_without_recoding">(Отправка нескольких видео приведет к их отправке в исходном виде, что может потребовать значительного объема данных. Чтобы уменьшить размер файлов и сэкономить трафик, отправляйте каждое видео в отдельном сообщении.)</string>
<string name="videos_sent_without_recoding">(Отправка нескольких видео приведет к их отправке в исходном виде, что может потребовать значительного объема данных. Чтобы уменьшить размер файлов и сэкономить трафик, отправляйте каждое видео в отдельном сообщении)</string>
<string name="share_text_multiple_chats">Отправить текст в %1$d чата(ов)?\n\n\"%2$s\"</string>
<string name="share_abort">Общий доступ прерван из-за отсутствия необходимых разрешений.</string>
@@ -747,7 +764,7 @@
<string name="pref_enter_sends">Отправка по Enter</string>
<string name="pref_enter_sends_explain">Нажатие клавиши Enter отправит текстовые сообщения</string>
<string name="pref_outgoing_media_quality">Качество отправляемых медиафайлов</string>
<string name="pref_outgoing_media_quality_hint">Чтобы отправить файлы в оригинальном качестве, прикрепляйте их как \"Файл\". Это потребует больше трафика для вас и ваших получателей.</string>
<string name="pref_outgoing_media_quality_hint">Чтобы отправить файлы в исходном качестве, прикрепляйте их как \"Файл\". Это потребует больше трафика для вас и ваших получателей.</string>
<string name="pref_outgoing_balanced">Сбалансированное</string>
<string name="pref_outgoing_worse">Низкое качество, малый размер</string>
<string name="pref_vibrate">Вибрация</string>
@@ -796,7 +813,7 @@
<string name="pref_other">Другое</string>
<string name="pref_backup">Резервное копирование</string>
<string name="pref_backup_explain">Резервное копирование чатов на внешний носитель</string>
<string name="pref_backup_export_explain">Резервная копия поможет вам восстановить данные на этом или на другом устройстве.\n\nРезервная копия содержит все сообщения, контакты и чаты, а также параметры сквозного шифрования Autocrypt. Храните файл резервной копии в безопасном месте или удалите его как можно скорее.</string>
<string name="pref_backup_export_explain">Резервная копия поможет вам восстановить данные на этом или на другом устройстве.\n\nРезервная копия содержит все сообщения, контакты и чаты, а также параметры сквозного шифрования Autocrypt. Храните файл резервной копии в безопасном месте или удалите его при первой возможности.</string>
<string name="pref_backup_export_this">Экспортировать профиль</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">Экспорт всех %1$d профилей</string>
@@ -809,8 +826,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_multidevice_explain">Синхронизируйте свои сообщения с другими устройствами. Функция включается автоматически при добавлении второго устройства</string>
<string name="pref_multidevice_change_warn">Режим нескольких устройств должен быть включен, если вы используете один и тот же профиль на нескольких устройствах. Отключайте эту настройку, только если вы удалили этот профиль со всех остальных устройств.\n\nОтключение режима нескольких устройств при использовании профиля на нескольких устройствах приведет к потере сообщений и возникновению других проблем.</string>
<string name="pref_auto_folder_moves">Автоперемещение в папку DeltaChat</string>
<string name="pref_only_fetch_mvbox_title">Загружать только из папки DeltaChat</string>
<string name="pref_show_emails">Показывать обычную почту</string>
@@ -831,10 +848,6 @@
<string name="up_to_x_most_worse_quality_images">До %1$s, изображения плохого качества</string>
<string name="up_to_x_most_balanced_quality_images">До %1$s, изображения среднего качества</string>
<string name="download_failed">Загрузка не удалась</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 сообщение</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Максимальная загрузка доступна до %1$s</string>
<string name="profile_image_select">Выбрать изображение профиля</string>
<string name="select_your_new_profile_image">Выбрать новое изображение профиля</string>
<string name="profile_image_delete">Удалить изображение профиля</string>
@@ -843,7 +856,7 @@
<string name="send_stats_to_devs">Отправить статистику разработчикам Delta Chat</string>
<string name="stats_device_message">Хотите помочь улучшить Delta Chat и поддержать исследования, отправляя еженедельную анонимную статистику использования?\n\n👉 Нажмите здесь...👈</string>
<string name="stats_confirmation_dialog">Хотите помочь улучшить Delta Chat и поддержать исследования, отправляя еженедельную анонимную статистику использования?</string>
<string name="stats_thanks">Спасибо! Вы всегда можете отключить отправку в разделе \"Настройки -> Дополнительно\"\n\nУ вас есть 5 минут, чтобы принять участие в научном исследовании по безопасности Delta Chat?</string>
<string name="stats_thanks">Спасибо! Вы всегда можете отключить отправку в разделе \"Настройки -> Дополнительно\"\n\nУ вас есть 5 минут, чтобы принять участие в исследовании безопасности Delta Chat?</string>
<string name="stats_disable_dialog">Отправка статистики уже включена.\n\nВы хотите отключить её?</string>
<string name="disable">Отключить</string>
<string name="stats_keep_sending">Продолжить отправку</string>
@@ -876,7 +889,7 @@
<string name="autodel_device_title">Удалять сообщения с устройства</string>
<string name="autodel_server_title">Удалять сообщения с сервера</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 сообщений сейчас и все новые сообщения \"%2$s\" в дальнейшем?\n\n• Это относится ко всем медиафайлам\n\n• Сообщения будут удалены, независимо от того, были ли они просмотрены или нет.\n\n• \"Сохранённые сообщения\" будут пропущены при локальном удалении</string>
<string name="autodel_device_ask">Вы хотите удалить %1$d сообщений сейчас и все новые сообщения \"%2$s\" в дальнейшем?\n\n• Это действие затронет все медиафайлы\n\n• Сообщения будут удалены, независимо от того, были ли они прочитаны или нет.\n\n• \"Сохранённые сообщения\" не будут удалены с устройства</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 сообщений сейчас и все новые сообщения \"%2$s\" в дальнейшем?\n\n⚠️ Это относится ко всем электронным письмам, медиафайлам и \"Сохранённым сообщениям\" во всех папках на сервере.\n\n⚠️ Не используйте эту функцию, если хотите сохранить данные на сервере или если вы используете обычные клиенты электронной почты</string>
<!-- shown below enabled autodel_server-option, should be a summary of autodel_server_ask and remind about the impact -->
@@ -967,18 +980,19 @@
<!-- %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 включает автоудаление сообщений через %1$s недель.</string>
<string name="chat_unencrypted_explanation">Сообщения в этом чате используют обычную электронную почту и не зашифрованы сквозным шифрованием.</string>
<string name="messages_are_e2ee">Сообщения защищены сквозным шифрованием.</string>
<string name="chat_protection_enabled_tap_to_learn_more">Сообщения защищены сквозным шифрованием. Нажмите, чтобы узнать больше.</string>
<string name="chat_protection_enabled_explanation">Все сообщения в этом чате зашифрованы сквозным шифрованием.\n\nСквозное шифрование обеспечивает конфиденциальность переписки между вами и вашими собеседниками. Ни серверы, ни провайдеры, ни релеи не смогут прочитать сообщения.</string>
<string name="invalid_unencrypted_tap_to_learn_more">⚠️ %1$s требуется сквозное шифрование, которое ещё не настроено для данного чата. Нажмите, чтобы узнать больше.</string>
<string name="invalid_unencrypted_explanation">Чтобы установить сквозное шифрование, вы можете встретиться с контактами лично и отсканировать их QR-код, чтобы подтвердить их личность.</string>
<string name="learn_more">Узнать больше</string>
<string name="devicemsg_self_deleted">Вы удалили чат \"Сохраненные сообщения\".\n\nℹ️ Чтобы снова использовать функцию \"Сохраненные сообщения\", создайте новый чат с самим собой.</string>
<string name="devicemsg_self_deleted">Вы удалили чат \"Сохраненные сообщения\".\n\nℹ️ Чтобы снова использовать функцию \"Сохраненные сообщения\", создайте новый чат с собой.</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">⚠️ Хранилище вашего провайдера почти заполнено: %1$s%% уже использовано.\n\nЕсли хранилище полностью заполнено, вы не сможете получать сообщения.\n\n👉 Проверьте, можно ли удалить старые данные в веб-интерфейсе провайдера, и подумайте об включении \"Настройки / Чаты / Удалить старые сообщения\". Текущее использование хранилища можно проверить в любое время в разделе \"Настройки / Соединение\".</string>
<string name="devicemsg_storage_exceeding">⚠️ Место на сервере вашего провайдера скоро закончится, уже использовано: %1$s%%.\n\nЕсли место на сервере будет полностью заполнено, вы не сможете получать сообщения.\n\n👉 Проверьте, можно ли удалить старые данные через веб-интерфейс вашего провайдера, и подумайте о включении опции \"Настройки / Чаты / Удалить старые сообщения\". Вы всегда можете проверить текущее использование места на сервере в разделе \"Настройки / Соединение\".</string>
<!-- %1%s will be replaced by date and time in some human-readable format -->
<string name="devicemsg_bad_time">⚠️ Вероятно, дата или время на вашем устройстве неточные (%1$s).\n\nУстановите правильное время ⏰🔧 чтобы обеспечить правильное получение сообщений.</string>
<string name="devicemsg_update_reminder">⚠️ Вероятно, ваша версия Delta Chat устарела.\n\nЭто может привести к проблемам, поскольку ваши собеседники используют более новые версии, а вы не имеете доступа к последним функциям 😳\nПроверьте https://get.delta.chat или магазин приложений на наличие обновлений.</string>
<string name="devicemsg_bad_time">⚠️ Вероятно, дата или время на вашем устройстве установлены неверно (%1$s).\n\nНастройте часы ⏰🔧 чтобы ваши сообщения доставлялись правильно.</string>
<string name="devicemsg_update_reminder">⚠️ Вероятно, ваша версия Delta Chat устарела.\n\nЭто может вызвать проблемы, поскольку ваши собеседники используют более новые версии, а у вас нет доступа к последним функциям. 😳\nПожалуйста, проверьте наличие обновлений на сайте https://get.delta.chat или в магазине приложений.</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">Разблокируйте для продолжения</string>
@@ -993,13 +1007,13 @@
<string name="qrscan_ask_join_group">Вы хотите присоединиться к группе \"%1$s\"?</string>
<string name="qrscan_ask_join_channel">Вы хотите присоединиться к каналу \"%1$s\"?</string>
<string name="qrscan_fingerprint_mismatch">Отсканированный отпечаток не совпадает с последним известным отпечатком %1$s.</string>
<string name="qrscan_no_addr_found">QR-код содержит отпечаток ключа, но не содержит адрес электронной почты.\n\nДля проверки подлинности с использованием альтернативного канала связи следует сначала установить с получателем защищённое соединение.</string>
<string name="qrscan_no_addr_found">Этот QR-код содержит отпечаток ключа, но не содержит адрес.\n\nДля проверки подлинности с использованием альтернативного канала связи следует сначала установить с получателем защищённое соединение.</string>
<string name="qrscan_contains_text">Текст из QR-кода:\n\n%1$s</string>
<string name="qrscan_contains_url">URL из QR-кода:\n\n%1$s</string>
<string name="qrscan_fingerprint_label">Отпечаток</string>
<string name="withdraw_verifycontact_explain">Этот QR-код могут сканировать другие люди, чтобы связаться с вами.\n\nВы можете сбросить его, чтобы существующий QR-код или ссылка-приглашение больше не работали.</string>
<string name="withdraw_verifygroup_explain">Этот QR-код могут сканировать другие люди, чтобы присоединиться к группе \"%1$s\".\n\nВы можете сбросить его, чтобы существующий QR-код или ссылка-приглашение больше не работали.</string>
<string name="withdraw_joinbroadcast_explain">Этот QR-код могут сканировать другие пользователи для присоединения к каналу \"%1$s\".\n\nВы можете сбросить его, чтобы существующий QR-код или пригласительная ссылка перестали работать.</string>
<string name="withdraw_verifycontact_explain">Этот QR-код можно отсканировать, чтобы связаться с вами.\n\nВы можете сбросить его и тогда существующий QR-код или ссылка-приглашение перестанут работать.</string>
<string name="withdraw_verifygroup_explain">Этот QR-код можно отсканировать, чтобы присоединиться к группе \"%1$s\".\n\nВы можете сбросить его и тогда существующий QR-код или ссылка-приглашение перестанут работать.</string>
<string name="withdraw_joinbroadcast_explain">Этот QR-код можно отсканировать, чтобы присоединиться к каналу \"%1$s\".\n\nВы можете сбросить его и тогда существующий QR-код или ссылка-приглашение перестанут работать.</string>
<string name="withdraw_qr_code">Сбросить QR-код</string>
<string name="revive_verifycontact_explain">Этот QR-код был сброшен и больше не работает.</string>
<string name="revive_qr_code">Активировать QR-код</string>
@@ -1016,15 +1030,15 @@
<string name="qrshow_join_contact_hint">Отсканируйте, чтобы начать чат с %1$s</string>
<string name="qrshow_join_contact_no_connection_toast">Нет соединения с интернетом, невозможно выполнить настройку QR-кода.</string>
<string name="qraccount_ask_create_and_login">Создать новый профиль на \"%1$s\" и авторизоваться?</string>
<string name="set_name_and_avatar_explain">Укажите имя, которое будут видеть ваши контакты. Также можно установить изображение профиля.</string>
<string name="set_name_and_avatar_explain">Укажите имя, которое будут видеть ваши контакты. Вы также можете установить изображение профиля.</string>
<string name="please_enter_name">Введите имя.</string>
<string name="qraccount_qr_code_cannot_be_used">Отсканированный QR-код не может быть использован для создания нового профиля.</string>
<!-- the placeholder will be replaced by the address of the profile -->
<string name="qrlogin_ask_login">Авторизоваться в \"%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 приглашает вас в группу.\n\nОжидаем ответ от %2$s…</string>
<string name="secure_join_started">%1$s приглашает вас присоединиться к этой группе.\n\nОжидаем ответа от устройства %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>
<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>
@@ -1058,12 +1072,12 @@
<!-- permissions -->
<string name="perm_required_title">Требуется разрешение</string>
<string name="perm_continue">Продолжить</string>
<string name="perm_explain_access_to_camera_denied">Чтобы делать фотографии, снимать видео, откройте настройки приложения, выберите \"Разрешения\" и включите \"Камера\".</string>
<string name="perm_explain_access_to_camera_denied">Чтобы делать фотографии, снимать видео, откройте настройки приложения, выберите \"Разрешения\" и включите доступ к \"Камере\".</string>
<!-- give the user an idea where to find the "Microphone" option. the hint is only shown if access was initially denied by the user. pick up wordings really used on the systems, but do not be overly precise: things shift around often and there are too many system we support to track every detail and click path -->
<string name="perm_explain_access_to_mic_denied">Чтобы отправлять аудиосообщения, откройте настройки приложения, выберите \"Разрешения\" или \"Конфиденциальность и безопасность\" и включите \"Микрофон\".</string>
<string name="perm_explain_access_to_storage_denied">Чтобы получать или отправлять файлы, откройте настройки приложения, выберите \"Разрешения\" и включите \"Хранилище\".</string>
<string name="perm_explain_access_to_location_denied">Чтобы прикрепить местоположение, откройте настройки приложения, выберите \"Разрешения\" и включите \"Местоположение\".</string>
<string name="perm_explain_access_to_notifications_denied">Для получения уведомлений, перейдите в \"Настройки системы / Приложения / Delta Chat\" и включите \"Уведомления\".</string>
<string name="perm_explain_access_to_mic_denied">Чтобы отправлять аудиосообщения, откройте настройки приложения, выберите \"Разрешения\" или \"Конфиденциальность и безопасность\" и включите доступ к \"Микрофону\".</string>
<string name="perm_explain_access_to_storage_denied">Чтобы получать или отправлять файлы, откройте настройки приложения, выберите \"Разрешения\" и включите доступ к \"Хранилищу\".</string>
<string name="perm_explain_access_to_location_denied">Чтобы прикрепить местоположение, откройте настройки приложения, выберите \"Разрешения\" и включите доступ к \"Местоположению\".</string>
<string name="perm_explain_access_to_notifications_denied">Для получения уведомлений, перейдите в \"Настройки системы / Приложения / Delta Chat\" и включите доступ к \"Уведомлениям\".</string>
<!-- ImageEditorHud -->
<string name="ImageEditorHud_draw_anywhere_to_blur">Нарисуйте где угодно, чтобы размыть</string>
@@ -1148,11 +1162,11 @@
<string name="share_account_not_configured">Профиль не настроен.</string>
<string name="cannot_play_audio_file">Аудиофайл не может быть воспроизведён.</string>
<!-- iOS camera permission alert -->
<string name="perm_ios_explain_access_to_camera_denied">Чтобы делать фотографии, снимать видео и использовать сканер QR-кодов, откройте системные настройки и включите \"Камера\".</string>
<string name="perm_ios_explain_access_to_camera_denied">Чтобы делать фотографии, снимать видео и использовать сканер QR-кодов, откройте системные настройки и включите доступ к \"Камере\".</string>
<string name="open_settings">Открыть настройки</string>
<!-- iOS device contact alert -->
<string name="import_device_contacts">Импорт контактов устройства</string>
<string name="import_device_contacts_hint">Чтобы переписываться с контактами из вашей адресной книги, откройте \"Настройки\" и включите \"Контакты\".</string>
<string name="import_device_contacts_hint">Чтобы переписываться с контактами из вашей адресной книги, откройте \"Настройки\" и включите доступ к \"Контактам\".</string>
<!-- iOS notification settings -->
<string name="disabled_in_dc">Отключено в Delta Chat.</string>
<string name="disabled_in_system_settings">Отключено в системных настройках</string>
@@ -1173,26 +1187,26 @@
<!-- use the same translation for "Widget" as the system is using; often the term "Widget" stays untranslated -->
<string name="add_to_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 использует вашу камеру для фото- и видеосъёмки, и сканирования QR-кодов.</string>
<string name="InfoPlist_NSLocationAlwaysAndWhenInUseUsageDescription">Delta Chat требует разрешения на передачу вашего местоположения в течение того времени, на которое вы включили обмен местоположением.</string>
<string name="InfoPlist_NSLocationWhenInUseUsageDescription">Delta Chat требует разрешения на передачу вашего местоположения в течение того времени, на которое вы включили обмен местоположением.</string>
<string name="InfoPlist_NSCameraUsageDescription">Delta Chat использует вашу камеру для фото- и видеосъёмки, а также для сканирования QR-кодов.</string>
<string name="InfoPlist_NSLocationAlwaysAndWhenInUseUsageDescription">Delta Chat требует разрешения на передачу информации о вашем местоположении на тот период времени, в течение которого вы разрешили обмен данными о местоположении.</string>
<string name="InfoPlist_NSLocationWhenInUseUsageDescription">Delta Chat требует разрешения на передачу информации о вашем местоположении на тот период времени, в течение которого вы разрешили обмен данными о местоположении.</string>
<string name="InfoPlist_NSMicrophoneUsageDescription">Delta Chat использует микрофон для записи и отправки голосовых сообщений, а также видео со звуком.</string>
<string name="InfoPlist_NSPhotoLibraryUsageDescription">Delta Chat предоставит вам возможность выбирать какие фотографии и из какой галереи отправлять.</string>
<string name="InfoPlist_NSPhotoLibraryAddUsageDescription">Delta Chat хочет сохранить изображения в вашей галерее.</string>
<string name="InfoPlist_NSFaceIDUsageDescription">Delta Chat поддерживает использование Face ID для защиты локального профиля, создания резервных копий и настройки второго устройства.</string>
<string name="InfoPlist_NSFaceIDUsageDescription">Delta Chat может использовать Face ID для защиты локального профиля, создания резервных копий и настройки второго устройства.</string>
<!-- android specific strings, developers: please take care to remove strings that are no longer used! -->
<string name="pref_instant_delivery">Мгновенная доставка</string>
<string name="pref_background_notifications">Использовать соединение в фоновом режиме</string>
<string name="pref_background_notifications_explain">Требуется отключение оптимизации батареи, используйте, если уведомления не приходят вовремя.</string>
<string name="pref_background_notifications_explain">Требуется отключение оптимизации батареи, используйте, если уведомления приходят с задержкой</string>
<string name="pref_reliable_service">Принудительное соединение в фоновом режиме</string>
<string name="pref_reliable_service_explain">Активирует постоянное уведомление</string>
<string name="pref_background_notifications_rationale">Чтобы поддерживать соединение с сервером электронной почты и получать сообщения в фоновом режиме, отключите оптимизацию батареи на следующем шаге.\n\nDelta Chat расходует мало ресурсов и бережно относится к вашей батарее.</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">Нажмите здесь, чтобы получать сообщения, когда Delta Chat находится в фоновом режиме.</string>
<string name="perm_enable_bg_already_done">Вы уже разрешили Delta Chat получать сообщения в фоновом режиме.\n\nЕсли сообщения не приходят в фоновом режиме, проверьте также системные настройки.</string>
<string name="perm_enable_bg_already_done">Вы уже разрешили Delta Chat получать сообщения в фоновом режиме.\n\nЕсли сообщения все равно не приходят в фоновом режиме, также проверьте настройки вашей системы.</string>
<!-- device messages for updates -->
<string name="update_2_0">Что нового?\n\n💯 Сквозное шифрование стало постоянной и надёжной функцией. 🔒 Замков больше нет!\n\n✉️ Обычные письма без сквозного шифрования помечены значком конверта\n\n😻 Обновлённый экран профиля для всех контактов\n\n🔲 Новая кнопка быстрого доступа к приложениям, используемым в чате\n\n❤️ Поддержите нас финансово, чтобы мы сохранили независимость и продолжали вносить улучшения: %1$s</string>
<string name="update_2_0">Что нового?\n\n💯 Сквозное шифрование стало постоянной и надёжной функцией. Значки замков 🔒 убраны!\n\n✉️ Обычные письма без сквозного шифрования помечены значком конверта\n\n😻 Обновлённый экран профиля для всех контактов\n\n🔲 Новая кнопка быстрого доступа к приложениям, используемым в чате\n\n❤️ Пожалуйста, сделайте пожертвование, чтобы помочь нам сохранить независимость и продолжать работу над улучшениями: %1$s</string>
</resources>
-4
View File
@@ -705,10 +705,6 @@
<string name="up_to_x_most_worse_quality_images">Až do %1$s, väčšina obrázkov v horšej kvalite</string>
<string name="up_to_x_most_balanced_quality_images">Až do %1$s, väčšina obrázkov vo vyváženej kvalite</string>
<string name="download_failed">Sťahovanie zlyhalo</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 správa</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Maximálny počet stiahnutí k dispozícii do %1$s</string>
<string name="profile_image_select">Vyberte profilový obrázok</string>
<string name="select_your_new_profile_image">Vyberte svoj nový profilový obrázok</string>
<string name="profile_image_delete">Odstrániť profilový obrázok</string>
+6 -5
View File
@@ -68,6 +68,8 @@
<string name="mark_as_read">Vëri Shenjë Si të Lexuar</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">Të lexuar</string>
<!-- Shortest text for "Mark as being unread". In english, this could be "Unread" (past tense of "to unread"), in german, this could be "Ungelesen". -->
<string name="mark_as_unread_short">I palexuar</string>
<!-- Placeholder text when something is loading -->
<string name="loading">Po ngarkohet…</string>
<string name="hide">Fshihe</string>
@@ -409,6 +411,8 @@
<string name="declined_call">Thirrje e hedhur poshtë</string>
<string name="canceled_call">Thirrje e anuluar</string>
<string name="missed_call">Thirrje e humbur</string>
<string name="already_in_call">Tashmë në një thirrje</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">Jeni i sigurt se doni të largoheni?</string>
@@ -662,7 +666,7 @@
<string name="hide_from_contacts">Fshihe prej Kontaktesh</string>
<string name="hidden_from_contacts">Fshehur prej kontakteve</string>
<!-- Hint for the list of relays -->
<string name="transport_list_hint">Mesazhet merren në krejt reletë.</string>
<string name="transport_list_hint">Mesazhet merren në të tëra reletë.\n\n⚠️ Nëse ndryshoni diçka këtu, sigurohuni se krejt pajisjet tuaja xhirojnë të paktën versionin 2.47.0. Përndryshe, pajisje më të vjetra mund të humbin mesazhe.</string>
<!-- shown if a QR code was scanned that can be used as a relay -->
<string name="confirm_add_transport">Të shtohet kjo rele?</string>
<string name="invalid_transport_qr">Kodi QR i skanuar spërmban një rele të vlefshme.</string>
@@ -805,10 +809,6 @@
<string name="up_to_x_most_worse_quality_images">Deri në %1$s, figura me cilësinë më të dobët</string>
<string name="up_to_x_most_balanced_quality_images">Deri në %1$s, figurat me cilësinë më të baraspeshuar</string>
<string name="download_failed">Shkarkimi dështoi</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">Mesazh %1$s</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Shkarko maksimumin e mundshëm deri më %1$s</string>
<string name="profile_image_select">Përzgjidhni Figurë Profili</string>
<string name="select_your_new_profile_image">Përzgjidhni figurën tuaj të re të profilit</string>
<string name="profile_image_delete">Fshini Figurën e Profilit</string>
@@ -941,6 +941,7 @@
<!-- %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">Kohëmatësi i zhdukjes së mesazheve u vu në %1$s javë nga %2$s.</string>
<string name="chat_unencrypted_explanation">Mesazhet në këtë fjalosje përdorin email klasik dhe sjanë të fshehtëzuar skaj-më-skaj.</string>
<string name="messages_are_e2ee">Mesazhet janë të fshehtëzuara skaj-më-skaj</string>
<string name="chat_protection_enabled_tap_to_learn_more">Mesazhet fshehtëzohen skaj-më-skaj. Prekeni, që të mësoni më tepër.</string>
<string name="chat_protection_enabled_explanation">Krejt mesazhet në këtë fjalosje janë të fshehtëzuar skaj-më-skaj.\n\nFshehtëzimi skaj-më-skaj i mban private mesazhet mes jush dhe partnerëve tuaj të fjalosjes. Ato smund ti lexojnë as shërbyesit, shërbime apo reletë.</string>
<string name="invalid_unencrypted_tap_to_learn_more">⚠️ %1$s lyp fshehtëzim, i cili s’është ujdisur ende për këtë fjalosje. Që të mësoni më tepër, prekeni.</string>
-4
View File
@@ -582,10 +582,6 @@
<string name="up_to_x_most_worse_quality_images">Повећано на %1$s, најлошији квалитет слика</string>
<string name="up_to_x_most_balanced_quality_images">Повећано на %1$s, уравнотежен квалитет слика</string>
<string name="download_failed">Преузимање није успело</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 поруке</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Преузмите максимално доступно до %1$s</string>
<string name="profile_image_select">Изабери слику за профил</string>
<string name="select_your_new_profile_image">Изаберите нову слику за ваш профил</string>
<string name="profile_image_delete">Избриши слику профила</string>
-4
View File
@@ -796,10 +796,6 @@
<string name="up_to_x_most_worse_quality_images">Upp till %1$s, sämst bildkvalitet</string>
<string name="up_to_x_most_balanced_quality_images">Upp till %1$s, balanserad bildkvalitet</string>
<string name="download_failed">Nerladdning misslyckades</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 meddelande</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Ladda ner max tillgängligt till %1$s</string>
<string name="profile_image_select">Välj profilbild</string>
<string name="select_your_new_profile_image">Välj din nya profilbild</string>
<string name="profile_image_delete">Ta bort profilbild</string>
+19 -4
View File
@@ -69,6 +69,8 @@
<string name="mark_as_read">Okundu Olarak İmle</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">Okundu İmi</string>
<!-- Shortest text for "Mark as being unread". In english, this could be "Unread" (past tense of "to unread"), in german, this could be "Ungelesen". -->
<string name="mark_as_unread_short">Okunmayan</string>
<!-- Placeholder text when something is loading -->
<string name="loading">Yükleniyor…</string>
<string name="hide">Gizle</string>
@@ -163,6 +165,7 @@
<item quantity="other">%d Seçildi</item>
</plurals>
<string name="selected_colon">Seçilen:</string>
<string name="selected">Seçilen</string>
<string name="self">Ben</string>
<string name="draft">Taslak</string>
<string name="image">Görsel</string>
@@ -395,6 +398,12 @@
<string name="audio_call">Sesli arama</string>
<!-- Used in call bubbles, summaries, notifications -->
<string name="video_call">Görüntülü arama</string>
<string name="call_initializing">Başlatılıyor…</string>
<string name="call_incoming">Gelen arama</string>
<string name="call_ringing">Çalıyor…</string>
<string name="call_reconnecting">Yeniden bağlanıyor…</string>
<string name="call_ended">Arama sonlandırıldı</string>
<string name="call_failed">Arama başarısız</string>
<!-- deprecated, use "Audio call" instead -->
<string name="outgoing_audio_call">Giden sesli arama</string>
<!-- deprecated, use "Video call" instead -->
@@ -406,6 +415,8 @@
<string name="declined_call">Reddedilen arama</string>
<string name="canceled_call">İptal edilen arama</string>
<string name="missed_call">Yanıtlanmayan arama</string>
<string name="already_in_call">Zaten bir aramada</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">Ayrılmak istediğinizden emin misiniz?</string>
@@ -655,9 +666,16 @@
<string name="add_transport">Aktarıcı Ekle</string>
<string name="remove_transport">Aktarıcıyı Kaldır</string>
<string name="edit_transport">Aktarıcıyı Düzenle</string>
<string name="used_for_sending">Gönderim için kullanıldı</string>
<string name="hide_from_contacts">Kişilerden Gizle</string>
<string name="hidden_from_contacts">Kişilerden gizlendi</string>
<!-- Hint for the list of relays -->
<string name="transport_list_hint">İletiler tüm aktarıcılardan alınır.\n\n⚠️ Burada herhangi bir şeyi değiştirirseniz, tüm aygıtlarınızın en azından 2.47.0 sürümünü çalıştırdığından emin olun. Yoksa daha eski aygıtlar iletileri kaçırabilir.</string>
<!-- shown if a QR code was scanned that can be used as a relay -->
<string name="confirm_add_transport">Bu aktarıcı eklensin mi?</string>
<string name="invalid_transport_qr">Taranan QR kodu, geçerli bir aktarıcı içermiyor.</string>
<!-- placeholder will be replaced by a relay server name -->
<string name="confirm_remove_or_hide_transport_x">“%1$s” aktarıcısı kaldırılsın mı?\n\nYalnızca bu aktarıcıyı bilen kişiler, onlara yeniden ileti gönderene kadar size ulaşamayabilir.\n\nKuşkuluysanız, onu kaldırmak yerine gizleyin.</string>
<string name="login_certificate_checks">Sertifika Denetimleri</string>
<string name="login_error_mail">Lütfen geçerli bir e-posta adresi girin</string>
<string name="login_error_server">Lütfen geçerli bir sunucu / IP adresi girin</string>
@@ -796,10 +814,6 @@
<string name="up_to_x_most_worse_quality_images">%1$s kadar, en kötü kaliteli görseller</string>
<string name="up_to_x_most_balanced_quality_images">%1$s kadar, en dengeli kaliteli görseller</string>
<string name="download_failed">İndirme başarısız</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 ileti</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">İndirme en fazla %1$s zamanına kadar kullanılabilir</string>
<string name="profile_image_select">Profil Görseli Seç</string>
<string name="select_your_new_profile_image">Yeni profil görselinizi seçin</string>
<string name="profile_image_delete">Profil Görselini Sil</string>
@@ -932,6 +946,7 @@
<!-- %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 tarafından kaybolan iletiler zamanlayıcısı %1$s hafta olarak ayarlandı.</string>
<string name="chat_unencrypted_explanation">Bu sohbetteki iletiler klasik e-posta kullanıyor ve uçtan uca şifreli değil.</string>
<string name="messages_are_e2ee">İletiler uçtan uca şifrelidir.</string>
<string name="chat_protection_enabled_tap_to_learn_more">İletiler uçtan uca şifreleniyor. Daha fazlasını öğrenmek için dokunun.</string>
<string name="chat_protection_enabled_explanation">Bu sohbetteki tüm iletiler uçtan uca şifreleniyor.\n\nUçtan uca şifreleme, iletilerinizi sizin ve sohbet ortaklarınızın arasında özel tutar. Sunucular, sağlayıcılar ya da aktarıcılar bile onları okuyamaz.</string>
<string name="invalid_unencrypted_tap_to_learn_more">⚠️ %1$s, bu sohbet için henüz ayarlanmayan uçtan uca şifrelemeyi gerektirir. Daha fazlasını öğrenmek için dokunun.</string>
-4
View File
@@ -801,10 +801,6 @@
<string name="up_to_x_most_worse_quality_images">До %1$s, зображення найгіршої якості</string>
<string name="up_to_x_most_balanced_quality_images">До %1$s, найбільш збалансована якість зображень</string>
<string name="download_failed">Не вдалося завантажити</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\'</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Доступний для завантаження максимум до %1$s</string>
<string name="profile_image_select">Вибрати зображення профілю</string>
<string name="select_your_new_profile_image">Вибрати ваше нове зображення профілю</string>
<string name="profile_image_delete">Видалити зображення профілю</string>
-4
View File
@@ -573,10 +573,6 @@
<string name="up_to_x_most_worse_quality_images">Lên đến %1$s, hầu hết các hình ảnh có chất lượng kém hơn</string>
<string name="up_to_x_most_balanced_quality_images">Lên đến %1$s, hình ảnh có chất lượng cân bằng nhất</string>
<string name="download_failed">Tải xuống không thành công</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 tin nhắn</string>
<!-- %1$s will be replaced by human-readable date and time -->
<string name="download_max_available_until">Tải xuống tối đa có sẵn cho đến %1$s</string>
<string name="profile_image_select">Chọn ảnh hồ sơ</string>
<string name="select_your_new_profile_image">Chọn hình ảnh hồ sơ mới của bạn</string>
<string name="profile_image_delete">Xóa ảnh hồ sơ</string>

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