Compare commits

..

4 Commits

Author SHA1 Message Date
copilot-swe-agent[bot] f0e95a59d1 Simplify sendComplete: remove chatId param and dead refreshFragment logic
sendComplete is only ever called from the message-send path, never the
draft path, so the passed chatId always equals this.chatId. The
refreshFragment branch and this.chatId = chatId assignment were dead
code. Removing them also eliminates the class of bug where an old
chatId could accidentally revert the activity's current chat.

Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-03-13 20:09:26 +00:00
copilot-swe-agent[bot] d44ad7de0c Fix introduced bug: restore sendComplete to use dcChat.getId()
The previous fix captured `currentChatId` to stop the setDraft race
condition, but also changed `sendComplete(dcChat.getId())` to
`sendComplete(currentChatId)`. Java lambdas access instance fields via
`this`, so `dcChat.getId()` in the lambda reads the current value when
it runs — after `initializeResources()` updates `dcChat` to the private
chat, `sendComplete` naturally received the private chat ID. Passing
`currentChatId` (old group chat ID) instead caused `sendComplete` to
write `this.chatId = GROUP_CHAT_ID`, reverting the activity to the old
chat and triggering an unwanted fragment reload.

Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-03-13 20:00:04 +00:00
copilot-swe-agent[bot] 3e0a283633 Fix: reply privately sometimes doesn't quote the message
Two fixes for the race condition that caused "reply privately" to
sometimes open the 1:1 chat without the quoted message:

1. In processComposeControls(), capture dcChat.getId() as currentChatId
   before the background thread starts. Without this, initializeResources()
   can change dcChat to the new private chat before the background thread
   runs, causing it to overwrite the quote draft with an empty/wrong draft.

2. In InputPanel.setQuote(), if getMeasuredHeight() returns 0 after
   measure(0,0) (view not yet laid out), fall back to WRAP_CONTENT instead
   of animating to 0 height which would keep the quote view at 1px height.

Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-03-13 18:00:02 +00:00
copilot-swe-agent[bot] ac01395482 Initial plan 2026-03-13 17:36:33 +00:00
267 changed files with 8488 additions and 21394 deletions
+20
View File
@@ -0,0 +1,20 @@
name: add artifact links to pull request
on:
workflow_run:
workflows: ["Upload Preview APK"]
types: [completed]
jobs:
artifacts-url-comments:
name: add artifact links to pull request
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- name: add artifact links to pull request
uses: tonyhallett/artifacts-url-comments@v1.1.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
prefix: "**To test the changes in this pull request, install this apk:**"
format: "[📦 {name}]({url})"
addTo: pull
-53
View File
@@ -1,53 +0,0 @@
name: Core Cache
on:
push:
branches: [main]
paths:
- 'jni/deltachat-core-rust'
- 'jni/Android.mk'
- 'jni/Application.mk'
- 'jni/dc_wrapper.c'
- 'scripts/ndk-make.sh'
permissions: {}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: recursive
persist-credentials: false
- uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4.0.1
- uses: nttld/setup-ndk@ed92fe6cadad69be94a966a7ee3271275e62f779 # v1.6.0
id: setup-ndk
with:
ndk-version: r27
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
workspaces: jni/deltachat-core-rust
- name: Get deltachat-core-rust submodule hash
id: core-hash
run: echo "hash=$(git rev-parse HEAD:jni/deltachat-core-rust)" >> $GITHUB_OUTPUT
- name: Cache compiled core
id: cache-core
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
jni/arm64-v8a
libs/arm64-v8a
key: core-arm64-v8a-${{ steps.core-hash.outputs.hash }}-${{ hashFiles('jni/Android.mk', 'jni/Application.mk', 'jni/dc_wrapper.c', 'scripts/ndk-make.sh') }}-ndk-r27
- name: Compile core
if: steps.cache-core.outputs.cache-hit != 'true'
env:
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
run: |
export PATH="${PATH}:${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64/bin/"
scripts/install-toolchains.sh && scripts/ndk-make.sh arm64-v8a
-49
View File
@@ -1,49 +0,0 @@
name: Code Format
on:
push:
branches: [main]
pull_request:
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
spotless:
name: Check code format (Spotless)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
java-version: 17
distribution: temurin
- name: Restore Gradle cache
id: gradle-cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
- name: Check formatting
run: ./gradlew spotlessCheck
- name: Save Gradle cache
if: github.event_name == 'push' && steps.gradle-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
+12 -53
View File
@@ -6,31 +6,25 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
permissions: {}
jobs:
build:
name: Upload Preview APK
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v5
with:
submodules: recursive
persist-credentials: false
- name: Validate Fastlane Metadata
uses: ashutoshgngwr/validate-fastlane-supply-metadata@c8857fdbbd3e00f9a5cbe8604bcecfa95ce8fef8 # v2.1.0
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
uses: ashutoshgngwr/validate-fastlane-supply-metadata@v2
- uses: Swatinem/rust-cache@v2
with:
working-directory: jni/deltachat-core-rust
- uses: actions/setup-java@v5
with:
java-version: 17
distribution: 'temurin'
- uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
- uses: android-actions/setup-android@v3
- uses: actions/cache@v4
with:
path: |
~/.gradle/caches
@@ -38,35 +32,15 @@ jobs:
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
- uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4.0.1
- uses: nttld/setup-ndk@ed92fe6cadad69be94a966a7ee3271275e62f779 # v1.6.0
- uses: nttld/setup-ndk@v1
id: setup-ndk
with:
ndk-version: r27
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
workspaces: jni/deltachat-core-rust
- name: Get deltachat-core-rust submodule hash
id: core-hash
run: echo "hash=$(git rev-parse HEAD:jni/deltachat-core-rust)" >> $GITHUB_OUTPUT
- name: Restore compiled core
id: core-cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
jni/arm64-v8a
libs/arm64-v8a
key: core-arm64-v8a-${{ steps.core-hash.outputs.hash }}-${{ hashFiles('jni/Android.mk', 'jni/Application.mk', 'jni/dc_wrapper.c', 'scripts/ndk-make.sh') }}-ndk-r27
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@v4
- name: Compile core
if: steps.core-cache.outputs.cache-hit != 'true'
env:
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
run: |
@@ -77,22 +51,7 @@ jobs:
run: ./gradlew --no-daemon -PABI_FILTER=arm64-v8a assembleFossDebug
- name: Upload APK
id: upload
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v4
with:
name: app-preview.apk
path: 'build/outputs/apk/foss/debug/*.apk'
- name: Add artifact links to PR
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
ARTIFACT_URL: ${{ steps.upload.outputs.artifact-url }}
with:
script: |
const url = process.env.ARTIFACT_URL;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `**To test the changes in this pull request, install this apk:**\n\n[📦 app-preview.apk](${url})`,
});
+17 -6
View File
@@ -23,18 +23,18 @@ jobs:
name: Upload Release APK
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v3
with:
submodules: recursive
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
- uses: Swatinem/rust-cache@v2
with:
working-directory: jni/deltachat-core-rust
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
- uses: actions/setup-java@v3
with:
java-version: 17
distribution: 'temurin'
- uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4.0.1
- uses: nttld/setup-ndk@ed92fe6cadad69be94a966a7ee3271275e62f779 # v1.6.0
- uses: android-actions/setup-android@v3
- uses: nttld/setup-ndk@v1
id: setup-ndk
with:
ndk-version: r27
@@ -63,7 +63,7 @@ jobs:
mv build/outputs/mapping/gplayRelease/mapping.txt build/outputs/mapping/fossRelease/mapping-gplay.txt
- name: Release on GitHub
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
uses: softprops/action-gh-release@v1
with:
token: "${{ secrets.GITHUB_TOKEN }}"
body: '[<img src="store/get-it-on-gplay.png" alt="Get it on Google Play" height="48">](https://play.google.com/store/apps/details?id=com.github.arcanechat) [<img src="store/get-it-on-fdroid.png" alt="Get it on F-Droid" height="48">](https://f-droid.org/packages/chat.delta.lite) [<img src="store/get-it-on-github.png" alt="Get it on GitHub" height="48">](https://github.com/ArcaneChat/android/releases/latest/download/ArcaneChat-gplay.apk)'
@@ -72,3 +72,14 @@ 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
-26
View File
@@ -1,26 +0,0 @@
name: GitHub Actions Security Analysis with zizmor
on:
push:
branches: ["main"]
pull_request:
branches: ["**"]
permissions: {}
jobs:
zizmor:
name: Run zizmor
runs-on: ubuntu-latest
permissions:
security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files.
contents: read
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Run zizmor
uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3
-6
View File
@@ -1,6 +0,0 @@
rules:
unpinned-uses:
config:
policies:
actions/*: ref-pin
dependabot/*: ref-pin
+1 -13
View File
@@ -27,7 +27,7 @@ install Rust tooling (read sections below) and the [dcrpcgen tool](https://githu
then generate the code running the script:
```
./scripts/update-rpc-bindings.sh
./scripts/generate-rpc-bindings.sh
```
## Build Using Nix
@@ -243,15 +243,3 @@ $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 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/
+2 -71
View File
@@ -1,85 +1,16 @@
# Delta Chat Android Changelog
## v2.50.0
2026-05
* Better incoming call system integration
* Calls are not experimental anymore and don't need to be manually enabled
* Calls can be answered by tapping messages
* Notify the user when they try to make a call while the device is offline
* Channels are no longer experimental and are available by default
* Display a permanent notification when doing location streaming and get rid of dangerous "Access Location in Background" permission
* Autoplay all voice messages in a chat
* Allow to share location for 24 hours
* Allow mini-apps to play audio without user interaction
* Allow to paste and open invitation links from search
* Mark chats as unread (long tap a chat and select the corresponding option from the three-dot-menu)
* Add "Mark all as read" option to profile menu in the profile switcher
* Fix process of upgrading from a very old version of the app
* Show more recent added stickers at the top of the sticker picker
* Allow to open links in messages via actions in TalkBack menu
* Allow to open map if user clicks "Location streaming enabled" system message
* Allow to disable incoming calls notifications
* Add an option to process unencrypted messages; by default, only encrypted messages can be sent or received
* Fix: do not accidentally set draft in chats that don't allow sending messages
* Fix swipe navigation between tabs in RTL languages
* Remove "Move to DeltaChat folder", in case you are using the option, a device message shows how to proceed
* Remove "Only fetch from DeltaChat folder" option, the functionality is preserved for existing profiles
* Remove "Delete Messages from Server" option, this is now up to the server:
Chatmail handles that automatically, classic email servers used as relay often have lots of storage or options themselves
* Remove "Show Email" options, all messages are shown by default, shared usage of email account is not supported
* Allow otherwise invalid TLS connections if the key is unchanged
* Adapt quota warning to automatic cleanup.
* Don't show non-delivery-notfications in broadcast channels
* Resend the last 10 messages to new broadcast channel member
* Enable PQC (Post-Quantum Cryptography) support. We do not generate PQC keys yet, this step is needed for forward compatibility
* Improve avatar quality
* Fix: avoid invalid empty "~" notifications when some peer is streaming location
* Fix: Improve detection of stickers
* Fix text direction issues for RTL languages in "Show full message" view
* Fix: Reconnect when removing a relay
* Update to core 2.50.0
## 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
## Unreleased
* Allow to set chat description
* Unified date display in call bubbles
* Explain at "Settings / Chats / Outgoing Media Quality" how to send original quality
* Add a basic sticker picker
* Leave groups and channels before deletion
* Further minimize metadata in messages and while getting in contact.
* Increase resilience of multi-relay usage: if on relay goes down, messages are still received in the others.
* Allow to hide a relay from contacts instead of removing, allowing smoother relay changes
* HTML emails: allow to review and copy links before opening them
* Mark call message as seen when accepting/declining a call
* Fix: keep original sent timestamp for resent messages
* Fix: make clicking on broadcast member-added messages work always
* Fix: remove notification when a message is deleted by sender
* 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.47.0
* Update to core 2.44.0
## v2.43.0
2026-02
+8 -5
View File
@@ -73,15 +73,18 @@ esp. before/after screenshots.
### Coding Conventions
Project language is Java.
Source files are partly derived from different other open source projects
and may follow different coding styles and conventions.
Code formatting is enforced via [Spotless](https://github.com/diffplug/spotless) Gradle plugin.
Auto-format all files by running `./gradlew spotlessApply` before opening a PR.
CI will fail if files are not formatted correctly so make sure to run the formatter before pushing.
If you do a PR fixing a bug or adding a feature,
please embrace the coding convention you see in the corresponding files,
so that the result fits well together.
If you do a PR fixing a bug or adding a feature, do not refactor or rename things in the same PR
Do not refactor or rename things in the same PR
to make the diff small and the PR easy to review.
Project language is Java.
By using [Delta Chat Core](https://github.com/deltachat/deltachat-core-rust)
there is already a strong separation between "UI" and "Model".
Further separations and abstraction layers are often not helpful
+21 -64
View File
@@ -1,7 +1,6 @@
plugins {
id 'com.android.application' version '8.11.1'
id 'com.google.gms.google-services' version '4.4.1'
id 'com.diffplug.spotless' version '8.3.0'
}
repositories {
@@ -34,8 +33,8 @@ android {
useLibrary 'org.apache.http.legacy'
defaultConfig {
versionCode 30000743
versionName "2.50.0"
versionCode 30000737
versionName "2.43.0"
applicationId "chat.delta.lite"
multiDexEnabled true
@@ -73,20 +72,10 @@ android {
packagingOptions {
jniLibs {
doNotStrip '**/*.so'
keepDebugSymbols += [
'*/mips/*.so',
'*/mips64/*.so'
]
keepDebugSymbols += ['*/mips/*.so', '*/mips64/*.so']
}
resources {
excludes += [
'LICENSE.txt',
'LICENSE',
'NOTICE',
'asm-license.txt',
'META-INF/LICENSE',
'META-INF/NOTICE'
]
excludes += ['LICENSE.txt', 'LICENSE', 'NOTICE', 'asm-license.txt', 'META-INF/LICENSE', 'META-INF/NOTICE']
}
}
@@ -165,14 +154,14 @@ android {
}
if(!project.hasProperty("ABI_FILTER")) {
splits {
abi {
enable true
reset()
include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
universalApk true
}
splits {
abi {
enable true
reset()
include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
universalApk true
}
}
}
project.ext.versionCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, 'x86': 3, 'x86_64': 4]
@@ -180,16 +169,16 @@ android {
android.applicationVariants.all { variant ->
variant.outputs.all { output ->
output.outputFileName = output.outputFileName
.replace("android", "ArcaneChat")
.replace("-release", "")
.replace(".apk", "-${variant.versionName}.apk")
.replace("android", "ArcaneChat")
.replace("-release", "")
.replace(".apk", "-${variant.versionName}.apk")
if(project.hasProperty("ABI_FILTER")) {
output.versionCodeOverride =
variant.versionCode * 10 + project.ext.versionCodes.get(ABI_FILTER)
output.versionCodeOverride =
variant.versionCode * 10 + project.ext.versionCodes.get(ABI_FILTER)
} else {
output.versionCodeOverride =
variant.versionCode * 10 + project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 4)
}
output.versionCodeOverride =
variant.versionCode * 10 + project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 4)
}
}
}
@@ -210,31 +199,7 @@ android {
renderScript true
aidl true
}
}
spotless {
java {
target 'src/*/java/**/*.java'
targetExclude 'src/main/java/chat/delta/**' // ignore auto-generated code
googleJavaFormat() // Google style = 2-space indent, matches Android Studio defaults
removeUnusedImports()
trimTrailingWhitespace()
endWithNewline()
}
format 'xml', {
target 'src/*/res/**/*.xml', 'src/*/AndroidManifest.xml'
targetExclude 'src/*/res/values*/strings*.xml' // line-break changes invalidate translations
eclipseWtp('xml').configFile('spotless/eclipse-wtp-xml.prefs')
trimTrailingWhitespace()
endWithNewline()
}
groovyGradle {
target '*.gradle'
greclipse()
leadingTabsToSpaces(4)
trimTrailingWhitespace()
endWithNewline()
}
}
final def markwon_version = '4.6.2'
@@ -249,11 +214,6 @@ dependencies {
def media3_version = "1.8.0" // 1.9.0 need minSdkVersion 23
implementation 'androidx.concurrent:concurrent-futures:1.3.0'
implementation 'androidx.core:core:1.17.0'
implementation 'androidx.core:core-telecom:1.0.1'
implementation 'im.conversations.webrtc:webrtc-android:129.0.0'
// For Kotlin->Java interpolation
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.9.4'
implementation 'androidx.sharetarget:sharetarget:1.2.0'
implementation 'androidx.webkit:webkit:1.14.0'
implementation 'androidx.multidex:multidex:2.0.1'
@@ -291,8 +251,7 @@ dependencies {
implementation 'com.github.amulyakhare:TextDrawable:558677ea31' // number of unread messages,
// the one-letter circle for the contacts (when there is not avatar) and a white background.
implementation 'com.googlecode.mp4parser:isoparser:1.0.6' // MP4 recoding; upgrading eg. to 1.1.22 breaks recoding, however, i have not investigated further, just reset to 1.0.6
implementation ('com.davemorrissey.labs:subsampling-scale-image-view:3.10.0') {
// for the zooming on photos / media
implementation ('com.davemorrissey.labs:subsampling-scale-image-view:3.10.0') { // for the zooming on photos / media
exclude group: 'com.android.support', module: 'support-annotations'
}
@@ -301,13 +260,11 @@ dependencies {
// <https://github.com/cketti/SafeContentResolver>
implementation 'de.cketti.safecontentresolver:safe-content-resolver-v21:1.0.0'
gplayImplementation('com.google.firebase:firebase-messaging:24.1.2') {
// for PUSH notifications, don't upgrade: v25.0.0 requires minSdk>=23
gplayImplementation('com.google.firebase:firebase-messaging:24.1.2') { // for PUSH notifications, don't upgrade: v25.0.0 requires minSdk>=23
exclude group: 'com.google.firebase', module: 'firebase-core'
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,8 +1,6 @@
ArcaneChat is a decentralized and secure instant messenger that is easy to use for friends and family.
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.
Anonymous. Instant onboarding without a phone number, e-mail or other private data.
• Flexible. Supports multiple chat profiles and is easy to setup on multiple devices.
@@ -25,7 +23,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 24 hours</li>
<li>Extra option to share location for 12 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": 1775939535,
"narHash": "sha256-Zq1U7Vhw5ctvhJQy+3ShqIv7Mplf3XkgJY+QJnhfUGQ=",
"lastModified": 1756239746,
"narHash": "sha256-0ibN685tT+u/Nbmbrrq9G3mRUzct2Votyv/a7Wwv26s=",
"owner": "tadfisher",
"repo": "android-nixpkgs",
"rev": "d2e16192309bf3101e4998ffa62e914819dee077",
"rev": "256631d162ec883b2341ee59621516e1f65f0f6b",
"type": "github"
},
"original": {
@@ -28,11 +28,11 @@
]
},
"locked": {
"lastModified": 1768818222,
"narHash": "sha256-460jc0+CZfyaO8+w8JNtlClB2n4ui1RbHfPTLkpwhU8=",
"lastModified": 1741473158,
"narHash": "sha256-kWNaq6wQUbUMlPgw8Y+9/9wP0F8SHkjy24/mN3UAppg=",
"owner": "numtide",
"repo": "devshell",
"rev": "255a2b1725a20d060f566e4755dbf571bbbb5f76",
"rev": "7c9e793ebe66bcba8292989a68c0419b737a22a0",
"type": "github"
},
"original": {
@@ -79,11 +79,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1775710090,
"narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=",
"lastModified": 1756125398,
"narHash": "sha256-XexyKZpf46cMiO5Vbj+dWSAXOnr285GHsMch8FBoHbc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4c1018dae018162ec878d42fec712642d214fdfa",
"rev": "3b9f00d7a7bf68acd4c4abb9d43695afb04e03a5",
"type": "github"
},
"original": {
@@ -95,11 +95,11 @@
},
"nixpkgs_2": {
"locked": {
"lastModified": 1775823930,
"narHash": "sha256-ALT447J7FcxP/97J01A/gp/hgdO5lXRsm+zLMt+gIjc=",
"lastModified": 1756159630,
"narHash": "sha256-ohMvsjtSVdT/bruXf5ClBh8ZYXRmD4krmjKrXhEvwMg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8c11f88bb9573a10a7d6bf87161ef08455ac70b9",
"rev": "84c256e42600cb0fdf25763b48d28df2f25a0c8b",
"type": "github"
},
"original": {
@@ -138,11 +138,11 @@
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1775877051,
"narHash": "sha256-wpSQm2PD/w4uRo2wb8utk0b5hOBkkg/CZ1xICY+qB7M=",
"lastModified": 1763347184,
"narHash": "sha256-6QH8hpCYJxifvyHEYg+Da0BotUn03BwLIvYo3JAxuqQ=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "08b4f3633471874c8894632ade1b78d75dbda002",
"rev": "08895cce80433978d5bfd668efa41c5e24578cbd",
"type": "github"
},
"original": {
+27 -1
View File
@@ -946,6 +946,18 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcContext_getContactEncrInfo(JNIEnv *e
}
JNIEXPORT jstring Java_com_b44t_messenger_DcContext_initiateKeyTransfer(JNIEnv *env, jobject obj)
{
jstring setup_code = NULL;
char* temp = dc_initiate_key_transfer(get_dc_context(env, obj));
if (temp) {
setup_code = JSTRING_NEW(temp);
dc_str_unref(temp);
}
return setup_code;
}
JNIEXPORT void Java_com_b44t_messenger_DcContext_imex(JNIEnv *env, jobject obj, jint what, jstring dir)
{
CHAR_REF(dir);
@@ -1393,7 +1405,6 @@ 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));
@@ -1619,6 +1630,15 @@ JNIEXPORT jboolean Java_com_b44t_messenger_DcMsg_hasHtml(JNIEnv *env, jobject ob
}
JNIEXPORT jstring Java_com_b44t_messenger_DcMsg_getSetupCodeBegin(JNIEnv *env, jobject obj)
{
char* temp = dc_msg_get_setupcodebegin(get_dc_msg(env, obj));
jstring ret = JSTRING_NEW(temp);
dc_str_unref(temp);
return ret;
}
JNIEXPORT void Java_com_b44t_messenger_DcMsg_setSubject(JNIEnv *env, jobject obj, jstring text)
{
CHAR_REF(text);
@@ -1643,6 +1663,12 @@ JNIEXPORT void Java_com_b44t_messenger_DcMsg_setHtml(JNIEnv *env, jobject obj, j
}
JNIEXPORT void Java_com_b44t_messenger_DcMsg_forceSticker(JNIEnv *env, jobject obj)
{
dc_msg_force_sticker(get_dc_msg(env, obj));
}
JNIEXPORT jstring Java_com_b44t_messenger_DcMsg_getPOILocation(JNIEnv *env, jobject obj)
{
char* temp = dc_msg_get_poi_location(get_dc_msg(env, obj));
-5
View File
@@ -13,8 +13,3 @@
-keep class org.thoughtcrime.securesms.crypto.KeyStoreHelper* { *; }
-dontwarn com.google.firebase.analytics.connector.AnalyticsConnector
# Keep WebRTC classes
-keep class org.webrtc.** { *; }
-keepclassmembers class org.webrtc.** { *; }
-keepattributes InnerClasses
-1
View File
@@ -9,4 +9,3 @@ cd "$ROOT_DIR"
# generate code
dcrpcgen java --schema schema.json -o ./src/main/java/
rm schema.json
-1
View File
@@ -34,7 +34,6 @@ 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/
-1
View File
@@ -1,6 +1,5 @@
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
-7
View File
@@ -1,7 +0,0 @@
eclipse.preferences.version=1
lineWidth=100
splitMultiAttrs=true
indentMultipleAttributes=true
indentationChar=space
indentationSize=4
spaceBeforeEmptyCloseTag=true
@@ -39,7 +39,7 @@ public class EnterChatsBenchmark {
// PLEASE BACKUP YOUR ACCOUNT BEFORE RUNNING THIS!
// ==============================================================================================
private static final String TAG = "EnterChatsBenchmark";
private static final String TAG = EnterChatsBenchmark.class.getSimpleName();
@Rule
public ActivityScenarioRule<ConversationListActivity> activityRule =
@@ -1,17 +0,0 @@
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";
private LocationSourceFactory() {}
public static LocationSource create(Context context) {
Log.i(TAG, "Non-GMS build, Using platform LocationManager");
return new PlatformLocationSource();
}
}
+4
View File
@@ -3,6 +3,10 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- force compiling emojipicker on sdk<21 and firebase on sdk<19; runtime checks are required then -->
<uses-sdk
tools:overrideLibrary="androidx.emoji2.emojipicker, com.google.firebase.messaging, com.google.android.gms.cloudmessaging" />
<meta-data
android:name="firebase_analytics_collection_deactivated"
android:value="true" />
@@ -1,61 +0,0 @@
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";
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;
}
}
}
@@ -1,35 +0,0 @@
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";
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;
}
}
}
@@ -17,7 +17,7 @@ import org.thoughtcrime.securesms.service.FetchForegroundService;
import org.thoughtcrime.securesms.util.Util;
public class FcmReceiveService extends FirebaseMessagingService {
private static final String TAG = "FcmReceiveService";
private static final String TAG = FcmReceiveService.class.getSimpleName();
private static final Object INIT_LOCK = new Object();
private static boolean initialized;
private static volatile boolean triedRegistering;
File diff suppressed because it is too large Load Diff
+35 -255
View File
@@ -5,6 +5,7 @@
<li><a href="#howtoe2ee">How can I find people to chat with?</a></li>
<li><a href="#why-is-a-chat-marked-as-request">Why is a chat marked as “Request”?</a></li>
<li><a href="#how-can-i-put-two-of-my-friends-in-contact-with-each-other">How can I put two of my friends in contact with each other?</a></li>
<li><a href="#podporuje-delta-chat-obrázky-videa-a-jiné-přílohy">Podporuje Delta Chat obrázky, videa a jiné přílohy?</a></li>
<li><a href="#multiple-accounts">What are profiles? How can I switch between them?</a></li>
<li><a href="#kdo-uvidí-můj-profilový-obrázek">Kdo uvidí můj profilový obrázek?</a></li>
<li><a href="#signature">Can I set a Bio/Status with Delta Chat?</a></li>
@@ -13,7 +14,6 @@
<li><a href="#what-does-the-green-dot-mean">What does the green dot mean?</a></li>
<li><a href="#what-do-the-ticks-shown-beside-outgoing-messages-mean">What do the ticks shown beside outgoing messages mean?</a></li>
<li><a href="#edit">Correct typos and delete messages after sending</a></li>
<li><a href="#mediaquality">How is media quality handled?</a></li>
<li><a href="#ephemeralmsgs">How do disappearing messages work?</a></li>
<li><a href="#delold">What happens if I turn on “Delete Messages from Device”?</a></li>
<li><a href="#remove-account">How can I delete my chat profile?</a></li>
@@ -29,21 +29,6 @@
<li><a href="#how-many-members-can-participate-in-a-single-group">How many members can participate in a single group?</a></li>
</ul>
</li>
<li><a href="#channels">Channels</a>
<ul>
<li><a href="#subscribe-to-a-channel">Subscribe to a channel</a></li>
<li><a href="#create-a-channel">Create a channel</a></li>
<li><a href="#how-many-subscribers-can-a-channel-have">How many subscribers can a channel have?</a></li>
</ul>
</li>
<li><a href="#calls">Calls</a>
<ul>
<li><a href="#place-a-call">Place a call</a></li>
<li><a href="#accept-or-reject-a-call">Accept or reject a call</a></li>
<li><a href="#during-a-call">During a call</a></li>
<li><a href="#missed-calls-and-notifications">Missed calls and notifications</a></li>
</ul>
</li>
<li><a href="#webxdc">In-chat apps</a>
<ul>
<li><a href="#where-can-i-get-in-chat-apps">Where can I get in-chat apps?</a></li>
@@ -233,6 +218,19 @@ You can also add a little introduction message.</p>
<p>The second contact will receive a <strong>card</strong> then
and can tap it to start chatting with the first contact.</p>
<h3 id="podporuje-delta-chat-obrázky-videa-a-jiné-přílohy">
Podporuje Delta Chat obrázky, videa a jiné přílohy? <a href="#podporuje-delta-chat-obrázky-videa-a-jiné-přílohy" class="anchor"></a>
</h3>
<p>Yes. Images, videos, files, voice messages etc. can be sent using the <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attachment-</strong>
or <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Voice Message</strong> buttons</p>
<p>For performance, images are optimized and sent at a smaller size by default, but you can send it as a “file” to preserve the original.</p>
<h3 id="multiple-accounts">
@@ -414,32 +412,6 @@ Notifications are not sent and there is no time limit.</p>
<p>Note, that the original message may still be received by chat members
who could have already replied, forwarded, saved, screenshotted or otherwise copied the message.</p>
<h3 id="mediaquality">
How is media quality handled? <a href="#mediaquality" class="anchor"></a>
</h3>
<p>Images, videos, files, voice messages etc. can be sent using the <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attach-</strong>
or <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Voice Message</strong> buttons.</p>
<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>
</li>
<li>
<p>In regions with worse connectivity,
you can choose higher compression at <strong>Settings → Chats → Outgoing Media Quality</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>
</li>
</ul>
<h3 id="ephemeralmsgs">
@@ -643,197 +615,6 @@ but more than 150 is not recommended.</p>
where Delta Chat is a private messenger for chatting with <a href="#groups">equal rights</a>.
See <a href="https://en.wikipedia.org/wiki/Dunbar%27s_number">Dunbars number</a> for more insights.</p>
<h2 id="channels">
Channels <a href="#channels" class="anchor"></a>
</h2>
<p>Channels are a one-to-many tool for broadcasting messages.</p>
<h3 id="subscribe-to-a-channel">
Subscribe to a channel <a href="#subscribe-to-a-channel" class="anchor"></a>
</h3>
<ul>
<li>Scan the <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> <strong>QR code</strong>
or tap the <strong>invite link</strong> you got from the channel owner.</li>
</ul>
<p>Thats all!
You will receive a few of the messages from the channel history
and, from that point on, all new messages from the channel.</p>
<p><strong>Dont worry,</strong> if that does not happen immediately.
Once the channel owner comes online, your join request will be processed.</p>
<p>As all of Delta Chat, also Channels are private and decentralized,
there is no public discovery.</p>
<p>Other channel subscribers will not see that you subscribed and cannot message you.
The channel owner, however, can message you.
They will also see that you read a message unless you have read receipts disabled.</p>
<p>If you do not want to share your main profile,
you can also create a <a href="#multiple-accounts">dedicated profile</a> for joining a channel.</p>
<h3 id="create-a-channel">
Create a channel <a href="#create-a-channel" class="anchor"></a>
</h3>
<ul>
<li>
<p>Tap <strong>New Chat</strong> and choose <strong>New Channel</strong>.</p>
</li>
<li>
<p>Enter a <strong>name</strong>, optionally set an <strong>image</strong> and <strong>description</strong>, and hit the <strong>Create</strong> button.</p>
</li>
<li>
<p>You can now send and manage messages as usual.</p>
</li>
<li>
<p>From the channels profile, <strong>share the QR code or invite link with others</strong>.</p>
</li>
</ul>
<p>Subscribers will receive your messages,
but they cannot send messages in your channel.
When subscribing, they will receive <strong>a few of the latest messages of the channel history</strong>.</p>
<p>You can see the <strong>view count</strong> beside each message.
Note that this only counts subscribers who have read receipts enabled,
so the real view count may be larger.</p>
<h3 id="how-many-subscribers-can-a-channel-have">
How many subscribers can a channel have? <a href="#how-many-subscribers-can-a-channel-have" class="anchor"></a>
</h3>
<p>Channels are designed for much larger audiences than <a href="#groups">groups</a>.</p>
<p>The practical limit depends on the used <a href="#relays">relay</a>,
so there is no single fixed number that applies everywhere.</p>
<p>For really large channels with several tens of thousands of subscribers,
we recommend using a <a href="#multiple-accounts">dedicated profile</a> for the channel
and checking whether the relay is suitable.</p>
<p>But dont be too hesitant: Delta Chat is designed to be relay-agnostic,
so you can change your relay at any point easily -
your existing subscribers will not even notice.
You only have to update the invite link you share with new subscribers in that case.</p>
<h2 id="calls">
Calls <a href="#calls" class="anchor"></a>
</h2>
<p>Delta Chat supports one-to-one <strong>audio calls</strong> and <strong>video calls</strong>.</p>
<p>Calls are supported on Desktop, Ubuntu Touch, iOS and Android 8 and newer.</p>
<h3 id="place-a-call">
Place a call <a href="#place-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>In a one-to-one chat, tap the 📞 <strong>call icon</strong>.</p>
</li>
<li>
<p>This opens a small menu
where you can choose whether to place an <strong>Audio Call</strong> or a <strong>Video Call</strong>.</p>
</li>
</ul>
<h3 id="accept-or-reject-a-call">
Accept or reject a call <a href="#accept-or-reject-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>When someone calls you,
Delta Chat shows an <strong>incoming call screen</strong> or notification.</p>
</li>
<li>
<p>Tap <strong>Accept</strong> to answer
or <strong>Decline</strong> to reject the call.</p>
</li>
</ul>
<h3 id="during-a-call">
During a call <a href="#during-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>You can <strong>mute</strong> your microphone.</p>
</li>
<li>
<p>You can <strong>enable or disable your camera</strong>.</p>
</li>
<li>
<p>On mobile, you can <strong>switch between front and back cameras</strong>.</p>
</li>
</ul>
<p>Depending on the device, you can also select the audio output or use picture-in-picture.
On desktop, the call is using a dedicated window
and you can continue using the main Delta Chat window as usual.</p>
<h3 id="missed-calls-and-notifications">
Missed calls and notifications <a href="#missed-calls-and-notifications" class="anchor"></a>
</h3>
<ul>
<li>
<p>If you do not answer, do not hear the ringing, or do not have your device at hand,
the call appears as a <strong>missed call</strong>.</p>
</li>
<li>
<p><strong>Only your accepted contacts</strong> can make your device ring.
Contact requests will appear as usual and will not ring.</p>
</li>
<li>
<p>At <strong>Settings → Notifications → Calls</strong>,
you can disable the special call ringing screen completely.
If you do so, you will not be disturbed by any ringing notification,
you can still pick up the call by tapping the incoming call message bubble in its chat.</p>
</li>
</ul>
<h2 id="webxdc">
@@ -1249,26 +1030,22 @@ 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 <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at 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">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>
<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>
</li>
<li>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</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>
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>
</li>
</ul>
@@ -1631,20 +1408,23 @@ can not be identified easily.</p>
</h3>
<p>The used <a href="#relays">relays</a> need to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#calls">call</a>
<p>The used <a href="#relays">relay</a> needs to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#experiments">call</a>
or use <a href="#webxdc">apps</a> together.</p>
<p>IP Addresses are needed for connectivity and efficiency.
Delta Chat neither persists nor exposes them.
Note that IP Addresses
are not like an address you give to a delivery service,
but typically less precise, often defining city or region only.</p>
They are neither persisted nor exposed.
Note that the IP Address
is not like a detailed address you give to a delivery service,
but much more coarse, often defining region or country only.</p>
<p>If you see your IP Address as a risk,
we recommend to use a VPN for the whole system.
Per-app options leave gaps across your system.
For example, tapping a link can expose IP Addresses to unknown parties, which is by far the larger risk.</p>
<p>As this is just how the internet and other messengers work by default,
we do not offer options here or ask upfront questions.</p>
<p>If you see your IP Address as a security or privacy risk,
we recommend to use a VPN, in combination with system lockdown mode.
Hunting down options in all apps on your system will leave gaps.
For example, tapping a link exposes IP Addresses to unknown parties and is the by far larger risk here.</p>
<h3 id="sealedsender">
@@ -1781,7 +1561,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/serverguide">recommended on this site</a>.
<a href="https://delta.chat/cs/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>
+55 -264
View File
@@ -5,6 +5,7 @@
<li><a href="#howtoe2ee">Wie finde ich Leute, mit denen ich chatten kann?</a></li>
<li><a href="#warum-ist-ein-chat-als-anfrage-markiert">Warum ist ein Chat als “Anfrage” markiert?</a></li>
<li><a href="#wie-kann-ich-zwei-meiner-freunde-miteinander-in-kontakt-bringen">Wie kann ich zwei meiner Freunde miteinander in Kontakt bringen?</a></li>
<li><a href="#unterstützt-delta-chat-bilder-videos-und-dateianhänge">Unterstützt Delta Chat Bilder, Videos und Dateianhänge?</a></li>
<li><a href="#multiple-accounts">Was sind Profile? Wie kann ich zwischen ihnen wechseln?</a></li>
<li><a href="#wer-sieht-mein-profilbild">Wer sieht mein Profilbild?</a></li>
<li><a href="#signature">Kann ich einen Status festlegen?</a></li>
@@ -13,7 +14,6 @@
<li><a href="#was-bedeutet-der-grüne-punkt">Was bedeutet der grüne Punkt?</a></li>
<li><a href="#was-bedeuten-die-häkchen-neben-den-ausgehenden-nachrichten">Was bedeuten die Häkchen neben den ausgehenden Nachrichten?</a></li>
<li><a href="#edit">Schreibfehler korrigieren und Nachrichten nach dem Senden löschen</a></li>
<li><a href="#mediaquality">Medienqualität und Datenverbrauch</a></li>
<li><a href="#ephemeralmsgs">Wie funktionieren “Verschwindende Nachrichten”?</a></li>
<li><a href="#delold">Was passiert, wenn ich “Nachrichten vom Gerät löschen” aktiviere?</a></li>
<li><a href="#remove-account">Wie kann ich mein Chat-Profil löschen?</a></li>
@@ -29,21 +29,6 @@
<li><a href="#wie-viele-mitglieder-können-in-einer-einzelnen-gruppe-sein">Wie viele Mitglieder können in einer einzelnen Gruppe sein?</a></li>
</ul>
</li>
<li><a href="#channels">Kanäle</a>
<ul>
<li><a href="#einem-kanal-beitreten">Einem Kanal beitreten</a></li>
<li><a href="#einen-kanal-erstellen">Einen Kanal erstellen</a></li>
<li><a href="#wie-viele-empfänger-kann-ein-kanal-haben">Wie viele Empfänger kann ein Kanal haben?</a></li>
</ul>
</li>
<li><a href="#calls">Anrufe</a>
<ul>
<li><a href="#jemanden-anrufen">Jemanden anrufen</a></li>
<li><a href="#einen-anruf-annehmen-oder-ablehnen">Einen Anruf annehmen oder ablehnen</a></li>
<li><a href="#während-des-anrufs">Während des Anrufs</a></li>
<li><a href="#verpasste-anrufe-und-benachrichtigungen">Verpasste Anrufe und Benachrichtigungen</a></li>
</ul>
</li>
<li><a href="#webxdc">In-Chat-Apps</a>
<ul>
<li><a href="#wo-bekomme-ich-in-chat-apps">Wo bekomme ich In-Chat-Apps?</a></li>
@@ -124,7 +109,7 @@
<ul>
<li>
<p>Einfache Erstellung von <strong>privaten Chat-Profilen</strong> mit sicheren, schnellen und interoperablen <a href="https://chatmail.at/relays">Chatmail-Servern</a>,
<p>Einfache Erstellung von <strong>privaten Chat-Profile</strong> mit sicheren, schnellen und interoperablen <a href="https://chatmail.at/relays">Chatmail-Servern</a>,
die sofortige Push-Benachrichtigungen für iOS- und Android-Geräte bieten.</p>
</li>
<li>
@@ -152,17 +137,17 @@ basierend auf <a href="https://github.com/chatmail/core/blob/main/standards.md#s
</h3>
<p>Beachte zunächst, dass Delta Chat ein privater Messenger ist.
Es gibt kein öffentliches Verzeichnis, du entscheidest selbst über deine Kontakte.</p>
Es gibt keine öffentliches Verzeichnis, du entscheiden selbst über deine Kontakte.</p>
<ul>
<li>
<p>Wenn du <strong>persönlich</strong> mit deinen Freunden oder Familie zusammen bist,
tippe auf das <strong>QR-Code</strong>-Symbol <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" />
auf dem Hauptbildschirm.<br />
Bitte dann deinen Chatpartner den QR-Code mit Delta Chat zu <strong>scannen</strong>.</p>
Bitte deinen Chatpartner den QR-Code mit Delta Chat zu <strong>scannen</strong>.</p>
</li>
<li>
<p>Für eine Kontaktaufnahme <strong>aus der Ferne</strong> klicke im selben Bildschirm auf “Kopieren” oder “Teilen” und sende den <strong>Einladungslink</strong> über einen anderen privaten Chat.</p>
<p>Für eine Kontaktaufnahme <strong>aus der Ferne</strong>, klicke im selben Bildschirm auf “Kopieren” oder “Teilen” und sende den <strong>Einladungslink</strong> über einen anderen privaten Chat.</p>
</li>
</ul>
@@ -192,7 +177,7 @@ wird eine Ende-zu-Ende-Verschlüsselung zwischen allen Mitgliedern eingerichtet.
</h3>
<p>Da Delta Chat ein privater Messenger ist, können dir zunächst nur Freunde und Familienmitglieder schreiben, denen du deinen <a href="#howtoe2ee">QR-Code oder Einladungslink</a> schickst.</p>
<p>Da Delta Chat ein privater Messenger ist, können dir zunächst nur Freunde und Familienmitglieder, denen du deinen <a href="#howtoe2ee">QR-Code oder Einladungslink</a> schickst, schreiben.</p>
<p>Deine Freunde können deine Kontaktdaten dann mit anderen Freunden teilen. Dies wird als <b style="border: 1px solid currentColor; padding: 0 3px; font-size:90%">Anfrage</b> angezeigt.</p>
@@ -201,12 +186,12 @@ wird eine Ende-zu-Ende-Verschlüsselung zwischen allen Mitgliedern eingerichtet.
<p>Du musst die Anfrage <strong>akzeptieren</strong>, bevor du antworten kannst.</p>
</li>
<li>
<p>Du kannst sie auch “löschen”, wenn du vorerst nicht mit dieser Person chatten möchtest.</p>
<p>Du kannst sie auch “löschen”, wenn du vorerst nicht mit ihm chatten möchten.</p>
</li>
<li>
<p>Wenn du eine Anfrage löschst, werden zukünftige Nachrichten von diesem Kontakt weiterhin
als Nachrichtenanfrage angezeigt, sodass du deine Meinung ändern kannst. Wenn du wirklich keine
Nachrichten von dieser Person erhalten möchtest, solltest du sie <strong>blockieren</strong>.</p>
<p>If you delete a request, future messages from that contact will still appear
as message request, so you can change your mind. If you really dont want to
receive messages from this person, consider <strong>blocking</strong> them.</p>
</li>
</ul>
@@ -224,6 +209,19 @@ Du kannst auch eine kurze Nachricht hinzufügen.</p>
<p>Der zweite Kontakt erhält dann die <strong>Kontaktdaten</strong> und
kann darauf tippen, um mit dem ersten Kontakt zu chatten.</p>
<h3 id="unterstützt-delta-chat-bilder-videos-und-dateianhänge">
Unterstützt Delta Chat Bilder, Videos und Dateianhänge? <a href="#unterstützt-delta-chat-bilder-videos-und-dateianhänge" class="anchor"></a>
</h3>
<p>Ja. Bilder, Videos, Dateien, Sprachnachrichten und mehr können über die <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Anhang-</strong>
bzw. <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Sprachnachricht</strong>-Buttons hinzugefügt werden</p>
<p>Um die Leistung zu verbessern, werden die Bilder standardmäßig optimiert und in einer kleineren Größe gesendet, aber du kannst sie auch als “Datei” senden, um das Original zu erhalten.</p>
<h3 id="multiple-accounts">
@@ -243,7 +241,7 @@ oder <strong>Profile zu wechseln</strong>.</p>
<p>Du kannst separate Profile für politische, familiäre oder berufliche Aktivitäten verwenden.</p>
<p>Vielleicht möchtest du auch erfahren, wie du <a href="#multiclient">Profile auf mehreren Geräten verwenden kannst</a>.</p>
<p>Vielleicht möchtest due auch erfahren, wie du <a href="#multiclient">Profile auf mehreren Geräten verwenden kannst</a>.</p>
<h3 id="wer-sieht-mein-profilbild">
@@ -281,7 +279,7 @@ Sobald du eine Nachricht an einen Kontakt sendest, kann dieser deine Signatur in
<ul>
<li>
<p><strong>Angeheftete Chats</strong> bleiben immer ganz oben in der Chatliste. So kannst du schnell auf deine Lieblingschats zugreifen oder du verwendest vorübergehend angeheftete Chats, um Dinge nicht zu vergessen.</p>
<p><strong>Angeheftete Chats</strong> bleiben immer ganz oben in der Chatliste. So kannst du schnell auf deine Lieblingschats zugreifen oder du verwendest vorübergehend angeheftete Chats um Dinge nicht zu vergessen.</p>
</li>
<li>
<p><strong>Stummgeschaltete Chats</strong> erhalten keine Benachrichtigungen, bleiben ansonsten aber an ihrem Platz. Du kannst auch stummgeschaltete Chats anheften.</p>
@@ -294,7 +292,7 @@ Sobald du eine Nachricht an einen Kontakt sendest, kann dieser deine Signatur in
</li>
</ul>
<p>Um die Funktionen zu nutzen, tippe lang auf einen Chat in der Chatliste oder klicke den Chat mit der rechten Maustaste an.</p>
<p>Um die Funktionen zu nutzen, lang auf einen Chat in der Chatliste tippen oder den Chat mit der rechten Maustaste anklicken.</p>
<h3 id="save">
@@ -304,11 +302,11 @@ Sobald du eine Nachricht an einen Kontakt sendest, kann dieser deine Signatur in
</h3>
<p><strong>Gespeicherte Nachrichten</strong> ist ein Chat, den du verwenden kannst, um dir Nachrichten zu merken und sie wiederzufinden.</p>
<p><strong>Gespeicherte Nachrichten</strong> ist ein Chat, den du verwenden kannst, um dir Nachrichten zu merken und wiederzufinden.</p>
<ul>
<li>
<p>Tippe im Chat lange auf eine Nachricht oder klicke mit der rechten Maustaste darauf und wähle <strong>Speichern</strong>.</p>
<p>Tippen in einem beliebigen Chat lange auf eine Nachricht oder klicken mit der rechten Maustaste darauf und wähle <strong>Speichern</strong>.</p>
</li>
<li>
<p>Gespeicherte Nachrichten werden mit dem Symbol
@@ -321,7 +319,7 @@ Durch Tippen auf <img style="vertical-align:middle; width:1.2em; margin:1px" src
kannst du zu der ursprünglichen Nachricht im ursprünglichen Chat zurückkehren</p>
</li>
<li>
<p>Schließlich kannst du auch „Gespeicherte Nachrichten“ verwenden, um <strong>persönliche Notizen</strong> zu machen. Öffne den Chat, gib etwas ein, fügen ein Foto oder eine Sprachnachricht hinzu usw.</p>
<p>Schließlich kannst du auch „Gespeicherte Nachrichten“ verwenden, um <strong>persönliche Notizen</strong> zu machen - öffnen den Chat, gib etwas ein, fügen ein Foto oder eine Sprachnachricht hinzu usw.</p>
</li>
<li>
<p>Da „Gespeicherte Nachrichten“ synchronisiert werden, können sie sehr praktisch für die Übertragung von Daten zwischen Geräten sein</p>
@@ -362,7 +360,7 @@ sei es durch den <a href="#edit">Absender</a>, durch <a href="#delold">Automatis
<p>In <a href="#groups">Gruppen</a> bedeutet das zweite Häkchen, dass die Nachricht von mindestens einem Mitglied gelesen wurde.</p>
<p>Du erhältst nur dann das zweite Häkchen, wenn sowohl du als auch einer der Empfänger, die die Nachricht gelesen haben, <strong>Einstellungen → Chats → Lesebestätigungen</strong> aktiviert haben.</p>
<p>Du erhälst nur dann das zweite Häkchen, wenn sowohl du als auch einer der Empfänger, die die Nachricht gelesen haben, <strong>Einstellungen → Chats → Lesebestätigungen</strong> aktiviert haben.</p>
<h3 id="edit">
@@ -390,31 +388,6 @@ Es werden keine Benachrichtigungen verschickt und es gibt kein Zeitlimit.</p>
<p>Beachten, dass die ursprüngliche Nachricht dennoch von Chatteilnehmern empfangen worden sein könnte,
die die Nachricht bereits beantwortet, weitergeleitet, gespeichert, mit einem Screenshot versehen oder anderweitig kopiert haben könnten.</p>
<h3 id="mediaquality">
Medienqualität und Datenverbrauch <a href="#mediaquality" class="anchor"></a>
</h3>
<p>Bilder, Videos, Dateien, Sprachnachrichten und mehr können über die <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Anhang-</strong>
bzw. <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Sprachnachricht</strong>-Buttons hinzugefügt werden.</p>
<ul>
<li>
<p>Standardmäßig sorgt Komprimierung für eine <strong>schnelle, effiziente Übertragung</strong>, die die Datenlimits und Speicherplatzkapazitäten aller Beteiligten berücksichtigt.
Dies ist ideal für die tägliche Kommunikation.</p>
</li>
<li>
<p>In Regionen mit schlechter Verbindung können Sie unter <strong>Einstellungen → Chats → Medienqualität beim Senden</strong> eine höhere Komprimierung wählen.</p>
</li>
<li>
<p>Wenn Sie Medien ausdrücklich in ihrer <strong>Originalqualität</strong> senden müssen, verwende im Chat die Option <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Anhängen → Datei</strong>.
Verwende diese Methode nur sparsam, da das Senden von Originaldateien den Datenverbrauch für dich und alle Empfänger im Chat erheblich erhöht.</p>
</li>
</ul>
<h3 id="ephemeralmsgs">
@@ -530,7 +503,7 @@ und seine <a href="#edit">eigenen Nachrichten von Geräten der Mitglieder lösch
<p>Wenn das Mitglied noch nicht in deiner Kontaktliste ist, sie sich aber <strong>persönlich</strong> treffen, wählen Sie dort <strong>QR-Einladungscode</strong> an. Dein Chat-Partner kann nun den QR-Code mit seiner Delta Chat-App <strong>scannen</strong> indem er auf <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> auf dem Hauptbildschirm tippt.</p>
</li>
<li>
<p>Für eine Kontaktaufnahme <strong>aus der Ferne</strong> tippe dort “Kopieren” oder “Teilen” und sende den Einladungslink über einen anderen privaten Chat zum neuen Mitglied.</p>
<p>Für eine Kontaktaufnahme <strong>aus der Ferne</strong>, tippe dort “Kopieren” oder “Teilen” und sende den Einladungslink über einen anderen privaten Chat zum neuen Mitglied.</p>
</li>
</ul>
@@ -560,7 +533,7 @@ Kein Problem, bitte einfach ein anderes Gruppenmitglied in einem normalen Chat,
Wenn du der Gruppe später erneut beitreten möchtest, bitten ein anderes Gruppenmitglied, dich hinzuzufügen.</li>
</ul>
<p>Alternativ kannst du eine Gruppe auch “stummschalten” - dies bedeutet, dass du weiterhin alle Nachrichten erhältst und neue schreiben kannst, aber nicht mehr über neue Nachrichten informiert wirst.</p>
<p>Alternativ kannst du eine Gruppe auch “stummschalten” - dies bedeutet, dass du weiterhin alle Nachrichten erhälst und neue schreiben kannst, aber nicht mehr über neue Nachrichten informiert wirst.</p>
<h3 id="eine-gruppe-klonen">
@@ -599,197 +572,6 @@ aber mehr als 150 sind nicht empfohlen.</p>
<p>Wenn Gruppen größer werden, können sie sozial instabil werden und benötigen möglicherweise eine Hierarchie - und Delta Chat ist ein privater Messenger für Chats mit <a href="#groups">gleichen Rechten</a>. Vgl. <a href="https://de.wikipedia.org/wiki/Dunbar-Zahl">Dunbar-Zahl</a>.</p>
<h2 id="channels">
Kanäle <a href="#channels" class="anchor"></a>
</h2>
<p>Kanäle dienen der Verbreitung von Nachrichten an viele Empfänger.</p>
<h3 id="einem-kanal-beitreten">
Einem Kanal beitreten <a href="#einem-kanal-beitreten" class="anchor"></a>
</h3>
<ul>
<li>Scan the <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> <strong>QR code</strong>
or tap the <strong>invite link</strong> you got from the channel owner.</li>
</ul>
<p>Thats all!
You will receive a few of the messages from the channel history
and, from that point on, all new messages from the channel.</p>
<p><strong>Dont worry,</strong> if that does not happen immediately.
Once the channel owner comes online, your join request will be processed.</p>
<p>As all of Delta Chat, also Channels are private and decentralized,
there is no public discovery.</p>
<p>Other channel subscribers will not see that you subscribed and cannot message you.
The channel owner, however, can message you.
They will also see that you read a message unless you have read receipts disabled.</p>
<p>If you do not want to share your main profile,
you can also create a <a href="#multiple-accounts">dedicated profile</a> for joining a channel.</p>
<h3 id="einen-kanal-erstellen">
Einen Kanal erstellen <a href="#einen-kanal-erstellen" class="anchor"></a>
</h3>
<ul>
<li>
<p>Tap <strong>New Chat</strong> and choose <strong>New Channel</strong>.</p>
</li>
<li>
<p>Enter a <strong>name</strong>, optionally set an <strong>image</strong> and <strong>description</strong>, and hit the <strong>Create</strong> button.</p>
</li>
<li>
<p>You can now send and manage messages as usual.</p>
</li>
<li>
<p>From the channels profile, <strong>share the QR code or invite link with others</strong>.</p>
</li>
</ul>
<p>Subscribers will receive your messages,
but they cannot send messages in your channel.
When subscribing, they will receive <strong>a few of the latest messages of the channel history</strong>.</p>
<p>You can see the <strong>view count</strong> beside each message.
Note that this only counts subscribers who have read receipts enabled,
so the real view count may be larger.</p>
<h3 id="wie-viele-empfänger-kann-ein-kanal-haben">
Wie viele Empfänger kann ein Kanal haben? <a href="#wie-viele-empfänger-kann-ein-kanal-haben" class="anchor"></a>
</h3>
<p>Channels are designed for much larger audiences than <a href="#groups">groups</a>.</p>
<p>The practical limit depends on the used <a href="#relays">relay</a>,
so there is no single fixed number that applies everywhere.</p>
<p>For really large channels with several tens of thousands of subscribers,
we recommend using a <a href="#multiple-accounts">dedicated profile</a> for the channel
and checking whether the relay is suitable.</p>
<p>But dont be too hesitant: Delta Chat is designed to be relay-agnostic,
so you can change your relay at any point easily -
your existing subscribers will not even notice.
You only have to update the invite link you share with new subscribers in that case.</p>
<h2 id="calls">
Anrufe <a href="#calls" class="anchor"></a>
</h2>
<p>Delta Chat supports one-to-one <strong>audio calls</strong> and <strong>video calls</strong>.</p>
<p>Calls are supported on Desktop, Ubuntu Touch, iOS and Android 8 and newer.</p>
<h3 id="jemanden-anrufen">
Jemanden anrufen <a href="#jemanden-anrufen" class="anchor"></a>
</h3>
<ul>
<li>
<p>In a one-to-one chat, tap the 📞 <strong>call icon</strong>.</p>
</li>
<li>
<p>This opens a small menu
where you can choose whether to place an <strong>Audio Call</strong> or a <strong>Video Call</strong>.</p>
</li>
</ul>
<h3 id="einen-anruf-annehmen-oder-ablehnen">
Einen Anruf annehmen oder ablehnen <a href="#einen-anruf-annehmen-oder-ablehnen" class="anchor"></a>
</h3>
<ul>
<li>
<p>When someone calls you,
Delta Chat shows an <strong>incoming call screen</strong> or notification.</p>
</li>
<li>
<p>Tap <strong>Accept</strong> to answer
or <strong>Decline</strong> to reject the call.</p>
</li>
</ul>
<h3 id="während-des-anrufs">
Während des Anrufs <a href="#während-des-anrufs" class="anchor"></a>
</h3>
<ul>
<li>
<p>You can <strong>mute</strong> your microphone.</p>
</li>
<li>
<p>You can <strong>enable or disable your camera</strong>.</p>
</li>
<li>
<p>On mobile, you can <strong>switch between front and back cameras</strong>.</p>
</li>
</ul>
<p>Depending on the device, you can also select the audio output or use picture-in-picture.
On desktop, the call is using a dedicated window
and you can continue using the main Delta Chat window as usual.</p>
<h3 id="verpasste-anrufe-und-benachrichtigungen">
Verpasste Anrufe und Benachrichtigungen <a href="#verpasste-anrufe-und-benachrichtigungen" class="anchor"></a>
</h3>
<ul>
<li>
<p>If you do not answer, do not hear the ringing, or do not have your device at hand,
the call appears as a <strong>missed call</strong>.</p>
</li>
<li>
<p><strong>Only your accepted contacts</strong> can make your device ring.
Contact requests will appear as usual and will not ring.</p>
</li>
<li>
<p>At <strong>Settings → Notifications → Calls</strong>,
you can disable the special call ringing screen completely.
If you do so, you will not be disturbed by any ringing notification,
you can still pick up the call by tapping the incoming call message bubble in its chat.</p>
</li>
</ul>
<h2 id="webxdc">
@@ -1176,16 +958,18 @@ 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">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>
<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>
</li>
<li>
<p>Tippe ein Relay an, um es <strong>Zum Senden zu verwenden</strong></p>
<p><strong>Standard</strong> legt das Relay fest, an das deine Chatpartner zukünftig Nachrichten senden.</p>
</li>
<li>
<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>
<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>
</li>
</ul>
@@ -1535,16 +1319,23 @@ im Falle einer Beschlagnahmung des Geräts nicht ohne Weiteres identifiziert wer
</h3>
<p>Die verwendeten <a href="#relays">Relays</a> müssen deine IP-Adresse kennen,
sowie manchmal auch die Geräte deiner Kontakte, wenn du einen <a href="#calls">Anruf</a> tätigst
oder ihr gemeinsam <a href="#webxdc">Apps</a> verwendet.</p>
<p>Das verwendete <a href="#relays">Relay</a> muss Ihre IP-Adresse kennen,
sowie manchmal auch die Geräte Ihrer Kontakte, wenn Sie einen <a href="#experiments">Anruf</a> tätigen
oder gemeinsam <a href="#webxdc">Apps</a> verwenden.</p>
<p>IP-Adressen sind für Verbindungen und für Effizienz erforderlich.
Sie werden von Delta Chat weder gespeichert noch offengelegt.
IP-Adressen sind nicht mit einer Adresse, die du einem Lieferdienst gibst, vergleichbar - sondern viel gröber und oft nur die Stadt oder die Region beschreibend.</p>
Sie werden weder gespeichert noch offengelegt.
Beachten Sie, dass die IP-Adresse
nicht mit einer Adresse, die Sie einem Lieferdienst geben, vergleichbar ist -
sondern viel gröber ist und oft nur die Region oder das Land angibt.</p>
<p>Wenn du deine IP-Adresse als Risiko betrachtest, empfehlen wir, ein VPN für das gesamte System zu verwenden.
Einstellungen auf App-Ebene hinterlassen Lücken überall im System. Wenn man beispielsweise auf einen Link tippt, können IP-Adressen an Unbekannte weitergegeben werden, was bei weitem das größere Risiko darstellt</p>
<p>Da dies die Standardfunktion des Internets und anderer Messenger ist,
bieten wir hier keine Optionen an und stellen auch keine Fragen im Voraus.</p>
<p>Wenn Sie Ihre IP-Adresse als Sicherheits- oder Datenschutzrisiko betrachten,
empfehlen wir Ihnen, ein VPN in Kombination mit dem System-Lockdown-Modus zu verwenden.
Alle einzelnen Apps auf Ihrem System nach IP-Optionen abzusuchen wird nicht zufriedenstellen sein;
beispielsweise legt das Antippen eines Links IP-Adressen gegenüber unbekannten Parteien offen und stellt hier das weitaus größere Risiko dar.</p>
<h3 id="sealedsender">
@@ -1667,7 +1458,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/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/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>
</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.
+35 -255
View File
@@ -5,6 +5,7 @@
<li><a href="#howtoe2ee">How can I find people to chat with?</a></li>
<li><a href="#why-is-a-chat-marked-as-request">Why is a chat marked as “Request”?</a></li>
<li><a href="#how-can-i-put-two-of-my-friends-in-contact-with-each-other">How can I put two of my friends in contact with each other?</a></li>
<li><a href="#does-delta-chat-support-images-videos-and-other-attachments">Does Delta Chat support images, videos and other attachments?</a></li>
<li><a href="#multiple-accounts">What are profiles? How can I switch between them?</a></li>
<li><a href="#who-sees-my-profile-picture">Who sees my profile picture?</a></li>
<li><a href="#signature">Can I set a Bio/Status with Delta Chat?</a></li>
@@ -13,7 +14,6 @@
<li><a href="#what-does-the-green-dot-mean">What does the green dot mean?</a></li>
<li><a href="#what-do-the-ticks-shown-beside-outgoing-messages-mean">What do the ticks shown beside outgoing messages mean?</a></li>
<li><a href="#edit">Correct typos and delete messages after sending</a></li>
<li><a href="#mediaquality">How is media quality handled?</a></li>
<li><a href="#ephemeralmsgs">How do disappearing messages work?</a></li>
<li><a href="#delold">What happens if I turn on “Delete Messages from Device”?</a></li>
<li><a href="#remove-account">How can I delete my chat profile?</a></li>
@@ -29,21 +29,6 @@
<li><a href="#how-many-members-can-participate-in-a-single-group">How many members can participate in a single group?</a></li>
</ul>
</li>
<li><a href="#channels">Channels</a>
<ul>
<li><a href="#subscribe-to-a-channel">Subscribe to a channel</a></li>
<li><a href="#create-a-channel">Create a channel</a></li>
<li><a href="#how-many-subscribers-can-a-channel-have">How many subscribers can a channel have?</a></li>
</ul>
</li>
<li><a href="#calls">Calls</a>
<ul>
<li><a href="#place-a-call">Place a call</a></li>
<li><a href="#accept-or-reject-a-call">Accept or reject a call</a></li>
<li><a href="#during-a-call">During a call</a></li>
<li><a href="#missed-calls-and-notifications">Missed calls and notifications</a></li>
</ul>
</li>
<li><a href="#webxdc">In-chat apps</a>
<ul>
<li><a href="#where-can-i-get-in-chat-apps">Where can I get in-chat apps?</a></li>
@@ -233,6 +218,19 @@ You can also add a little introduction message.</p>
<p>The second contact will receive a <strong>card</strong> then
and can tap it to start chatting with the first contact.</p>
<h3 id="does-delta-chat-support-images-videos-and-other-attachments">
Does Delta Chat support images, videos and other attachments? <a href="#does-delta-chat-support-images-videos-and-other-attachments" class="anchor"></a>
</h3>
<p>Yes. Images, videos, files, voice messages etc. can be sent using the <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attachment-</strong>
or <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Voice Message</strong> buttons</p>
<p>For performance, images are optimized and sent at a smaller size by default, but you can send it as a “file” to preserve the original.</p>
<h3 id="multiple-accounts">
@@ -413,32 +411,6 @@ Notifications are not sent and there is no time limit.</p>
<p>Note, that the original message may still be received by chat members
who could have already replied, forwarded, saved, screenshotted or otherwise copied the message.</p>
<h3 id="mediaquality">
How is media quality handled? <a href="#mediaquality" class="anchor"></a>
</h3>
<p>Images, videos, files, voice messages etc. can be sent using the <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attach-</strong>
or <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Voice Message</strong> buttons.</p>
<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>
</li>
<li>
<p>In regions with worse connectivity,
you can choose higher compression at <strong>Settings → Chats → Outgoing Media Quality</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>
</li>
</ul>
<h3 id="ephemeralmsgs">
@@ -642,197 +614,6 @@ but more than 150 is not recommended.</p>
where Delta Chat is a private messenger for chatting with <a href="#groups">equal rights</a>.
See <a href="https://en.wikipedia.org/wiki/Dunbar%27s_number">Dunbars number</a> for more insights.</p>
<h2 id="channels">
Channels <a href="#channels" class="anchor"></a>
</h2>
<p>Channels are a one-to-many tool for broadcasting messages.</p>
<h3 id="subscribe-to-a-channel">
Subscribe to a channel <a href="#subscribe-to-a-channel" class="anchor"></a>
</h3>
<ul>
<li>Scan the <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> <strong>QR code</strong>
or tap the <strong>invite link</strong> you got from the channel owner.</li>
</ul>
<p>Thats all!
You will receive a few of the messages from the channel history
and, from that point on, all new messages from the channel.</p>
<p><strong>Dont worry,</strong> if that does not happen immediately.
Once the channel owner comes online, your join request will be processed.</p>
<p>As all of Delta Chat, also Channels are private and decentralized,
there is no public discovery.</p>
<p>Other channel subscribers will not see that you subscribed and cannot message you.
The channel owner, however, can message you.
They will also see that you read a message unless you have read receipts disabled.</p>
<p>If you do not want to share your main profile,
you can also create a <a href="#multiple-accounts">dedicated profile</a> for joining a channel.</p>
<h3 id="create-a-channel">
Create a channel <a href="#create-a-channel" class="anchor"></a>
</h3>
<ul>
<li>
<p>Tap <strong>New Chat</strong> and choose <strong>New Channel</strong>.</p>
</li>
<li>
<p>Enter a <strong>name</strong>, optionally set an <strong>image</strong> and <strong>description</strong>, and hit the <strong>Create</strong> button.</p>
</li>
<li>
<p>You can now send and manage messages as usual.</p>
</li>
<li>
<p>From the channels profile, <strong>share the QR code or invite link with others</strong>.</p>
</li>
</ul>
<p>Subscribers will receive your messages,
but they cannot send messages in your channel.
When subscribing, they will receive <strong>a few of the latest messages of the channel history</strong>.</p>
<p>You can see the <strong>view count</strong> beside each message.
Note that this only counts subscribers who have read receipts enabled,
so the real view count may be larger.</p>
<h3 id="how-many-subscribers-can-a-channel-have">
How many subscribers can a channel have? <a href="#how-many-subscribers-can-a-channel-have" class="anchor"></a>
</h3>
<p>Channels are designed for much larger audiences than <a href="#groups">groups</a>.</p>
<p>The practical limit depends on the used <a href="#relays">relay</a>,
so there is no single fixed number that applies everywhere.</p>
<p>For really large channels with several tens of thousands of subscribers,
we recommend using a <a href="#multiple-accounts">dedicated profile</a> for the channel
and checking whether the relay is suitable.</p>
<p>But dont be too hesitant: Delta Chat is designed to be relay-agnostic,
so you can change your relay at any point easily -
your existing subscribers will not even notice.
You only have to update the invite link you share with new subscribers in that case.</p>
<h2 id="calls">
Calls <a href="#calls" class="anchor"></a>
</h2>
<p>Delta Chat supports one-to-one <strong>audio calls</strong> and <strong>video calls</strong>.</p>
<p>Calls are supported on Desktop, Ubuntu Touch, iOS and Android 8 and newer.</p>
<h3 id="place-a-call">
Place a call <a href="#place-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>In a one-to-one chat, tap the 📞 <strong>call icon</strong>.</p>
</li>
<li>
<p>This opens a small menu
where you can choose whether to place an <strong>Audio Call</strong> or a <strong>Video Call</strong>.</p>
</li>
</ul>
<h3 id="accept-or-reject-a-call">
Accept or reject a call <a href="#accept-or-reject-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>When someone calls you,
Delta Chat shows an <strong>incoming call screen</strong> or notification.</p>
</li>
<li>
<p>Tap <strong>Accept</strong> to answer
or <strong>Decline</strong> to reject the call.</p>
</li>
</ul>
<h3 id="during-a-call">
During a call <a href="#during-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>You can <strong>mute</strong> your microphone.</p>
</li>
<li>
<p>You can <strong>enable or disable your camera</strong>.</p>
</li>
<li>
<p>On mobile, you can <strong>switch between front and back cameras</strong>.</p>
</li>
</ul>
<p>Depending on the device, you can also select the audio output or use picture-in-picture.
On desktop, the call is using a dedicated window
and you can continue using the main Delta Chat window as usual.</p>
<h3 id="missed-calls-and-notifications">
Missed calls and notifications <a href="#missed-calls-and-notifications" class="anchor"></a>
</h3>
<ul>
<li>
<p>If you do not answer, do not hear the ringing, or do not have your device at hand,
the call appears as a <strong>missed call</strong>.</p>
</li>
<li>
<p><strong>Only your accepted contacts</strong> can make your device ring.
Contact requests will appear as usual and will not ring.</p>
</li>
<li>
<p>At <strong>Settings → Notifications → Calls</strong>,
you can disable the special call ringing screen completely.
If you do so, you will not be disturbed by any ringing notification,
you can still pick up the call by tapping the incoming call message bubble in its chat.</p>
</li>
</ul>
<h2 id="webxdc">
@@ -1249,26 +1030,22 @@ 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 <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at 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">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>
<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>
</li>
<li>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</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>
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>
</li>
</ul>
@@ -1631,20 +1408,23 @@ can not be identified easily.</p>
</h3>
<p>The used <a href="#relays">relays</a> need to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#calls">call</a>
<p>The used <a href="#relays">relay</a> needs to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#experiments">call</a>
or use <a href="#webxdc">apps</a> together.</p>
<p>IP Addresses are needed for connectivity and efficiency.
Delta Chat neither persists nor exposes them.
Note that IP Addresses
are not like an address you give to a delivery service,
but typically less precise, often defining city or region only.</p>
They are neither persisted nor exposed.
Note that the IP Address
is not like a detailed address you give to a delivery service,
but much more coarse, often defining region or country only.</p>
<p>If you see your IP Address as a risk,
we recommend to use a VPN for the whole system.
Per-app options leave gaps across your system.
For example, tapping a link can expose IP Addresses to unknown parties, which is by far the larger risk.</p>
<p>As this is just how the internet and other messengers work by default,
we do not offer options here or ask upfront questions.</p>
<p>If you see your IP Address as a security or privacy risk,
we recommend to use a VPN, in combination with system lockdown mode.
Hunting down options in all apps on your system will leave gaps.
For example, tapping a link exposes IP Addresses to unknown parties and is the by far larger risk here.</p>
<h3 id="sealedsender">
@@ -1781,7 +1561,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/serverguide">recommended on this site</a>.
<a href="https://delta.chat/en/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
File diff suppressed because it is too large Load Diff
+183 -331
View File
@@ -2,48 +2,33 @@
<html lang="fr"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<li><a href="#quest-ce-que-delta-chat-">Quest-ce que Delta Chat ?</a>
<ul>
<li><a href="#howtoe2ee">Comment trouver des personnes avec qui discuter ?</a></li>
<li><a href="#pourquoi-une-discussion-est-marquée-comme-invitation-">Pourquoi une discussion est marquée comme “invitation” ?</a></li>
<li><a href="#comment-mettre-deux-personnes-en-relation-entre-elles-">Comment mettre deux personnes en relation entre elles ?</a></li>
<li><a href="#multiple-accounts">À quoi correspondent les profils ? Comment est-ce que je peux passer de lun à lautre ?</a></li>
<li><a href="#howtoe2ee">How can I find people to chat with?</a></li>
<li><a href="#why-is-a-chat-marked-as-request">Why is a chat marked as “Request”?</a></li>
<li><a href="#how-can-i-put-two-of-my-friends-in-contact-with-each-other">How can I put two of my friends in contact with each other?</a></li>
<li><a href="#delta-chat-prend-il-en-charge-les-images-vidéos-et-autres-pièces-jointes-">Delta Chat prend-il en charge les images, vidéos et autres pièces jointes ?</a></li>
<li><a href="#multiple-accounts">What are profiles? How can I switch between them?</a></li>
<li><a href="#qui-peut-voir-ma-photo-de-profil-">Qui peut voir ma photo de profil ?</a></li>
<li><a href="#signature">Est-il possible de définir une Bio/un Statut avec Delta Chat ?</a></li>
<li><a href="#signature">Can I set a Bio/Status with Delta Chat?</a></li>
<li><a href="#que-signifient-épingler-sourdine-et-archiver-">Que signifient “épingler”, “sourdine” et “archiver” ?</a></li>
<li><a href="#save">Comment fonctionnent les “Messages enregistrés” ?</a></li>
<li><a href="#save">How do “Saved Messages” work?</a></li>
<li><a href="#que-signifie-le-point-vert-">Que signifie le point vert ?</a></li>
<li><a href="#que-signifient-les-coches-affichées-à-côté-des-messages-sortants-">Que signifient les coches affichées à côté des messages sortants ?</a></li>
<li><a href="#edit">Corriger des fautes et supprimer des messages après les avoir envoyés</a></li>
<li><a href="#mediaquality">Comment est gérée la qualité des médias envoyés ?</a></li>
<li><a href="#ephemeralmsgs">Comment fonctionnent les messages éphémères ?</a></li>
<li><a href="#edit">Correct typos and delete messages after sending</a></li>
<li><a href="#ephemeralmsgs">How do disappearing messages work?</a></li>
<li><a href="#delold">Que se passe-t-il si jactive loption “Supprimer les anciens messages de lappareil” ?</a></li>
<li><a href="#remove-account">Comment puis-je supprimer un profil de discussion ?</a></li>
<li><a href="#remove-account">How can I delete my chat profile?</a></li>
</ul>
</li>
<li><a href="#groups">Groupes</a>
<li><a href="#groups">Groups</a>
<ul>
<li><a href="#création-dun-groupe">Création dun groupe</a></li>
<li><a href="#addmembers">Ajouter et supprimer des membres</a></li>
<li><a href="#addmembers">Add and remove members</a></li>
<li><a href="#jai-quitté-un-groupe-par-accident">Jai quitté un groupe par accident.</a></li>
<li><a href="#je-ne-souhaite-plus-recevoir-les-messages-dun-groupe">Je ne souhaite plus recevoir les messages dun groupe.</a></li>
<li><a href="#cloning-a-group">Cloning a group</a></li>
<li><a href="#how-many-members-can-participate-in-a-single-group">How many members can participate in a single group?</a></li>
</ul>
</li>
<li><a href="#channels">Channels</a>
<ul>
<li><a href="#subscribe-to-a-channel">Subscribe to a channel</a></li>
<li><a href="#create-a-channel">Create a channel</a></li>
<li><a href="#how-many-subscribers-can-a-channel-have">How many subscribers can a channel have?</a></li>
</ul>
</li>
<li><a href="#calls">Calls</a>
<ul>
<li><a href="#place-a-call">Place a call</a></li>
<li><a href="#accept-or-reject-a-call">Accept or reject a call</a></li>
<li><a href="#during-a-call">During a call</a></li>
<li><a href="#missed-calls-and-notifications">Missed calls and notifications</a></li>
</ul>
</li>
<li><a href="#webxdc">In-chat apps</a>
<ul>
<li><a href="#where-can-i-get-in-chat-apps">Where can I get in-chat apps?</a></li>
@@ -120,71 +105,90 @@
</h2>
<p>Delta Chat est une application de messagerie fiable, décentralisée et sécurisée, disponible pour smartphones et ordinateurs de bureau.</p>
<p>Delta Chat is a reliable, decentralized and secure instant messaging app,
available for mobile and desktop platforms.</p>
<ul>
<li>
<p>Création à la volée de <strong>profils de discussion privés</strong> grâce à des <a href="https://chatmail.at/relays">relais chatmail</a> sécurisés et interopérables qui permettent des échanges instantanés et des notifications Push pour les appareils iOS et Android.</p>
<p>Instant creation of <strong>private chat profiles</strong>
with secure and interoperable <a href="https://chatmail.at/relays">chatmail relays</a>
that offer instant message delivery, and Push Notifications for iOS and Android devices.</p>
</li>
<li>
<p>Fonctionne en <a href="#multiple-accounts">multi-profils</a> et <a href="#multiclient">multi-appareils</a> sur toutes les plateformes et quelque soit <a href="https://chatmail.at/clients">lapplication chatmail</a> utilisée.</p>
<p>Pervasive <a href="#multiple-accounts">multi-profile</a> and
<a href="#multiclient">multi-device</a> support on all platforms
and between different <a href="https://chatmail.at/clients">chatmail apps</a>.</p>
</li>
<li>
<p><a href="#webxdc">Applications</a> interactives permettant de jouer et collaborer au sein des discussions.</p>
<p>Interactive <a href="#webxdc">in-chat apps</a> for gaming and collaboration</p>
</li>
<li>
<p><a href="#security-audits">Chiffrement de bout-en-bout audité</a> protégé contre les attaques réseaux et de serveurs.</p>
<p><a href="#security-audits">Audited end-to-end encryption</a>
safe against network and server attacks.</p>
</li>
<li>
<p>Logiciel libre et gratuit, autant côté serveur que côté application, développé autour des <a href="https://github.com/chatmail/core/blob/main/standards.md#standards-used-in-delta-chat">standards dinternet</a>.</p>
<p>Free and Open Source software, both app and server side,
built on <a href="https://github.com/chatmail/core/blob/main/standards.md#standards-used-in-delta-chat">Internet Standards</a>.</p>
</li>
</ul>
<h3 id="howtoe2ee">
Comment trouver des personnes avec qui discuter ? <a href="#howtoe2ee" class="anchor"></a>
How can I find people to chat with? <a href="#howtoe2ee" class="anchor"></a>
</h3>
<p>Delta Chat est une application de messagerie privée. Il ny a pas de mécanisme de découverte publique des utilisateurs et utilisatrices. <em>Vous</em> décidez avec qui vous échangez dans Delta Chat.</p>
<p>First, note that Delta Chat is a private messenger.
There is no public discovery, <em>you</em> decide about your contacts.</p>
<ul>
<li>
<p>En <strong>présence physique</strong> de vos amis ou de membres de votre famille, appuyez sur licône <strong>QR code</strong> <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> sur l’écran principal. Demandez à votre contact de <strong>scanner</strong> le QR code avec leur application Delta Chat.</p>
<p>If you are <strong>face to face</strong> with your friend or family,
tap the <strong>QR Code</strong> icon <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" />
on the main screen.<br />
Ask your chat partner to <strong>scan</strong> the QR image
with their Delta Chat app.</p>
</li>
<li>
<p>Pour la mise en place dun échange <strong>à distance</strong>, cliquez sur licône <strong>QR code</strong> sur l’écran principal, puis sur le bouton “Lien”, puis sur “Copier” ou “Partager” et envoyez le lien dinvitation via une autre messagerie privée.</p>
<p>For a <strong>remote</strong> contact setup,
from the same screen,
click “Copy” or “Share” and send the <strong>invite link</strong>
through another private chat.</p>
</li>
</ul>
<p>Il suffit ensuite dattendre que la connexion soit établie.</p>
<p>Now wait while connection gets established.</p>
<ul>
<li>
<p>Si les deux contacts sont en ligne, ils verront rapidement apparaître une nouvelle discussion et pourront tout de suite commencer une discussion sécurisée.</p>
<p>If both sides are online, they will soon see a chat
and can start messaging securely.</p>
</li>
<li>
<p>Si lun des contacts nest pas en ligne ou a des problèmes de réseau, la possibilité de démarrer la discussion aura lieu dès que la connectivité sera rétablie.</p>
<p>If one side is offline or in bad network,
the ability to chat is delayed until connectivity is restored.</p>
</li>
</ul>
<p>Félicitations !
Vous utiliserez désormais le <a href="#e2ee">chiffrement de bout-en-bout</a> avec ce contact.
Si vous vous ajoutez à des discussions de <a href="#groups">groupe</a> le chiffrement de bout-en-bout sera établi entre tous les membres de la discussion.</p>
<p>Congratulations!
You now will automatically use <a href="#e2ee">end-to-end encryption</a> with this contact.
If you add each other to <a href="#groups">groups</a>, end-to-end encryption will be established among all members.</p>
<h3 id="pourquoi-une-discussion-est-marquée-comme-invitation-">
<h3 id="why-is-a-chat-marked-as-request">
Pourquoi une discussion est marquée comme “invitation” ? <a href="#pourquoi-une-discussion-est-marquée-comme-invitation-" class="anchor"></a>
Why is a chat marked as “Request”? <a href="#why-is-a-chat-marked-as-request" class="anchor"></a>
</h3>
<p>Delta Chat étant une messagerie privée, seules les personnes avec qui vous <a href="#howtoe2ee">partagez votre QR code ou lien dinvitation</a> peuvent vous écrire.</p>
<p>As being a private messenger,
only friends and family you <a href="#howtoe2ee">share your QR code or invite link with</a> can write to you.</p>
<p>Vos contacts peuvent cependant partager votre profil avec dautres personnes, ce qui apparaît alors comme une <b style="border: 1px solid currentColor; padding: 0 3px; font-size:90%">invitation</b>.</p>
<p>Your friends may share your contact with other friends,
this appears as <b style="border: 1px solid currentColor; padding: 0 3px; font-size:90%">Request</b></p>
<ul>
<li>
@@ -200,37 +204,53 @@ recevoir de messages de cette personne, nous vous conseillons de la <strong>bloq
</li>
</ul>
<h3 id="comment-mettre-deux-personnes-en-relation-entre-elles-">
<h3 id="how-can-i-put-two-of-my-friends-in-contact-with-each-other">
Comment mettre deux personnes en relation entre elles ? <a href="#comment-mettre-deux-personnes-en-relation-entre-elles-" class="anchor"></a>
How can I put two of my friends in contact with each other? <a href="#how-can-i-put-two-of-my-friends-in-contact-with-each-other" class="anchor"></a>
</h3>
<p>Joignez le premier contact à votre discussion avec le second en utilisant <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Bouton pièce jointe → Contact</strong>.
Profitez-en pour ajouter un message de présentation.</p>
<p>Attach the first contact to the chat of the second using <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attachment Button → Contact</strong>.
You can also add a little introduction message.</p>
<p>Le deuxième contact recevra une <strong>carte</strong> quil pourra cliquer pour commencer une discussion avec le premier.</p>
<p>The second contact will receive a <strong>card</strong> then
and can tap it to start chatting with the first contact.</p>
<h3 id="delta-chat-prend-il-en-charge-les-images-vidéos-et-autres-pièces-jointes-">
Delta Chat prend-il en charge les images, vidéos et autres pièces jointes ? <a href="#delta-chat-prend-il-en-charge-les-images-vidéos-et-autres-pièces-jointes-" class="anchor"></a>
</h3>
<p>Oui. Images, videos, files, voice messages etc. can be sent using the <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attachment-</strong>
or <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Voice Message</strong> buttons</p>
<p>Pour améliorer les performances, les images sont redimensionnées et envoyées en taille réduite par défaut ; mais vous pouvez les envoyer en tant que “fichier” pour en conserver la taille originale.</p>
<h3 id="multiple-accounts">
À quoi correspondent les profils ? Comment est-ce que je peux passer de lun à lautre ? <a href="#multiple-accounts" class="anchor"></a>
What are profiles? How can I switch between them? <a href="#multiple-accounts" class="anchor"></a>
</h3>
<p>Un profil est <strong>un nom, une photo</strong> et quelques informations supplémentaires pour chiffrer des messages.
Un profil nest présent que sur votre appareil/vos appareils et utilise le serveur seulement pour relayer les messages vers vos contacts.</p>
<p>A profile is <strong>a name, a picture</strong> and some additional information for encrypting messages.
A profile lives on your device(s) only
and uses the server only to relay messages.</p>
<p>Lors de la première installation de Delta Chat un premier profil est créé.</p>
<p>On first installation of Delta Chat a first profile is created.</p>
<p>Par la suite vous pourrez cliquer sur votre image de profil en haut à gauche de l’écran principal pour <strong>Ajouter un profil</strong> ou <strong>Changer de profil</strong>.</p>
<p>Later, you can tap your profile image in the upper left corner to <strong>Add Profiles</strong>
or to <strong>Switch Profiles</strong>.</p>
<p>Vous souhaiterez peut être utiliser des profils différents pour vos activités familiales, professionnelles ou politiques.</p>
<p>You may want to use separate profiles for political, family or work related activities.</p>
<p>Vous souhaiterez peut être aussi apprendre <a href="#multiclient">comment utiliser le même profil sur plusieurs appareils</a>.</p>
<p>You may also wish to learn <a href="#multiclient">how to use the same profile on multiple devices</a>.</p>
<h3 id="qui-peut-voir-ma-photo-de-profil-">
@@ -247,13 +267,15 @@ Un profil nest présent que sur votre appareil/vos appareils et utilise le se
<h3 id="signature">
Est-il possible de définir une Bio/un Statut avec Delta Chat ? <a href="#signature" class="anchor"></a>
Can I set a Bio/Status with Delta Chat? <a href="#signature" class="anchor"></a>
</h3>
<p>Oui, vous pouvez le faire dans <strong>Paramètres → Profil → Signature</strong>.
À partir du moment ou vous échangez avec quelquun, cette personne pourra voir votre Signature en regardant les détails de votre profil.</p>
<p>Yes,
you can do so under <strong>Settings → Profile → Bio</strong>.
Once you sent a message to a contact,
they will see it when they view your contact details.</p>
<h3 id="que-signifient-épingler-sourdine-et-archiver-">
@@ -287,33 +309,37 @@ pour mettre une discussion en sourdine, ouvrez le menu de la conversation (Andro
<h3 id="save">
Comment fonctionnent les “Messages enregistrés” ? <a href="#save" class="anchor"></a>
How do “Saved Messages” work? <a href="#save" class="anchor"></a>
</h3>
<p><strong>Messages enregistrés</strong> est une discussion que vous pouvez utiliser pour sauvegarder et retrouver facilement des messages.</p>
<p><strong>Saved Messages</strong> is a chat that you can use to easily remember and find messages.</p>
<ul>
<li>
<p>Dans nimporte quelle discussion, touchez <img style="vertical-align:middle; width:1.2em; margin:1px;" src="../saved-icon.png" alt="Saved icon" /> ou faites un clic droit sur un message et sélectionnez <strong>Enregistrer</strong>.</p>
<p>In any chat, long tap or right click a message and select <strong>Save</strong></p>
</li>
<li>
<p>Les messages enregistrés sont identifiés par le symbole <img style="vertical-align:middle; width:1.2em; margin:1px" src="../saved-icon.png" alt="Saved icon" /> à côté de lhorodatage du message.</p>
<p>Saved messages are marked by the symbol
<img style="vertical-align:middle; width:1.2em; margin:1px" src="../saved-icon.png" alt="Saved icon" />
next to the timestamp</p>
</li>
<li>
<p>Par la suite vous pourrez retrouver tous ces messages dans la discussion “Messages enregistrés”.
En touchant <img style="vertical-align:middle; width:1.2em; margin:1px" src="../go-to-original.png" alt="Arrow-right icon" /> vous retrouverez le message dans sa discussion dorigine.</p>
<p>Later, open the “Saved Messages” chat - and you will see the saved messages there.
By tapping <img style="vertical-align:middle; width:1.2em; margin:1px" src="../go-to-original.png" alt="Arrow-right icon" />,
you can go back to the original message in the original chat</p>
</li>
<li>
<p>Enfin vous pouvez aussi utiliser la discussion “Messages enregistrés” pour prendre des <strong>notes privées</strong> - ouvrez la discussion, écrivez votre message, ajoutez une photo, un message vocal, etc.</p>
<p>Finally, you can also use “Saved Messages” to take <strong>personal notes</strong> - open the chat, type something, add a photo or a voice message etc.</p>
</li>
<li>
<p>La discussion “Messages enregistrés” étant synchronisée elle peut être très pratique pour transférer des informations dun appareil à un autre.</p>
<p>As “Saved Message” are synced, they can become very handy for transferring data between devices</p>
</li>
</ul>
<p>Les messages restent enregistrés même lorsquils sont édités ou supprimés, que ça soit par <a href="#edit">lexpéditeur⋅rice</a>, par <a href="#delold">suppression des anciens messages de lappareil</a> ou par <a href="#ephemeralmsgs">disparition des messages éphémères au sein dune discussion</a>.</p>
<p>Messages stay saved even if they are edited or deleted -
may it be by <a href="#edit">sender</a>, by <a href="#delold">device cleanup</a> or by <a href="#ephemeralmsgs">disappearing messages of other chats</a>.</p>
<h3 id="que-signifie-le-point-vert-">
@@ -323,10 +349,13 @@ En touchant <img style="vertical-align:middle; width:1.2em; margin:1px" src="../
</h3>
<p>Vous pourrez parfois voir un <strong>point vert</strong> <img style="vertical-align:middle; width:1.2em; margin:1px" src="../green-dot.png" alt="" /> sur le profil dun contact.
Cela veut dire que vous avez <strong>récemment vu</strong> le contact dans les 10 dernières minutes, par exemple parce que vous avez échangé avec ce contact, ou que vous avez reçu un accusé de lecture.</p>
<p>You can sometimes see a <strong>green dot</strong> <img style="vertical-align:middle; width:1.2em; margin:1px" src="../green-dot.png" alt="" />
next to the avatar of a contact.
It means they were <strong>recently seen by you</strong> in the last 10 minutes,
e.g. because they messaged you or sent a read receipt.</p>
<p>Ceci nest donc pas un indicateur temps réel de présence en ligne et vos contacts ne pourront donc pas toujours voir si vous êtes en ligne ou non.</p>
<p>So this is not a real time online status
and others will as well not always see that you are “online”.</p>
<h3 id="que-signifient-les-coches-affichées-à-côté-des-messages-sortants-">
@@ -338,75 +367,79 @@ Cela veut dire que vous avez <strong>récemment vu</strong> le contact dans les
<ul>
<li>
<p><strong>Une marque</strong> <img style="vertical-align:middle; width:1.5em; margin:1px" src="../tick1.png" alt="" /> veut dire que le message a été envoyé correctement au <a href="#relays">relai</a>.</p>
<p><strong>One tick</strong> <img style="vertical-align:middle; width:1.5em; margin:1px" src="../tick1.png" alt="" />
means that the message was sent successfully to the <a href="#relays">relay</a>.</p>
</li>
<li>
<p><strong>Deux marques</strong> <img style="vertical-align:middle; width:1.5em; margin:1px" src="../tick2.png" alt="" /> indiquent que votre contact a lu le message.</p>
<p><strong>Two ticks</strong> <img style="vertical-align:middle; width:1.5em; margin:1px" src="../tick2.png" alt="" />
indicate your contact has read the message.</p>
</li>
</ul>
<p>Au sein des <a href="#groups">groupes</a> la seconde marque veut dire quau moins un membre du groupe a pu lire le message.</p>
<p>In <a href="#groups">groups</a> the second tick means that at least one member has reported back having read the message.</p>
<p>La seconde marque ne peut safficher que si vous et lun de vos destinataires du message avez activé <strong>Paramètres → Discussions → Accusés de lecture</strong>.</p>
<p>You will only get the second tick if both you and one of the recipients who read the message
has <strong>Settings → Chats → Read Receipts</strong> enabled.</p>
<h3 id="edit">
Corriger des fautes et supprimer des messages après les avoir envoyés <a href="#edit" class="anchor"></a>
Correct typos and delete messages after sending <a href="#edit" class="anchor"></a>
</h3>
<ul>
<li>
<p>Vous pouvez éditer le texte de votre message après lavoir envoyé. Pour cela, appuyer longtemps ou faire clic droit sur le message et choisir <strong>Modifier</strong> ou <img style="vertical-align:middle; width:1.2em; margin:1px" src="../edit-icon.png" alt="Edit icon" />.</p>
<p>You can edit the text of your messages after sending.
For that, long tap or right click the message and select <strong>Edit</strong>
or <img style="vertical-align:middle; width:1.2em; margin:1px" src="../edit-icon.png" alt="Edit icon" />.</p>
</li>
<li>
<p>Si vous avez envoyé un message par erreur vous pouvez le supprimer depuis le même menu en choisissant <strong>Supprimer le message</strong> puis <strong>Supprimer pour moi</strong> ou <strong>Supprimer pour tout le monde</strong>.</p>
<p>If you have sent a message accidentally,
from the same menu, select <strong>Delete</strong> and then <strong>Delete for Everyone</strong>.</p>
</li>
</ul>
<p>Les messages modifiés seront indiqués par le mot “Modifié” à côté de lhorodatage du message, tandis que les messages supprimés le sont sans notification, ni dans la discussion, ni sur lappareil, et peuvent l’être sans limite dans le temps.</p>
<p>While edited messages will have the word “Edited” next to the timestamp,
deleted messages will be removed without a marker in the chat.
Notifications are not sent and there is no time limit.</p>
<p>Remarque : le message original peut toutefois encore exister sil a déjà été transféré, enregistré, ou si lun des membres de la discussion en a fait une copie ou une capture d’écran.</p>
<h3 id="mediaquality">
Comment est gérée la qualité des médias envoyés ? <a href="#mediaquality" class="anchor"></a>
</h3>
<p>Images, vidéos, fichiers, messages vocaux etc. peuvent être envoyé avec les boutons <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Pièce jointe</strong> ou <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Message vocal</strong>.</p>
<ul>
<li>
<p>Par défaut le niveau de compression choisi permet un <strong>envoi rapide et efficace</strong> des messages en respectant les limites de données et de stockage de tout le monde. Cest un réglage idéal pour des échanges standards.</p>
</li>
<li>
<p>Dans les régions avec une connectivité limitée il est possible de choisir une compression plus forte dans <strong>Paramètres → Discussions → Qualité du média en sortie</strong>.</p>
</li>
<li>
<p>Si vous souhaitez absolument transmettre un fichier en <strong>qualité originale</strong>, utilisez <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Pièce jointe → Fichier</strong> dans la discussion. Pensez à utiliser cette fonctionnalité avec parcimonie pour limiter lutilisation des données par toutes les personnes dans la discussion.</p>
</li>
</ul>
<p>Note, that the original message may still be received by chat members
who could have already replied, forwarded, saved, screenshotted or otherwise copied the message.</p>
<h3 id="ephemeralmsgs">
Comment fonctionnent les messages éphémères ? <a href="#ephemeralmsgs" class="anchor"></a>
How do disappearing messages work? <a href="#ephemeralmsgs" class="anchor"></a>
</h3>
<p>Vous pouvez activer les “Messages éphémères” dans les paramètres dune discussion, dans le menu en haut à droite, en choisissant une durée prédéfinie entre 5 min et 1 an.</p>
<p>You can turn on “disappearing messages”
in the settings of a chat,
at the top right of the chat window,
by selecting a time span
between 5 minutes and 1 year.</p>
<p>Tant que le paramètre reste activé, les applications de chacun des membres se chargent de supprimer les messages qui dépassent la durée définie. La durée est comptabilisée à partir du moment où le message est vu pour la première fois, et le message est supprimé au sein de lapplication et sur le serveur.</p>
<p>Until the setting is turned off again,
each chat members Delta Chat app takes care
of deleting the messages
after the selected time span.
The time span begins
when the receiver first sees the message in Delta Chat.
The messages are deleted both,
on the servers,
and in the apps itself.</p>
<p>Remarque : ne faites confiance au réglage “Messages éphémères” que si vous faites confiance aux membres de la discussion. Des membres mal intentionné⋅es peuvent très bien prendre des photos, copier, sauvegarder, transférer un message avant quil ne soit supprimé.</p>
<p>Note that you can rely on disappearing messages
only as long as you trust your chat partners;
malicious chat partners can take photos,
or otherwise save, copy or forward messages before deletion.</p>
<p>Enfin, si lun⋅e des membres dune discussion désinstalle lapplication Delta Chat, les messages (chiffrés, pour rappel) peuvent rester plus longtemps sur le serveur avant d’être supprimés.</p>
<p>Apart from that,
if one chat partner uninstalls Delta Chat,
the (anyway encrypted) messages may take longer to get deleted from their server.</p>
<h3 id="delold">
@@ -423,28 +456,39 @@ Cela veut dire que vous avez <strong>récemment vu</strong> le contact dans les
<h3 id="remove-account">
Comment puis-je supprimer un profil de discussion ? <a href="#remove-account" class="anchor"></a>
How can I delete my chat profile? <a href="#remove-account" class="anchor"></a>
</h3>
<p>Si vous utilisez plus dun profil dans lapplication, vous pouvez supprimer un profil spécifique via le menu (en haut sur Android et iOS), ou dans la barre latérale (sur lapplication de bureau) permettant de passer dun profil à un autre. Les profils ne sont toujours supprimés que sur lappareil en question. Les profils continueront donc dexister sur les autres appareils le cas échéant.</p>
<p>If you are using more than one chat profile,
you can remove single ones in the top profile switcher menu (on Android and iOS),
or in the sidebar with a right click (in the Desktop app).
Chat profiles are only removed on the device where deletion was triggered.
Chat profiles on other devices will continue to fully function.</p>
<p>Si vous nutilisez quun seul profil vous pouvez tout simplement désinstaller lapplication. Cela déclenchera la suppression des données liées au profil en question sur le serveur chatmail. Pour plus dinformations voir <a href="https://nine.testrun.org/info.html#account-deletion">Account deletion sur nine.testrun.org</a> ou la page respective du <a href="https://chatmail.at/relays">serveur chatmail</a> que vous utilisez.</p>
<p>If you use a single default chat profile you can simply uninstall the app.
This will still automatically trigger deletion of all associated address data on the chatmail server.
For more info, please refer to <a href="https://nine.testrun.org/info.html#account-deletion">nine.testrun.org address-deletion</a>
or the respective page from your chosen <a href="https://chatmail.at/relays">3rd party chatmail server</a>.</p>
<h2 id="groups">
Groupes <a href="#groups" class="anchor"></a>
Groups <a href="#groups" class="anchor"></a>
</h2>
<p>Les groupes permettent à plusieurs personnes davoir une discussion. Chaque membre du groupe à les <strong>même droits</strong>.</p>
<p>Groups let several people chat together privately with <strong>equal rights</strong>.</p>
<p>Chaque membre du groupe peut changer le nom du groupe ou lavatar, <a href="#addmembers">ajouter ou supprimer des membres</a>, activer/désactiver <a href="#ephemeralmsgs">les messages éphémères</a>, et <a href="#edit">supprimer leurs propres messages</a> sur tous les appareils des membres de la discussion.</p>
<p>Anyone can
change the group name or avatar,
<a href="#addmembers">add or remove members</a>,
set <a href="#ephemeralmsgs">disappearing messages</a>,
and <a href="#edit">delete their own messages</a> from all members devices.</p>
<p>Puisque tous les membres dune discussion ont les mêmes droits, les groupes fonctionnent mieux avec des <strong>personnes de confiance et la famille</strong>.</p>
<p>Because all members have the same rights, groups work best among <strong>trusted friends and family</strong>.</p>
<h3 id="création-dun-groupe">
@@ -469,7 +513,7 @@ Cela veut dire que vous avez <strong>récemment vu</strong> le contact dans les
<h3 id="addmembers">
Ajouter et supprimer des membres <a href="#addmembers" class="anchor"></a>
Add and remove members <a href="#addmembers" class="anchor"></a>
</h3>
@@ -562,197 +606,6 @@ but more than 150 is not recommended.</p>
where Delta Chat is a private messenger for chatting with <a href="#groups">equal rights</a>.
See <a href="https://en.wikipedia.org/wiki/Dunbar%27s_number">Dunbars number</a> for more insights.</p>
<h2 id="channels">
Channels <a href="#channels" class="anchor"></a>
</h2>
<p>Channels are a one-to-many tool for broadcasting messages.</p>
<h3 id="subscribe-to-a-channel">
Subscribe to a channel <a href="#subscribe-to-a-channel" class="anchor"></a>
</h3>
<ul>
<li>Scan the <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> <strong>QR code</strong>
or tap the <strong>invite link</strong> you got from the channel owner.</li>
</ul>
<p>Thats all!
You will receive a few of the messages from the channel history
and, from that point on, all new messages from the channel.</p>
<p><strong>Dont worry,</strong> if that does not happen immediately.
Once the channel owner comes online, your join request will be processed.</p>
<p>As all of Delta Chat, also Channels are private and decentralized,
there is no public discovery.</p>
<p>Other channel subscribers will not see that you subscribed and cannot message you.
The channel owner, however, can message you.
They will also see that you read a message unless you have read receipts disabled.</p>
<p>If you do not want to share your main profile,
you can also create a <a href="#multiple-accounts">dedicated profile</a> for joining a channel.</p>
<h3 id="create-a-channel">
Create a channel <a href="#create-a-channel" class="anchor"></a>
</h3>
<ul>
<li>
<p>Tap <strong>New Chat</strong> and choose <strong>New Channel</strong>.</p>
</li>
<li>
<p>Enter a <strong>name</strong>, optionally set an <strong>image</strong> and <strong>description</strong>, and hit the <strong>Create</strong> button.</p>
</li>
<li>
<p>You can now send and manage messages as usual.</p>
</li>
<li>
<p>From the channels profile, <strong>share the QR code or invite link with others</strong>.</p>
</li>
</ul>
<p>Subscribers will receive your messages,
but they cannot send messages in your channel.
When subscribing, they will receive <strong>a few of the latest messages of the channel history</strong>.</p>
<p>You can see the <strong>view count</strong> beside each message.
Note that this only counts subscribers who have read receipts enabled,
so the real view count may be larger.</p>
<h3 id="how-many-subscribers-can-a-channel-have">
How many subscribers can a channel have? <a href="#how-many-subscribers-can-a-channel-have" class="anchor"></a>
</h3>
<p>Channels are designed for much larger audiences than <a href="#groups">groups</a>.</p>
<p>The practical limit depends on the used <a href="#relays">relay</a>,
so there is no single fixed number that applies everywhere.</p>
<p>For really large channels with several tens of thousands of subscribers,
we recommend using a <a href="#multiple-accounts">dedicated profile</a> for the channel
and checking whether the relay is suitable.</p>
<p>But dont be too hesitant: Delta Chat is designed to be relay-agnostic,
so you can change your relay at any point easily -
your existing subscribers will not even notice.
You only have to update the invite link you share with new subscribers in that case.</p>
<h2 id="calls">
Calls <a href="#calls" class="anchor"></a>
</h2>
<p>Delta Chat supports one-to-one <strong>audio calls</strong> and <strong>video calls</strong>.</p>
<p>Calls are supported on Desktop, Ubuntu Touch, iOS and Android 8 and newer.</p>
<h3 id="place-a-call">
Place a call <a href="#place-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>In a one-to-one chat, tap the 📞 <strong>call icon</strong>.</p>
</li>
<li>
<p>This opens a small menu
where you can choose whether to place an <strong>Audio Call</strong> or a <strong>Video Call</strong>.</p>
</li>
</ul>
<h3 id="accept-or-reject-a-call">
Accept or reject a call <a href="#accept-or-reject-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>When someone calls you,
Delta Chat shows an <strong>incoming call screen</strong> or notification.</p>
</li>
<li>
<p>Tap <strong>Accept</strong> to answer
or <strong>Decline</strong> to reject the call.</p>
</li>
</ul>
<h3 id="during-a-call">
During a call <a href="#during-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>You can <strong>mute</strong> your microphone.</p>
</li>
<li>
<p>You can <strong>enable or disable your camera</strong>.</p>
</li>
<li>
<p>On mobile, you can <strong>switch between front and back cameras</strong>.</p>
</li>
</ul>
<p>Depending on the device, you can also select the audio output or use picture-in-picture.
On desktop, the call is using a dedicated window
and you can continue using the main Delta Chat window as usual.</p>
<h3 id="missed-calls-and-notifications">
Missed calls and notifications <a href="#missed-calls-and-notifications" class="anchor"></a>
</h3>
<ul>
<li>
<p>If you do not answer, do not hear the ringing, or do not have your device at hand,
the call appears as a <strong>missed call</strong>.</p>
</li>
<li>
<p><strong>Only your accepted contacts</strong> can make your device ring.
Contact requests will appear as usual and will not ring.</p>
</li>
<li>
<p>At <strong>Settings → Notifications → Calls</strong>,
you can disable the special call ringing screen completely.
If you do so, you will not be disturbed by any ringing notification,
you can still pick up the call by tapping the incoming call message bubble in its chat.</p>
</li>
</ul>
<h2 id="webxdc">
@@ -1163,26 +1016,22 @@ 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 <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at 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">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>
<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>
</li>
<li>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</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>
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>
</li>
</ul>
@@ -1544,20 +1393,23 @@ can not be identified easily.</p>
</h3>
<p>The used <a href="#relays">relays</a> need to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#calls">call</a>
<p>The used <a href="#relays">relay</a> needs to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#experiments">call</a>
or use <a href="#webxdc">apps</a> together.</p>
<p>IP Addresses are needed for connectivity and efficiency.
Delta Chat neither persists nor exposes them.
Note that IP Addresses
are not like an address you give to a delivery service,
but typically less precise, often defining city or region only.</p>
They are neither persisted nor exposed.
Note that the IP Address
is not like a detailed address you give to a delivery service,
but much more coarse, often defining region or country only.</p>
<p>If you see your IP Address as a risk,
we recommend to use a VPN for the whole system.
Per-app options leave gaps across your system.
For example, tapping a link can expose IP Addresses to unknown parties, which is by far the larger risk.</p>
<p>As this is just how the internet and other messengers work by default,
we do not offer options here or ask upfront questions.</p>
<p>If you see your IP Address as a security or privacy risk,
we recommend to use a VPN, in combination with system lockdown mode.
Hunting down options in all apps on your system will leave gaps.
For example, tapping a link exposes IP Addresses to unknown parties and is the by far larger risk here.</p>
<h3 id="sealedsender">
@@ -1686,7 +1538,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/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/fr/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>
+35 -255
View File
@@ -5,6 +5,7 @@
<li><a href="#howtoe2ee">How can I find people to chat with?</a></li>
<li><a href="#why-is-a-chat-marked-as-request">Why is a chat marked as “Request”?</a></li>
<li><a href="#how-can-i-put-two-of-my-friends-in-contact-with-each-other">How can I put two of my friends in contact with each other?</a></li>
<li><a href="#apakah-delta-chat-mendukung-gambar-vidio-dan-lampiran-lainnya">Apakah Delta Chat mendukung gambar, vidio dan lampiran lainnya?</a></li>
<li><a href="#multiple-accounts">What are profiles? How can I switch between them?</a></li>
<li><a href="#siapa-yang-dapat-melihat-foto-profil-saya">Siapa yang dapat melihat Foto Profil saya?</a></li>
<li><a href="#signature">Can I set a Bio/Status with Delta Chat?</a></li>
@@ -13,7 +14,6 @@
<li><a href="#what-does-the-green-dot-mean">What does the green dot mean?</a></li>
<li><a href="#what-do-the-ticks-shown-beside-outgoing-messages-mean">What do the ticks shown beside outgoing messages mean?</a></li>
<li><a href="#edit">Correct typos and delete messages after sending</a></li>
<li><a href="#mediaquality">How is media quality handled?</a></li>
<li><a href="#ephemeralmsgs">How do disappearing messages work?</a></li>
<li><a href="#delold">What happens if I turn on “Delete Messages from Device”?</a></li>
<li><a href="#remove-account">How can I delete my chat profile?</a></li>
@@ -29,21 +29,6 @@
<li><a href="#how-many-members-can-participate-in-a-single-group">How many members can participate in a single group?</a></li>
</ul>
</li>
<li><a href="#channels">Channels</a>
<ul>
<li><a href="#subscribe-to-a-channel">Subscribe to a channel</a></li>
<li><a href="#create-a-channel">Create a channel</a></li>
<li><a href="#how-many-subscribers-can-a-channel-have">How many subscribers can a channel have?</a></li>
</ul>
</li>
<li><a href="#calls">Calls</a>
<ul>
<li><a href="#place-a-call">Place a call</a></li>
<li><a href="#accept-or-reject-a-call">Accept or reject a call</a></li>
<li><a href="#during-a-call">During a call</a></li>
<li><a href="#missed-calls-and-notifications">Missed calls and notifications</a></li>
</ul>
</li>
<li><a href="#webxdc">In-chat apps</a>
<ul>
<li><a href="#where-can-i-get-in-chat-apps">Where can I get in-chat apps?</a></li>
@@ -233,6 +218,19 @@ You can also add a little introduction message.</p>
<p>The second contact will receive a <strong>card</strong> then
and can tap it to start chatting with the first contact.</p>
<h3 id="apakah-delta-chat-mendukung-gambar-vidio-dan-lampiran-lainnya">
Apakah Delta Chat mendukung gambar, vidio dan lampiran lainnya? <a href="#apakah-delta-chat-mendukung-gambar-vidio-dan-lampiran-lainnya" class="anchor"></a>
</h3>
<p>Yes. Images, videos, files, voice messages etc. can be sent using the <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attachment-</strong>
or <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Voice Message</strong> buttons</p>
<p>For performance, images are optimized and sent at a smaller size by default, but you can send it as a “file” to preserve the original.</p>
<h3 id="multiple-accounts">
@@ -413,32 +411,6 @@ Notifications are not sent and there is no time limit.</p>
<p>Note, that the original message may still be received by chat members
who could have already replied, forwarded, saved, screenshotted or otherwise copied the message.</p>
<h3 id="mediaquality">
How is media quality handled? <a href="#mediaquality" class="anchor"></a>
</h3>
<p>Images, videos, files, voice messages etc. can be sent using the <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attach-</strong>
or <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Voice Message</strong> buttons.</p>
<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>
</li>
<li>
<p>In regions with worse connectivity,
you can choose higher compression at <strong>Settings → Chats → Outgoing Media Quality</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>
</li>
</ul>
<h3 id="ephemeralmsgs">
@@ -642,197 +614,6 @@ but more than 150 is not recommended.</p>
where Delta Chat is a private messenger for chatting with <a href="#groups">equal rights</a>.
See <a href="https://en.wikipedia.org/wiki/Dunbar%27s_number">Dunbars number</a> for more insights.</p>
<h2 id="channels">
Channels <a href="#channels" class="anchor"></a>
</h2>
<p>Channels are a one-to-many tool for broadcasting messages.</p>
<h3 id="subscribe-to-a-channel">
Subscribe to a channel <a href="#subscribe-to-a-channel" class="anchor"></a>
</h3>
<ul>
<li>Scan the <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> <strong>QR code</strong>
or tap the <strong>invite link</strong> you got from the channel owner.</li>
</ul>
<p>Thats all!
You will receive a few of the messages from the channel history
and, from that point on, all new messages from the channel.</p>
<p><strong>Dont worry,</strong> if that does not happen immediately.
Once the channel owner comes online, your join request will be processed.</p>
<p>As all of Delta Chat, also Channels are private and decentralized,
there is no public discovery.</p>
<p>Other channel subscribers will not see that you subscribed and cannot message you.
The channel owner, however, can message you.
They will also see that you read a message unless you have read receipts disabled.</p>
<p>If you do not want to share your main profile,
you can also create a <a href="#multiple-accounts">dedicated profile</a> for joining a channel.</p>
<h3 id="create-a-channel">
Create a channel <a href="#create-a-channel" class="anchor"></a>
</h3>
<ul>
<li>
<p>Tap <strong>New Chat</strong> and choose <strong>New Channel</strong>.</p>
</li>
<li>
<p>Enter a <strong>name</strong>, optionally set an <strong>image</strong> and <strong>description</strong>, and hit the <strong>Create</strong> button.</p>
</li>
<li>
<p>You can now send and manage messages as usual.</p>
</li>
<li>
<p>From the channels profile, <strong>share the QR code or invite link with others</strong>.</p>
</li>
</ul>
<p>Subscribers will receive your messages,
but they cannot send messages in your channel.
When subscribing, they will receive <strong>a few of the latest messages of the channel history</strong>.</p>
<p>You can see the <strong>view count</strong> beside each message.
Note that this only counts subscribers who have read receipts enabled,
so the real view count may be larger.</p>
<h3 id="how-many-subscribers-can-a-channel-have">
How many subscribers can a channel have? <a href="#how-many-subscribers-can-a-channel-have" class="anchor"></a>
</h3>
<p>Channels are designed for much larger audiences than <a href="#groups">groups</a>.</p>
<p>The practical limit depends on the used <a href="#relays">relay</a>,
so there is no single fixed number that applies everywhere.</p>
<p>For really large channels with several tens of thousands of subscribers,
we recommend using a <a href="#multiple-accounts">dedicated profile</a> for the channel
and checking whether the relay is suitable.</p>
<p>But dont be too hesitant: Delta Chat is designed to be relay-agnostic,
so you can change your relay at any point easily -
your existing subscribers will not even notice.
You only have to update the invite link you share with new subscribers in that case.</p>
<h2 id="calls">
Calls <a href="#calls" class="anchor"></a>
</h2>
<p>Delta Chat supports one-to-one <strong>audio calls</strong> and <strong>video calls</strong>.</p>
<p>Calls are supported on Desktop, Ubuntu Touch, iOS and Android 8 and newer.</p>
<h3 id="place-a-call">
Place a call <a href="#place-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>In a one-to-one chat, tap the 📞 <strong>call icon</strong>.</p>
</li>
<li>
<p>This opens a small menu
where you can choose whether to place an <strong>Audio Call</strong> or a <strong>Video Call</strong>.</p>
</li>
</ul>
<h3 id="accept-or-reject-a-call">
Accept or reject a call <a href="#accept-or-reject-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>When someone calls you,
Delta Chat shows an <strong>incoming call screen</strong> or notification.</p>
</li>
<li>
<p>Tap <strong>Accept</strong> to answer
or <strong>Decline</strong> to reject the call.</p>
</li>
</ul>
<h3 id="during-a-call">
During a call <a href="#during-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>You can <strong>mute</strong> your microphone.</p>
</li>
<li>
<p>You can <strong>enable or disable your camera</strong>.</p>
</li>
<li>
<p>On mobile, you can <strong>switch between front and back cameras</strong>.</p>
</li>
</ul>
<p>Depending on the device, you can also select the audio output or use picture-in-picture.
On desktop, the call is using a dedicated window
and you can continue using the main Delta Chat window as usual.</p>
<h3 id="missed-calls-and-notifications">
Missed calls and notifications <a href="#missed-calls-and-notifications" class="anchor"></a>
</h3>
<ul>
<li>
<p>If you do not answer, do not hear the ringing, or do not have your device at hand,
the call appears as a <strong>missed call</strong>.</p>
</li>
<li>
<p><strong>Only your accepted contacts</strong> can make your device ring.
Contact requests will appear as usual and will not ring.</p>
</li>
<li>
<p>At <strong>Settings → Notifications → Calls</strong>,
you can disable the special call ringing screen completely.
If you do so, you will not be disturbed by any ringing notification,
you can still pick up the call by tapping the incoming call message bubble in its chat.</p>
</li>
</ul>
<h2 id="webxdc">
@@ -1249,26 +1030,22 @@ 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 <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at 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">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>
<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>
</li>
<li>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</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>
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>
</li>
</ul>
@@ -1631,20 +1408,23 @@ can not be identified easily.</p>
</h3>
<p>The used <a href="#relays">relays</a> need to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#calls">call</a>
<p>The used <a href="#relays">relay</a> needs to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#experiments">call</a>
or use <a href="#webxdc">apps</a> together.</p>
<p>IP Addresses are needed for connectivity and efficiency.
Delta Chat neither persists nor exposes them.
Note that IP Addresses
are not like an address you give to a delivery service,
but typically less precise, often defining city or region only.</p>
They are neither persisted nor exposed.
Note that the IP Address
is not like a detailed address you give to a delivery service,
but much more coarse, often defining region or country only.</p>
<p>If you see your IP Address as a risk,
we recommend to use a VPN for the whole system.
Per-app options leave gaps across your system.
For example, tapping a link can expose IP Addresses to unknown parties, which is by far the larger risk.</p>
<p>As this is just how the internet and other messengers work by default,
we do not offer options here or ask upfront questions.</p>
<p>If you see your IP Address as a security or privacy risk,
we recommend to use a VPN, in combination with system lockdown mode.
Hunting down options in all apps on your system will leave gaps.
For example, tapping a link exposes IP Addresses to unknown parties and is the by far larger risk here.</p>
<h3 id="sealedsender">
@@ -1781,7 +1561,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/serverguide">recommended on this site</a>.
<a href="https://delta.chat/id/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>
+35 -257
View File
@@ -5,6 +5,7 @@
<li><a href="#howtoe2ee">Come posso trovare persone con cui chattare?</a></li>
<li><a href="#perché-una-chat-è-contrassegnata-come-richiesta">Perché una chat è contrassegnata come “Richiesta”?</a></li>
<li><a href="#come-posso-mettere-in-contatto-due-miei-amici">Come posso mettere in contatto due miei amici?</a></li>
<li><a href="#delta-chat-supporta-immagini-video-e-altri-allegati">Delta Chat supporta immagini, video e altri allegati?</a></li>
<li><a href="#multiple-accounts">Cosa sono i profili? Come posso passare dalluno allaltro?</a></li>
<li><a href="#chi-vede-la-mia-immagine-del-profilo">Chi vede la mia immagine del profilo?</a></li>
<li><a href="#signature">Posso impostare una Biografia/Stato con Delta Chat?</a></li>
@@ -13,7 +14,6 @@
<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">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>
@@ -29,21 +29,6 @@
<li><a href="#quanti-membri-possono-partecipare-a-un-singolo-gruppo">Quanti membri possono partecipare a un singolo gruppo?</a></li>
</ul>
</li>
<li><a href="#channels">Channels</a>
<ul>
<li><a href="#subscribe-to-a-channel">Subscribe to a channel</a></li>
<li><a href="#create-a-channel">Create a channel</a></li>
<li><a href="#how-many-subscribers-can-a-channel-have">How many subscribers can a channel have?</a></li>
</ul>
</li>
<li><a href="#calls">Calls</a>
<ul>
<li><a href="#place-a-call">Place a call</a></li>
<li><a href="#accept-or-reject-a-call">Accept or reject a call</a></li>
<li><a href="#during-a-call">During a call</a></li>
<li><a href="#missed-calls-and-notifications">Missed calls and notifications</a></li>
</ul>
</li>
<li><a href="#webxdc">Apps in chat</a>
<ul>
<li><a href="#dove-posso-trovare-le-apps-in-chat">Dove posso trovare le apps in chat?</a></li>
@@ -232,6 +217,19 @@ Puoi anche aggiungere un breve messaggio di presentazione.</p>
<p>Il secondo contatto riceverà una <strong>scheda</strong>
e potrà toccarla per iniziare a chattare con il primo contatto.</p>
<h3 id="delta-chat-supporta-immagini-video-e-altri-allegati">
Delta Chat supporta immagini, video e altri allegati? <a href="#delta-chat-supporta-immagini-video-e-altri-allegati" class="anchor"></a>
</h3>
<p>Sì. Immagini, video, files, messaggi vocali ecc. possono essere inviati utilizzando <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Allegato-</strong>
o <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> pulsanti <strong>Messaggio Vocale</strong></p>
<p>Per le prestazioni, le immagini sono ottimizzate e inviate in dimensioni inferiori per impostazione predefinita, ma è possibile inviarle come “file” per preservare loriginale.</p>
<h3 id="multiple-accounts">
@@ -411,32 +409,6 @@ Non vengono inviate notifiche e non c’è limite di tempo.</p>
<p>Nota che il messaggio originale potrebbe essere ancora sui dispositivi dei membri della chat
che avrebbero già potuto rispondere, inoltrare, salvare, scattare una schermata o copiare il messaggio in altri modi.</p>
<h3 id="mediaquality">
Come viene gestita la qualità dei media? <a href="#mediaquality" class="anchor"></a>
</h3>
<p>Immagini, video, files, messaggi vocali ecc. possono essere inviati utilizzando <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Allegato-</strong>
o <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> pulsanti <strong>Messaggio Vocale</strong></p>
<ul>
<li>
<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>Nelle regioni con connettività peggiore,
è possibile scegliere una compressione più elevata in <strong>Impostazioni → Chat → Qualità Media in Uscita</strong>.</p>
</li>
<li>
<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>
<h3 id="ephemeralmsgs">
@@ -635,197 +607,6 @@ ma non è consigliabile superare i 150.</p>
dove Delta Chat è un servizio di messaggistica privato per chattare con <a href="#groups">uguali diritti</a>.
Vedi <a href="https://en.wikipedia.org/wiki/Dunbar%27s_number">numero di Dunbar</a> per ulteriori approfondimenti.</p>
<h2 id="channels">
Channels <a href="#channels" class="anchor"></a>
</h2>
<p>Channels are a one-to-many tool for broadcasting messages.</p>
<h3 id="subscribe-to-a-channel">
Subscribe to a channel <a href="#subscribe-to-a-channel" class="anchor"></a>
</h3>
<ul>
<li>Scan the <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> <strong>QR code</strong>
or tap the <strong>invite link</strong> you got from the channel owner.</li>
</ul>
<p>Thats all!
You will receive a few of the messages from the channel history
and, from that point on, all new messages from the channel.</p>
<p><strong>Dont worry,</strong> if that does not happen immediately.
Once the channel owner comes online, your join request will be processed.</p>
<p>As all of Delta Chat, also Channels are private and decentralized,
there is no public discovery.</p>
<p>Other channel subscribers will not see that you subscribed and cannot message you.
The channel owner, however, can message you.
They will also see that you read a message unless you have read receipts disabled.</p>
<p>If you do not want to share your main profile,
you can also create a <a href="#multiple-accounts">dedicated profile</a> for joining a channel.</p>
<h3 id="create-a-channel">
Create a channel <a href="#create-a-channel" class="anchor"></a>
</h3>
<ul>
<li>
<p>Tap <strong>New Chat</strong> and choose <strong>New Channel</strong>.</p>
</li>
<li>
<p>Enter a <strong>name</strong>, optionally set an <strong>image</strong> and <strong>description</strong>, and hit the <strong>Create</strong> button.</p>
</li>
<li>
<p>You can now send and manage messages as usual.</p>
</li>
<li>
<p>From the channels profile, <strong>share the QR code or invite link with others</strong>.</p>
</li>
</ul>
<p>Subscribers will receive your messages,
but they cannot send messages in your channel.
When subscribing, they will receive <strong>a few of the latest messages of the channel history</strong>.</p>
<p>You can see the <strong>view count</strong> beside each message.
Note that this only counts subscribers who have read receipts enabled,
so the real view count may be larger.</p>
<h3 id="how-many-subscribers-can-a-channel-have">
How many subscribers can a channel have? <a href="#how-many-subscribers-can-a-channel-have" class="anchor"></a>
</h3>
<p>Channels are designed for much larger audiences than <a href="#groups">groups</a>.</p>
<p>The practical limit depends on the used <a href="#relays">relay</a>,
so there is no single fixed number that applies everywhere.</p>
<p>For really large channels with several tens of thousands of subscribers,
we recommend using a <a href="#multiple-accounts">dedicated profile</a> for the channel
and checking whether the relay is suitable.</p>
<p>But dont be too hesitant: Delta Chat is designed to be relay-agnostic,
so you can change your relay at any point easily -
your existing subscribers will not even notice.
You only have to update the invite link you share with new subscribers in that case.</p>
<h2 id="calls">
Calls <a href="#calls" class="anchor"></a>
</h2>
<p>Delta Chat supports one-to-one <strong>audio calls</strong> and <strong>video calls</strong>.</p>
<p>Calls are supported on Desktop, Ubuntu Touch, iOS and Android 8 and newer.</p>
<h3 id="place-a-call">
Place a call <a href="#place-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>In a one-to-one chat, tap the 📞 <strong>call icon</strong>.</p>
</li>
<li>
<p>This opens a small menu
where you can choose whether to place an <strong>Audio Call</strong> or a <strong>Video Call</strong>.</p>
</li>
</ul>
<h3 id="accept-or-reject-a-call">
Accept or reject a call <a href="#accept-or-reject-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>When someone calls you,
Delta Chat shows an <strong>incoming call screen</strong> or notification.</p>
</li>
<li>
<p>Tap <strong>Accept</strong> to answer
or <strong>Decline</strong> to reject the call.</p>
</li>
</ul>
<h3 id="during-a-call">
During a call <a href="#during-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>You can <strong>mute</strong> your microphone.</p>
</li>
<li>
<p>You can <strong>enable or disable your camera</strong>.</p>
</li>
<li>
<p>On mobile, you can <strong>switch between front and back cameras</strong>.</p>
</li>
</ul>
<p>Depending on the device, you can also select the audio output or use picture-in-picture.
On desktop, the call is using a dedicated window
and you can continue using the main Delta Chat window as usual.</p>
<h3 id="missed-calls-and-notifications">
Missed calls and notifications <a href="#missed-calls-and-notifications" class="anchor"></a>
</h3>
<ul>
<li>
<p>If you do not answer, do not hear the ringing, or do not have your device at hand,
the call appears as a <strong>missed call</strong>.</p>
</li>
<li>
<p><strong>Only your accepted contacts</strong> can make your device ring.
Contact requests will appear as usual and will not ring.</p>
</li>
<li>
<p>At <strong>Settings → Notifications → Calls</strong>,
you can disable the special call ringing screen completely.
If you do so, you will not be disturbed by any ringing notification,
you can still pick up the call by tapping the incoming call message bubble in its chat.</p>
</li>
</ul>
<h2 id="webxdc">
@@ -1240,22 +1021,16 @@ 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">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>
<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>
</li>
<li>
<p>Tocca un ripetitore per impostarlo come <strong>utilizzato per linvio</strong>.</p>
<p>Lopzione <strong>predefinita</strong> definisce quella a cui i tuoi partner di chat invieranno messaggi futuri.</p>
</li>
<li>
<p>Se <strong>rimuovi</strong> un ripetitore,
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>
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>
</li>
</ul>
@@ -1618,20 +1393,23 @@ non possono essere identificati facilmente.</p>
</h3>
<p>The used <a href="#relays">relays</a> need to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#calls">call</a>
or use <a href="#webxdc">apps</a> together.</p>
<p>Il <a href="#relays">ripetitore</a> utilizzato deve conoscere il tuo indirizzo IP,
e talvolta anche i dispositivi dei tuoi contatti se avete una <a href="#experiments">chiamata</a>
o utilizzate <a href="#webxdc">apps</a> insieme.</p>
<p>IP Addresses are needed for connectivity and efficiency.
Delta Chat neither persists nor exposes them.
Note that IP Addresses
are not like an address you give to a delivery service,
but typically less precise, often defining city or region only.</p>
<p>Gli indirizzi IP sono necessari per la connettività e lefficienza.
Non sono né persistenti né esposti.
Si noti che lindirizzo IP
non è come un indirizzo dettagliato che si fornisce a un servizio di consegna,
ma molto più generico, che spesso definisce solo la regione o il paese.</p>
<p>If you see your IP Address as a risk,
we recommend to use a VPN for the whole system.
Per-app options leave gaps across your system.
For example, tapping a link can expose IP Addresses to unknown parties, which is by far the larger risk.</p>
<p>Poiché questo è il modo in cui Internet e altri servizi di messaggistica funzionano di default,
non offriamo opzioni né poniamo domande in anticipo.</p>
<p>Se ritieni che il tuo indirizzo IP rappresenti un rischio per la sicurezza o la privacy,
ti consigliamo di utilizzare una VPN, in combinazione con la modalità di blocco del sistema.
Esplorare le opzioni in tutte le app del tuo sistema lascerà delle lacune.
Ad esempio, cliccare su un link espone gli indirizzi IP a sconosciuti e rappresenta il rischio di gran lunga maggiore.</p>
<h3 id="sealedsender">
@@ -1769,7 +1547,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/serverguide">consigliato su questo sito</a>.
<a href="https://delta.chat/it/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>
+35 -255
View File
@@ -5,6 +5,7 @@
<li><a href="#howtoe2ee">How can I find people to chat with?</a></li>
<li><a href="#why-is-a-chat-marked-as-request">Why is a chat marked as “Request”?</a></li>
<li><a href="#how-can-i-put-two-of-my-friends-in-contact-with-each-other">How can I put two of my friends in contact with each other?</a></li>
<li><a href="#ondersteunt-delta-chat-afbeeldingen-videos-en-ander-soort-bijlagen">Ondersteunt Delta Chat afbeeldingen, videos en ander soort bijlagen?</a></li>
<li><a href="#multiple-accounts">What are profiles? How can I switch between them?</a></li>
<li><a href="#wie-kan-mijn-profielfoto-zien">Wie kan mijn profielfoto zien?</a></li>
<li><a href="#signature">Can I set a Bio/Status with Delta Chat?</a></li>
@@ -13,7 +14,6 @@
<li><a href="#wat-betekent-die-groene-stip">Wat betekent die groene stip?</a></li>
<li><a href="#wat-betekenen-de-vinkjes-naast-verzonden-berichten">Wat betekenen de vinkjes naast verzonden berichten?</a></li>
<li><a href="#edit">Correct typos and delete messages after sending</a></li>
<li><a href="#mediaquality">How is media quality handled?</a></li>
<li><a href="#ephemeralmsgs">How do disappearing messages work?</a></li>
<li><a href="#delold">Wat gebeurt er als ik Oude berichten van server verwijderen inschakel?</a></li>
<li><a href="#remove-account">How can I delete my chat profile?</a></li>
@@ -29,21 +29,6 @@
<li><a href="#how-many-members-can-participate-in-a-single-group">How many members can participate in a single group?</a></li>
</ul>
</li>
<li><a href="#channels">Channels</a>
<ul>
<li><a href="#subscribe-to-a-channel">Subscribe to a channel</a></li>
<li><a href="#create-a-channel">Create a channel</a></li>
<li><a href="#how-many-subscribers-can-a-channel-have">How many subscribers can a channel have?</a></li>
</ul>
</li>
<li><a href="#calls">Calls</a>
<ul>
<li><a href="#place-a-call">Place a call</a></li>
<li><a href="#accept-or-reject-a-call">Accept or reject a call</a></li>
<li><a href="#during-a-call">During a call</a></li>
<li><a href="#missed-calls-and-notifications">Missed calls and notifications</a></li>
</ul>
</li>
<li><a href="#webxdc">In-chat apps</a>
<ul>
<li><a href="#where-can-i-get-in-chat-apps">Where can I get in-chat apps?</a></li>
@@ -233,6 +218,19 @@ You can also add a little introduction message.</p>
<p>The second contact will receive a <strong>card</strong> then
and can tap it to start chatting with the first contact.</p>
<h3 id="ondersteunt-delta-chat-afbeeldingen-videos-en-ander-soort-bijlagen">
Ondersteunt Delta Chat afbeeldingen, videos en ander soort bijlagen? <a href="#ondersteunt-delta-chat-afbeeldingen-videos-en-ander-soort-bijlagen" class="anchor"></a>
</h3>
<p>Yes. Images, videos, files, voice messages etc. can be sent using the <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attachment-</strong>
or <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Voice Message</strong> buttons</p>
<p>Om de prestaties te verhogen, worden afbeeldingen standaard geoptimaliseerd en verkleind verstuurd, maar je kunt ze als een bestand verzenden om het origineel te sturen.</p>
<h3 id="multiple-accounts">
@@ -412,32 +410,6 @@ Notifications are not sent and there is no time limit.</p>
<p>Note, that the original message may still be received by chat members
who could have already replied, forwarded, saved, screenshotted or otherwise copied the message.</p>
<h3 id="mediaquality">
How is media quality handled? <a href="#mediaquality" class="anchor"></a>
</h3>
<p>Images, videos, files, voice messages etc. can be sent using the <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attach-</strong>
or <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Voice Message</strong> buttons.</p>
<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>
</li>
<li>
<p>In regions with worse connectivity,
you can choose higher compression at <strong>Settings → Chats → Outgoing Media Quality</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>
</li>
</ul>
<h3 id="ephemeralmsgs">
@@ -637,197 +609,6 @@ but more than 150 is not recommended.</p>
where Delta Chat is a private messenger for chatting with <a href="#groups">equal rights</a>.
See <a href="https://en.wikipedia.org/wiki/Dunbar%27s_number">Dunbars number</a> for more insights.</p>
<h2 id="channels">
Channels <a href="#channels" class="anchor"></a>
</h2>
<p>Channels are a one-to-many tool for broadcasting messages.</p>
<h3 id="subscribe-to-a-channel">
Subscribe to a channel <a href="#subscribe-to-a-channel" class="anchor"></a>
</h3>
<ul>
<li>Scan the <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> <strong>QR code</strong>
or tap the <strong>invite link</strong> you got from the channel owner.</li>
</ul>
<p>Thats all!
You will receive a few of the messages from the channel history
and, from that point on, all new messages from the channel.</p>
<p><strong>Dont worry,</strong> if that does not happen immediately.
Once the channel owner comes online, your join request will be processed.</p>
<p>As all of Delta Chat, also Channels are private and decentralized,
there is no public discovery.</p>
<p>Other channel subscribers will not see that you subscribed and cannot message you.
The channel owner, however, can message you.
They will also see that you read a message unless you have read receipts disabled.</p>
<p>If you do not want to share your main profile,
you can also create a <a href="#multiple-accounts">dedicated profile</a> for joining a channel.</p>
<h3 id="create-a-channel">
Create a channel <a href="#create-a-channel" class="anchor"></a>
</h3>
<ul>
<li>
<p>Tap <strong>New Chat</strong> and choose <strong>New Channel</strong>.</p>
</li>
<li>
<p>Enter a <strong>name</strong>, optionally set an <strong>image</strong> and <strong>description</strong>, and hit the <strong>Create</strong> button.</p>
</li>
<li>
<p>You can now send and manage messages as usual.</p>
</li>
<li>
<p>From the channels profile, <strong>share the QR code or invite link with others</strong>.</p>
</li>
</ul>
<p>Subscribers will receive your messages,
but they cannot send messages in your channel.
When subscribing, they will receive <strong>a few of the latest messages of the channel history</strong>.</p>
<p>You can see the <strong>view count</strong> beside each message.
Note that this only counts subscribers who have read receipts enabled,
so the real view count may be larger.</p>
<h3 id="how-many-subscribers-can-a-channel-have">
How many subscribers can a channel have? <a href="#how-many-subscribers-can-a-channel-have" class="anchor"></a>
</h3>
<p>Channels are designed for much larger audiences than <a href="#groups">groups</a>.</p>
<p>The practical limit depends on the used <a href="#relays">relay</a>,
so there is no single fixed number that applies everywhere.</p>
<p>For really large channels with several tens of thousands of subscribers,
we recommend using a <a href="#multiple-accounts">dedicated profile</a> for the channel
and checking whether the relay is suitable.</p>
<p>But dont be too hesitant: Delta Chat is designed to be relay-agnostic,
so you can change your relay at any point easily -
your existing subscribers will not even notice.
You only have to update the invite link you share with new subscribers in that case.</p>
<h2 id="calls">
Calls <a href="#calls" class="anchor"></a>
</h2>
<p>Delta Chat supports one-to-one <strong>audio calls</strong> and <strong>video calls</strong>.</p>
<p>Calls are supported on Desktop, Ubuntu Touch, iOS and Android 8 and newer.</p>
<h3 id="place-a-call">
Place a call <a href="#place-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>In a one-to-one chat, tap the 📞 <strong>call icon</strong>.</p>
</li>
<li>
<p>This opens a small menu
where you can choose whether to place an <strong>Audio Call</strong> or a <strong>Video Call</strong>.</p>
</li>
</ul>
<h3 id="accept-or-reject-a-call">
Accept or reject a call <a href="#accept-or-reject-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>When someone calls you,
Delta Chat shows an <strong>incoming call screen</strong> or notification.</p>
</li>
<li>
<p>Tap <strong>Accept</strong> to answer
or <strong>Decline</strong> to reject the call.</p>
</li>
</ul>
<h3 id="during-a-call">
During a call <a href="#during-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>You can <strong>mute</strong> your microphone.</p>
</li>
<li>
<p>You can <strong>enable or disable your camera</strong>.</p>
</li>
<li>
<p>On mobile, you can <strong>switch between front and back cameras</strong>.</p>
</li>
</ul>
<p>Depending on the device, you can also select the audio output or use picture-in-picture.
On desktop, the call is using a dedicated window
and you can continue using the main Delta Chat window as usual.</p>
<h3 id="missed-calls-and-notifications">
Missed calls and notifications <a href="#missed-calls-and-notifications" class="anchor"></a>
</h3>
<ul>
<li>
<p>If you do not answer, do not hear the ringing, or do not have your device at hand,
the call appears as a <strong>missed call</strong>.</p>
</li>
<li>
<p><strong>Only your accepted contacts</strong> can make your device ring.
Contact requests will appear as usual and will not ring.</p>
</li>
<li>
<p>At <strong>Settings → Notifications → Calls</strong>,
you can disable the special call ringing screen completely.
If you do so, you will not be disturbed by any ringing notification,
you can still pick up the call by tapping the incoming call message bubble in its chat.</p>
</li>
</ul>
<h2 id="webxdc">
@@ -1243,26 +1024,22 @@ 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 <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at 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">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>
<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>
</li>
<li>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</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>
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>
</li>
</ul>
@@ -1625,20 +1402,23 @@ can not be identified easily.</p>
</h3>
<p>The used <a href="#relays">relays</a> need to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#calls">call</a>
<p>The used <a href="#relays">relay</a> needs to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#experiments">call</a>
or use <a href="#webxdc">apps</a> together.</p>
<p>IP Addresses are needed for connectivity and efficiency.
Delta Chat neither persists nor exposes them.
Note that IP Addresses
are not like an address you give to a delivery service,
but typically less precise, often defining city or region only.</p>
They are neither persisted nor exposed.
Note that the IP Address
is not like a detailed address you give to a delivery service,
but much more coarse, often defining region or country only.</p>
<p>If you see your IP Address as a risk,
we recommend to use a VPN for the whole system.
Per-app options leave gaps across your system.
For example, tapping a link can expose IP Addresses to unknown parties, which is by far the larger risk.</p>
<p>As this is just how the internet and other messengers work by default,
we do not offer options here or ask upfront questions.</p>
<p>If you see your IP Address as a security or privacy risk,
we recommend to use a VPN, in combination with system lockdown mode.
Hunting down options in all apps on your system will leave gaps.
For example, tapping a link exposes IP Addresses to unknown parties and is the by far larger risk here.</p>
<h3 id="sealedsender">
@@ -1775,7 +1555,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/serverguide">beschreven op onze site</a>.
<a href="https://delta.chat/nl/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>
+136 -304
View File
@@ -2,18 +2,18 @@
<html lang="pl"><head><meta charset="UTF-8" /><meta name="viewport" content="initial-scale=1.0" /><link rel="stylesheet" href="../help.css" /></head><body><ul id="top">
<li><a href="#czym-jest-delta-chat">Czym jest Delta Chat?</a>
<ul>
<li><a href="#howtoe2ee">Jak znaleźć osoby do czatu?</a></li>
<li><a href="#dlaczego-czat-jest-oznaczony-jako-prośba">Dlaczego czat jest oznaczony jako „Prośba”?</a></li>
<li><a href="#jak-mogę-skontaktować-ze-sobą-dwóch-znajomych">Jak mogę skontaktować ze sobą dwóch znajomych?</a></li>
<li><a href="#howtoe2ee">How can I find people to chat with?</a></li>
<li><a href="#why-is-a-chat-marked-as-request">Why is a chat marked as “Request”?</a></li>
<li><a href="#how-can-i-put-two-of-my-friends-in-contact-with-each-other">How can I put two of my friends in contact with each other?</a></li>
<li><a href="#czy-delta-chat-obsługuje-obrazy-filmy-i-inne-załączniki">Czy Delta Chat obsługuje obrazy, filmy i inne załączniki?</a></li>
<li><a href="#multiple-accounts">Czym są profile? Jak mogę przełączać się między nimi?</a></li>
<li><a href="#kto-widzi-moje-zdjęcie-profilowe">Kto widzi moje zdjęcie profilowe?</a></li>
<li><a href="#signature">Czy w Delta Chat mogę ustawić biografię/status?</a></li>
<li><a href="#signature">Can I set a Bio/Status with Delta Chat?</a></li>
<li><a href="#co-oznacza-przypinanie-wyciszanie-i-archiwizowanie">Co oznacza przypinanie, wyciszanie i archiwizowanie?</a></li>
<li><a href="#save">Jak działają „Zapisane wiadomości”?</a></li>
<li><a href="#co-oznacza-zielona-kropka">Co oznacza zielona kropka?</a></li>
<li><a href="#co-oznaczają-znaczniki-wyświetlane-obok-wiadomości-wychodzących">Co oznaczają znaczniki wyświetlane obok wiadomości wychodzących?</a></li>
<li><a href="#edit">Poprawianie literówek i usuwanie wiadomości po wysłaniu</a></li>
<li><a href="#mediaquality">Jak obsługiwana jest jakość multimediów?</a></li>
<li><a href="#ephemeralmsgs">Jak działają znikające wiadomości?</a></li>
<li><a href="#delold">Co się stanie, jeśli włączę opcję „Usuń wiadomości z urządzenia”?</a></li>
<li><a href="#remove-account">How can I delete my chat profile?</a></li>
@@ -29,21 +29,6 @@
<li><a href="#how-many-members-can-participate-in-a-single-group">How many members can participate in a single group?</a></li>
</ul>
</li>
<li><a href="#channels">Channels</a>
<ul>
<li><a href="#subscribe-to-a-channel">Subscribe to a channel</a></li>
<li><a href="#create-a-channel">Create a channel</a></li>
<li><a href="#how-many-subscribers-can-a-channel-have">How many subscribers can a channel have?</a></li>
</ul>
</li>
<li><a href="#calls">Calls</a>
<ul>
<li><a href="#place-a-call">Place a call</a></li>
<li><a href="#accept-or-reject-a-call">Accept or reject a call</a></li>
<li><a href="#during-a-call">During a call</a></li>
<li><a href="#missed-calls-and-notifications">Missed calls and notifications</a></li>
</ul>
</li>
<li><a href="#webxdc">In-chat apps</a>
<ul>
<li><a href="#where-can-i-get-in-chat-apps">Where can I get in-chat apps?</a></li>
@@ -122,67 +107,87 @@
<p>Delta Chat to niezawodna, zdecentralizowana i bezpieczna aplikacja do błyskawicznego przesyłania wiadomości, dostępna na platformy mobilne i stacjonarne.</p>
<p>Natychmiastowe tworzenie <strong>prywatnych profili czatu</strong> z bezpiecznymi i interoperacyjnymi <a href="https://chatmail.at/relays">przekaźnikami chatmail</a>, które oferują natychmiastowe dostarczanie wiadomości oraz powiadomienia push dla urządzeń z systemem iOS i Android.</p>
<ul>
<li>
<p>Wszechstronna obsługa <a href="#multiple-accounts">wielu profili</a> i <a href="#multiclient">wielu urządzeń</a> na wszystkich platformach i między różnymi <a href="https://chatmail.at/clients">aplikacjami chatmail</a>.</p>
<p>Instant creation of <strong>private chat profiles</strong>
with secure and interoperable <a href="https://chatmail.at/relays">chatmail relays</a>
that offer instant message delivery, and Push Notifications for iOS and Android devices.</p>
</li>
<li>
<p>Interaktywne <a href="#webxdc">aplikacje do czatu</a> w grach i do współpracy</p>
<p>Pervasive <a href="#multiple-accounts">multi-profile</a> and
<a href="#multiclient">multi-device</a> support on all platforms
and between different <a href="https://chatmail.at/clients">chatmail apps</a>.</p>
</li>
<li>
<p><a href="#security-audits">Audytowne szyfrowanie end-to-end</a> zabezpieczające przed atakami sieciowymi i serwerowymi.</p>
<p>Interactive <a href="#webxdc">in-chat apps</a> for gaming and collaboration</p>
</li>
<li>
<p>Bezpłatne i otwartoźródłowe oprogramowanie zarówno po stronie aplikacji, jak i serwera, stworzone w oparciu o <a href="https://github.com/chatmail/core/blob/main/standards.md#standards-used-in-delta-chat">standardy internetowe</a>.</p>
<p><a href="#security-audits">Audited end-to-end encryption</a>
safe against network and server attacks.</p>
</li>
<li>
<p>Free and Open Source software, both app and server side,
built on <a href="https://github.com/chatmail/core/blob/main/standards.md#standards-used-in-delta-chat">Internet Standards</a>.</p>
</li>
</ul>
<h3 id="howtoe2ee">
Jak znaleźć osoby do czatu? <a href="#howtoe2ee" class="anchor"></a>
How can I find people to chat with? <a href="#howtoe2ee" class="anchor"></a>
</h3>
<p>Najpierw pamiętaj, że Delta Chat to prywatny komunikator. Nie ma możliwości publicznego wyszukiwania, sam decydujesz o swoich kontaktach.</p>
<p>First, note that Delta Chat is a private messenger.
There is no public discovery, <em>you</em> decide about your contacts.</p>
<ul>
<li>
<p>Jeśli jesteś twarzą w twarz ze znajomym lub rodziną, dotknij ikony <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> <strong>kodu QR</strong> na ekranie głównym.
Poproś partnera czatu o <strong>zeskanowanie</strong> obrazu QR za pomocą aplikacji Delta Chat.</p>
<p>If you are <strong>face to face</strong> with your friend or family,
tap the <strong>QR Code</strong> icon <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" />
on the main screen.<br />
Ask your chat partner to <strong>scan</strong> the QR image
with their Delta Chat app.</p>
</li>
<li>
<p>Aby skonfigurować kontakt <strong>zdalny</strong>, na tym samym ekranie naciśnij „Kopiuj” lub „Udostępnij” i wyślij <strong>link zaproszenia</strong> za pośrednictwem innego prywatnego czatu.</p>
<p>For a <strong>remote</strong> contact setup,
from the same screen,
click “Copy” or “Share” and send the <strong>invite link</strong>
through another private chat.</p>
</li>
</ul>
<p>Poczekaj, aż połączenie zostanie nawiązane.</p>
<p>Now wait while connection gets established.</p>
<ul>
<li>
<p>Jeśli obie strony są online, wkrótce zobaczą czat i będą mogły bezpiecznie wysyłać wiadomości.</p>
<p>If both sides are online, they will soon see a chat
and can start messaging securely.</p>
</li>
<li>
<p>Jeśli jedna ze stron jest offline lub ma słaby zasięg, możliwość czatowania zostanie wstrzymana do czasu przywrócenia połączenia.</p>
<p>If one side is offline or in bad network,
the ability to chat is delayed until connectivity is restored.</p>
</li>
</ul>
<p>Gratulacje! Teraz będziesz automatycznie korzystać z <a href="#e2ee">szyfrowania typu end-to-end</a> dla tego kontaktu. Jeśli dodacie się nawzajem do <a href="#groups">grup</a>, szyfrowanie typu end-to-end zostanie nawiązane między wszystkimi członkami.</p>
<p>Congratulations!
You now will automatically use <a href="#e2ee">end-to-end encryption</a> with this contact.
If you add each other to <a href="#groups">groups</a>, end-to-end encryption will be established among all members.</p>
<h3 id="dlaczego-czat-jest-oznaczony-jako-prośba">
<h3 id="why-is-a-chat-marked-as-request">
Dlaczego czat jest oznaczony jako „Prośba”? <a href="#dlaczego-czat-jest-oznaczony-jako-prośba" class="anchor"></a>
Why is a chat marked as “Request”? <a href="#why-is-a-chat-marked-as-request" class="anchor"></a>
</h3>
<p>Ponieważ jest to prywatny komunikator, tylko znajomi i rodzina, którym <a href="#howtoe2ee">udostępnisz swój kod QR lub link zaproszenia</a>, mogą do ciebie pisać.</p>
<p>As being a private messenger,
only friends and family you <a href="#howtoe2ee">share your QR code or invite link with</a> can write to you.</p>
<p>Twoi znajomi mogą udostępniać twoje dane kontaktowe innym znajomym, co jest oznaczone jako <b style="border: 1px solid currentColor; padding: 0 3px; font-size:90%">Prośba</b></p>
<p>Your friends may share your contact with other friends,
this appears as <b style="border: 1px solid currentColor; padding: 0 3px; font-size:90%">Request</b></p>
<ul>
<li>
@@ -196,17 +201,32 @@ Poproś partnera czatu o <strong>zeskanowanie</strong> obrazu QR za pomocą apli
</li>
</ul>
<h3 id="jak-mogę-skontaktować-ze-sobą-dwóch-znajomych">
<h3 id="how-can-i-put-two-of-my-friends-in-contact-with-each-other">
Jak mogę skontaktować ze sobą dwóch znajomych? <a href="#jak-mogę-skontaktować-ze-sobą-dwóch-znajomych" class="anchor"></a>
How can I put two of my friends in contact with each other? <a href="#how-can-i-put-two-of-my-friends-in-contact-with-each-other" class="anchor"></a>
</h3>
<p>Dołącz pierwszy kontakt do czatu drugiego, używając przycisku <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>DołączaniaKontakt</strong>. Możesz również dodać krótką wiadomość powitalną.</p>
<p>Attach the first contact to the chat of the second using <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attachment ButtonContact</strong>.
You can also add a little introduction message.</p>
<p>Drugi kontakt otrzyma wtedy <strong>kartkę</strong> i może ją nacisnąć, aby rozpocząć czat z pierwszym kontaktem.</p>
<p>The second contact will receive a <strong>card</strong> then
and can tap it to start chatting with the first contact.</p>
<h3 id="czy-delta-chat-obsługuje-obrazy-filmy-i-inne-załączniki">
Czy Delta Chat obsługuje obrazy, filmy i inne załączniki? <a href="#czy-delta-chat-obsługuje-obrazy-filmy-i-inne-załączniki" class="anchor"></a>
</h3>
<p>Tak. Images, videos, files, voice messages etc. can be sent using the <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attachment-</strong>
or <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Voice Message</strong> buttons</p>
<p>Ze względu na wydajność obrazy są domyślnie optymalizowane i wysyłane w mniejszym rozmiarze, ale można je wysłać jako „plik”, aby zachować oryginał.</p>
<h3 id="multiple-accounts">
@@ -216,13 +236,15 @@ Poproś partnera czatu o <strong>zeskanowanie</strong> obrazu QR za pomocą apli
</h3>
<p>Profil składa się z <strong>nazwy, zdjęcia</strong> i dodatkowych informacji służących do szyfrowania wiadomości. Profil jest dostępny tylko na twoim urządzeniu (urządzeniach) i korzysta z serwera wyłącznie do przekazywania wiadomości.</p>
<p>A profile is <strong>a name, a picture</strong> and some additional information for encrypting messages.
A profile lives on your device(s) only
and uses the server only to relay messages.</p>
<p>Podczas pierwszej instalacji Delta Chat tworzony jest pierwszy profil.</p>
<p>Później możesz dotknąć swojego zdjęcia profilowego w lewym górnym rogu, aby <strong>Dodać profile</strong> lub <strong>Przełączyć profile</strong>.</p>
<p>Możesz używać osobnych profili dla aktywności politycznych, rodzinnych lub zawodowych.</p>
<p>You may want to use separate profiles for political, family or work related activities.</p>
<p>Możesz także dowiedzieć się, <a href="#multiclient">jak używać tego samego profilu na wielu urządzeniach</a>.</p>
@@ -234,19 +256,22 @@ Poproś partnera czatu o <strong>zeskanowanie</strong> obrazu QR za pomocą apli
</h3>
<p>Możesz dodać zdjęcie profilowe w swoich ustawieniach. Jeśli napiszesz do swoich kontaktów lub dodasz je za pomocą kodu QR, automatycznie zobaczą je jako twoje zdjęcie profilowe.</p>
<p>Możesz dodać zdjęcie profilowe w swoich ustawieniach. Jeśli napiszesz do swoich kontaktów lub dodasz je za pomocą kodu QR, automatycznie zobaczą je jako Twoje zdjęcie profilowe.</p>
<p>Ze względów prywatności nikt nie widzi twojego zdjęcia profilowego, dopóki nie napiszesz do niego wiadomości.</p>
<p>Ze względów prywatności nikt nie widzi Twojego zdjęcia profilowego, dopóki nie napiszesz do niego wiadomości.</p>
<h3 id="signature">
Czy w Delta Chat mogę ustawić biografię/status? <a href="#signature" class="anchor"></a>
Can I set a Bio/Status with Delta Chat? <a href="#signature" class="anchor"></a>
</h3>
<p>Tak, możesz to zrobić w <strong>Ustawieniach → Profil → Biografia</strong>. Po wysłaniu wiadomości do kontaktu zostanie ona wyświetlona, gdy będzie on przeglądał twoje dane kontaktowe.</p>
<p>Yes,
you can do so under <strong>Settings → Profile → Bio</strong>.
Once you sent a message to a contact,
they will see it when they view your contact details.</p>
<h3 id="co-oznacza-przypinanie-wyciszanie-i-archiwizowanie">
@@ -266,7 +291,7 @@ Poproś partnera czatu o <strong>zeskanowanie</strong> obrazu QR za pomocą apli
<p><strong>Wycisz czaty</strong>, jeśli nie chcesz otrzymywać z nich powiadomień. Wyciszone czaty pozostają na swoim miejscu i możesz też przypiąć wyciszony czat.</p>
</li>
<li>
<p><strong>Archiwizuj czaty</strong>, jeśli nie chcesz ich już widzieć na liście czatów. Pozostają dostępne nad listą czatów lub poprzez wyszukiwanie i są oznaczone jako <b style="border: 1px solid currentColor; padding: 0 3px; font-size:90%">Zarchiwizowane</b></p>
<p><strong>Archiwizuj czaty</strong>, jeśli nie chcesz ich już widzieć na liście czatów. Zarchiwizowane czaty pozostają dostępne nad listą czatów lub poprzez wyszukiwanie.</p>
</li>
<li>
<p>Gdy zarchiwizowany czat otrzyma nową wiadomość, o ile nie zostanie wyciszony, <strong>wyskoczy z archiwum</strong> i wróci na twoją listę czatów.
@@ -297,7 +322,7 @@ Poproś partnera czatu o <strong>zeskanowanie</strong> obrazu QR za pomocą apli
<p>Później otwórz czat „Zapisane wiadomości” — zobaczysz tam zapisane wiadomości. Naciskając <img style="vertical-align:middle; width:1.2em; margin:1px" src="../go-to-original.png" alt="ikona strzałki w prawo" />, możesz wrócić do oryginalnej wiadomości w oryginalnym czacie</p>
</li>
<li>
<p>Na koniec możesz również użyć „Zapisanych wiadomości”, aby robić <strong>osobiste notatki</strong> — otwórz czat, wpisz coś, dodaj zdjęcie lub wiadomość głosową itp.</p>
<p>Na koniec możesz również użyć „Zapisz wiadomości”, aby robić <strong>osobiste notatki</strong> — otwórz czat, wpisz coś, dodaj zdjęcie lub wiadomość głosową itp.</p>
</li>
<li>
<p>Ponieważ „Zapisane wiadomości” są zsynchronizowane, mogą być bardzo przydatne do przesyłania danych między urządzeniami</p>
@@ -314,9 +339,13 @@ Poproś partnera czatu o <strong>zeskanowanie</strong> obrazu QR za pomocą apli
</h3>
<p>Czasami można zobaczyć <strong>zieloną kropkę</strong> <img style="vertical-align:middle; width:1.2em; margin:1px" src="../green-dot.png" alt="" /> obok awatara kontaktu. Oznacza to, że był on <strong>niedawno widziany przez ciebie</strong> w ciągu ostatnich 10 minut, np. wysłał ci wiadomość lub potwierdzenie odczytu.</p>
<p>You can sometimes see a <strong>green dot</strong> <img style="vertical-align:middle; width:1.2em; margin:1px" src="../green-dot.png" alt="" />
next to the avatar of a contact.
It means they were <strong>recently seen by you</strong> in the last 10 minutes,
e.g. because they messaged you or sent a read receipt.</p>
<p>Nie jest to więc status online w czasie rzeczywistym i inni również nie zawsze zobaczą, że jesteś „online”.</p>
<p>So this is not a real time online status
and others will as well not always see that you are “online”.</p>
<h3 id="co-oznaczają-znaczniki-wyświetlane-obok-wiadomości-wychodzących">
@@ -328,16 +357,19 @@ Poproś partnera czatu o <strong>zeskanowanie</strong> obrazu QR za pomocą apli
<ul>
<li>
<p><strong>Jeden znacznik</strong> <img style="vertical-align:middle; width:1.5em; margin:1px" src="../tick1.png" alt="" /> oznacza, że wiadomość została pomyślnie wysłana do <a href="#relays">przekaźnika</a>.</p>
<p><strong>One tick</strong> <img style="vertical-align:middle; width:1.5em; margin:1px" src="../tick1.png" alt="" />
means that the message was sent successfully to the <a href="#relays">relay</a>.</p>
</li>
<li>
<p><strong>Dwa znaczniki</strong> <img style="vertical-align:middle; width:1.5em; margin:1px" src="../tick2.png" alt="" /> oznaczają, że twój kontakt przeczytał wiadomość.</p>
<p><strong>Two ticks</strong> <img style="vertical-align:middle; width:1.5em; margin:1px" src="../tick2.png" alt="" />
indicate your contact has read the message.</p>
</li>
</ul>
<p>W <a href="#groups">grupach</a> drugi znacznik oznacza, że co najmniej jeden członek potwierdził przeczytanie wiadomości.</p>
<p>In <a href="#groups">groups</a> the second tick means that at least one member has reported back having read the message.</p>
<p>Drugi znacznik pojawi się tylko wtedy, gdy ty i jeden z odbiorców, którzy przeczytali wiadomość, macie włączoną opcję <strong>Ustawienia → Czaty → Potwierdzenie odczytu</strong>.</p>
<p>You will only get the second tick if both you and one of the recipients who read the message
has <strong>Settings → Chats → Read Receipts</strong> enabled.</p>
<h3 id="edit">
@@ -360,28 +392,6 @@ Poproś partnera czatu o <strong>zeskanowanie</strong> obrazu QR za pomocą apli
<p>Pamiętaj, że oryginalną wiadomość nadal mogą otrzymać członkowie czatu, którzy mogli już odpowiedzieć, przesłać dalej, zapisać, wykonać zrzut ekranu lub w inny sposób skopiować wiadomość.</p>
<h3 id="mediaquality">
Jak obsługiwana jest jakość multimediów? <a href="#mediaquality" class="anchor"></a>
</h3>
<p>Obrazy, filmy, pliki, wiadomości głosowe itp. można wysyłać za pomocą przycisków: <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Załącz</strong> lub <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /><strong>Wiadomość głosowa</strong>.</p>
<ul>
<li>
<p>Domyślnie kompresja zapewnia <strong>szybką i wydajną dostawę</strong>, respektując limity danych i pamięci wszystkich użytkowników. Jest to idealne rozwiązanie do codziennej komunikacji.</p>
</li>
<li>
<p>W regionach o słabszej łączności można wybrać wyższą kompresję w <strong>Ustawieniach → Czaty → Jakość mediów wychodzących</strong>.</p>
</li>
<li>
<p>Jeśli chcesz wysłać multimedia w <strong>oryginalnej jakości</strong>, użyj w czacie opcji <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Załącz → Plik</strong>. Używaj tej metody oszczędnie, ponieważ wysyłanie oryginalnych plików znacznie zwiększy zużycie danych przez ciebie i wszystkich odbiorców na czacie.</p>
</li>
</ul>
<h3 id="ephemeralmsgs">
@@ -392,11 +402,21 @@ Poproś partnera czatu o <strong>zeskanowanie</strong> obrazu QR za pomocą apli
<p>Możesz włączyć „znikające wiadomości” w ustawieniach czatu, w prawym górnym rogu okna czatu, wybierając przedział czasu od 5 minut do 1 roku.</p>
<p>Dopóki ustawienie nie zostanie ponownie wyłączone, aplikacja Delta Chat u każdego członka czatu zajmie się usuwaniem wiadomości po wybranym okresie. Przedział czasu rozpoczyna się w momencie, gdy odbiorca po raz pierwszy zobaczy wiadomość w Delta Chat. Wiadomości są usuwane zarówno na serwerze, jak i w samej aplikacji.</p>
<p>Until the setting is turned off again,
each chat members Delta Chat app takes care
of deleting the messages
after the selected time span.
The time span begins
when the receiver first sees the message in Delta Chat.
The messages are deleted both,
on the servers,
and in the apps itself.</p>
<p>Pamiętaj, że na znikających wiadomościach możesz polegać tylko wtedy, gdy ufasz swoim partnerom czatu; złośliwi partnerzy czatu mogą robić zdjęcia lub w inny sposób zapisywać, kopiować lub przesyłać dalej wiadomości przed usunięciem.</p>
<p>Poza tym, jeśli jeden z uczestników czatu odinstaluje aplikację Delta Chat, usunięcie (i tak zaszyfrowanych) wiadomości z jego serwera może potrwać dłużej.</p>
<p>Apart from that,
if one chat partner uninstalls Delta Chat,
the (anyway encrypted) messages may take longer to get deleted from their server.</p>
<h3 id="delold">
@@ -406,9 +426,9 @@ Poproś partnera czatu o <strong>zeskanowanie</strong> obrazu QR za pomocą apli
</h3>
<p>Jeśli chcesz zaoszczędzić miejsce na swoim urządzeniu, możesz wybrać opcję automatycznego usuwania starych wiadomości.</p>
<p>Jeśli chcesz zaoszczędzić miejsce na urządzeniu, możesz wybrać opcję automatycznego usuwania starych wiadomości.</p>
<p>Aby ją włączyć, przejdź do <strong>Ustawienia → Czaty → Usuń wiadomości z urządzenia</strong> . Możesz ustawić przedział czasowy pomiędzy „po 1 godzinie” a „po 1 roku”; w ten sposób <em>wszystkie</em> wiadomości zostaną usunięte z urządzenia, gdy tylko staną się starsze.</p>
<p>Aby ją włączyć, przejdź do „Usuń wiadomości z urządzenia” w ustawieniach w sekcji „Czaty i media”. Możesz ustawić przedział czasowy pomiędzy „po 1 godzinie” a „po 1 roku”; w ten sposób <em>wszystkie</em> wiadomości zostaną usunięte z urządzenia, gdy tylko staną się starsze.</p>
<h3 id="remove-account">
@@ -460,10 +480,10 @@ and <a href="#edit">delete their own messages</a> from all members devices.</
<p>Wybierz <strong>Nowy czat</strong>, a następnie <strong>Nowa grupa</strong> z menu w prawym górnym rogu lub naciśnij odpowiedni przycisk na Androidzie / iOS.</p>
</li>
<li>
<p>Na następnym ekranie wybierz <strong>członków grupy</strong> i zdefiniuj <strong>nazwę grupy</strong>. Możesz też wybrać <strong>awatar grupy</strong>.</p>
<p>Na następnym ekranie wybierz <strong>członków grupy</strong> i zdefiniuj <strong>nazwę grupy</strong>. Możesz też wybrać awatar <strong>grupy</strong>.</p>
</li>
<li>
<p>Gdy tylko napiszesz <strong>pierwszą wiadomość</strong> w grupie, wszyscy członkowie zostaną poinformowani o nowej grupie i będą mogli odpowiadać w grupie (dopóki nie napiszesz wiadomości w grupie, grupa będzie niewidoczna dla członków).</p>
<p>Zaraz po napisaniu pierwszej wiadomości w grupie wszyscy członkowie zostaną poinformowani o nowej grupie i mogą odpowiedzieć w grupie (jeżeli nie napiszesz wiadomości w grupie, grupa jest niewidoczna dla członków).</p>
</li>
</ul>
@@ -506,7 +526,8 @@ However, since groups are <a href="#groups">meant for trusted people</a>, avoid
</h3>
<p>Ponieważ nie jesteś członkiem grupy, nie możesz dodać siebie ponownie. Jednak nie ma problemu, po prostu poproś dowolnego członka grupy na normalnym czacie, aby dodał cię ponownie.</p>
<p>Ponieważ nie jesteś członkiem grupy, nie możesz dodać siebie ponownie.
Jednak nie ma problemu, po prostu poproś dowolnego członka grupy na normalnym czacie, aby dodał cię ponownie.</p>
<h3 id="nie-chcę-już-otrzymywać-wiadomości-od-grupy">
@@ -521,7 +542,8 @@ However, since groups are <a href="#groups">meant for trusted people</a>, avoid
Jeśli później będziesz chciał ponownie dołączyć do grupy, poproś innego członka grupy, aby dodał cię do grupy.</li>
</ul>
<p>Alternatywnie możesz też „Wyłączyć powiadomienia” dla grupy, dzięki temu otrzymasz wszystkie wiadomości i nadal będziesz mógł pisać, ale nie będziesz już powiadamiany o żadnych nowych wiadomościach.</p>
<p>Alternatywnie możesz też „Wyłączyć powiadomienia” dla grupy dzięki temu otrzymasz wszystkie wiadomości i
nadal będziesz mógł pisać, ale nie będziesz już powiadamiany o żadnych nowych wiadomościach.</p>
<h3 id="cloning-a-group">
@@ -562,197 +584,6 @@ but more than 150 is not recommended.</p>
where Delta Chat is a private messenger for chatting with <a href="#groups">equal rights</a>.
See <a href="https://en.wikipedia.org/wiki/Dunbar%27s_number">Dunbars number</a> for more insights.</p>
<h2 id="channels">
Channels <a href="#channels" class="anchor"></a>
</h2>
<p>Channels are a one-to-many tool for broadcasting messages.</p>
<h3 id="subscribe-to-a-channel">
Subscribe to a channel <a href="#subscribe-to-a-channel" class="anchor"></a>
</h3>
<ul>
<li>Scan the <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> <strong>QR code</strong>
or tap the <strong>invite link</strong> you got from the channel owner.</li>
</ul>
<p>Thats all!
You will receive a few of the messages from the channel history
and, from that point on, all new messages from the channel.</p>
<p><strong>Dont worry,</strong> if that does not happen immediately.
Once the channel owner comes online, your join request will be processed.</p>
<p>As all of Delta Chat, also Channels are private and decentralized,
there is no public discovery.</p>
<p>Other channel subscribers will not see that you subscribed and cannot message you.
The channel owner, however, can message you.
They will also see that you read a message unless you have read receipts disabled.</p>
<p>If you do not want to share your main profile,
you can also create a <a href="#multiple-accounts">dedicated profile</a> for joining a channel.</p>
<h3 id="create-a-channel">
Create a channel <a href="#create-a-channel" class="anchor"></a>
</h3>
<ul>
<li>
<p>Tap <strong>New Chat</strong> and choose <strong>New Channel</strong>.</p>
</li>
<li>
<p>Enter a <strong>name</strong>, optionally set an <strong>image</strong> and <strong>description</strong>, and hit the <strong>Create</strong> button.</p>
</li>
<li>
<p>You can now send and manage messages as usual.</p>
</li>
<li>
<p>From the channels profile, <strong>share the QR code or invite link with others</strong>.</p>
</li>
</ul>
<p>Subscribers will receive your messages,
but they cannot send messages in your channel.
When subscribing, they will receive <strong>a few of the latest messages of the channel history</strong>.</p>
<p>You can see the <strong>view count</strong> beside each message.
Note that this only counts subscribers who have read receipts enabled,
so the real view count may be larger.</p>
<h3 id="how-many-subscribers-can-a-channel-have">
How many subscribers can a channel have? <a href="#how-many-subscribers-can-a-channel-have" class="anchor"></a>
</h3>
<p>Channels are designed for much larger audiences than <a href="#groups">groups</a>.</p>
<p>The practical limit depends on the used <a href="#relays">relay</a>,
so there is no single fixed number that applies everywhere.</p>
<p>For really large channels with several tens of thousands of subscribers,
we recommend using a <a href="#multiple-accounts">dedicated profile</a> for the channel
and checking whether the relay is suitable.</p>
<p>But dont be too hesitant: Delta Chat is designed to be relay-agnostic,
so you can change your relay at any point easily -
your existing subscribers will not even notice.
You only have to update the invite link you share with new subscribers in that case.</p>
<h2 id="calls">
Calls <a href="#calls" class="anchor"></a>
</h2>
<p>Delta Chat supports one-to-one <strong>audio calls</strong> and <strong>video calls</strong>.</p>
<p>Calls are supported on Desktop, Ubuntu Touch, iOS and Android 8 and newer.</p>
<h3 id="place-a-call">
Place a call <a href="#place-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>In a one-to-one chat, tap the 📞 <strong>call icon</strong>.</p>
</li>
<li>
<p>This opens a small menu
where you can choose whether to place an <strong>Audio Call</strong> or a <strong>Video Call</strong>.</p>
</li>
</ul>
<h3 id="accept-or-reject-a-call">
Accept or reject a call <a href="#accept-or-reject-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>When someone calls you,
Delta Chat shows an <strong>incoming call screen</strong> or notification.</p>
</li>
<li>
<p>Tap <strong>Accept</strong> to answer
or <strong>Decline</strong> to reject the call.</p>
</li>
</ul>
<h3 id="during-a-call">
During a call <a href="#during-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>You can <strong>mute</strong> your microphone.</p>
</li>
<li>
<p>You can <strong>enable or disable your camera</strong>.</p>
</li>
<li>
<p>On mobile, you can <strong>switch between front and back cameras</strong>.</p>
</li>
</ul>
<p>Depending on the device, you can also select the audio output or use picture-in-picture.
On desktop, the call is using a dedicated window
and you can continue using the main Delta Chat window as usual.</p>
<h3 id="missed-calls-and-notifications">
Missed calls and notifications <a href="#missed-calls-and-notifications" class="anchor"></a>
</h3>
<ul>
<li>
<p>If you do not answer, do not hear the ringing, or do not have your device at hand,
the call appears as a <strong>missed call</strong>.</p>
</li>
<li>
<p><strong>Only your accepted contacts</strong> can make your device ring.
Contact requests will appear as usual and will not ring.</p>
</li>
<li>
<p>At <strong>Settings → Notifications → Calls</strong>,
you can disable the special call ringing screen completely.
If you do so, you will not be disturbed by any ringing notification,
you can still pick up the call by tapping the incoming call message bubble in its chat.</p>
</li>
</ul>
<h2 id="webxdc">
@@ -1005,7 +836,8 @@ Welcome to the power of the interoperable chatmail relay network :)</p>
<p>Sprawdź dokładnie, czy oba urządzenia są w tym <strong>samym Wi-Fi lub tej samej sieci</strong></p>
</li>
<li>
<p>Na <strong>Windowsie</strong>, przejdź do Panel sterowania / Sieć i internet” i upewnij się, że <strong>Sieć prywatna</strong> jest wybrana jako Typ profilu sieci” (po przeniesieniu możesz wrócić do pierwotnej wartości)</p>
<p>Na <strong>Windowsie</strong>, przejdź do Panel sterowania / Sieć i internet” i upewnij się, że <strong>Sieć prywatna</strong> jest wybrana jako Typ profilu sieci”
(po przeniesieniu możesz wrócić do pierwotnej wartości)</p>
</li>
<li>
<p>W systemie <strong>iOS</strong> upewnij się, że jest przydzielony dostęp do opcji „Ustawienia » Aplikacje » Delta Chat » <strong>Sieć lokalna</strong></p>
@@ -1049,14 +881,15 @@ Welcome to the power of the interoperable chatmail relay network :)</p>
<ul>
<li>
<p>Na starym urządzeniu przejdź do <strong>Ustawienia → Czaty → Eksport kopii zapasowej</strong>. Wprowadź swój PIN odblokowania ekranu, wzór lub hasło. Następnie możesz nacisnąć „Utwórz kopię”. Spowoduje to zapisanie pliku kopii zapasowej na urządzeniu. Teraz musisz jakoś przenieść go na inne urządzenie.</p>
<p>Na starym urządzeniu przejdź do <strong>Ustawienia → Czaty i media → Eksport kopii zapasowej</strong>. Wprowadź swój PIN odblokowania ekranu, wzór lub hasło. Następnie możesz nacisnąć „Utwórz kopię”. Spowoduje to zapisanie pliku kopii zapasowej na urządzeniu. Teraz musisz jakoś przenieść go na inne urządzenie.</p>
</li>
<li>
<p>Na nowym urządzeniu wybierz: <strong>Mam już profil → Przywróć z kopii zapasowej</strong>. Jeśli korzystasz z iOS i napotykasz trudności, może <a href="https://support.delta.chat/t/import-backup-to-ios/1628">ten poradnik</a> ci pomoże.</p>
<p>Na nowym urządzeniu, na ekranie logowania, zamiast logować się na swoje konto e-mail, wybierz <strong>Przywróć z kopii zapasowej</strong>. Po zaimportowaniu Twoje rozmowy, klucze szyfrujące i multimedia powinny zostać skopiowane na nowe urządzenie.
Jeśli korzystasz z iOS i napotykasz trudności, może <a href="https://support.delta.chat/t/import-backup-to-ios/1628">ten poradnik</a> Ci pomoże.</p>
</li>
</ul>
<p>Jesteś teraz zsynchronizowany i w komunikacji ze swoimi partnerami możesz używać obu urządzeń do wysyłania i odbierania wiadomości zaszyfrowanych metodą end-to-end.</p>
<p>Jesteś teraz zsynchronizowany i możesz używać obu urządzeń do wysyłania i odbierania wiadomości zaszyfrowanych end-to-end w komunikacji ze swoimi partnerami.</p>
<h3 id="czy-są-jakieś-plany-wprowadzenia-klienta-web-delta-chat">
@@ -1113,26 +946,22 @@ 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 <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at 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">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>
<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>
</li>
<li>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</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>
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>
</li>
</ul>
@@ -1443,20 +1272,23 @@ can not be identified easily.</p>
</h3>
<p>The used <a href="#relays">relays</a> need to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#calls">call</a>
<p>The used <a href="#relays">relay</a> needs to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#experiments">call</a>
or use <a href="#webxdc">apps</a> together.</p>
<p>IP Addresses are needed for connectivity and efficiency.
Delta Chat neither persists nor exposes them.
Note that IP Addresses
are not like an address you give to a delivery service,
but typically less precise, often defining city or region only.</p>
They are neither persisted nor exposed.
Note that the IP Address
is not like a detailed address you give to a delivery service,
but much more coarse, often defining region or country only.</p>
<p>If you see your IP Address as a risk,
we recommend to use a VPN for the whole system.
Per-app options leave gaps across your system.
For example, tapping a link can expose IP Addresses to unknown parties, which is by far the larger risk.</p>
<p>As this is just how the internet and other messengers work by default,
we do not offer options here or ask upfront questions.</p>
<p>If you see your IP Address as a security or privacy risk,
we recommend to use a VPN, in combination with system lockdown mode.
Hunting down options in all apps on your system will leave gaps.
For example, tapping a link exposes IP Addresses to unknown parties and is the by far larger risk here.</p>
<h3 id="sealedsender">
@@ -1564,7 +1396,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/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/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>
</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>
+35 -255
View File
@@ -5,6 +5,7 @@
<li><a href="#howtoe2ee">Como posso encontrar pessoas para conversar?</a></li>
<li><a href="#por-que-um-chat-está-marcado-como-solicitação">Por que um chat está marcado como “Solicitação”?</a></li>
<li><a href="#como-posso-colocar-dois-dos-meus-amigos-em-contato-um-com-o-outro">Como posso colocar dois dos meus amigos em contato um com o outro?</a></li>
<li><a href="#dá-para-mandar-imagens-vídeos-e-outros-anexos-pelo-delta-chat">Dá para mandar imagens, vídeos e outros anexos pelo Delta Chat?</a></li>
<li><a href="#multiple-accounts">O que são perfis? Como posso alternar entre eles?</a></li>
<li><a href="#quem-consegue-ver-a-imagem-do-meu-perfil">Quem consegue ver a imagem do meu perfil?</a></li>
<li><a href="#signature">Posso definir uma Bio/Assinatura com o Delta Chat?</a></li>
@@ -13,7 +14,6 @@
<li><a href="#o-que-significa-o-ponto-verde">O que significa o ponto verde?</a></li>
<li><a href="#o-que-significam-os-carrapatos-mostrados-ao-lado-das-mensagens-de-saída">O que significam os carrapatos mostrados ao lado das mensagens de saída?</a></li>
<li><a href="#edit">Corrigir erros de digitação e excluir mensagens após o envio</a></li>
<li><a href="#mediaquality">How is media quality handled?</a></li>
<li><a href="#ephemeralmsgs">Como funcionam as mensagens efêmeras?</a></li>
<li><a href="#delold">O que acontece se eu ativar a opção “Apagar mensagens do dispositivo”?</a></li>
<li><a href="#remove-account">Como posso excluir minha conta?</a></li>
@@ -29,21 +29,6 @@
<li><a href="#quantos-membros-podem-participar-em-um-único-grupo">Quantos membros podem participar em um único grupo?</a></li>
</ul>
</li>
<li><a href="#channels">Channels</a>
<ul>
<li><a href="#subscribe-to-a-channel">Subscribe to a channel</a></li>
<li><a href="#create-a-channel">Create a channel</a></li>
<li><a href="#how-many-subscribers-can-a-channel-have">How many subscribers can a channel have?</a></li>
</ul>
</li>
<li><a href="#calls">Calls</a>
<ul>
<li><a href="#place-a-call">Place a call</a></li>
<li><a href="#accept-or-reject-a-call">Accept or reject a call</a></li>
<li><a href="#during-a-call">During a call</a></li>
<li><a href="#missed-calls-and-notifications">Missed calls and notifications</a></li>
</ul>
</li>
<li><a href="#webxdc">Aplicativos embutidos</a>
<ul>
<li><a href="#onde-posso-obter-aplicativos-embutidos">Onde posso obter aplicativos embutidos?</a></li>
@@ -233,6 +218,19 @@ Você também pode adicionar uma pequena mensagem de apresentação.</p>
<p>O segundo contato receberá um <strong>cartão</strong>
e poderá tocar nele para começar a conversar com o primeiro contato.</p>
<h3 id="dá-para-mandar-imagens-vídeos-e-outros-anexos-pelo-delta-chat">
Dá para mandar imagens, vídeos e outros anexos pelo Delta Chat? <a href="#dá-para-mandar-imagens-vídeos-e-outros-anexos-pelo-delta-chat" class="anchor"></a>
</h3>
<p>Sim. Imagens, vídeos, arquivos, mensagens de voz etc. podem ser enviados usando os botões de<img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Anexo-</strong>
ou <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Mensagem de Voz</strong></p>
<p>Para fins de desempenho, as imagens são otimizadas e enviadas em um tamanho menor por padrão, mas você pode enviá-las como um “arquivo” para preservar o original.</p>
<h3 id="multiple-accounts">
@@ -411,32 +409,6 @@ Não são enviadas notificações e não há limite de tempo.</p>
<p>Observe que a mensagem original ainda pode ser recebida pelos membros do conversa
que podem ter respondido, encaminhado, salvo, capturado a tela ou copiado a mensagem.</p>
<h3 id="mediaquality">
How is media quality handled? <a href="#mediaquality" class="anchor"></a>
</h3>
<p>Imagens, vídeos, arquivos, mensagens de voz etc. podem ser enviados usando os botões de<img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Anexo-</strong>
ou <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Mensagem de Voz</strong></p>
<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>
</li>
<li>
<p>In regions with worse connectivity,
you can choose higher compression at <strong>Settings → Chats → Outgoing Media Quality</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>
</li>
</ul>
<h3 id="ephemeralmsgs">
@@ -637,197 +609,6 @@ mas não é recomendável mais de 150.</p>
mas o Delta Chat é um mensageiro privado para conversar com <a href="#groups">direitos iguais</a>.
Consulte o <a href="https://pt.wikipedia.org/wiki/N%C3%BAmero_de_Dunbar">Número de Dunbar</a> para obter mais informações.</p>
<h2 id="channels">
Channels <a href="#channels" class="anchor"></a>
</h2>
<p>Channels are a one-to-many tool for broadcasting messages.</p>
<h3 id="subscribe-to-a-channel">
Subscribe to a channel <a href="#subscribe-to-a-channel" class="anchor"></a>
</h3>
<ul>
<li>Scan the <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> <strong>QR code</strong>
or tap the <strong>invite link</strong> you got from the channel owner.</li>
</ul>
<p>Thats all!
You will receive a few of the messages from the channel history
and, from that point on, all new messages from the channel.</p>
<p><strong>Dont worry,</strong> if that does not happen immediately.
Once the channel owner comes online, your join request will be processed.</p>
<p>As all of Delta Chat, also Channels are private and decentralized,
there is no public discovery.</p>
<p>Other channel subscribers will not see that you subscribed and cannot message you.
The channel owner, however, can message you.
They will also see that you read a message unless you have read receipts disabled.</p>
<p>If you do not want to share your main profile,
you can also create a <a href="#multiple-accounts">dedicated profile</a> for joining a channel.</p>
<h3 id="create-a-channel">
Create a channel <a href="#create-a-channel" class="anchor"></a>
</h3>
<ul>
<li>
<p>Tap <strong>New Chat</strong> and choose <strong>New Channel</strong>.</p>
</li>
<li>
<p>Enter a <strong>name</strong>, optionally set an <strong>image</strong> and <strong>description</strong>, and hit the <strong>Create</strong> button.</p>
</li>
<li>
<p>You can now send and manage messages as usual.</p>
</li>
<li>
<p>From the channels profile, <strong>share the QR code or invite link with others</strong>.</p>
</li>
</ul>
<p>Subscribers will receive your messages,
but they cannot send messages in your channel.
When subscribing, they will receive <strong>a few of the latest messages of the channel history</strong>.</p>
<p>You can see the <strong>view count</strong> beside each message.
Note that this only counts subscribers who have read receipts enabled,
so the real view count may be larger.</p>
<h3 id="how-many-subscribers-can-a-channel-have">
How many subscribers can a channel have? <a href="#how-many-subscribers-can-a-channel-have" class="anchor"></a>
</h3>
<p>Channels are designed for much larger audiences than <a href="#groups">groups</a>.</p>
<p>The practical limit depends on the used <a href="#relays">relay</a>,
so there is no single fixed number that applies everywhere.</p>
<p>For really large channels with several tens of thousands of subscribers,
we recommend using a <a href="#multiple-accounts">dedicated profile</a> for the channel
and checking whether the relay is suitable.</p>
<p>But dont be too hesitant: Delta Chat is designed to be relay-agnostic,
so you can change your relay at any point easily -
your existing subscribers will not even notice.
You only have to update the invite link you share with new subscribers in that case.</p>
<h2 id="calls">
Calls <a href="#calls" class="anchor"></a>
</h2>
<p>Delta Chat supports one-to-one <strong>audio calls</strong> and <strong>video calls</strong>.</p>
<p>Calls are supported on Desktop, Ubuntu Touch, iOS and Android 8 and newer.</p>
<h3 id="place-a-call">
Place a call <a href="#place-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>In a one-to-one chat, tap the 📞 <strong>call icon</strong>.</p>
</li>
<li>
<p>This opens a small menu
where you can choose whether to place an <strong>Audio Call</strong> or a <strong>Video Call</strong>.</p>
</li>
</ul>
<h3 id="accept-or-reject-a-call">
Accept or reject a call <a href="#accept-or-reject-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>When someone calls you,
Delta Chat shows an <strong>incoming call screen</strong> or notification.</p>
</li>
<li>
<p>Tap <strong>Accept</strong> to answer
or <strong>Decline</strong> to reject the call.</p>
</li>
</ul>
<h3 id="during-a-call">
During a call <a href="#during-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>You can <strong>mute</strong> your microphone.</p>
</li>
<li>
<p>You can <strong>enable or disable your camera</strong>.</p>
</li>
<li>
<p>On mobile, you can <strong>switch between front and back cameras</strong>.</p>
</li>
</ul>
<p>Depending on the device, you can also select the audio output or use picture-in-picture.
On desktop, the call is using a dedicated window
and you can continue using the main Delta Chat window as usual.</p>
<h3 id="missed-calls-and-notifications">
Missed calls and notifications <a href="#missed-calls-and-notifications" class="anchor"></a>
</h3>
<ul>
<li>
<p>If you do not answer, do not hear the ringing, or do not have your device at hand,
the call appears as a <strong>missed call</strong>.</p>
</li>
<li>
<p><strong>Only your accepted contacts</strong> can make your device ring.
Contact requests will appear as usual and will not ring.</p>
</li>
<li>
<p>At <strong>Settings → Notifications → Calls</strong>,
you can disable the special call ringing screen completely.
If you do so, you will not be disturbed by any ringing notification,
you can still pick up the call by tapping the incoming call message bubble in its chat.</p>
</li>
</ul>
<h2 id="webxdc">
@@ -1244,26 +1025,22 @@ 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 <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at 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">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>
<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>
</li>
<li>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</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>
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>
</li>
</ul>
@@ -1626,20 +1403,23 @@ can not be identified easily.</p>
</h3>
<p>The used <a href="#relays">relays</a> need to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#calls">call</a>
<p>The used <a href="#relays">relay</a> needs to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#experiments">call</a>
or use <a href="#webxdc">apps</a> together.</p>
<p>IP Addresses are needed for connectivity and efficiency.
Delta Chat neither persists nor exposes them.
Note that IP Addresses
are not like an address you give to a delivery service,
but typically less precise, often defining city or region only.</p>
They are neither persisted nor exposed.
Note that the IP Address
is not like a detailed address you give to a delivery service,
but much more coarse, often defining region or country only.</p>
<p>If you see your IP Address as a risk,
we recommend to use a VPN for the whole system.
Per-app options leave gaps across your system.
For example, tapping a link can expose IP Addresses to unknown parties, which is by far the larger risk.</p>
<p>As this is just how the internet and other messengers work by default,
we do not offer options here or ask upfront questions.</p>
<p>If you see your IP Address as a security or privacy risk,
we recommend to use a VPN, in combination with system lockdown mode.
Hunting down options in all apps on your system will leave gaps.
For example, tapping a link exposes IP Addresses to unknown parties and is the by far larger risk here.</p>
<h3 id="sealedsender">
@@ -1776,7 +1556,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/serverguide">recommended on this site</a>.
<a href="https://delta.chat/pt/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>
+44 -261
View File
@@ -5,6 +5,7 @@
<li><a href="#howtoe2ee">Как найти людей для общения?</a></li>
<li><a href="#почему-чат-помечен-как-запрос">Почему чат помечен как “Запрос”?</a></li>
<li><a href="#как-я-могу-связать-двух-своих-друзей-друг-с-другом">Как я могу связать двух своих друзей друг с другом?</a></li>
<li><a href="#поддерживает-ли-delta-chat-изображения-видео-и-другие-вложения">Поддерживает ли Delta Chat изображения, видео и другие вложения?</a></li>
<li><a href="#multiple-accounts">Что такое профили? Как я могу переключатся между ними?</a></li>
<li><a href="#кто-видит-изображение-моего-профиля">Кто видит изображение моего профиля?</a></li>
<li><a href="#signature">Могу ли я установить статус/подпись в Delta Chat?</a></li>
@@ -13,7 +14,6 @@
<li><a href="#что-означает-зеленая-точка">Что означает зеленая точка?</a></li>
<li><a href="#что-означают-галочки-рядом-с-исходящими-сообщениями">Что означают галочки рядом с исходящими сообщениями?</a></li>
<li><a href="#edit">Исправление опечаток и удаление сообщений после отправки</a></li>
<li><a href="#mediaquality">Как обеспечивается качество мультимедиа?</a></li>
<li><a href="#ephemeralmsgs">Как работают исчезающие сообщения?</a></li>
<li><a href="#delold">Что произойдет, если я включу функцию “Удалять сообщения с устройства”?</a></li>
<li><a href="#remove-account">Как удалить свой профиль в чате?</a></li>
@@ -29,21 +29,6 @@
<li><a href="#сколько-участников-может-быть-в-одной-группе">Сколько участников может быть в одной группе?</a></li>
</ul>
</li>
<li><a href="#channels">Channels</a>
<ul>
<li><a href="#subscribe-to-a-channel">Subscribe to a channel</a></li>
<li><a href="#create-a-channel">Create a channel</a></li>
<li><a href="#how-many-subscribers-can-a-channel-have">How many subscribers can a channel have?</a></li>
</ul>
</li>
<li><a href="#calls">Calls</a>
<ul>
<li><a href="#place-a-call">Place a call</a></li>
<li><a href="#accept-or-reject-a-call">Accept or reject a call</a></li>
<li><a href="#during-a-call">During a call</a></li>
<li><a href="#missed-calls-and-notifications">Missed calls and notifications</a></li>
</ul>
</li>
<li><a href="#webxdc">Встроенные приложения чата</a>
<ul>
<li><a href="#где-можно-найти-встроенные-приложения">Где можно найти встроенные приложения?</a></li>
@@ -229,6 +214,19 @@
<p>Второй контакт получит <strong>карточку</strong>
на которую можно нажать, чтобы начать общение с первым контактом.</p>
<h3 id="поддерживает-ли-delta-chat-изображения-видео-и-другие-вложения">
Поддерживает ли Delta Chat изображения, видео и другие вложения? <a href="#поддерживает-ли-delta-chat-изображения-видео-и-другие-вложения" class="anchor"></a>
</h3>
<p>Да. Изображения, видео, файлы, голосовые сообщения и т.д. можно отправлять с помощью кнопок <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Вложение</strong>
или <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Голосовое сообщение</strong>.</p>
<p>Для повышения производительности, изображения оптимизируются и отправляются по умолчанию в уменьшенном размере, но вы можете отправить их как “файл”, чтобы сохранить оригинал.</p>
<h3 id="multiple-accounts">
@@ -408,32 +406,6 @@
<p>Обратите внимание, что исходное сообщение все еще может быть получено участниками чата
которые могли уже ответить, переслать, сохранить, сделать скриншот или иным образом скопировать сообщение.</p>
<h3 id="mediaquality">
Как обеспечивается качество мультимедиа? <a href="#mediaquality" class="anchor"></a>
</h3>
<p>Изображения, видео, файлы, голосовые сообщения и т.д. можно отправлять с помощью кнопок <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Вложение</strong>
или <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Голосовое сообщение</strong>.</p>
<ul>
<li>
<p>По умолчанию сжатие обеспечивает <strong>быструю и эффективную доставку</strong> сообщений, учитывая ограничения по объему данных и хранилищу у всех пользователей.
Это идеально подходит для повседневного общения.</p>
</li>
<li>
<p>В регионах с плохим качеством связи,
вы можете выбрать более высокую степень сжатия в меню <strong>Настройки → Чаты → Качество отправляемых медиафайлов</strong>.</p>
</li>
<li>
<p>Если вам необходимо отправить мультимедийный файл в <strong>исходном качестве</strong>, используйте значок <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Прикрепить → Файл</strong> в чате.
Используйте этот метод с осторожностью, так как отправка файлов в исходном качестве значительно увеличит объем трафика как для вас, так и для всех участников чата.</p>
</li>
</ul>
<h3 id="ephemeralmsgs">
@@ -637,197 +609,6 @@
в то время как Delta Chat - это приватный мессенджер для общения на <a href="#groups">равных правах</a>.
Смотрите <a href="https://en.wikipedia.org/wiki/Dunbar%27s_number">число Данбара</a> для более глубокого понимания.</p>
<h2 id="channels">
Channels <a href="#channels" class="anchor"></a>
</h2>
<p>Channels are a one-to-many tool for broadcasting messages.</p>
<h3 id="subscribe-to-a-channel">
Subscribe to a channel <a href="#subscribe-to-a-channel" class="anchor"></a>
</h3>
<ul>
<li>Scan the <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> <strong>QR code</strong>
or tap the <strong>invite link</strong> you got from the channel owner.</li>
</ul>
<p>Thats all!
You will receive a few of the messages from the channel history
and, from that point on, all new messages from the channel.</p>
<p><strong>Dont worry,</strong> if that does not happen immediately.
Once the channel owner comes online, your join request will be processed.</p>
<p>As all of Delta Chat, also Channels are private and decentralized,
there is no public discovery.</p>
<p>Other channel subscribers will not see that you subscribed and cannot message you.
The channel owner, however, can message you.
They will also see that you read a message unless you have read receipts disabled.</p>
<p>If you do not want to share your main profile,
you can also create a <a href="#multiple-accounts">dedicated profile</a> for joining a channel.</p>
<h3 id="create-a-channel">
Create a channel <a href="#create-a-channel" class="anchor"></a>
</h3>
<ul>
<li>
<p>Tap <strong>New Chat</strong> and choose <strong>New Channel</strong>.</p>
</li>
<li>
<p>Enter a <strong>name</strong>, optionally set an <strong>image</strong> and <strong>description</strong>, and hit the <strong>Create</strong> button.</p>
</li>
<li>
<p>You can now send and manage messages as usual.</p>
</li>
<li>
<p>From the channels profile, <strong>share the QR code or invite link with others</strong>.</p>
</li>
</ul>
<p>Subscribers will receive your messages,
but they cannot send messages in your channel.
When subscribing, they will receive <strong>a few of the latest messages of the channel history</strong>.</p>
<p>You can see the <strong>view count</strong> beside each message.
Note that this only counts subscribers who have read receipts enabled,
so the real view count may be larger.</p>
<h3 id="how-many-subscribers-can-a-channel-have">
How many subscribers can a channel have? <a href="#how-many-subscribers-can-a-channel-have" class="anchor"></a>
</h3>
<p>Channels are designed for much larger audiences than <a href="#groups">groups</a>.</p>
<p>The practical limit depends on the used <a href="#relays">relay</a>,
so there is no single fixed number that applies everywhere.</p>
<p>For really large channels with several tens of thousands of subscribers,
we recommend using a <a href="#multiple-accounts">dedicated profile</a> for the channel
and checking whether the relay is suitable.</p>
<p>But dont be too hesitant: Delta Chat is designed to be relay-agnostic,
so you can change your relay at any point easily -
your existing subscribers will not even notice.
You only have to update the invite link you share with new subscribers in that case.</p>
<h2 id="calls">
Calls <a href="#calls" class="anchor"></a>
</h2>
<p>Delta Chat supports one-to-one <strong>audio calls</strong> and <strong>video calls</strong>.</p>
<p>Calls are supported on Desktop, Ubuntu Touch, iOS and Android 8 and newer.</p>
<h3 id="place-a-call">
Place a call <a href="#place-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>In a one-to-one chat, tap the 📞 <strong>call icon</strong>.</p>
</li>
<li>
<p>This opens a small menu
where you can choose whether to place an <strong>Audio Call</strong> or a <strong>Video Call</strong>.</p>
</li>
</ul>
<h3 id="accept-or-reject-a-call">
Accept or reject a call <a href="#accept-or-reject-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>When someone calls you,
Delta Chat shows an <strong>incoming call screen</strong> or notification.</p>
</li>
<li>
<p>Tap <strong>Accept</strong> to answer
or <strong>Decline</strong> to reject the call.</p>
</li>
</ul>
<h3 id="during-a-call">
During a call <a href="#during-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>You can <strong>mute</strong> your microphone.</p>
</li>
<li>
<p>You can <strong>enable or disable your camera</strong>.</p>
</li>
<li>
<p>On mobile, you can <strong>switch between front and back cameras</strong>.</p>
</li>
</ul>
<p>Depending on the device, you can also select the audio output or use picture-in-picture.
On desktop, the call is using a dedicated window
and you can continue using the main Delta Chat window as usual.</p>
<h3 id="missed-calls-and-notifications">
Missed calls and notifications <a href="#missed-calls-and-notifications" class="anchor"></a>
</h3>
<ul>
<li>
<p>If you do not answer, do not hear the ringing, or do not have your device at hand,
the call appears as a <strong>missed call</strong>.</p>
</li>
<li>
<p><strong>Only your accepted contacts</strong> can make your device ring.
Contact requests will appear as usual and will not ring.</p>
</li>
<li>
<p>At <strong>Settings → Notifications → Calls</strong>,
you can disable the special call ringing screen completely.
If you do so, you will not be disturbed by any ringing notification,
you can still pick up the call by tapping the incoming call message bubble in its chat.</p>
</li>
</ul>
<h2 id="webxdc">
@@ -1243,26 +1024,22 @@ PIN-код разблокировки экрана, графический кл
<p>По умолчанию, после установки, релей <strong>настраивается автоматически</strong>,
поэтому вам не нужно об этом волноваться.
Однако, если хотите,
вы можете настроить релеи в меню <strong>Настройки → Дополнительно → Релеи</strong>:</p>
вы можете настроить релеи в <strong>Настройки → Дополнительно → Релеи</strong>:</p>
<ul>
<li>
<p>Вы можете <strong>добавить</strong> релей, отсканировав его QR-код;
на сайте <a href="https://chatmail.at/relays">chatmail.at/relays</a> представлен список известных.
Если у вас настроено несколько релеев, сообщения будут доставляться через все из них.
Ваши контакты автоматически узнают о текущих используемых вами релеях при обмене сообщениями.</p>
<a href="https://chatmail.at/relays">https://chatmail.at/relays</a> содержит список известных релеев.
Если у вас несколько релеев, вы будете получать сообщения на все из них.</p>
</li>
<li>
<p>Нажмите на релей, чтобы установить его как <strong>используемый для отправки</strong>.</p>
<p><strong>По умолчанию</strong> определяет релей, на который ваши собеседники будут отправлять будущие сообщения.</p>
</li>
<li>
<p>Если вы <strong>удалите</strong> релей,
контакты, которые знают только этот релей, не смогут связаться с вами до тех пор, пока вы снова не напишите им.
Чтобы оставаться на связи в это время, выберите опцию <strong>Скрыть из контактов</strong> во всплывающем окне
вместо того, чтобы удалять его сразу.</p>
</li>
<li>
<p>Чтобы снова <strong>показать</strong> скрытый релей, нажмите на него.</p>
<p>Если вы <strong>удаляете</strong> релей,
убедитесь, что другой релей по умолчанию использовался в течение достаточного количества времени.
В противном случае сообщения от ваших собеседников не будут доходить до вас.
Если сомневаетесь, удалите его позже.</p>
</li>
</ul>
@@ -1624,20 +1401,25 @@ Delta Chat вместо этого использует реализацию Ope
</h3>
<p>The used <a href="#relays">relays</a> need to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#calls">call</a>
or use <a href="#webxdc">apps</a> together.</p>
<p>Используемый <a href="#relays">релей</a> должен знать ваш IP-адрес,
а также иногда устройства ваших контактов, если вы проводите совместные <a href="#experiments">звонки</a>
или используете <a href="#webxdc">приложения</a>.</p>
<p>IP Addresses are needed for connectivity and efficiency.
Delta Chat neither persists nor exposes them.
Note that IP Addresses
are not like an address you give to a delivery service,
but typically less precise, often defining city or region only.</p>
<p>IP-адреса необходимы для обеспечения соединения и эффективности.
Они не сохраняются и не передаются третьим лицам.
Обратите внимание, что IP-адрес</p>
<ul>
<li>это не подробный адрес, который вы указываете службе доставки,
а скорее приблизительный, обычно определяющий регион или страну.</li>
</ul>
<p>If you see your IP Address as a risk,
we recommend to use a VPN for the whole system.
Per-app options leave gaps across your system.
For example, tapping a link can expose IP Addresses to unknown parties, which is by far the larger risk.</p>
<p>Поскольку именно так по умолчанию работает интернет и другие мессенджеры,
мы не предлагаем здесь никаких настроек и не задаём предварительных вопросов</p>
<p>Если вы считаете свой IP-адрес угрозой безопасности или конфиденциальности,
мы рекомендуем использовать VPN в сочетании с режимом блокировки системы.
Поиск настроек во всех приложениях на вашем устройстве оставит уязвимости.
Например, нажатие на ссылку раскрывает IP-адрес неизвестным лицам и представляет собой гораздо больший риск в данном случае.</p>
<h3 id="sealedsender">
@@ -1772,10 +1554,11 @@ 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/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/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>
</li>
<li>
<p>2020 год, <a href="https://includesecurity.com">Include Security</a> проанализировала Delta
+35 -255
View File
@@ -5,6 +5,7 @@
<li><a href="#howtoe2ee">How can I find people to chat with?</a></li>
<li><a href="#why-is-a-chat-marked-as-request">Why is a chat marked as “Request”?</a></li>
<li><a href="#how-can-i-put-two-of-my-friends-in-contact-with-each-other">How can I put two of my friends in contact with each other?</a></li>
<li><a href="#podporuje-delta-chat-obrázky-videá-a-iné-prílohy">Podporuje Delta Chat obrázky, videá a iné prílohy?</a></li>
<li><a href="#multiple-accounts">What are profiles? How can I switch between them?</a></li>
<li><a href="#kto-vidí-moju-profilovú-fotku">Kto vidí moju profilovú fotku?</a></li>
<li><a href="#signature">Can I set a Bio/Status with Delta Chat?</a></li>
@@ -13,7 +14,6 @@
<li><a href="#what-does-the-green-dot-mean">What does the green dot mean?</a></li>
<li><a href="#čo-znamenajú-zaškrtnutia-zobrazené-vedľa-odchádzajúcich-správ">Čo znamenajú zaškrtnutia zobrazené vedľa odchádzajúcich správ?</a></li>
<li><a href="#edit">Correct typos and delete messages after sending</a></li>
<li><a href="#mediaquality">How is media quality handled?</a></li>
<li><a href="#ephemeralmsgs">How do disappearing messages work?</a></li>
<li><a href="#delold">What happens if I turn on “Delete Messages from Device”?</a></li>
<li><a href="#remove-account">How can I delete my chat profile?</a></li>
@@ -29,21 +29,6 @@
<li><a href="#how-many-members-can-participate-in-a-single-group">How many members can participate in a single group?</a></li>
</ul>
</li>
<li><a href="#channels">Channels</a>
<ul>
<li><a href="#subscribe-to-a-channel">Subscribe to a channel</a></li>
<li><a href="#create-a-channel">Create a channel</a></li>
<li><a href="#how-many-subscribers-can-a-channel-have">How many subscribers can a channel have?</a></li>
</ul>
</li>
<li><a href="#calls">Calls</a>
<ul>
<li><a href="#place-a-call">Place a call</a></li>
<li><a href="#accept-or-reject-a-call">Accept or reject a call</a></li>
<li><a href="#during-a-call">During a call</a></li>
<li><a href="#missed-calls-and-notifications">Missed calls and notifications</a></li>
</ul>
</li>
<li><a href="#webxdc">In-chat apps</a>
<ul>
<li><a href="#where-can-i-get-in-chat-apps">Where can I get in-chat apps?</a></li>
@@ -233,6 +218,19 @@ You can also add a little introduction message.</p>
<p>The second contact will receive a <strong>card</strong> then
and can tap it to start chatting with the first contact.</p>
<h3 id="podporuje-delta-chat-obrázky-videá-a-iné-prílohy">
Podporuje Delta Chat obrázky, videá a iné prílohy? <a href="#podporuje-delta-chat-obrázky-videá-a-iné-prílohy" class="anchor"></a>
</h3>
<p>Yes. Images, videos, files, voice messages etc. can be sent using the <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attachment-</strong>
or <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Voice Message</strong> buttons</p>
<p>For performance, images are optimized and sent at a smaller size by default, but you can send it as a “file” to preserve the original.</p>
<h3 id="multiple-accounts">
@@ -413,32 +411,6 @@ Notifications are not sent and there is no time limit.</p>
<p>Note, that the original message may still be received by chat members
who could have already replied, forwarded, saved, screenshotted or otherwise copied the message.</p>
<h3 id="mediaquality">
How is media quality handled? <a href="#mediaquality" class="anchor"></a>
</h3>
<p>Images, videos, files, voice messages etc. can be sent using the <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attach-</strong>
or <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Voice Message</strong> buttons.</p>
<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>
</li>
<li>
<p>In regions with worse connectivity,
you can choose higher compression at <strong>Settings → Chats → Outgoing Media Quality</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>
</li>
</ul>
<h3 id="ephemeralmsgs">
@@ -642,197 +614,6 @@ but more than 150 is not recommended.</p>
where Delta Chat is a private messenger for chatting with <a href="#groups">equal rights</a>.
See <a href="https://en.wikipedia.org/wiki/Dunbar%27s_number">Dunbars number</a> for more insights.</p>
<h2 id="channels">
Channels <a href="#channels" class="anchor"></a>
</h2>
<p>Channels are a one-to-many tool for broadcasting messages.</p>
<h3 id="subscribe-to-a-channel">
Subscribe to a channel <a href="#subscribe-to-a-channel" class="anchor"></a>
</h3>
<ul>
<li>Scan the <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> <strong>QR code</strong>
or tap the <strong>invite link</strong> you got from the channel owner.</li>
</ul>
<p>Thats all!
You will receive a few of the messages from the channel history
and, from that point on, all new messages from the channel.</p>
<p><strong>Dont worry,</strong> if that does not happen immediately.
Once the channel owner comes online, your join request will be processed.</p>
<p>As all of Delta Chat, also Channels are private and decentralized,
there is no public discovery.</p>
<p>Other channel subscribers will not see that you subscribed and cannot message you.
The channel owner, however, can message you.
They will also see that you read a message unless you have read receipts disabled.</p>
<p>If you do not want to share your main profile,
you can also create a <a href="#multiple-accounts">dedicated profile</a> for joining a channel.</p>
<h3 id="create-a-channel">
Create a channel <a href="#create-a-channel" class="anchor"></a>
</h3>
<ul>
<li>
<p>Tap <strong>New Chat</strong> and choose <strong>New Channel</strong>.</p>
</li>
<li>
<p>Enter a <strong>name</strong>, optionally set an <strong>image</strong> and <strong>description</strong>, and hit the <strong>Create</strong> button.</p>
</li>
<li>
<p>You can now send and manage messages as usual.</p>
</li>
<li>
<p>From the channels profile, <strong>share the QR code or invite link with others</strong>.</p>
</li>
</ul>
<p>Subscribers will receive your messages,
but they cannot send messages in your channel.
When subscribing, they will receive <strong>a few of the latest messages of the channel history</strong>.</p>
<p>You can see the <strong>view count</strong> beside each message.
Note that this only counts subscribers who have read receipts enabled,
so the real view count may be larger.</p>
<h3 id="how-many-subscribers-can-a-channel-have">
How many subscribers can a channel have? <a href="#how-many-subscribers-can-a-channel-have" class="anchor"></a>
</h3>
<p>Channels are designed for much larger audiences than <a href="#groups">groups</a>.</p>
<p>The practical limit depends on the used <a href="#relays">relay</a>,
so there is no single fixed number that applies everywhere.</p>
<p>For really large channels with several tens of thousands of subscribers,
we recommend using a <a href="#multiple-accounts">dedicated profile</a> for the channel
and checking whether the relay is suitable.</p>
<p>But dont be too hesitant: Delta Chat is designed to be relay-agnostic,
so you can change your relay at any point easily -
your existing subscribers will not even notice.
You only have to update the invite link you share with new subscribers in that case.</p>
<h2 id="calls">
Calls <a href="#calls" class="anchor"></a>
</h2>
<p>Delta Chat supports one-to-one <strong>audio calls</strong> and <strong>video calls</strong>.</p>
<p>Calls are supported on Desktop, Ubuntu Touch, iOS and Android 8 and newer.</p>
<h3 id="place-a-call">
Place a call <a href="#place-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>In a one-to-one chat, tap the 📞 <strong>call icon</strong>.</p>
</li>
<li>
<p>This opens a small menu
where you can choose whether to place an <strong>Audio Call</strong> or a <strong>Video Call</strong>.</p>
</li>
</ul>
<h3 id="accept-or-reject-a-call">
Accept or reject a call <a href="#accept-or-reject-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>When someone calls you,
Delta Chat shows an <strong>incoming call screen</strong> or notification.</p>
</li>
<li>
<p>Tap <strong>Accept</strong> to answer
or <strong>Decline</strong> to reject the call.</p>
</li>
</ul>
<h3 id="during-a-call">
During a call <a href="#during-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>You can <strong>mute</strong> your microphone.</p>
</li>
<li>
<p>You can <strong>enable or disable your camera</strong>.</p>
</li>
<li>
<p>On mobile, you can <strong>switch between front and back cameras</strong>.</p>
</li>
</ul>
<p>Depending on the device, you can also select the audio output or use picture-in-picture.
On desktop, the call is using a dedicated window
and you can continue using the main Delta Chat window as usual.</p>
<h3 id="missed-calls-and-notifications">
Missed calls and notifications <a href="#missed-calls-and-notifications" class="anchor"></a>
</h3>
<ul>
<li>
<p>If you do not answer, do not hear the ringing, or do not have your device at hand,
the call appears as a <strong>missed call</strong>.</p>
</li>
<li>
<p><strong>Only your accepted contacts</strong> can make your device ring.
Contact requests will appear as usual and will not ring.</p>
</li>
<li>
<p>At <strong>Settings → Notifications → Calls</strong>,
you can disable the special call ringing screen completely.
If you do so, you will not be disturbed by any ringing notification,
you can still pick up the call by tapping the incoming call message bubble in its chat.</p>
</li>
</ul>
<h2 id="webxdc">
@@ -1249,26 +1030,22 @@ 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 <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at 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">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>
<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>
</li>
<li>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</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>
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>
</li>
</ul>
@@ -1631,20 +1408,23 @@ can not be identified easily.</p>
</h3>
<p>The used <a href="#relays">relays</a> need to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#calls">call</a>
<p>The used <a href="#relays">relay</a> needs to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#experiments">call</a>
or use <a href="#webxdc">apps</a> together.</p>
<p>IP Addresses are needed for connectivity and efficiency.
Delta Chat neither persists nor exposes them.
Note that IP Addresses
are not like an address you give to a delivery service,
but typically less precise, often defining city or region only.</p>
They are neither persisted nor exposed.
Note that the IP Address
is not like a detailed address you give to a delivery service,
but much more coarse, often defining region or country only.</p>
<p>If you see your IP Address as a risk,
we recommend to use a VPN for the whole system.
Per-app options leave gaps across your system.
For example, tapping a link can expose IP Addresses to unknown parties, which is by far the larger risk.</p>
<p>As this is just how the internet and other messengers work by default,
we do not offer options here or ask upfront questions.</p>
<p>If you see your IP Address as a security or privacy risk,
we recommend to use a VPN, in combination with system lockdown mode.
Hunting down options in all apps on your system will leave gaps.
For example, tapping a link exposes IP Addresses to unknown parties and is the by far larger risk here.</p>
<h3 id="sealedsender">
@@ -1781,7 +1561,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/serverguide">recommended on this site</a>.
<a href="https://delta.chat/sk/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>
+35 -255
View File
@@ -5,6 +5,7 @@
<li><a href="#howtoe2ee">How can I find people to chat with?</a></li>
<li><a href="#why-is-a-chat-marked-as-request">Why is a chat marked as “Request”?</a></li>
<li><a href="#how-can-i-put-two-of-my-friends-in-contact-with-each-other">How can I put two of my friends in contact with each other?</a></li>
<li><a href="#a-mbulon-delta-chat-i-figura-video-dhe-bashkëngjitje-të-tjera">A mbulon Delta Chat-i figura, video dhe bashkëngjitje të tjera?</a></li>
<li><a href="#multiple-accounts">What are profiles? How can I switch between them?</a></li>
<li><a href="#kush-e-sheh-profilin-tim">Kush e sheh profilin tim?</a></li>
<li><a href="#signature">Can I set a Bio/Status with Delta Chat?</a></li>
@@ -13,7 +14,6 @@
<li><a href="#çdo-të-thotë-pika-e-gjelbër">Ç’do të thotë pika e gjelbër?</a></li>
<li><a href="#çduan-të-thonë-shenjat-e-shfaqura-pas-mesazheve-që-dërgohen">Ç’duan të thonë shenjat e shfaqura pas mesazheve që dërgohen?</a></li>
<li><a href="#edit">Correct typos and delete messages after sending</a></li>
<li><a href="#mediaquality">How is media quality handled?</a></li>
<li><a href="#ephemeralmsgs">How do disappearing messages work?</a></li>
<li><a href="#delold">Ç’ndodh, nëse aktivizoj “Fshi prej pajisjes mesazhe të vjetër”?</a></li>
<li><a href="#remove-account">How can I delete my chat profile?</a></li>
@@ -29,21 +29,6 @@
<li><a href="#how-many-members-can-participate-in-a-single-group">How many members can participate in a single group?</a></li>
</ul>
</li>
<li><a href="#channels">Channels</a>
<ul>
<li><a href="#subscribe-to-a-channel">Subscribe to a channel</a></li>
<li><a href="#create-a-channel">Create a channel</a></li>
<li><a href="#how-many-subscribers-can-a-channel-have">How many subscribers can a channel have?</a></li>
</ul>
</li>
<li><a href="#calls">Calls</a>
<ul>
<li><a href="#place-a-call">Place a call</a></li>
<li><a href="#accept-or-reject-a-call">Accept or reject a call</a></li>
<li><a href="#during-a-call">During a call</a></li>
<li><a href="#missed-calls-and-notifications">Missed calls and notifications</a></li>
</ul>
</li>
<li><a href="#webxdc">In-chat apps</a>
<ul>
<li><a href="#where-can-i-get-in-chat-apps">Where can I get in-chat apps?</a></li>
@@ -233,6 +218,19 @@ You can also add a little introduction message.</p>
<p>The second contact will receive a <strong>card</strong> then
and can tap it to start chatting with the first contact.</p>
<h3 id="a-mbulon-delta-chat-i-figura-video-dhe-bashkëngjitje-të-tjera">
A mbulon Delta Chat-i figura, video dhe bashkëngjitje të tjera? <a href="#a-mbulon-delta-chat-i-figura-video-dhe-bashkëngjitje-të-tjera" class="anchor"></a>
</h3>
<p>Po Images, videos, files, voice messages etc. can be sent using the <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attachment-</strong>
or <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Voice Message</strong> buttons</p>
<p>Si parazgjedhje, për funksionim më të mirë, figurat optimizohen dhe dërgohen në madhësi më të vogël, por mund ta dërgoni si një “kartelë”, që të ruhet origjinali.</p>
<h3 id="multiple-accounts">
@@ -412,32 +410,6 @@ Notifications are not sent and there is no time limit.</p>
<p>Note, that the original message may still be received by chat members
who could have already replied, forwarded, saved, screenshotted or otherwise copied the message.</p>
<h3 id="mediaquality">
How is media quality handled? <a href="#mediaquality" class="anchor"></a>
</h3>
<p>Images, videos, files, voice messages etc. can be sent using the <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attach-</strong>
or <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Voice Message</strong> buttons.</p>
<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>
</li>
<li>
<p>In regions with worse connectivity,
you can choose higher compression at <strong>Settings → Chats → Outgoing Media Quality</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>
</li>
</ul>
<h3 id="ephemeralmsgs">
@@ -642,197 +614,6 @@ but more than 150 is not recommended.</p>
where Delta Chat is a private messenger for chatting with <a href="#groups">equal rights</a>.
See <a href="https://en.wikipedia.org/wiki/Dunbar%27s_number">Dunbars number</a> for more insights.</p>
<h2 id="channels">
Channels <a href="#channels" class="anchor"></a>
</h2>
<p>Channels are a one-to-many tool for broadcasting messages.</p>
<h3 id="subscribe-to-a-channel">
Subscribe to a channel <a href="#subscribe-to-a-channel" class="anchor"></a>
</h3>
<ul>
<li>Scan the <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> <strong>QR code</strong>
or tap the <strong>invite link</strong> you got from the channel owner.</li>
</ul>
<p>Thats all!
You will receive a few of the messages from the channel history
and, from that point on, all new messages from the channel.</p>
<p><strong>Dont worry,</strong> if that does not happen immediately.
Once the channel owner comes online, your join request will be processed.</p>
<p>As all of Delta Chat, also Channels are private and decentralized,
there is no public discovery.</p>
<p>Other channel subscribers will not see that you subscribed and cannot message you.
The channel owner, however, can message you.
They will also see that you read a message unless you have read receipts disabled.</p>
<p>If you do not want to share your main profile,
you can also create a <a href="#multiple-accounts">dedicated profile</a> for joining a channel.</p>
<h3 id="create-a-channel">
Create a channel <a href="#create-a-channel" class="anchor"></a>
</h3>
<ul>
<li>
<p>Tap <strong>New Chat</strong> and choose <strong>New Channel</strong>.</p>
</li>
<li>
<p>Enter a <strong>name</strong>, optionally set an <strong>image</strong> and <strong>description</strong>, and hit the <strong>Create</strong> button.</p>
</li>
<li>
<p>You can now send and manage messages as usual.</p>
</li>
<li>
<p>From the channels profile, <strong>share the QR code or invite link with others</strong>.</p>
</li>
</ul>
<p>Subscribers will receive your messages,
but they cannot send messages in your channel.
When subscribing, they will receive <strong>a few of the latest messages of the channel history</strong>.</p>
<p>You can see the <strong>view count</strong> beside each message.
Note that this only counts subscribers who have read receipts enabled,
so the real view count may be larger.</p>
<h3 id="how-many-subscribers-can-a-channel-have">
How many subscribers can a channel have? <a href="#how-many-subscribers-can-a-channel-have" class="anchor"></a>
</h3>
<p>Channels are designed for much larger audiences than <a href="#groups">groups</a>.</p>
<p>The practical limit depends on the used <a href="#relays">relay</a>,
so there is no single fixed number that applies everywhere.</p>
<p>For really large channels with several tens of thousands of subscribers,
we recommend using a <a href="#multiple-accounts">dedicated profile</a> for the channel
and checking whether the relay is suitable.</p>
<p>But dont be too hesitant: Delta Chat is designed to be relay-agnostic,
so you can change your relay at any point easily -
your existing subscribers will not even notice.
You only have to update the invite link you share with new subscribers in that case.</p>
<h2 id="calls">
Calls <a href="#calls" class="anchor"></a>
</h2>
<p>Delta Chat supports one-to-one <strong>audio calls</strong> and <strong>video calls</strong>.</p>
<p>Calls are supported on Desktop, Ubuntu Touch, iOS and Android 8 and newer.</p>
<h3 id="place-a-call">
Place a call <a href="#place-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>In a one-to-one chat, tap the 📞 <strong>call icon</strong>.</p>
</li>
<li>
<p>This opens a small menu
where you can choose whether to place an <strong>Audio Call</strong> or a <strong>Video Call</strong>.</p>
</li>
</ul>
<h3 id="accept-or-reject-a-call">
Accept or reject a call <a href="#accept-or-reject-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>When someone calls you,
Delta Chat shows an <strong>incoming call screen</strong> or notification.</p>
</li>
<li>
<p>Tap <strong>Accept</strong> to answer
or <strong>Decline</strong> to reject the call.</p>
</li>
</ul>
<h3 id="during-a-call">
During a call <a href="#during-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>You can <strong>mute</strong> your microphone.</p>
</li>
<li>
<p>You can <strong>enable or disable your camera</strong>.</p>
</li>
<li>
<p>On mobile, you can <strong>switch between front and back cameras</strong>.</p>
</li>
</ul>
<p>Depending on the device, you can also select the audio output or use picture-in-picture.
On desktop, the call is using a dedicated window
and you can continue using the main Delta Chat window as usual.</p>
<h3 id="missed-calls-and-notifications">
Missed calls and notifications <a href="#missed-calls-and-notifications" class="anchor"></a>
</h3>
<ul>
<li>
<p>If you do not answer, do not hear the ringing, or do not have your device at hand,
the call appears as a <strong>missed call</strong>.</p>
</li>
<li>
<p><strong>Only your accepted contacts</strong> can make your device ring.
Contact requests will appear as usual and will not ring.</p>
</li>
<li>
<p>At <strong>Settings → Notifications → Calls</strong>,
you can disable the special call ringing screen completely.
If you do so, you will not be disturbed by any ringing notification,
you can still pick up the call by tapping the incoming call message bubble in its chat.</p>
</li>
</ul>
<h2 id="webxdc">
@@ -1251,26 +1032,22 @@ 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 <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at 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">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>
<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>
</li>
<li>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</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>
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>
</li>
</ul>
@@ -1633,20 +1410,23 @@ can not be identified easily.</p>
</h3>
<p>The used <a href="#relays">relays</a> need to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#calls">call</a>
<p>The used <a href="#relays">relay</a> needs to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#experiments">call</a>
or use <a href="#webxdc">apps</a> together.</p>
<p>IP Addresses are needed for connectivity and efficiency.
Delta Chat neither persists nor exposes them.
Note that IP Addresses
are not like an address you give to a delivery service,
but typically less precise, often defining city or region only.</p>
They are neither persisted nor exposed.
Note that the IP Address
is not like a detailed address you give to a delivery service,
but much more coarse, often defining region or country only.</p>
<p>If you see your IP Address as a risk,
we recommend to use a VPN for the whole system.
Per-app options leave gaps across your system.
For example, tapping a link can expose IP Addresses to unknown parties, which is by far the larger risk.</p>
<p>As this is just how the internet and other messengers work by default,
we do not offer options here or ask upfront questions.</p>
<p>If you see your IP Address as a security or privacy risk,
we recommend to use a VPN, in combination with system lockdown mode.
Hunting down options in all apps on your system will leave gaps.
For example, tapping a link exposes IP Addresses to unknown parties and is the by far larger risk here.</p>
<h3 id="sealedsender">
@@ -1784,7 +1564,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/serverguide">të rekomanduarin në këtë sajt</a>.
shërbyesi poste si <a href="https://delta.chat/sq/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>
+35 -255
View File
@@ -5,6 +5,7 @@
<li><a href="#howtoe2ee">Як мені знайти людей для спілкування?</a></li>
<li><a href="#why-is-a-chat-marked-as-request">Why is a chat marked as “Request”?</a></li>
<li><a href="#how-can-i-put-two-of-my-friends-in-contact-with-each-other">How can I put two of my friends in contact with each other?</a></li>
<li><a href="#чи-підтримує-delta-chat-вкладення-у-вигляді-фото-відео-тощо">Чи підтримує Delta Chat вкладення у вигляді фото, відео тощо?</a></li>
<li><a href="#multiple-accounts">Що таке профілі? Як я можу перемикатися між ними?</a></li>
<li><a href="#хто-бачить-моє-зображення-профілю">Хто бачить моє зображення профілю?</a></li>
<li><a href="#signature">Чи можу я встановити біографію/статус у Delta Chat?</a></li>
@@ -13,7 +14,6 @@
<li><a href="#що-означає-зелена-точка">Що означає зелена точка?</a></li>
<li><a href="#що-означають-галочки-біля-вихідних-повідомлень">Що означають галочки біля вихідних повідомлень?</a></li>
<li><a href="#edit">Виправлення помилок та видалення повідомлень після надсилання</a></li>
<li><a href="#mediaquality">How is media quality handled?</a></li>
<li><a href="#ephemeralmsgs">Як працюють повідомлення, що зникають?</a></li>
<li><a href="#delold">Що станеться, якщо я ввімкну «Видаляти старі повідомлення з пристрою»?</a></li>
</ul>
@@ -28,21 +28,6 @@
<li><a href="#how-many-members-can-participate-in-a-single-group">How many members can participate in a single group?</a></li>
</ul>
</li>
<li><a href="#channels">Channels</a>
<ul>
<li><a href="#subscribe-to-a-channel">Subscribe to a channel</a></li>
<li><a href="#create-a-channel">Create a channel</a></li>
<li><a href="#how-many-subscribers-can-a-channel-have">How many subscribers can a channel have?</a></li>
</ul>
</li>
<li><a href="#calls">Calls</a>
<ul>
<li><a href="#place-a-call">Place a call</a></li>
<li><a href="#accept-or-reject-a-call">Accept or reject a call</a></li>
<li><a href="#during-a-call">During a call</a></li>
<li><a href="#missed-calls-and-notifications">Missed calls and notifications</a></li>
</ul>
</li>
<li><a href="#webxdc">In-chat apps</a>
<ul>
<li><a href="#where-can-i-get-in-chat-apps">Where can I get in-chat apps?</a></li>
@@ -219,6 +204,19 @@ You can also add a little introduction message.</p>
<p>The second contact will receive a <strong>card</strong> then
and can tap it to start chatting with the first contact.</p>
<h3 id="чи-підтримує-delta-chat-вкладення-у-вигляді-фото-відео-тощо">
Чи підтримує Delta Chat вкладення у вигляді фото, відео тощо? <a href="#чи-підтримує-delta-chat-вкладення-у-вигляді-фото-відео-тощо" class="anchor"></a>
</h3>
<p>Так. Images, videos, files, voice messages etc. can be sent using the <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attachment-</strong>
or <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Voice Message</strong> buttons</p>
<p>З міркувань продуктивності, зображення оптимізовані та надсилаються в меншому розмірі за замовчуванням, але ви можете надіслати їх як «файл», щоб зберегти оригінал.</p>
<h3 id="multiple-accounts">
@@ -383,32 +381,6 @@ has <strong>Settings → Chats → Read Receipts</strong> enabled.</p>
<p>Зауважте, що початкове повідомлення все ще може бути отримане учасниками чату які могли вже відповісти, переслати, зберегти, зробити знімок екрану або іншим чином скопіювати повідомлення.</p>
<h3 id="mediaquality">
How is media quality handled? <a href="#mediaquality" class="anchor"></a>
</h3>
<p>Images, videos, files, voice messages etc. can be sent using the <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attach-</strong>
or <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Voice Message</strong> buttons.</p>
<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>
</li>
<li>
<p>In regions with worse connectivity,
you can choose higher compression at <strong>Settings → Chats → Outgoing Media Quality</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>
</li>
</ul>
<h3 id="ephemeralmsgs">
@@ -597,197 +569,6 @@ but more than 150 is not recommended.</p>
where Delta Chat is a private messenger for chatting with <a href="#groups">equal rights</a>.
See <a href="https://en.wikipedia.org/wiki/Dunbar%27s_number">Dunbars number</a> for more insights.</p>
<h2 id="channels">
Channels <a href="#channels" class="anchor"></a>
</h2>
<p>Channels are a one-to-many tool for broadcasting messages.</p>
<h3 id="subscribe-to-a-channel">
Subscribe to a channel <a href="#subscribe-to-a-channel" class="anchor"></a>
</h3>
<ul>
<li>Scan the <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" /> <strong>QR code</strong>
or tap the <strong>invite link</strong> you got from the channel owner.</li>
</ul>
<p>Thats all!
You will receive a few of the messages from the channel history
and, from that point on, all new messages from the channel.</p>
<p><strong>Dont worry,</strong> if that does not happen immediately.
Once the channel owner comes online, your join request will be processed.</p>
<p>As all of Delta Chat, also Channels are private and decentralized,
there is no public discovery.</p>
<p>Other channel subscribers will not see that you subscribed and cannot message you.
The channel owner, however, can message you.
They will also see that you read a message unless you have read receipts disabled.</p>
<p>If you do not want to share your main profile,
you can also create a <a href="#multiple-accounts">dedicated profile</a> for joining a channel.</p>
<h3 id="create-a-channel">
Create a channel <a href="#create-a-channel" class="anchor"></a>
</h3>
<ul>
<li>
<p>Tap <strong>New Chat</strong> and choose <strong>New Channel</strong>.</p>
</li>
<li>
<p>Enter a <strong>name</strong>, optionally set an <strong>image</strong> and <strong>description</strong>, and hit the <strong>Create</strong> button.</p>
</li>
<li>
<p>You can now send and manage messages as usual.</p>
</li>
<li>
<p>From the channels profile, <strong>share the QR code or invite link with others</strong>.</p>
</li>
</ul>
<p>Subscribers will receive your messages,
but they cannot send messages in your channel.
When subscribing, they will receive <strong>a few of the latest messages of the channel history</strong>.</p>
<p>You can see the <strong>view count</strong> beside each message.
Note that this only counts subscribers who have read receipts enabled,
so the real view count may be larger.</p>
<h3 id="how-many-subscribers-can-a-channel-have">
How many subscribers can a channel have? <a href="#how-many-subscribers-can-a-channel-have" class="anchor"></a>
</h3>
<p>Channels are designed for much larger audiences than <a href="#groups">groups</a>.</p>
<p>The practical limit depends on the used <a href="#relays">relay</a>,
so there is no single fixed number that applies everywhere.</p>
<p>For really large channels with several tens of thousands of subscribers,
we recommend using a <a href="#multiple-accounts">dedicated profile</a> for the channel
and checking whether the relay is suitable.</p>
<p>But dont be too hesitant: Delta Chat is designed to be relay-agnostic,
so you can change your relay at any point easily -
your existing subscribers will not even notice.
You only have to update the invite link you share with new subscribers in that case.</p>
<h2 id="calls">
Calls <a href="#calls" class="anchor"></a>
</h2>
<p>Delta Chat supports one-to-one <strong>audio calls</strong> and <strong>video calls</strong>.</p>
<p>Calls are supported on Desktop, Ubuntu Touch, iOS and Android 8 and newer.</p>
<h3 id="place-a-call">
Place a call <a href="#place-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>In a one-to-one chat, tap the 📞 <strong>call icon</strong>.</p>
</li>
<li>
<p>This opens a small menu
where you can choose whether to place an <strong>Audio Call</strong> or a <strong>Video Call</strong>.</p>
</li>
</ul>
<h3 id="accept-or-reject-a-call">
Accept or reject a call <a href="#accept-or-reject-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>When someone calls you,
Delta Chat shows an <strong>incoming call screen</strong> or notification.</p>
</li>
<li>
<p>Tap <strong>Accept</strong> to answer
or <strong>Decline</strong> to reject the call.</p>
</li>
</ul>
<h3 id="during-a-call">
During a call <a href="#during-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>You can <strong>mute</strong> your microphone.</p>
</li>
<li>
<p>You can <strong>enable or disable your camera</strong>.</p>
</li>
<li>
<p>On mobile, you can <strong>switch between front and back cameras</strong>.</p>
</li>
</ul>
<p>Depending on the device, you can also select the audio output or use picture-in-picture.
On desktop, the call is using a dedicated window
and you can continue using the main Delta Chat window as usual.</p>
<h3 id="missed-calls-and-notifications">
Missed calls and notifications <a href="#missed-calls-and-notifications" class="anchor"></a>
</h3>
<ul>
<li>
<p>If you do not answer, do not hear the ringing, or do not have your device at hand,
the call appears as a <strong>missed call</strong>.</p>
</li>
<li>
<p><strong>Only your accepted contacts</strong> can make your device ring.
Contact requests will appear as usual and will not ring.</p>
</li>
<li>
<p>At <strong>Settings → Notifications → Calls</strong>,
you can disable the special call ringing screen completely.
If you do so, you will not be disturbed by any ringing notification,
you can still pick up the call by tapping the incoming call message bubble in its chat.</p>
</li>
</ul>
<h2 id="webxdc">
@@ -1151,26 +932,22 @@ 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 <strong>Settings → Advanced → Relays</strong>:</p>
you can configure relays at 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">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>
<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>
</li>
<li>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
<p>The <strong>default</strong> defines the one where your chat partners send future messages to.</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>
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>
</li>
</ul>
@@ -1474,20 +1251,23 @@ can not be identified easily.</p>
</h3>
<p>The used <a href="#relays">relays</a> need to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#calls">call</a>
<p>The used <a href="#relays">relay</a> needs to know your IP Address,
as well as sometimes your contacts devices if you have a <a href="#experiments">call</a>
or use <a href="#webxdc">apps</a> together.</p>
<p>IP Addresses are needed for connectivity and efficiency.
Delta Chat neither persists nor exposes them.
Note that IP Addresses
are not like an address you give to a delivery service,
but typically less precise, often defining city or region only.</p>
They are neither persisted nor exposed.
Note that the IP Address
is not like a detailed address you give to a delivery service,
but much more coarse, often defining region or country only.</p>
<p>If you see your IP Address as a risk,
we recommend to use a VPN for the whole system.
Per-app options leave gaps across your system.
For example, tapping a link can expose IP Addresses to unknown parties, which is by far the larger risk.</p>
<p>As this is just how the internet and other messengers work by default,
we do not offer options here or ask upfront questions.</p>
<p>If you see your IP Address as a security or privacy risk,
we recommend to use a VPN, in combination with system lockdown mode.
Hunting down options in all apps on your system will leave gaps.
For example, tapping a link exposes IP Addresses to unknown parties and is the by far larger risk here.</p>
<h3 id="sealedsender">
@@ -1594,7 +1374,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/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/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>
</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>
File diff suppressed because it is too large Load Diff
@@ -14,7 +14,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
/* Basic RPC Transport implementation */
public abstract class BaseRpcTransport implements Rpc.RpcTransport {
public abstract class BaseTransport implements Rpc.Transport {
private final Map<Integer, SettableFuture<JsonNode>> requestFutures = new ConcurrentHashMap<>();
private int requestId = 0;
private final ObjectMapper mapper = new ObjectMapper();
+26 -143
View File
@@ -9,16 +9,16 @@ import chat.delta.rpc.types.*;
public class Rpc {
public interface RpcTransport {
public interface Transport {
void call(String method, JsonNode... params) throws RpcException;
<T> T callForResult(TypeReference<T> resultType, String method, JsonNode... params) throws RpcException;
ObjectMapper getObjectMapper();
}
public final RpcTransport transport;
public final Transport transport;
private final ObjectMapper mapper;
public Rpc(RpcTransport transport) {
public Rpc(Transport transport) {
this.transport = transport;
this.mapper = transport.getObjectMapper();
}
@@ -289,7 +289,6 @@ public class Rpc {
* from a server encoded in a QR code.
* - [Self::list_transports()] to get a list of all configured transports.
* - [Self::delete_transport()] to remove a transport.
* - [Self::set_transport_unpublished()] to set whether contacts see this transport.
*/
public void addOrUpdateTransport(Integer accountId, EnteredLoginParam param) throws RpcException {
transport.call("add_or_update_transport", mapper.valueToTree(accountId), mapper.valueToTree(param));
@@ -313,22 +312,11 @@ public class Rpc {
* Returns the list of all email accounts that are used as a transport in the current profile.
* Use [Self::add_or_update_transport()] to add or change a transport
* and [Self::delete_transport()] to delete a transport.
* Use [Self::list_transports_ex()] to additionally query
* whether the transports are marked as 'unpublished'.
*/
public java.util.List<EnteredLoginParam> listTransports(Integer accountId) throws RpcException {
return transport.callForResult(new TypeReference<java.util.List<EnteredLoginParam>>(){}, "list_transports", mapper.valueToTree(accountId));
}
/**
* Returns the list of all email accounts that are used as a transport in the current profile.
* Use [Self::add_or_update_transport()] to add or change a transport
* and [Self::delete_transport()] to delete a transport.
*/
public java.util.List<TransportListEntry> listTransportsEx(Integer accountId) throws RpcException {
return transport.callForResult(new TypeReference<java.util.List<TransportListEntry>>(){}, "list_transports_ex", mapper.valueToTree(accountId));
}
/**
* Removes the transport with the specified email address
* (i.e. [EnteredLoginParam::addr]).
@@ -337,22 +325,6 @@ public class Rpc {
transport.call("delete_transport", mapper.valueToTree(accountId), mapper.valueToTree(addr));
}
/**
* Change whether the transport is unpublished.
* <p>
* Unpublished transports are not advertised to contacts,
* and self-sent messages are not sent there,
* so that we don't cause extra messages to the corresponding inbox,
* but can still receive messages from contacts who don't know our new transport addresses yet.
* <p>
* The default is false, but when the user updates from a version that didn't have this flag,
* existing secondary transports are set to unpublished,
* so that an existing transport address doesn't suddenly get spammed with a lot of messages.
*/
public void setTransportUnpublished(Integer accountId, String addr, Boolean unpublished) throws RpcException {
transport.call("set_transport_unpublished", mapper.valueToTree(accountId), mapper.valueToTree(addr), mapper.valueToTree(unpublished));
}
/** Signal an ongoing process to stop. */
public void stopOngoingProcess(Integer accountId) throws RpcException {
transport.call("stop_ongoing_process", mapper.valueToTree(accountId));
@@ -396,7 +368,7 @@ public class Rpc {
}
/**
* (deprecated) Gets messages to be processed by the bot and returns their IDs.
* 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,13 +376,6 @@ 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 {
@@ -418,7 +383,7 @@ public class Rpc {
}
/**
* (deprecated) Waits for messages to be processed by the bot and returns their IDs.
* 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.
@@ -429,13 +394,6 @@ 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 {
@@ -443,24 +401,23 @@ public class Rpc {
}
/**
* Estimates the number of messages that will be deleted
* by the `set_config()`-option `delete_device_after`.
* <p>
* Estimate the number of messages that will be deleted
* by the set_config()-options `delete_device_after` or `delete_server_after`.
* This is typically used to show the estimated impact to the user
* before actually enabling deletion of old messages.
* <p>
* Messages in the "Saved Messages" chat are not counted as they will not be deleted automatically.
* <p>
* Parameters:
* - `from_server`: Deprecated, pass `false` here
* - `seconds`: Count messages older than the given number of seconds.
* <p>
* Returns the number of messages that are older than the given number of seconds.
*/
public Integer estimateAutoDeletionCount(Integer accountId, Boolean fromServer, Integer seconds) throws RpcException {
return transport.callForResult(new TypeReference<Integer>(){}, "estimate_auto_deletion_count", mapper.valueToTree(accountId), mapper.valueToTree(fromServer), mapper.valueToTree(seconds));
}
public String initiateAutocryptKeyTransfer(Integer accountId) throws RpcException {
return transport.callForResult(new TypeReference<String>(){}, "initiate_autocrypt_key_transfer", mapper.valueToTree(accountId));
}
public void continueAutocryptKeyTransfer(Integer accountId, Integer messageId, String setupCode) throws RpcException {
transport.call("continue_autocrypt_key_transfer", mapper.valueToTree(accountId), mapper.valueToTree(messageId), mapper.valueToTree(setupCode));
}
public java.util.List<Integer> getChatlistEntries(Integer accountId, Integer listFlags, String queryString, Integer queryContactId) throws RpcException {
return transport.callForResult(new TypeReference<java.util.List<Integer>>(){}, "get_chatlist_entries", mapper.valueToTree(accountId), mapper.valueToTree(listFlags), mapper.valueToTree(queryString), mapper.valueToTree(queryContactId));
}
@@ -552,8 +509,6 @@ public class Rpc {
* if `checkQr()` returns `askVerifyContact` or `askVerifyGroup`
* an out-of-band-verification can be joined using `secure_join()`
* <p>
* @deprecated as of 2026-03; use create_qr_svg(get_chat_securejoin_qr_code()) instead.
* <p>
* chat_id: If set to a group-chat-id,
* the Verified-Group-Invite protocol is offered in the QR code;
* works for protected groups as well as for normal groups.
@@ -843,16 +798,6 @@ 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.
@@ -922,22 +867,8 @@ public class Rpc {
}
/**
* Get all message IDs belonging to a chat.
* Returns all messages of a particular chat.
* <p>
* The list is already sorted and starts with the oldest message.
* Clients should not try to re-sort the list as this would be an expensive action
* and would result in inconsistencies between clients.
* Note that the messages are not necessarily sorted by their ID or by their displayed timestamp;
* UIs need to handle both the case of descending message IDs
* and of decreasing timestamps.
* <p>
* Optionally, 'daymarkers' added to the ID array may help to
* implement virtual lists.
* <p>
* Parameters:
* <p>
* * chat_id The chat ID of which the messages IDs should be queried.
* * _info_only: Deprecated, pass `false` here.
* * `add_daymarker` - If `true`, add day markers as `DC_MSG_ID_DAYMARKER` to the result,
* e.g. [1234, 1237, 9, 1239]. The day marker timestamp is the midnight one for the
* corresponding (following) day in the local timezone.
@@ -955,14 +886,6 @@ public class Rpc {
return transport.callForResult(new TypeReference<java.util.List<Integer>>(){}, "get_existing_msg_ids", mapper.valueToTree(accountId), mapper.valueToTree(msgIds));
}
/**
* Get all messages belonging to a chat.
* <p>
* Similar to `get_message_ids` / `getMessageIds`,
* see that function for details.
* The difference is that this function here returns a list of `MessageListItem`,
* which is an enum of a message or a daymarker.
*/
public java.util.List<MessageListItem> getMessageListItems(Integer accountId, Integer chatId, Boolean infoOnly, Boolean addDaymarker) throws RpcException {
return transport.callForResult(new TypeReference<java.util.List<MessageListItem>>(){}, "get_message_list_items", mapper.valueToTree(accountId), mapper.valueToTree(chatId), mapper.valueToTree(infoOnly), mapper.valueToTree(addDaymarker));
}
@@ -1212,6 +1135,11 @@ public class Rpc {
return transport.callForResult(new TypeReference<String>(){}, "make_vcard", mapper.valueToTree(accountId), mapper.valueToTree(contacts));
}
/** Sets vCard containing the given contacts to the message draft. */
public void setDraftVcard(Integer accountId, Integer msgId, java.util.List<Integer> contacts) throws RpcException {
transport.call("set_draft_vcard", mapper.valueToTree(accountId), mapper.valueToTree(msgId), mapper.valueToTree(contacts));
}
/**
* Returns the [`ChatId`] for the 1:1 chat with `contact_id` if it exists.
* <p>
@@ -1283,19 +1211,12 @@ public class Rpc {
* even if there is no concurrent call to [`CommandApi::provide_backup`],
* but will fail after 60 seconds to avoid deadlocks.
* <p>
* @deprecated as of 2026-03; use `create_qr_svg(get_backup_qr())` instead.
* <p>
* Returns the QR code rendered as an SVG image.
*/
public String getBackupQrSvg(Integer accountId) throws RpcException {
return transport.callForResult(new TypeReference<String>(){}, "get_backup_qr_svg", mapper.valueToTree(accountId));
}
/** Renders the given text as a QR code SVG image. */
public String createQrSvg(String text) throws RpcException {
return transport.callForResult(new TypeReference<String>(){}, "create_qr_svg", mapper.valueToTree(text));
}
/**
* Gets a backup from a remote provider.
* <p>
@@ -1354,47 +1275,10 @@ public class Rpc {
return transport.callForResult(new TypeReference<String>(){}, "get_connectivity_html", mapper.valueToTree(accountId));
}
/**
* Sets current location.
* <p>
* Returns true if location streaming is currently
* enabled and locations should be updated.
* <p>
* Location is represented as latitude and longitude in degrees
* and horizontal accuracy in meters.
*/
public Boolean setLocation(Float latitude, Float longitude, Float accuracy) throws RpcException {
return transport.callForResult(new TypeReference<Boolean>(){}, "set_location", mapper.valueToTree(latitude), mapper.valueToTree(longitude), mapper.valueToTree(accuracy));
}
public java.util.List<Location> getLocations(Integer accountId, Integer chatId, Integer contactId, Integer timestampBegin, Integer timestampEnd) throws RpcException {
return transport.callForResult(new TypeReference<java.util.List<Location>>(){}, "get_locations", mapper.valueToTree(accountId), mapper.valueToTree(chatId), mapper.valueToTree(contactId), mapper.valueToTree(timestampBegin), mapper.valueToTree(timestampEnd));
}
/**
* Enables location streaming in chat identified by `chat_id` for `seconds` seconds.
* <p>
* Pass 0 as the number of seconds to disable location streaming in the chat.
*/
public void sendLocationsToChat(Integer accountId, Integer chatId, Integer seconds) throws RpcException {
transport.call("send_locations_to_chat", mapper.valueToTree(accountId), mapper.valueToTree(chatId), mapper.valueToTree(seconds));
}
/** Returns whether any chat is sending locations. */
public Boolean isSendingLocations(Integer accountId) throws RpcException {
return transport.callForResult(new TypeReference<Boolean>(){}, "is_sending_locations", mapper.valueToTree(accountId));
}
/** Returns whether `chat_id` is sending locations. */
public Boolean isSendingLocationsToChat(Integer accountId, Integer chatId) throws RpcException {
return transport.callForResult(new TypeReference<Boolean>(){}, "is_sending_locations_to_chat", mapper.valueToTree(accountId), mapper.valueToTree(chatId));
}
/** Stops sending locations to all chats. */
public void stopSendingLocations() throws RpcException {
transport.call("stop_sending_locations");
}
public void sendWebxdcStatusUpdate(Integer accountId, Integer instanceMsgId, String updateStr, String descr) throws RpcException {
transport.call("send_webxdc_status_update", mapper.valueToTree(accountId), mapper.valueToTree(instanceMsgId), mapper.valueToTree(updateStr), mapper.valueToTree(descr));
}
@@ -1530,18 +1414,17 @@ public class Rpc {
transport.call("resend_messages", mapper.valueToTree(accountId), mapper.valueToTree(messageIds));
}
/** @deprecated as of 2026-04; use `send_msg` with `Viewtype::Sticker` instead. */
public Integer sendSticker(Integer accountId, Integer chatId, String stickerPath) throws RpcException {
return transport.callForResult(new TypeReference<Integer>(){}, "send_sticker", mapper.valueToTree(accountId), mapper.valueToTree(chatId), mapper.valueToTree(stickerPath));
}
/**
* Sends a reaction to message.
* Send a reaction to message.
* <p>
* A reaction is a string that represents an emoji.
* You can call this function again to change the emoji;
* the last sent reaction overrides all previously sent reactions.
* It is possible to remove the reaction by sending an empty string.
* Reaction is a string of emojis separated by spaces. Reaction to a
* single message can be sent multiple times. The last reaction
* received overrides all previously received reactions. It is
* possible to remove all reactions by sending an empty string.
*/
public Integer sendReaction(Integer accountId, Integer messageId, java.util.List<String> reaction) throws RpcException {
return transport.callForResult(new TypeReference<Integer>(){}, "send_reaction", mapper.valueToTree(accountId), mapper.valueToTree(messageId), mapper.valueToTree(reaction));
@@ -12,13 +12,6 @@ public class EnteredLoginParam {
/** TLS options: whether to allow invalid certificates and/or invalid hostnames. Default: Automatic */
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
public EnteredCertificateChecks certificateChecks;
/**
* IMAP server folder.
* <p>
* Defaults to "INBOX" if not set. Should not be an empty string.
*/
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
public String imapFolder;
/** Imap server port. */
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
public Integer imapPort;
@@ -387,8 +387,6 @@ public abstract class EventType {
public static class IncomingCallAccepted extends EventType {
/** ID of the chat which the message belongs to. */
public Integer chat_id;
/** The call was accepted from this device (process). */
public Boolean from_this_device;
/** ID of the info message referring to the call. */
public Integer msg_id;
}
@@ -32,6 +32,7 @@ public class Message {
public Boolean isEdited;
public Boolean isForwarded;
public Boolean isInfo;
public Boolean isSetupmessage;
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
public Integer originalMsgId;
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
@@ -46,6 +47,8 @@ public class Message {
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
public Integer savedMessageId;
public Contact sender;
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
public String setupCodeBegin;
/**
* True if the message was correctly encrypted&signed, false otherwise. Historically, UIs showed a small padlock on the message then.
* <p>
@@ -42,6 +42,7 @@ public abstract class MessageLoadResult {
public Boolean isEdited;
public Boolean isForwarded;
public Boolean isInfo;
public Boolean isSetupmessage;
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
public Integer originalMsgId;
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
@@ -56,6 +57,8 @@ public abstract class MessageLoadResult {
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
public Integer savedMessageId;
public Contact sender;
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
public String setupCodeBegin;
/**
* True if the message was correctly encrypted&signed, false otherwise. Historically, UIs showed a small padlock on the message then.
* <p>
@@ -25,8 +25,6 @@ public abstract class Qr {
public String fingerprint;
/** Invite number. */
public String invitenumber;
/** Whether the inviter supports the new Securejoin v3 protocol */
public Boolean is_v3;
}
/** Ask the user whether to join the group. */
@@ -43,8 +41,6 @@ public abstract class Qr {
public String grpname;
/** Invite number. */
public String invitenumber;
/** Whether the inviter supports the new Securejoin v3 protocol */
public Boolean is_v3;
}
/** Ask the user whether to join the broadcast channel. */
@@ -59,8 +55,6 @@ public abstract class Qr {
public String grpid;
/** Invite number. */
public String invitenumber;
/** Whether the inviter supports the new Securejoin v3 protocol */
public Boolean is_v3;
/** The user-visible name of this broadcast channel */
public String name;
}
@@ -5,6 +5,6 @@ package chat.delta.rpc.types;
public class Reactions {
/** Unique reactions and their count, sorted in descending order. */
public java.util.List<Reaction> reactions;
/** Map from a contact to it's reaction to message. There is only a single reaction per contact, but this contains a list of reactions for historical reasons. */
/** Map from a contact to it's reaction to message. */
public java.util.Map<String, java.util.List<String>> reactionsByContact;
}
@@ -1,9 +0,0 @@
/* Autogenerated file, do not edit manually */
package chat.delta.rpc.types;
public class TransportListEntry {
/** Whether this transport is set to 'unpublished'. See `set_transport_unpublished` / `setTransportUnpublished` for details. */
public Boolean isUnpublished;
/** The login data entered by the user. */
public EnteredLoginParam param;
}
@@ -14,7 +14,7 @@ public enum Viewtype {
Gif,
/**
* Message containing a sticker, similar to image.
* Message containing a sticker, similar to image. NB: When sending, the message viewtype may be changed to `Image` by some heuristics like checking for transparent pixels. Use `Message::force_sticker()` to disable them.
* <p>
* If possible, the ui should display the image without borders in a transparent way. A click on a sticker will offer to install the sticker set in some future.
*/
@@ -15,10 +15,6 @@ public class WebxdcMessageInfo {
public String icon;
/** True if full internet access should be granted to the app. */
public Boolean internetAccess;
/** Define if the local user is the one who initially shared the webxdc application in the chat. */
public Boolean isAppSender;
/** Define if the app runs in a broadcasting context. */
public Boolean isBroadcast;
/**
* The name of the app.
* <p>
@@ -176,6 +176,8 @@ public class DcContext {
public native String getConnectivityHtml();
public native String initiateKeyTransfer();
public native void imex(int what, String dir);
public native String imexHasBackup(String dir);
@@ -25,6 +25,7 @@ public class DcMsg {
public static final int DC_INFO_GROUP_IMAGE_CHANGED = 3;
public static final int DC_INFO_MEMBER_ADDED_TO_GROUP = 4;
public static final int DC_INFO_MEMBER_REMOVED_FROM_GROUP = 5;
public static final int DC_INFO_AUTOCRYPT_SETUP_MESSAGE = 6;
public static final int DC_INFO_SECURE_JOIN_MESSAGE = 7;
public static final int DC_INFO_LOCATIONSTREAMING_ENABLED = 8;
public static final int DC_INFO_LOCATION_ONLY = 9;
@@ -59,6 +60,8 @@ public class DcMsg {
public static final int DC_VIDEOCHATTYPE_UNKNOWN = 0;
public static final int DC_VIDEOCHATTYPE_BASICWEBRTC = 1;
private static final String TAG = DcMsg.class.getSimpleName();
public DcMsg(DcContext context, int viewtype) {
msgCPtr = context.createMsgCPtr(viewtype);
}
@@ -188,12 +191,16 @@ public class DcMsg {
public native boolean hasHtml();
public native String getSetupCodeBegin();
public native void setText(String text);
public native void setSubject(String text);
public native void setHtml(String text);
public native void forceSticker();
public native void setFileAndDeduplicate(String file, String name, String filemime);
public native void setDimension(int width, int height);
@@ -1,9 +1,9 @@
package com.b44t.messenger;
import chat.delta.rpc.BaseRpcTransport;
import chat.delta.rpc.BaseTransport;
/* RPC transport over C FFI */
public class FFITransport extends BaseRpcTransport {
public class FFITransport extends BaseTransport {
private final DcJsonrpcInstance dcJsonrpcInstance;
public FFITransport(DcJsonrpcInstance dcJsonrpcInstance) {
@@ -4,6 +4,7 @@ import android.content.ComponentName;
import android.os.Bundle;
import android.util.Log;
import android.view.MenuItem;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
@@ -11,23 +12,21 @@ import androidx.appcompat.view.ActionMode;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.lifecycle.ViewModelProvider;
import androidx.media3.session.MediaController;
import androidx.media3.session.SessionCommand;
import androidx.media3.session.SessionToken;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import androidx.viewpager.widget.ViewPager;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcEvent;
import com.b44t.messenger.DcMsg;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
import org.thoughtcrime.securesms.components.audioplay.AudioPlaybackViewModel;
import org.thoughtcrime.securesms.components.audioplay.ChatAudioQueueProvider;
import org.thoughtcrime.securesms.connect.DcEventCenter;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.service.AudioPlaybackService;
@@ -36,7 +35,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class AllMediaActivity extends PassphraseRequiredActionBarActivity
implements DcEventCenter.DcEventDelegate {
private static final String TAG = "AllMediaActivity";
private static final String TAG = AllMediaActivity.class.getSimpleName();
public static final String CHAT_ID_EXTRA = "chat_id";
public static final String CONTACT_ID_EXTRA = "contact_id";
@@ -55,6 +54,7 @@ public class AllMediaActivity extends PassphraseRequiredActionBarActivity
this.type3 = type3;
}
}
;
private DcContext dcContext;
private int chatId;
@@ -63,7 +63,7 @@ public class AllMediaActivity extends PassphraseRequiredActionBarActivity
private final ArrayList<TabData> tabs = new ArrayList<>();
private Toolbar toolbar;
private TabLayout tabLayout;
private ViewPager2 viewPager;
private ViewPager viewPager;
private @Nullable MediaController mediaController;
private ListenableFuture<MediaController> mediaControllerFuture;
@@ -97,20 +97,8 @@ public class AllMediaActivity extends PassphraseRequiredActionBarActivity
isGlobalGallery() ? R.string.menu_all_media : R.string.apps_and_media);
}
AllMediaPagerAdapter adapter = new AllMediaPagerAdapter(this);
this.viewPager.setAdapter(adapter);
this.viewPager.registerOnPageChangeCallback(
new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
adapter.onPageChanged(position);
}
});
new TabLayoutMediator(
this.tabLayout,
this.viewPager,
(tab, position) -> tab.setText(getString(tabs.get(position).title)))
.attach();
this.tabLayout.setupWithViewPager(viewPager);
this.viewPager.setAdapter(new AllMediaPagerAdapter(getSupportFragmentManager()));
if (getIntent().getBooleanExtra(FORCE_GALLERY, false)) {
this.viewPager.setCurrentItem(1, false);
}
@@ -119,9 +107,7 @@ public class AllMediaActivity extends PassphraseRequiredActionBarActivity
eventCenter.addObserver(DcContext.DC_EVENT_CHAT_MODIFIED, this);
eventCenter.addObserver(DcContext.DC_EVENT_CONTACTS_CHANGED, this);
int accountId = DcHelper.getAccounts(this).getSelectedAccount().getAccountId();
playbackViewModel = new ViewModelProvider(this).get(AudioPlaybackViewModel.class);
playbackViewModel.setQueueProvider(new ChatAudioQueueProvider(this, chatId, accountId));
initializeMediaController();
}
@@ -133,7 +119,6 @@ public class AllMediaActivity extends PassphraseRequiredActionBarActivity
mediaController = null;
playbackViewModel.setMediaController(null);
}
playbackViewModel.setQueueProvider(null);
super.onDestroy();
}
@@ -197,16 +182,31 @@ public class AllMediaActivity extends PassphraseRequiredActionBarActivity
return contactId == 0 && chatId == 0;
}
private class AllMediaPagerAdapter extends FragmentStateAdapter {
private int currentPosition = -1;
private class AllMediaPagerAdapter extends FragmentStatePagerAdapter {
private Object currentFragment = null;
AllMediaPagerAdapter(FragmentActivity activity) {
super(activity);
AllMediaPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
@Override
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
super.setPrimaryItem(container, position, object);
if (currentFragment != null && currentFragment != object) {
ActionMode action = null;
if (currentFragment instanceof MessageSelectorFragment) {
action = ((MessageSelectorFragment) currentFragment).getActionMode();
}
if (action != null) {
action.finish();
}
}
currentFragment = object;
}
@NonNull
@Override
public Fragment createFragment(int position) {
public Fragment getItem(int position) {
TabData data = tabs.get(position);
Fragment fragment;
Bundle args = new Bundle();
@@ -229,24 +229,13 @@ public class AllMediaActivity extends PassphraseRequiredActionBarActivity
}
@Override
public int getItemCount() {
public int getCount() {
return tabs.size();
}
private void onPageChanged(int newPosition) {
if (currentPosition != -1 && currentPosition != newPosition) {
for (Fragment fragment : getSupportFragmentManager().getFragments()) {
if (!(fragment instanceof MessageSelectorFragment)) {
continue;
}
ActionMode action = ((MessageSelectorFragment) fragment).getActionMode();
if (action != null) {
action.finish();
}
}
}
currentPosition = newPosition;
@Override
public CharSequence getPageTitle(int position) {
return getString(tabs.get(position).title);
}
}
@@ -9,6 +9,7 @@ import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.app.NotificationManagerCompat;
@@ -18,19 +19,14 @@ import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.NetworkType;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager;
import chat.delta.rpc.Rpc;
import chat.delta.rpc.RpcException;
import com.b44t.messenger.DcAccounts;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcEvent;
import com.b44t.messenger.DcEventChannel;
import com.b44t.messenger.DcEventEmitter;
import com.b44t.messenger.FFITransport;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.TimeUnit;
import org.thoughtcrime.securesms.calls.CallCoordinator;
import org.thoughtcrime.securesms.connect.AccountManager;
import org.thoughtcrime.securesms.connect.DcEventCenter;
import org.thoughtcrime.securesms.connect.DcHelper;
@@ -40,6 +36,7 @@ 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;
@@ -51,26 +48,35 @@ import org.thoughtcrime.securesms.util.SignalProtocolLoggerProvider;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.webxdc.WebxdcGarbageCollectionWorker;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.TimeUnit;
import chat.delta.rpc.Rpc;
import chat.delta.rpc.RpcException;
public class ApplicationContext extends MultiDexApplication {
private static final String TAG = "ApplicationContext";
private static final String TAG = ApplicationContext.class.getSimpleName();
private static final Object initLock = new Object();
private static volatile boolean isInitialized = false;
private static DcAccounts dcAccounts;
private Rpc rpc;
private DcContext dcContext;
private static DcAccounts dcAccounts;
private Rpc rpc;
private DcContext dcContext;
private DcEventCenter eventCenter;
private NotificationCenter notificationCenter;
private JobManager jobManager;
private DcLocationManager dcLocationManager;
private DcEventCenter eventCenter;
private NotificationCenter notificationCenter;
private JobManager jobManager;
private int debugOnAvailableCount;
private int debugOnBlockedStatusChangedCount;
private int debugOnCapabilitiesChangedCount;
private int debugOnLinkPropertiesChangedCount;
private int debugOnAvailableCount;
private int debugOnBlockedStatusChangedCount;
private int debugOnCapabilitiesChangedCount;
private int debugOnLinkPropertiesChangedCount;
public static ApplicationContext getInstance(@NonNull Context context) {
return (ApplicationContext) context.getApplicationContext();
return (ApplicationContext)context.getApplicationContext();
}
private static void ensureInitialized() {
@@ -87,8 +93,8 @@ public class ApplicationContext extends MultiDexApplication {
}
/**
* Get DcAccounts instance, waiting for initialization if necessary. This method is thread-safe
* and will block until initialization is complete.
* Get DcAccounts instance, waiting for initialization if necessary.
* This method is thread-safe and will block until initialization is complete.
*/
public static DcAccounts getDcAccounts() {
ensureInitialized();
@@ -96,8 +102,8 @@ public class ApplicationContext extends MultiDexApplication {
}
/**
* Get Rpc instance, waiting for initialization if necessary. This method is thread-safe and will
* block until initialization is complete.
* Get Rpc instance, waiting for initialization if necessary.
* This method is thread-safe and will block until initialization is complete.
*/
public Rpc getRpc() {
ensureInitialized();
@@ -105,8 +111,8 @@ public class ApplicationContext extends MultiDexApplication {
}
/**
* Get DcContext instance, waiting for initialization if necessary. This method is thread-safe and
* will block until initialization is complete.
* Get DcContext instance, waiting for initialization if necessary.
* This method is thread-safe and will block until initialization is complete.
*/
public DcContext getDcContext() {
ensureInitialized();
@@ -115,8 +121,8 @@ public class ApplicationContext extends MultiDexApplication {
/**
* Set DcContext instance. This should only be called by AccountManager when switching accounts,
* which only happens after initial initialization is complete. This method is thread-safe but
* does NOT trigger initialization or notify waiting threads.
* which only happens after initial initialization is complete.
* This method is thread-safe but does NOT trigger initialization or notify waiting threads.
*/
public void setDcContext(DcContext dcContext) {
synchronized (initLock) {
@@ -125,8 +131,17 @@ public class ApplicationContext extends MultiDexApplication {
}
/**
* Get DcEventCenter instance, waiting for initialization if necessary. This method is thread-safe
* and will block until initialization is complete.
* 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.
*/
public DcEventCenter getEventCenter() {
ensureInitialized();
@@ -134,8 +149,8 @@ public class ApplicationContext extends MultiDexApplication {
}
/**
* Get NotificationCenter instance, waiting for initialization if necessary. This method is
* thread-safe and will block until initialization is complete.
* Get NotificationCenter instance, waiting for initialization if necessary.
* This method is thread-safe and will block until initialization is complete.
*/
public NotificationCenter getNotificationCenter() {
ensureInitialized();
@@ -146,30 +161,26 @@ public class ApplicationContext extends MultiDexApplication {
public void onCreate() {
super.onCreate();
Thread.setDefaultUncaughtExceptionHandler(
(thread, throwable) -> {
StringWriter stringWriter = new StringWriter();
throwable.printStackTrace(new PrintWriter(stringWriter, true));
String errorMsg =
"Android " + Build.VERSION.RELEASE + ":\n" + stringWriter.getBuffer().toString();
errorMsg += "\n" + LogViewFragment.grabLogcat();
String subject =
"ArcaneChat " + BuildConfig.VERSION_NAME + "-" + BuildConfig.FLAVOR + " Crash Report";
Intent intent = new Intent(android.content.Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
intent.putExtra(android.content.Intent.EXTRA_TEXT, subject + "\n\n" + errorMsg);
intent.putExtra(Intent.EXTRA_EMAIL, "adb@merlinux.eu");
Intent chooser = Intent.createChooser(intent, subject);
chooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
chooser.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
startActivity(chooser);
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
StringWriter stringWriter = new StringWriter();
throwable.printStackTrace(new PrintWriter(stringWriter, true));
String errorMsg = "Android " + Build.VERSION.RELEASE +":\n" + stringWriter.getBuffer().toString();
errorMsg += "\n" + LogViewFragment.grabLogcat();
String subject = "ArcaneChat " + BuildConfig.VERSION_NAME + "-" + BuildConfig.FLAVOR + " Crash Report";
Intent intent = new Intent(android.content.Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
intent.putExtra(android.content.Intent.EXTRA_TEXT, subject + "\n\n" + errorMsg);
intent.putExtra(Intent.EXTRA_EMAIL, "adb@merlinux.eu");
Intent chooser = Intent.createChooser(intent, subject);
chooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
chooser.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
startActivity(chooser);
try {
ApplicationContext.this.finalize();
} catch (Throwable e) {
}
});
try {
ApplicationContext.this.finalize();
} catch (Throwable e) {}
});
// if (LeakCanary.isInAnalyzerProcess(this)) {
// // This process is dedicated to LeakCanary for heap analysis.
@@ -183,107 +194,90 @@ public class ApplicationContext extends MultiDexApplication {
System.loadLibrary("native-utils");
// Initialize DcAccounts in background to avoid ANR during SQL migrations
Util.runOnBackground(
() -> {
synchronized (initLock) {
try {
DcEventChannel eventChannel = new DcEventChannel();
DcEventEmitter emitter = eventChannel.getEventEmitter();
eventCenter = new DcEventCenter(this);
Util.runOnBackground(() -> {
synchronized (initLock) {
try {
DcEventChannel eventChannel = new DcEventChannel();
DcEventEmitter emitter = eventChannel.getEventEmitter();
eventCenter = new DcEventCenter(this);
new Thread(
() -> {
Log.i(TAG, "Starting event loop");
while (true) {
DcEvent event = emitter.getNextEvent();
if (event == null) {
break;
}
if (isInitialized) {
eventCenter.handleEvent(event);
} else {
// not fully initialized, only handle logging events,
// ex. account migrations during DcAccounts initialization
eventCenter.handleLogging(event);
}
}
Log.i("DeltaChat", "shutting down event handler");
},
"eventThread")
.start();
dcAccounts =
new DcAccounts(
new File(getFilesDir(), "accounts").getAbsolutePath(), eventChannel);
Log.i(TAG, "DcAccounts created");
rpc = new Rpc(new FFITransport(dcAccounts.getJsonrpcInstance()));
Log.i(TAG, "Rpc created");
AccountManager.getInstance().migrateToDcAccounts(this, dcAccounts);
int[] allAccounts = dcAccounts.getAll();
Log.i(TAG, "Number of profiles: " + allAccounts.length);
for (int accountId : allAccounts) {
DcContext ac = dcAccounts.getAccount(accountId);
if (!ac.isOpen()) {
try {
DatabaseSecret secret =
DatabaseSecretProvider.getOrCreateDatabaseSecret(this, accountId);
boolean res = ac.open(secret.asString());
if (res)
Log.i(
TAG,
"Successfully opened account "
+ accountId
+ ", path: "
+ ac.getBlobdir());
else
Log.e(
TAG, "Error opening account " + accountId + ", path: " + ac.getBlobdir());
} catch (Exception e) {
Log.e(
TAG,
"Failed to open account "
+ accountId
+ ", path: "
+ ac.getBlobdir()
+ ": "
+ e);
e.printStackTrace();
}
new Thread(() -> {
Log.i(TAG, "Starting event loop");
while (true) {
DcEvent event = emitter.getNextEvent();
if (event == null) {
break;
}
// 2025-12-16: The setting was removed.
// Revert it to the default if it was changed in the past.
ac.setConfigInt("webxdc_realtime_enabled", 1);
}
if (allAccounts.length == 0) {
try {
rpc.addAccount();
} catch (RpcException e) {
e.printStackTrace();
if (isInitialized) {
eventCenter.handleEvent(event);
} else {
// not fully initialized, only handle logging events,
// ex. account migrations during DcAccounts initialization
eventCenter.handleLogging(event);
}
}
dcContext = dcAccounts.getSelectedAccount();
notificationCenter = new NotificationCenter(this);
Log.i("DeltaChat", "shutting down event handler");
}, "eventThread").start();
isInitialized = true;
initLock.notifyAll();
Log.i(TAG, "DcAccounts initialization complete");
dcAccounts = new DcAccounts(new File(getFilesDir(), "accounts").getAbsolutePath(), eventChannel);
Log.i(TAG, "DcAccounts created");
rpc = new Rpc(new FFITransport(dcAccounts.getJsonrpcInstance()));
Log.i(TAG, "Rpc created");
AccountManager.getInstance().migrateToDcAccounts(this);
// set translations before starting I/O to avoid sending untranslated MDNs (issue
// #2288)
DcHelper.setStockTranslations(this);
int[] allAccounts = dcAccounts.getAll();
Log.i(TAG, "Number of profiles: " + allAccounts.length);
for (int accountId : allAccounts) {
DcContext ac = dcAccounts.getAccount(accountId);
if (!ac.isOpen()) {
try {
DatabaseSecret secret = DatabaseSecretProvider.getOrCreateDatabaseSecret(this, accountId);
boolean res = ac.open(secret.asString());
if (res) Log.i(TAG, "Successfully opened account " + accountId + ", path: " + ac.getBlobdir());
else Log.e(TAG, "Error opening account " + accountId + ", path: " + ac.getBlobdir());
} catch (Exception e) {
Log.e(TAG, "Failed to open account " + accountId + ", path: " + ac.getBlobdir() + ": " + e);
e.printStackTrace();
}
}
dcAccounts.startIo();
} catch (Exception e) {
Log.e(TAG, "Fatal error during DcAccounts initialization", e);
// Mark as initialized even on error to avoid deadlock
isInitialized = true;
initLock.notifyAll();
throw new RuntimeException("Failed to initialize DcAccounts", e);
// 2025-12-16: The setting was removed.
// Revert it to the default if it was changed in the past.
ac.setConfigInt("webxdc_realtime_enabled", 1);
// 2025-11-12: this is needed until core starts ignoring "delete_server_after" for chatmail
if (ac.isChatmail()) {
ac.setConfig("delete_server_after", null); // reset
}
}
});
if (allAccounts.length == 0) {
try {
rpc.addAccount();
} catch (RpcException e) {
e.printStackTrace();
}
}
dcContext = dcAccounts.getSelectedAccount();
notificationCenter = new NotificationCenter(this);
dcLocationManager = new DcLocationManager(this, dcContext);
isInitialized = true;
initLock.notifyAll();
Log.i(TAG, "DcAccounts initialization complete");
// set translations before starting I/O to avoid sending untranslated MDNs (issue #2288)
DcHelper.setStockTranslations(this);
dcAccounts.startIo();
} catch (Exception e) {
Log.e(TAG, "Fatal error during DcAccounts initialization", e);
// Mark as initialized even on error to avoid deadlock
isInitialized = true;
initLock.notifyAll();
throw new RuntimeException("Failed to initialize DcAccounts", e);
}
}
});
// October-2025 migration: delete deprecated "permanent channel" id
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
@@ -294,50 +288,33 @@ public class ApplicationContext extends MultiDexApplication {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
ConnectivityManager connectivityManager =
(ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
connectivityManager.registerDefaultNetworkCallback(
new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(@NonNull android.net.Network network) {
Log.i(
"DeltaChat",
"++++++++++++++++++ NetworkCallback.onAvailable() #" + debugOnAvailableCount++);
getDcAccounts().maybeNetwork();
}
(ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
connectivityManager.registerDefaultNetworkCallback(new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(@NonNull android.net.Network network) {
Log.i("DeltaChat", "++++++++++++++++++ NetworkCallback.onAvailable() #" + debugOnAvailableCount++);
getDcAccounts().maybeNetwork();
}
@Override
public void onBlockedStatusChanged(
@NonNull android.net.Network network, boolean blocked) {
Log.i(
"DeltaChat",
"++++++++++++++++++ NetworkCallback.onBlockedStatusChanged() #"
+ debugOnBlockedStatusChangedCount++);
}
@Override
public void onBlockedStatusChanged(@NonNull android.net.Network network, boolean blocked) {
Log.i("DeltaChat", "++++++++++++++++++ NetworkCallback.onBlockedStatusChanged() #" + debugOnBlockedStatusChangedCount++);
}
@Override
public void onCapabilitiesChanged(
@NonNull android.net.Network network, NetworkCapabilities networkCapabilities) {
// usually called after onAvailable(), so a maybeNetwork seems contraproductive
Log.i(
"DeltaChat",
"++++++++++++++++++ NetworkCallback.onCapabilitiesChanged() #"
+ debugOnCapabilitiesChangedCount++);
}
@Override
public void onCapabilitiesChanged(@NonNull android.net.Network network, NetworkCapabilities networkCapabilities) {
// usually called after onAvailable(), so a maybeNetwork seems contraproductive
Log.i("DeltaChat", "++++++++++++++++++ NetworkCallback.onCapabilitiesChanged() #" + debugOnCapabilitiesChangedCount++);
}
@Override
public void onLinkPropertiesChanged(
@NonNull android.net.Network network, LinkProperties linkProperties) {
Log.i(
"DeltaChat",
"++++++++++++++++++ NetworkCallback.onLinkPropertiesChanged() #"
+ debugOnLinkPropertiesChangedCount++);
}
});
@Override
public void onLinkPropertiesChanged(@NonNull android.net.Network network, LinkProperties linkProperties) {
Log.i("DeltaChat", "++++++++++++++++++ NetworkCallback.onLinkPropertiesChanged() #" + debugOnLinkPropertiesChangedCount++);
}
});
} // no else: use old method for debugging
BroadcastReceiver networkStateReceiver = new NetworkStateReceiver();
registerReceiver(
networkStateReceiver,
new IntentFilter(android.net.ConnectivityManager.CONNECTIVITY_ACTION));
registerReceiver(networkStateReceiver, new IntentFilter(android.net.ConnectivityManager.CONNECTIVITY_ACTION));
KeepAliveService.maybeStartSelf(this);
@@ -345,23 +322,16 @@ public class ApplicationContext extends MultiDexApplication {
initializeJobManager();
InChatSounds.getInstance(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
EglUtils.getEglBase();
CallCoordinator.getInstance(this);
}
DynamicTheme.setDefaultDayNightMode(this);
IntentFilter filter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
registerReceiver(
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Util.localeChanged();
DcHelper.setStockTranslations(context);
}
},
filter);
}
}, filter);
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
@@ -372,34 +342,31 @@ public class ApplicationContext extends MultiDexApplication {
// MAYBE TODO: i think the ApplicationContext is also created
// when the app is stated by FetchWorker timeouts.
// in this case, the normal threads shall not be started.
Constraints constraints =
new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build();
PeriodicWorkRequest fetchWorkRequest =
new PeriodicWorkRequest.Builder(
FetchWorker.class,
PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS, // usually 15 minutes
TimeUnit.MILLISECONDS,
PeriodicWorkRequest
.MIN_PERIODIC_FLEX_MILLIS, // the start may be preferred by up to 5 minutes,
// so we run every 10-15 minutes
TimeUnit.MILLISECONDS)
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build();
PeriodicWorkRequest fetchWorkRequest = new PeriodicWorkRequest.Builder(
FetchWorker.class,
PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS, // usually 15 minutes
TimeUnit.MILLISECONDS,
PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS, // the start may be preferred by up to 5 minutes, so we run every 10-15 minutes
TimeUnit.MILLISECONDS)
.setConstraints(constraints)
.build();
WorkManager.getInstance(this)
.enqueueUniquePeriodicWork(
"FetchWorker", ExistingPeriodicWorkPolicy.KEEP, fetchWorkRequest);
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"FetchWorker",
ExistingPeriodicWorkPolicy.KEEP,
fetchWorkRequest);
}
PeriodicWorkRequest webxdcGarbageCollectionRequest =
new PeriodicWorkRequest.Builder(
WebxdcGarbageCollectionWorker.class,
PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS,
TimeUnit.MILLISECONDS,
PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS,
TimeUnit.MILLISECONDS)
PeriodicWorkRequest webxdcGarbageCollectionRequest = new PeriodicWorkRequest.Builder(
WebxdcGarbageCollectionWorker.class,
PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS,
TimeUnit.MILLISECONDS,
PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS,
TimeUnit.MILLISECONDS)
.build();
WorkManager.getInstance(this)
.enqueueUniquePeriodicWork(
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"WebxdcGarbageCollectionWorker",
ExistingPeriodicWorkPolicy.KEEP,
webxdcGarbageCollectionRequest);
@@ -407,14 +374,6 @@ public class ApplicationContext extends MultiDexApplication {
Log.i("DeltaChat", "+++++++++++ ApplicationContext.onCreate() finished ++++++++++");
}
@Override
public void onTerminate() {
super.onTerminate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
EglUtils.release();
}
}
public JobManager getJobManager() {
return jobManager;
}
@@ -20,7 +20,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public abstract class BaseActionBarActivity extends AppCompatActivity {
private static final String TAG = "BaseActionBarActivity";
private static final String TAG = BaseActionBarActivity.class.getSimpleName();
protected DynamicTheme dynamicTheme = new DynamicTheme();
protected void onPreCreate() {
@@ -81,14 +81,19 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
}
}
public void makeSearchMenuVisible(final Menu menu, final MenuItem searchItem) {
public void makeSearchMenuVisible(final Menu menu, final MenuItem searchItem, boolean visible) {
for (int i = 0; i < menu.size(); ++i) {
MenuItem item = menu.getItem(i);
int id = item.getItemId();
if (id == R.id.menu_search_up || id == R.id.menu_search_down) {
item.setVisible(true);
} else if (item != searchItem) {
item.setVisible(false); // hide all other items
item.setVisible(visible);
} else if (id == R.id.menu_search_counter) {
item.setVisible(false); // always hide menu_search_counter initially
} else if (item == searchItem) {
; // searchItem is just always visible
} else {
item.setVisible(
!visible); // if search is shown, other items are hidden - and the other way round
}
}
}
@@ -13,7 +13,6 @@ 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;
@@ -27,8 +26,6 @@ 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;
@@ -47,7 +44,6 @@ 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";
protected ActionMode actionMode;
protected PulsingFloatingActionButton fab;
@@ -174,17 +170,6 @@ 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 =
@@ -244,26 +229,6 @@ 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());
@@ -423,18 +388,9 @@ public abstract class BaseConversationListFragment extends Fragment implements A
.setIcon(IconCompat.createWithAdaptiveBitmap(avatar))
.setIntent(intent)
.build();
boolean success;
try {
success = ShortcutManagerCompat.requestPinShortcut(activity, shortcutInfoCompat, null);
} catch (Exception e) {
Log.e(TAG, "ErrAddToHomescreen: requestPinShortcut() failed", e);
success = false;
}
boolean finalSuccess = success;
Util.runOnMain(
() -> {
if (!finalSuccess) {
if (!ShortcutManagerCompat.requestPinShortcut(activity, shortcutInfoCompat, null)) {
Toast.makeText(
activity,
"ErrAddToHomescreen: requestPinShortcut() failed",
@@ -453,7 +409,6 @@ 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);
@@ -462,7 +417,6 @@ 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);
@@ -471,17 +425,12 @@ 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);
}
}
@@ -535,9 +484,6 @@ 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;
@@ -29,7 +29,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
*/
public abstract class ContactSelectionActivity extends PassphraseRequiredActionBarActivity
implements ContactSelectionListFragment.OnContactSelectedListener {
private static final String TAG = "ContactSelectionActivity";
private static final String TAG = ContactSelectionActivity.class.getSimpleName();
protected ContactSelectionListFragment contactsFragment;
@@ -67,7 +67,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
*/
public class ContactSelectionListFragment extends Fragment
implements LoaderManager.LoaderCallbacks<DcContactsLoader.Ret>, DcEventCenter.DcEventDelegate {
private static final String TAG = "ContactSelectionListFragment";
private static final String TAG = ContactSelectionListFragment.class.getSimpleName();
public static final String MULTI_SELECT = "multi_select";
public static final String SELECT_UNENCRYPTED_EXTRA = "select_unencrypted_extra";
File diff suppressed because it is too large Load Diff
@@ -80,7 +80,7 @@ import org.thoughtcrime.securesms.util.views.ConversationAdaptiveActionsToolbar;
@SuppressLint("StaticFieldLeak")
public class ConversationFragment extends MessageSelectorFragment {
private static final String TAG = "ConversationFragment";
private static final String TAG = ConversationFragment.class.getSimpleName();
private static final int SCROLL_ANIMATION_THRESHOLD = 50;
@@ -944,8 +944,6 @@ public class ConversationFragment extends MessageSelectorFragment {
WebxdcActivity.openWebxdcActivity(
getContext(), messageRecord.getParent(), messageRecord.getWebxdcHref());
}
} else if (messageRecord.getInfoType() == DcMsg.DC_INFO_LOCATIONSTREAMING_ENABLED) {
WebxdcActivity.openMaps(getContext(), (int) chatId);
} else if (messageRecord.getInfoType() == DcMsg.DC_INFO_CHAT_DESCRIPTION_CHANGED) {
Intent intent = new Intent(getContext(), ProfileActivity.class);
intent.putExtra(ProfileActivity.CHAT_ID_EXTRA, (int) chatId);
@@ -1061,6 +1059,7 @@ 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,
File diff suppressed because it is too large Load Diff
@@ -69,7 +69,6 @@ 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;
@@ -91,7 +90,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class ConversationListActivity extends PassphraseRequiredActionBarActivity
implements ConversationListFragment.ConversationSelectedListener {
private static final String TAG = "ConversationListActivity";
private static final String TAG = ConversationListActivity.class.getSimpleName();
private static final String OPENPGP4FPR = "openpgp4fpr";
private static final String NDK_ARCH_WARNED = "ndk_arch_warned";
public static final String CLEAR_NOTIFICATIONS = "clear_notifications";
@@ -450,9 +449,6 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
refreshTitle();
invalidateOptionsMenu();
DirectShareUtil.triggerRefreshDirectShare(this);
if (DcHelper.getContext(this).isSendingLocationsToChat(0)) {
LocationStreamingService.ensureRunning(this);
}
}
@Override
@@ -607,13 +603,6 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
}
}
public void handleQrFromSearch(String rawQrString) {
qrData = rawQrString;
new QrCodeHandler(this)
.handleQrData(
rawQrString, SecurejoinSource.Scan, SecurejoinUiPath.QrIcon, relayLockLauncher);
}
private void handleResetRelaying() {
resetRelayingMessageContent(this);
refreshTitle();
@@ -687,7 +676,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
// should be deleted.
try {
DcContext dcContext = DcHelper.getContext(this);
final String deviceMsgLabel = "update_2_46_0a_android";
final String deviceMsgLabel = "update_2_33_1_android";
if (!dcContext.wasDeviceMsgEverAdded(deviceMsgLabel)) {
DcMsg msg = null;
if (!getIntent().getBooleanExtra(FROM_WELCOME, false)) {
@@ -699,11 +688,7 @@ 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_33, "https://arcanechat.me/#contribute"));
}
dcContext.addDeviceMsg(deviceMsgLabel, msg);
@@ -23,6 +23,7 @@ import android.content.Context;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -55,11 +56,12 @@ public class ConversationListFragment extends BaseConversationListFragment
public static final String ARCHIVE = "archive";
public static final String RELOAD_LIST = "reload_list";
private static final String TAG = "ConversationListFragment";
private static final String TAG = ConversationListFragment.class.getSimpleName();
private RecyclerView list;
private View emptyState;
private TextView emptySearch;
private final String queryFilter = "";
private boolean archive;
private Timer reloadTimer;
private boolean chatlistJustLoaded;
@@ -283,17 +285,22 @@ public class ConversationListFragment extends BaseConversationListFragment
Log.w(TAG, "Ignoring call to loadChatlist()");
return;
}
long startMs = System.currentTimeMillis();
DcChatlist chatlist = DcHelper.getContext(context).getChatlist(listflags, null, 0);
Log.i(TAG, "⏰ getChatlist(): " + (System.currentTimeMillis() - startMs) + "ms");
DcChatlist chatlist =
DcHelper.getContext(context)
.getChatlist(listflags, queryFilter.isEmpty() ? null : queryFilter, 0);
Util.runOnMain(
() -> {
if (chatlist.getCnt() <= 0) {
if (chatlist.getCnt() <= 0 && TextUtils.isEmpty(queryFilter)) {
list.setVisibility(View.INVISIBLE);
emptyState.setVisibility(View.VISIBLE);
emptySearch.setVisibility(View.INVISIBLE);
fab.startPulse(3 * 1000);
} else if (chatlist.getCnt() <= 0 && !TextUtils.isEmpty(queryFilter)) {
list.setVisibility(View.INVISIBLE);
emptyState.setVisibility(View.GONE);
emptySearch.setVisibility(View.VISIBLE);
emptySearch.setText(getString(R.string.search_no_result_for_x, queryFilter));
} else {
list.setVisibility(View.VISIBLE);
emptyState.setVisibility(View.GONE);
@@ -44,11 +44,9 @@ import org.thoughtcrime.securesms.components.AvatarView;
import org.thoughtcrime.securesms.components.DeliveryStatusView;
import org.thoughtcrime.securesms.components.FromTextView;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.search.QrInviteData;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.Util;
@@ -224,40 +222,6 @@ public class ConversationListItem extends RelativeLayout
avatar.setSeenRecently(false);
}
public void bind(@NonNull QrInviteData inviteData, @NonNull GlideRequests glideRequests) {
this.selectedThreads = Collections.emptySet();
fromView.setText(inviteData.getDisplayTitle());
fromView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
subjectView.setVisibility(VISIBLE);
subjectView.setText(inviteData.getDisplaySubtitle());
subjectView.setTypeface(LIGHT_TYPEFACE);
subjectView.setTextColor(
ThemeUtil.getThemedColor(getContext(), R.attr.conversation_list_item_subject_color));
dateView.setText("");
dateView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
archivedBadgeView.setVisibility(GONE);
requestBadgeView.setVisibility(GONE);
unreadIndicator.setVisibility(GONE);
deliveryStatusIndicator.setNone();
setBatchState(false);
if (inviteData.getContactId() > 0) {
DcContext dcContext = DcHelper.getContext(getContext());
DcContact contact = dcContext.getContact(inviteData.getContactId());
Recipient recipient = new Recipient(getContext(), contact);
avatar.setAvatar(glideRequests, recipient, false);
avatar.setSeenRecently(contact.wasSeenRecently());
} else {
avatar.setImageDrawable(
new GeneratedContactPhoto("+")
.asDrawable(getContext(), ThemeUtil.getDummyContactColor(getContext())));
avatar.setSeenRecently(false);
}
}
@Override
public void unbind() {}
@@ -42,7 +42,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
@SuppressLint("StaticFieldLeak")
public class CreateProfileActivity extends BaseActionBarActivity {
private static final String TAG = "CreateProfileActivity";
private static final String TAG = CreateProfileActivity.class.getSimpleName();
private static final int REQUEST_CODE_AVATAR = 1;
@@ -1,24 +0,0 @@
package org.thoughtcrime.securesms;
import android.os.Build;
import androidx.annotation.RequiresApi;
import org.webrtc.EglBase;
@RequiresApi(Build.VERSION_CODES.M)
public class EglUtils {
private static EglBase eglBase;
public static synchronized EglBase getEglBase() {
if (eglBase == null) {
eglBase = EglBase.create();
}
return eglBase;
}
public static synchronized void release() {
if (eglBase != null) {
eglBase.release();
eglBase = null;
}
}
}
@@ -15,7 +15,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class EphemeralMessagesDialog {
private static final String TAG = "EphemeralMessagesDialog";
private static final String TAG = EphemeralMessagesDialog.class.getSimpleName();
public static void show(
final Context context,
@@ -257,9 +257,4 @@ public class FullMsgActivity extends WebViewActivity {
}
return res;
}
@Override
protected boolean shouldAskToOpenLink() {
return true;
}
}
@@ -46,7 +46,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
implements ItemClickListener {
private static final String TAG = "GroupCreateActivity";
private static final String TAG = GroupCreateActivity.class.getSimpleName();
public static final String EDIT_GROUP_CHAT_ID = "edit_group_chat_id";
public static final String CREATE_BROADCAST = "create_broadcast";
public static final String UNENCRYPTED = "unencrypted";
@@ -307,7 +307,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private boolean showGroupNameEmptyToast(String groupName) {
if (groupName == null) {
Toast.makeText(this, getString(R.string.please_enter_chat_name), Toast.LENGTH_LONG).show();
Toast.makeText(this, getString(R.string.group_please_enter_group_name), Toast.LENGTH_LONG)
.show();
return true;
}
return false;
@@ -65,7 +65,7 @@ import org.thoughtcrime.securesms.util.views.ProgressDialog;
public class InstantOnboardingActivity extends BaseActionBarActivity
implements DcEventCenter.DcEventDelegate {
private static final String TAG = "InstantOnboardingActivity";
private static final String TAG = InstantOnboardingActivity.class.getSimpleName();
private static final String DCACCOUNT = "dcaccount";
private static final String DCLOGIN = "dclogin";
private static final String INSTANCES_URL = "https://chatmail.at/relays";
@@ -18,7 +18,7 @@ import org.thoughtcrime.securesms.util.FileProviderUtil;
public class LogViewActivity extends BaseActionBarActivity {
private static final String TAG = "LogViewActivity";
private static final String TAG = LogViewActivity.class.getSimpleName();
LogViewFragment logViewFragment;
@@ -33,13 +33,14 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.ViewPager2;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcMediaGalleryElement;
@@ -47,6 +48,7 @@ import com.b44t.messenger.DcMsg;
import java.io.IOException;
import java.util.WeakHashMap;
import org.thoughtcrime.securesms.components.MediaView;
import org.thoughtcrime.securesms.components.viewpager.ExtendedOnPageChangedListener;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader;
@@ -67,7 +69,7 @@ import org.thoughtcrime.securesms.util.Util;
public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
implements RecipientModifiedListener, LoaderManager.LoaderCallbacks<DcMediaGalleryElement> {
private static final String TAG = "MediaPreviewActivity";
private static final String TAG = MediaPreviewActivity.class.getSimpleName();
public static final String ACTIVITY_TITLE_EXTRA = "activity_title";
public static final String EDIT_AVATAR_CHAT_ID = "avatar_for_chat_id";
@@ -86,7 +88,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
@Nullable private DcMsg messageRecord;
private DcContext dcContext;
private MediaItem initialMedia;
private ViewPager2 mediaPager;
private ViewPager mediaPager;
private Recipient conversationRecipient;
private boolean leftIsRecent;
@@ -188,7 +190,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
private void initializeViews() {
mediaPager = findViewById(R.id.media_pager);
mediaPager.setOffscreenPageLimit(1);
mediaPager.registerOnPageChangeCallback(new ViewPagerListener());
mediaPager.addOnPageChangeListener(new ViewPagerListener());
}
private void initializeResources() {
@@ -258,7 +260,10 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
private int cleanupMedia() {
int restartItem = mediaPager.getCurrentItem();
mediaPager.removeAllViews();
mediaPager.setAdapter(null);
return restartItem;
}
@@ -478,25 +483,22 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
@SuppressWarnings("ConstantConditions")
DcMediaPagerAdapter adapter =
new DcMediaPagerAdapter(this, GlideApp.with(this), getWindow(), data, leftIsRecent);
adapter.setActive(true);
mediaPager.setAdapter(adapter);
adapter.setActive(true);
if (restartItem < 0) mediaPager.setCurrentItem(data.getPosition(), false);
else mediaPager.setCurrentItem(restartItem, false);
if (restartItem < 0) mediaPager.setCurrentItem(data.getPosition());
else mediaPager.setCurrentItem(restartItem);
}
}
@Override
public void onLoaderReset(Loader<DcMediaGalleryElement> loader) {}
private class ViewPagerListener extends ViewPager2.OnPageChangeCallback {
private Integer currentPage = null;
private class ViewPagerListener extends ExtendedOnPageChangedListener {
@Override
public void onPageSelected(int position) {
if (currentPage != null && currentPage != position) onPageUnselected(currentPage);
currentPage = position;
super.onPageSelected(position);
MediaItemAdapter adapter = (MediaItemAdapter) mediaPager.getAdapter();
@@ -508,7 +510,8 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
}
}
private void onPageUnselected(int position) {
@Override
public void onPageUnselected(int position) {
MediaItemAdapter adapter = (MediaItemAdapter) mediaPager.getAdapter();
if (adapter != null) {
@@ -523,9 +526,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
}
}
private static class SingleItemPagerAdapter
extends RecyclerView.Adapter<SingleItemPagerAdapter.MediaViewHolder>
implements MediaItemAdapter {
private static class SingleItemPagerAdapter extends PagerAdapter implements MediaItemAdapter {
private final GlideRequests glideRequests;
private final Window window;
@@ -554,29 +555,37 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
}
@Override
public int getItemCount() {
public int getCount() {
return 1;
}
@NonNull
@Override
public MediaViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new MediaViewHolder(inflater.inflate(R.layout.media_view_page, parent, false));
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
@Override
public void onBindViewHolder(@NonNull MediaViewHolder holder, int position) {
public @NonNull Object instantiateItem(@NonNull ViewGroup container, int position) {
View itemView = inflater.inflate(R.layout.media_view_page, container, false);
MediaView mediaView = itemView.findViewById(R.id.media_view);
try {
holder.mediaView.set(glideRequests, window, uri, name, mediaType, size, true);
mediaView.set(glideRequests, window, uri, name, mediaType, size, true);
} catch (IOException e) {
Log.w(TAG, e);
}
container.addView(itemView);
return itemView;
}
@Override
public void onViewRecycled(@NonNull MediaViewHolder holder) {
super.onViewRecycled(holder);
holder.mediaView.cleanup();
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
MediaView mediaView = ((FrameLayout) object).findViewById(R.id.media_view);
mediaView.cleanup();
container.removeView((FrameLayout) object);
}
@Override
@@ -586,20 +595,9 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
@Override
public void pause(int position) {}
static class MediaViewHolder extends RecyclerView.ViewHolder {
final MediaView mediaView;
MediaViewHolder(@NonNull View itemView) {
super(itemView);
mediaView = itemView.findViewById(R.id.media_view);
}
}
}
private static class DcMediaPagerAdapter
extends RecyclerView.Adapter<DcMediaPagerAdapter.MediaViewHolder>
implements MediaItemAdapter {
private static class DcMediaPagerAdapter extends PagerAdapter implements MediaItemAdapter {
private final WeakHashMap<Integer, MediaView> mediaViews = new WeakHashMap<>();
@@ -632,20 +630,21 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
}
@Override
public int getItemCount() {
public int getCount() {
if (!active) return 0;
else return gallery.getCount();
}
@NonNull
@Override
public MediaViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(context).inflate(R.layout.media_view_page, parent, false);
return new MediaViewHolder(itemView);
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
@Override
public void onBindViewHolder(@NonNull MediaViewHolder holder, int position) {
public @NonNull Object instantiateItem(@NonNull ViewGroup container, int position) {
View itemView =
LayoutInflater.from(context).inflate(R.layout.media_view_page, container, false);
MediaView mediaView = itemView.findViewById(R.id.media_view);
boolean autoplay = position == autoPlayPosition;
int cursorPosition = getCursorPosition(position);
@@ -657,7 +656,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
try {
//noinspection ConstantConditions
holder.mediaView.set(
mediaView.set(
glideRequests,
window,
Uri.fromFile(msg.getFileAsFile()),
@@ -669,17 +668,19 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
Log.w(TAG, e);
}
mediaViews.put(position, holder.mediaView);
mediaViews.put(position, mediaView);
container.addView(itemView);
return itemView;
}
@Override
public void onViewRecycled(@NonNull MediaViewHolder holder) {
super.onViewRecycled(holder);
int pos = holder.getBindingAdapterPosition();
if (pos != RecyclerView.NO_POSITION) {
mediaViews.remove(pos);
}
holder.mediaView.cleanup();
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
MediaView mediaView = ((FrameLayout) object).findViewById(R.id.media_view);
mediaView.cleanup();
mediaViews.remove(position);
container.removeView((FrameLayout) object);
}
public MediaItem getMediaItemFor(int position) {
@@ -709,15 +710,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
if (leftIsRecent) return position;
else return gallery.getCount() - 1 - position;
}
static class MediaViewHolder extends RecyclerView.ViewHolder {
final MediaView mediaView;
MediaViewHolder(@NonNull View itemView) {
super(itemView);
mediaView = itemView.findViewById(R.id.media_view);
}
}
}
private static class MediaItem {
@@ -750,7 +742,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
}
}
private interface MediaItemAdapter {
interface MediaItemAdapter {
MediaItem getMediaItemFor(int position);
void pause(int position);
@@ -41,7 +41,7 @@ import org.thoughtcrime.securesms.qr.QrCodeHandler;
*/
public class NewConversationActivity extends ContactSelectionActivity {
private static final String TAG = "NewConversationActivity";
private static final String TAG = NewConversationActivity.class.getSimpleName();
@Override
public void onCreate(Bundle bundle, boolean ready) {
@@ -7,7 +7,7 @@ import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.service.GenericForegroundService;
public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarActivity {
private static final String TAG = "PassphraseRequiredActionBarActivity";
private static final String TAG = PassphraseRequiredActionBarActivity.class.getSimpleName();
@Override
protected final void onCreate(Bundle savedInstanceState) {
@@ -26,10 +26,11 @@ import java.util.Set;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.contacts.ContactSelectionListItem;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.Util;
public class ProfileAdapter extends RecyclerView.Adapter {
private static final String TAG = "ProfileAdapter";
private static final String TAG = ProfileAdapter.class.getSimpleName();
public static final int ITEM_AVATAR = 10;
public static final int ITEM_DIVIDER = 20;
@@ -38,7 +38,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class ProfileFragment extends Fragment
implements ProfileAdapter.ItemClickListener, DcEventCenter.DcEventDelegate {
private static final String TAG = "ProfileFragment";
private static final String TAG = ProfileFragment.class.getSimpleName();
public static final String CHAT_ID_EXTRA = "chat_id";
public static final String CONTACT_ID_EXTRA = "contact_id";
@@ -17,7 +17,7 @@ import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
public class ResolveMediaTask extends AsyncTask<Uri, Void, Uri> {
private static final String TAG = "ResolveMediaTask";
private static final String TAG = ResolveMediaTask.class.getSimpleName();
interface OnMediaResolvedListener {
void onMediaResolved(Uri uri);
@@ -47,7 +47,7 @@ import org.thoughtcrime.securesms.util.ShareUtil;
*/
public class ShareActivity extends PassphraseRequiredActionBarActivity
implements ResolveMediaTask.OnMediaResolvedListener {
private static final String TAG = "ShareActivity";
private static final String TAG = ShareActivity.class.getSimpleName();
public static final String EXTRA_ACC_ID = "acc_id";
public static final String EXTRA_CHAT_ID = "chat_id";
@@ -268,7 +268,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
accId = dcContext.getAccountId();
}
Intent composeIntent;
if (accId != -1 && chatId > 0) {
if (accId != -1 && chatId != -1) {
composeIntent = getBaseShareIntent(ConversationActivity.class);
composeIntent.putExtra(ConversationActivity.CHAT_ID_EXTRA, chatId);
composeIntent.putExtra(ConversationActivity.ACCOUNT_ID_EXTRA, accId);
@@ -18,19 +18,22 @@ public class ShareLocationDialog {
switch (which) {
default:
case 0:
shareLocationUnit = 30 * 60;
shareLocationUnit = 5 * 60;
break;
case 1:
shareLocationUnit = 60 * 60;
shareLocationUnit = 30 * 60;
break;
case 2:
shareLocationUnit = 2 * 60 * 60;
shareLocationUnit = 60 * 60;
break;
case 3:
shareLocationUnit = 6 * 60 * 60;
shareLocationUnit = 2 * 60 * 60;
break;
case 4:
shareLocationUnit = 24 * 60 * 60;
shareLocationUnit = 6 * 60 * 60;
break;
case 5:
shareLocationUnit = 12 * 60 * 60;
break;
}
@@ -22,8 +22,6 @@ 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;
@@ -32,10 +30,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class WebViewActivity extends PassphraseRequiredActionBarActivity
implements SearchView.OnQueryTextListener, WebView.FindListener {
private static final String TAG = "WebViewActivity";
// Regex to extract the host from a URL for IDN conversion.
private static final Pattern URL_PATTERN =
Pattern.compile("^((?:[a-zA-Z0-9]+://)?)([^/?#]+)(.*)$");
private static final String TAG = WebViewActivity.class.getSimpleName();
protected WebView webView;
@@ -199,13 +194,13 @@ public class WebViewActivity extends PassphraseRequiredActionBarActivity
public boolean onMenuItemActionExpand(final MenuItem item) {
searchMenu = menu;
WebViewActivity.this.lastQuery = "";
WebViewActivity.this.makeSearchMenuVisible(menu, searchItem);
WebViewActivity.this.makeSearchMenuVisible(menu, searchItem, true);
return true;
}
@Override
public boolean onMenuItemActionCollapse(final MenuItem item) {
WebViewActivity.this.invalidateOptionsMenu();
WebViewActivity.this.makeSearchMenuVisible(menu, searchItem, false);
return true;
}
});
@@ -325,17 +320,9 @@ 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(displayUrl)
.setMessage(IDN.toASCII(url))
.setNeutralButton(R.string.cancel, null)
.setPositiveButton(R.string.open, (d, w) -> IntentUtils.showInBrowser(this, url))
.setNegativeButton(
@@ -59,7 +59,7 @@ import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.Util;
public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcEventDelegate {
private static final String TAG = "WebxdcActivity";
private static final String TAG = WebxdcActivity.class.getSimpleName();
private static final String EXTRA_ACCOUNT_ID = "accountId";
private static final String EXTRA_APP_MSG_ID = "appMessageId";
private static final String EXTRA_HIDE_ACTION_BAR = "hideActionBar";
@@ -240,7 +240,6 @@ 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);
@@ -543,25 +542,11 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
.build();
Toast.makeText(context, R.string.one_moment, Toast.LENGTH_SHORT).show();
Util.runOnAnyBackgroundThread(
() -> {
boolean success;
try {
success = ShortcutManagerCompat.requestPinShortcut(context, shortcutInfoCompat, null);
} catch (Exception e) {
Log.e(TAG, "ErrAddToHomescreen: requestPinShortcut() failed", e);
success = false;
}
if (!success) {
Util.runOnMain(
() ->
Toast.makeText(
context,
"ErrAddToHomescreen: requestPinShortcut() failed",
Toast.LENGTH_LONG)
.show());
}
});
if (!ShortcutManagerCompat.requestPinShortcut(context, shortcutInfoCompat, null)) {
Toast.makeText(
context, "ErrAddToHomescreen: requestPinShortcut() failed", Toast.LENGTH_LONG)
.show();
}
} catch (Exception e) {
Toast.makeText(context, "ErrAddToHomescreen: " + e, Toast.LENGTH_LONG).show();
}
@@ -30,7 +30,7 @@ import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
public class WebxdcStoreActivity extends PassphraseRequiredActionBarActivity {
private static final String TAG = "WebxdcStoreActivity";
private static final String TAG = WebxdcStoreActivity.class.getSimpleName();
private DcContext dcContext;
private Rpc rpc;
@@ -48,7 +48,7 @@ public class WelcomeActivity extends BaseActionBarActivity
implements DcEventCenter.DcEventDelegate {
public static final String BACKUP_QR_EXTRA = "backup_qr_extra";
public static final int PICK_BACKUP = 20574;
private static final String TAG = "WelcomeActivity";
private static final String TAG = WelcomeActivity.class.getSimpleName();
public static final String TMP_BACKUP_FILE = "tmp-backup-file";
private ProgressDialog progressDialog = null;
@@ -41,7 +41,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class AccountSelectionListFragment extends DialogFragment
implements DcEventCenter.DcEventDelegate {
private static final String TAG = "AccountSelectionListFragment";
private static final String TAG = AccountSelectionListFragment.class.getSimpleName();
private static final String ARG_SELECT_ONLY = "select_only";
private RecyclerView recyclerView;
private AccountSelectionListAdapter adapter;
@@ -157,12 +157,6 @@ public class AccountSelectionListFragment extends DialogFragment
onSetTag(accountId);
} else if (itemId == R.id.menu_move_to_top) {
onMoveToTop(accountId);
} else if (itemId == R.id.menu_mark_all_as_read) {
try {
DcHelper.getRpc(requireActivity()).marknoticedAllChats(accountId);
} catch (RpcException e) {
Log.e(TAG, "Error calling rpc.marknoticedAllChats()", e);
}
}
}
@@ -6,21 +6,24 @@ import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.media.MediaRecorder;
import android.util.Log;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.Util;
public class AudioCodec {
private static final String TAG = "AudioCodec";
private static final String TAG = AudioCodec.class.getSimpleName();
private static final int SAMPLE_RATE_BALANCED = 44100;
private static final int SAMPLE_RATE_INDEX_BALANCED = 4;
private static final int BIT_RATE_BALANCED = 32000;
private static final int SAMPLE_RATE_WORSE = 24000;
private static final int SAMPLE_RATE_INDEX_WORSE = 6;
private static final int BIT_RATE_WORSE = 16000;
private static final int CHANNELS = 1;
@@ -29,15 +32,12 @@ public class AudioCodec {
private final MediaCodec mediaCodec;
private final AudioRecord audioRecord;
private final Context context;
private final String outputPath;
private volatile boolean running = true;
private volatile boolean finished = false;
private long startTime = 0;
private boolean running = true;
private boolean finished = false;
public AudioCodec(Context context, String outputPath) throws IOException {
public AudioCodec(Context context) throws IOException {
this.context = context;
this.outputPath = outputPath;
this.bufferSize =
AudioRecord.getMinBufferSize(
getSampleRate(), AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
@@ -49,7 +49,7 @@ public class AudioCodec {
try {
audioRecord.startRecording();
} catch (Exception e) {
Log.w(TAG, "Failed to start recording", e);
Log.w(TAG, e);
mediaCodec.release();
throw new IOException(e);
}
@@ -57,146 +57,44 @@ public class AudioCodec {
public synchronized void stop() {
running = false;
while (!finished) {
try {
wait(5000);
if (!finished) {
Log.w(TAG, "Timeout waiting for recording to finish");
break;
}
} catch (InterruptedException ie) {
Log.w(TAG, "Interrupted while waiting for recording to finish", ie);
Thread.currentThread().interrupt();
break;
}
}
while (!finished) Util.wait(this, 0);
}
public void start() {
public void start(final OutputStream outputStream) {
new Thread(
() -> {
this.startTime = System.nanoTime();
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
byte[] audioRecordData = new byte[bufferSize];
MediaMuxer muxer = null;
int audioTrackIndex = -1;
boolean muxerStarted = false;
try {
muxer = new MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
while (true) {
boolean running = isRunning();
handleCodecInput(audioRecord, audioRecordData, mediaCodec, running);
int codecOutputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 2000);
while (codecOutputBufferIndex != MediaCodec.INFO_TRY_AGAIN_LATER) {
if (codecOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// Get the format to add track to muxer
if (muxerStarted) {
Log.w(TAG, "Output format changed after muxer started, ignoring");
codecOutputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
continue;
}
MediaFormat newFormat = mediaCodec.getOutputFormat();
Log.d(TAG, "Output format changed: " + newFormat);
audioTrackIndex = muxer.addTrack(newFormat);
muxer.start();
muxerStarted = true;
Log.d(TAG, "Muxer started");
} else if (codecOutputBufferIndex >= 0) {
ByteBuffer encodedData = mediaCodec.getOutputBuffer(codecOutputBufferIndex);
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
// Codec config info, not actual data, skip it
Log.d(TAG, "Ignoring CODEC_CONFIG buffer");
bufferInfo.size = 0;
}
if (bufferInfo.size != 0 && encodedData != null) {
if (!muxerStarted) {
Log.w(TAG, "Muxer not started, dropping encoded data");
} else {
// Adjust ByteBuffer to match BufferInfo
encodedData.position(bufferInfo.offset);
encodedData.limit(bufferInfo.offset + bufferInfo.size);
// Write sample data to muxer
muxer.writeSampleData(audioTrackIndex, encodedData, bufferInfo);
}
}
mediaCodec.releaseOutputBuffer(codecOutputBufferIndex, false);
// Check for end of stream
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
Log.d(TAG, "End of stream reached");
break;
}
}
codecOutputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
}
if (!running && (bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
break;
}
}
} catch (Exception e) {
Log.w(TAG, "Error during encoding", e);
} finally {
try {
if (muxerStarted && muxer != null) {
try {
muxer.stop();
Log.d(TAG, "Muxer stopped");
} catch (IllegalStateException e) {
Log.w(TAG, "Muxer already stopped", e);
}
}
} catch (Exception e) {
Log.w(TAG, "Error stopping muxer", e);
}
if (muxer != null) {
try {
muxer.release();
Log.d(TAG, "Muxer released");
} catch (Exception e) {
Log.w(TAG, "Error releasing muxer", e);
}
}
new Runnable() {
@Override
public void run() {
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
byte[] audioRecordData = new byte[bufferSize];
ByteBuffer[] codecInputBuffers = mediaCodec.getInputBuffers();
ByteBuffer[] codecOutputBuffers = mediaCodec.getOutputBuffers();
try {
while (true) {
boolean running = isRunning();
handleCodecInput(
audioRecord, audioRecordData, mediaCodec, codecInputBuffers, running);
handleCodecOutput(mediaCodec, codecOutputBuffers, bufferInfo, outputStream);
if (!running) break;
}
} catch (IOException e) {
Log.w(TAG, e);
} finally {
mediaCodec.stop();
} catch (Exception e) {
Log.w(TAG, "Error stopping codec", e);
}
try {
audioRecord.stop();
} catch (Exception e) {
Log.w(TAG, "Error stopping audio record", e);
}
try {
mediaCodec.release();
} catch (Exception e) {
Log.w(TAG, "Error releasing codec", e);
}
try {
audioRecord.release();
} catch (Exception e) {
Log.w(TAG, "Error releasing audio record", e);
}
setFinished();
Util.close(outputStream);
setFinished();
}
}
},
"AudioCodec")
AudioCodec.class.getSimpleName())
.start();
}
@@ -210,35 +108,75 @@ public class AudioCodec {
}
private void handleCodecInput(
AudioRecord audioRecord, byte[] audioRecordData, MediaCodec mediaCodec, boolean running) {
AudioRecord audioRecord,
byte[] audioRecordData,
MediaCodec mediaCodec,
ByteBuffer[] codecInputBuffers,
boolean running) {
int length = audioRecord.read(audioRecordData, 0, audioRecordData.length);
if (length < 0) {
Log.w(TAG, "Error reading from AudioRecord: " + length);
return;
}
int codecInputBufferIndex = mediaCodec.dequeueInputBuffer(10 * 1000);
if (codecInputBufferIndex >= 0) {
ByteBuffer codecBuffer = mediaCodec.getInputBuffer(codecInputBufferIndex);
if (codecBuffer != null) {
codecBuffer.clear();
codecBuffer.put(audioRecordData, 0, length);
long presentationTimeUs = getPresentationTimeUs();
mediaCodec.queueInputBuffer(
codecInputBufferIndex,
0,
length,
presentationTimeUs,
running ? 0 : MediaCodec.BUFFER_FLAG_END_OF_STREAM);
}
ByteBuffer codecBuffer = codecInputBuffers[codecInputBufferIndex];
codecBuffer.clear();
codecBuffer.put(audioRecordData);
mediaCodec.queueInputBuffer(
codecInputBufferIndex, 0, length, 0, running ? 0 : MediaCodec.BUFFER_FLAG_END_OF_STREAM);
}
}
private long getPresentationTimeUs() {
return (System.nanoTime() - startTime) / 1000;
private void handleCodecOutput(
MediaCodec mediaCodec,
ByteBuffer[] codecOutputBuffers,
MediaCodec.BufferInfo bufferInfo,
OutputStream outputStream)
throws IOException {
int codecOutputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
while (codecOutputBufferIndex != MediaCodec.INFO_TRY_AGAIN_LATER) {
if (codecOutputBufferIndex >= 0) {
ByteBuffer encoderOutputBuffer = codecOutputBuffers[codecOutputBufferIndex];
encoderOutputBuffer.position(bufferInfo.offset);
encoderOutputBuffer.limit(bufferInfo.offset + bufferInfo.size);
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG)
!= MediaCodec.BUFFER_FLAG_CODEC_CONFIG) {
byte[] header = createAdtsHeader(bufferInfo.size - bufferInfo.offset);
outputStream.write(header);
byte[] data = new byte[encoderOutputBuffer.remaining()];
encoderOutputBuffer.get(data);
outputStream.write(data);
}
encoderOutputBuffer.clear();
mediaCodec.releaseOutputBuffer(codecOutputBufferIndex, false);
} else if (codecOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
codecOutputBuffers = mediaCodec.getOutputBuffers();
}
codecOutputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
}
}
private byte[] createAdtsHeader(int length) {
int frameLength = length + 7;
byte[] adtsHeader = new byte[7];
adtsHeader[0] = (byte) 0xFF; // Sync Word
adtsHeader[1] = (byte) 0xF1; // MPEG-4, Layer (0), No CRC
adtsHeader[2] = (byte) ((MediaCodecInfo.CodecProfileLevel.AACObjectLC - 1) << 6);
adtsHeader[2] |= (((byte) getSampleRateIndex()) << 2);
adtsHeader[2] |= (((byte) CHANNELS) >> 2);
adtsHeader[3] = (byte) (((CHANNELS & 3) << 6) | ((frameLength >> 11) & 0x03));
adtsHeader[4] = (byte) ((frameLength >> 3) & 0xFF);
adtsHeader[5] = (byte) (((frameLength & 0x07) << 5) | 0x1f);
adtsHeader[6] = (byte) 0xFC;
return adtsHeader;
}
private AudioRecord createAudioRecord(int bufferSize) {
@@ -252,9 +190,11 @@ public class AudioCodec {
private MediaCodec createMediaCodec(int bufferSize) throws IOException {
MediaCodec mediaCodec = MediaCodec.createEncoderByType("audio/mp4a-latm");
MediaFormat mediaFormat = new MediaFormat();
MediaFormat mediaFormat =
MediaFormat.createAudioFormat("audio/mp4a-latm", getSampleRate(), CHANNELS);
mediaFormat.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
mediaFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, getSampleRate());
mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, CHANNELS);
mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, bufferSize);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, getBitRate());
mediaFormat.setInteger(
@@ -275,6 +215,12 @@ public class AudioCodec {
return Prefs.isHardCompressionEnabled(context) ? SAMPLE_RATE_WORSE : SAMPLE_RATE_BALANCED;
}
private int getSampleRateIndex() {
return Prefs.isHardCompressionEnabled(context)
? SAMPLE_RATE_INDEX_WORSE
: SAMPLE_RATE_INDEX_BALANCED;
}
private int getBitRate() {
return Prefs.isHardCompressionEnabled(context) ? BIT_RATE_WORSE : BIT_RATE_BALANCED;
}
@@ -2,15 +2,14 @@ package org.thoughtcrime.securesms.audio;
import android.content.Context;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.NonNull;
import chat.delta.util.ListenableFuture;
import chat.delta.util.SettableFuture;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ThreadUtil;
@@ -18,24 +17,15 @@ import org.thoughtcrime.securesms.util.Util;
public class AudioRecorder {
private static final String TAG = "AudioRecorder";
private static final String TAG = AudioRecorder.class.getSimpleName();
private static final ExecutorService executor = ThreadUtil.newDynamicSingleThreadedExecutor();
private final Context context;
private final PersistentBlobProvider blobProvider;
private final AtomicReference<RecordingState> recordingState = new AtomicReference<>(null);
private static class RecordingState {
final AudioCodec audioCodec;
final String outputFilePath;
RecordingState(AudioCodec audioCodec, String outputFilePath) {
this.audioCodec = audioCodec;
this.outputFilePath = outputFilePath;
}
}
private AudioCodec audioCodec;
private Uri captureUri;
public AudioRecorder(@NonNull Context context) {
this.context = context;
@@ -49,22 +39,24 @@ public class AudioRecorder {
() -> {
Log.w(TAG, "Running startRecording() + " + Thread.currentThread().getId());
try {
RecordingState currentState = recordingState.get();
if (currentState != null) {
if (audioCodec != null) {
throw new AssertionError("We can only record once at a time.");
}
// Create temporary file for M4A output
File outputFile = File.createTempFile("voice", ".m4a", context.getCacheDir());
String outputFilePath = outputFile.getAbsolutePath();
ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
AudioCodec audioCodec = new AudioCodec(context, outputFilePath);
recordingState.set(new RecordingState(audioCodec, outputFilePath));
captureUri =
blobProvider.create(
context,
new ParcelFileDescriptor.AutoCloseInputStream(fds[0]),
MediaUtil.AUDIO_AAC,
"voice.aac",
null);
audioCodec = new AudioCodec(context);
audioCodec.start();
audioCodec.start(new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]));
} catch (IOException e) {
Log.w(TAG, e);
recordingState.set(null);
}
});
}
@@ -76,39 +68,24 @@ public class AudioRecorder {
executor.execute(
() -> {
RecordingState state = recordingState.getAndSet(null);
if (state == null || state.audioCodec == null) {
if (audioCodec == null) {
sendToFuture(
future, new IOException("MediaRecorder was never initialized successfully!"));
return;
}
state.audioCodec.stop();
audioCodec.stop();
try {
File outputFile = new File(state.outputFilePath);
if (!outputFile.exists()) {
sendToFuture(future, new IOException("Output file does not exist"));
return;
}
long size = outputFile.length();
// Create blob using synchronous file-based method
Uri captureUri =
blobProvider.create(context, outputFile, MediaUtil.AUDIO_M4A, "voice.m4a", size);
if (!outputFile.delete()) {
Log.d(TAG, "Temp file already moved or couldn't be deleted");
}
long size = MediaUtil.getMediaSize(context, captureUri);
sendToFuture(future, new Pair<>(captureUri, size));
} catch (IOException ioe) {
Log.w(TAG, "Failed to create blob from recording", ioe);
Log.w(TAG, ioe);
sendToFuture(future, ioe);
}
audioCodec = null;
captureUri = null;
});
return future;
@@ -1,123 +0,0 @@
package org.thoughtcrime.securesms.calls;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.telecom.CallEndpointCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import java.util.List;
import org.thoughtcrime.securesms.R;
/** Bottom sheet dialog for selecting audio output device */
@RequiresApi(Build.VERSION_CODES.O)
public class AudioDevicePickerDialog extends BottomSheetDialog {
private static final String TAG = "AudioDevicePickerDialog";
public interface OnDeviceSelectedListener {
void onDeviceSelected(CallEndpointCompat endpoint);
}
public AudioDevicePickerDialog(
@NonNull Context context,
@NonNull List<CallEndpointCompat> endpoints,
CallEndpointCompat currentEndpoint,
@NonNull OnDeviceSelectedListener listener) {
super(context);
setContentView(R.layout.dialog_audio_device_picker);
RecyclerView recyclerView = findViewById(R.id.audio_device_list);
if (recyclerView == null) {
Log.e(TAG, "RecyclerView not found in layout");
return;
}
recyclerView.setLayoutManager(new LinearLayoutManager(context));
AudioDeviceAdapter adapter =
new AudioDeviceAdapter(
endpoints,
currentEndpoint,
endpoint -> {
listener.onDeviceSelected(endpoint);
dismiss();
});
recyclerView.setAdapter(adapter);
}
/** RecyclerView adapter for audio devices */
private static class AudioDeviceAdapter
extends RecyclerView.Adapter<AudioDeviceAdapter.ViewHolder> {
private final List<CallEndpointCompat> endpoints;
private final CallEndpointCompat currentEndpoint;
private final OnDeviceSelectedListener listener;
AudioDeviceAdapter(
List<CallEndpointCompat> endpoints,
CallEndpointCompat currentEndpoint,
OnDeviceSelectedListener listener) {
this.endpoints = endpoints;
this.currentEndpoint = currentEndpoint;
this.listener = listener;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view =
LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_audio_device, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
CallEndpointCompat endpoint = endpoints.get(position);
boolean isCurrent =
currentEndpoint != null
&& endpoint.getIdentifier().equals(currentEndpoint.getIdentifier());
holder.bind(endpoint, isCurrent, listener);
}
@Override
public int getItemCount() {
return endpoints.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
private final ImageView iconView;
private final TextView nameView;
private final ImageView checkView;
ViewHolder(@NonNull View itemView) {
super(itemView);
iconView = itemView.findViewById(R.id.device_icon);
nameView = itemView.findViewById(R.id.device_name);
checkView = itemView.findViewById(R.id.device_check);
}
void bind(CallEndpointCompat endpoint, boolean isCurrent, OnDeviceSelectedListener listener) {
nameView.setText(endpoint.getName());
int iconRes = CallUtil.getIconResByCallEndpoint(endpoint);
iconView.setImageResource(iconRes);
checkView.setVisibility(isCurrent ? View.VISIBLE : View.GONE);
itemView.setOnClickListener(v -> listener.onDeviceSelected(endpoint));
}
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,587 +0,0 @@
package org.thoughtcrime.securesms.calls;
import android.app.Notification;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ServiceInfo;
import android.media.AudioAttributes;
import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.media.ToneGenerator;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import chat.delta.rpc.RpcException;
import org.thoughtcrime.securesms.webrtc.WebRTCClient;
import org.webrtc.AudioTrack;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
import org.webrtc.VideoTrack;
/**
* Foreground service for VoIP calls
*
* <p>Required to post CallStyle notifications on Android 12+. Owns WebRTC resources and keeps call
* alive.
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public class CallService extends Service implements WebRTCClient.Callbacks {
private static final String TAG = "CallService";
private final IBinder binder = new LocalBinder();
// WebRTC Resources
private WebRTCClient webRTCClient;
private MediaStreamManager mediaStreamManager;
// Ringtone Resources
private Ringtone ringtone;
private AudioManager audioManager;
private AudioFocusRequest audioFocusRequest;
private ToneGenerator toneGenerator;
private Runnable toneLoopRunnable;
private CallCoordinator callCoordinator;
public class LocalBinder extends Binder {
CallService getService() {
return CallService.this;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "CallService onCreate");
callCoordinator = CallCoordinator.getInstance(getApplicationContext());
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
Log.d(TAG, "CallService created");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "CallService started");
return START_STICKY;
}
// Initialization
/** Initialize call infrastructure Does NOT start camera/microphone */
public void initializeCall() {
Log.d(TAG, "initializeCall (Infrastructure only)");
if (webRTCClient != null) {
Log.w(TAG, "Infrastructure already initialized, ignoring");
return;
}
webRTCClient = new WebRTCClient(getApplicationContext(), this);
mediaStreamManager =
new MediaStreamManager(getApplicationContext(), webRTCClient.getPeerConnectionFactory());
fetchIceServersAndSetup();
}
private void fetchIceServersAndSetup() {
new Thread(
() -> {
if (!callCoordinator.hasActiveCall()) {
Log.d(TAG, "Call ended before ICE fetch, aborting");
return;
}
try {
String iceServersJson = callCoordinator.fetchIceServers();
Log.d(TAG, "ICE servers fetched: " + iceServersJson);
webRTCClient.configure(iceServersJson);
Log.d(TAG, "Infrastructure initialized, waiting for media capture");
} catch (RpcException e) {
Log.e(TAG, "Failed to fetch ICE servers", e);
callCoordinator.reportError("Failed to connect: " + e.getMessage());
}
})
.start();
}
/**
* Start media capture
*
* <p>Must be called when app is in foreground. Called by coordinator when ViewModel/Activity is
* ready.
*/
public void startMediaCapture() {
Log.d(TAG, "startMediaCapture");
if (webRTCClient != null && webRTCClient.hasLocalMediaStream()) {
Log.w(TAG, "Media already initialized, skipping");
return;
}
if (mediaStreamManager == null) {
Log.e(TAG, "MediaStreamManager not initialized");
callCoordinator.reportError("MediaStreamManager not initialized");
return;
}
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();
Log.d(TAG, "Creating media stream");
mediaStreamManager.createMediaStream(
new MediaStreamManager.Callback() {
@Override
public void onMediaStreamReady(MediaStream stream) {
Log.d(TAG, "Media stream ready");
webRTCClient.setLocalMediaStream(stream);
if (!stream.videoTracks.isEmpty()) {
VideoTrack localTrack = stream.videoTracks.get(0);
callCoordinator.updateLocalVideoTrack(localTrack);
}
callCoordinator.setVideoEnabled(startsWithVideo);
callCoordinator.updateMediaCaptureReady(true);
Log.d(TAG, "Media capture complete, ready for call");
}
@Override
public void onError(String error) {
Log.e(TAG, "Failed to setup media: " + error);
callCoordinator.reportError("Camera/microphone error: " + error);
}
});
}
// Ringtone Management
public void startIncomingRingtone() {
Log.d(TAG, "startIncomingRingtone");
if (ringtone != null && ringtone.isPlaying()) {
Log.d(TAG, "Ringtone already playing");
return;
}
try {
// Get system default ringtone URI
Uri ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
if (ringtoneUri == null) {
Log.w(TAG, "No default ringtone available");
return;
}
ringtone = RingtoneManager.getRingtone(getApplicationContext(), ringtoneUri);
if (ringtone == null) {
Log.e(TAG, "Failed to create Ringtone from URI: " + ringtoneUri);
return;
}
AudioAttributes audioAttributes =
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
.setLegacyStreamType(AudioManager.STREAM_RING)
.build();
ringtone.setAudioAttributes(audioAttributes);
// Request audio focus
audioFocusRequest =
new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
.setAudioAttributes(audioAttributes)
.setWillPauseWhenDucked(false)
.build();
int result = audioManager.requestAudioFocus(audioFocusRequest);
if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.w(TAG, "Audio focus not granted");
}
ringtone.play();
Log.d(TAG, "Ringtone started playing");
} catch (Exception e) {
Log.e(TAG, "Failed to start ringtone", e);
// Clean up on error
stopRingtone();
}
}
public void startOutgoingRingtone() {
Log.d(TAG, "startOutgoingRingtone");
if (toneGenerator != null) {
Log.d(TAG, "Tone already playing");
return;
}
try {
AudioAttributes audioAttributes =
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
.setLegacyStreamType(AudioManager.STREAM_VOICE_CALL)
.build();
audioFocusRequest =
new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK)
.setAudioAttributes(audioAttributes)
.setWillPauseWhenDucked(false)
.build();
int result = audioManager.requestAudioFocus(audioFocusRequest);
if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.w(TAG, "Audio focus not granted");
}
// Create ToneGenerator
toneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL, 80);
boolean started = toneGenerator.startTone(ToneGenerator.TONE_SUP_RINGTONE, -1);
if (started) {
Log.d(TAG, "Outgoing ringback tone started playing");
} else {
Log.e(TAG, "Failed to start tone");
stopRingtone();
}
} catch (Exception e) {
Log.e(TAG, "Failed to start outgoing ringtone", e);
stopRingtone();
}
}
/** Stop playing ringtone */
public void stopRingtone() {
Log.d(TAG, "stopRingtone");
if (toneGenerator != null) {
try {
toneGenerator.stopTone();
toneGenerator.release();
Log.d(TAG, "ToneGenerator stopped and released");
} catch (Exception e) {
Log.e(TAG, "Error stopping ToneGenerator", e);
}
toneGenerator = null;
}
if (ringtone != null) {
try {
if (ringtone.isPlaying()) {
ringtone.stop();
Log.d(TAG, "Ringtone stopped");
}
} catch (Exception e) {
Log.e(TAG, "Error stopping ringtone", e);
}
ringtone = null;
}
if (audioFocusRequest != null && audioManager != null) {
audioManager.abandonAudioFocusRequest(audioFocusRequest);
audioFocusRequest = null;
Log.d(TAG, "Audio focus abandoned");
}
}
// Call Control
public void startOutgoingCall() {
Log.d(TAG, "startOutgoingCall");
if (webRTCClient == null) {
Log.e(TAG, "Cannot start call, not initialized");
callCoordinator.reportError("Service not ready");
return;
}
if (!webRTCClient.hasLocalMediaStream()) {
Log.e(TAG, "Cannot start call, media not ready");
callCoordinator.reportError("Media not ready");
return;
}
webRTCClient.startOutgoingCall();
}
public void answerIncomingCall() {
Log.d(TAG, "answerIncomingCall");
String offerSdp = callCoordinator.getPendingOfferSdp();
if (offerSdp == null) {
Log.e(TAG, "No offer SDP available");
callCoordinator.reportError("Call data missing");
return;
}
if (webRTCClient == null) {
Log.e(TAG, "Cannot answer call, not initialized");
callCoordinator.reportError("Service not ready");
return;
}
if (!webRTCClient.hasLocalMediaStream()) {
Log.e(TAG, "Cannot answer, media not ready");
callCoordinator.reportError("Media not ready");
return;
}
callCoordinator.clearPendingOfferSdp();
webRTCClient.acceptIncomingCall(offerSdp);
}
public void handleAnswerSdp(String answerSdp) {
Log.d(TAG, "handleAnswerSdp");
if (webRTCClient == null) {
Log.e(TAG, "Cannot handle answer, not initialized");
return;
}
webRTCClient.handleAnswerSdp(answerSdp);
}
public void setAudioEnabled(boolean enabled) {
Log.d(TAG, "setAudioEnabled: " + enabled);
if (webRTCClient != null) {
webRTCClient.setAudioEnabled(enabled);
}
}
public boolean setVideoEnabled(boolean enabled) {
Log.d(TAG, "setVideoEnabled: " + enabled);
if (enabled) {
if (mediaStreamManager != null) {
boolean captureReady = mediaStreamManager.startVideoCapture();
if (!captureReady) {
Log.w(TAG, "Failed to start video capture");
return false;
}
callCoordinator.updateFrontCamera(mediaStreamManager.isFrontCamera());
}
if (webRTCClient != null) {
webRTCClient.setVideoEnabled(true);
}
} else {
if (webRTCClient != null) {
webRTCClient.setVideoEnabled(false);
}
if (mediaStreamManager != null) {
mediaStreamManager.stopVideoCapture();
}
}
return true;
}
public void sendMutedState(boolean audioEnabled, boolean videoEnabled) {
Log.d(TAG, "sendMutedState: audio=" + audioEnabled + ", video=" + videoEnabled);
if (webRTCClient != null) {
webRTCClient.sendMutedState(audioEnabled, videoEnabled);
}
}
public void switchCamera() {
Log.d(TAG, "switchCamera");
if (mediaStreamManager != null) {
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);
}
});
}
}
public void endCall() {
Log.d(TAG, "endCall");
disposeWebRTC();
try {
stopForeground(STOP_FOREGROUND_REMOVE);
} catch (Exception e) {
Log.w(TAG, "stopForeground failed", e);
}
stopService();
}
// WebRTCClient.Callbacks
@Override
public void onOfferReady(String offerSdp) {
Log.d(TAG, "onOfferReady callback");
callCoordinator.handleOfferReady(offerSdp);
}
@Override
public void onAnswerReady(String answerSdp) {
Log.d(TAG, "onAnswerReady callback");
callCoordinator.handleAnswerReady(answerSdp);
}
@Override
public void onRemoteVideoTrack(VideoTrack videoTrack) {
Log.d(TAG, "onRemoteVideoTrack callback");
callCoordinator.updateRemoteVideoTrack(videoTrack);
}
@Override
public void onRemoteAudioTrack(AudioTrack audioTrack) {
Log.d(TAG, "onRemoteAudioTrack callback");
}
@Override
public void onConnectionStateChanged(PeerConnection.PeerConnectionState state) {
Log.d(TAG, "onConnectionStateChanged: " + state);
callCoordinator.updateConnectionState(state);
}
@Override
public void onRemoteMutedStateChanged(boolean audioEnabled, boolean videoEnabled) {
Log.d(TAG, "onRemoteMutedStateChanged: audio=" + audioEnabled + ", video=" + videoEnabled);
callCoordinator.updateRemoteMutedState(audioEnabled, videoEnabled);
}
@Override
public void onRelayUsageChanged(Boolean isRelayUsed) {
Log.d(TAG, "onRelayUsageChanged: " + isRelayUsed);
callCoordinator.updateRelayUsage(isRelayUsed);
}
@Override
public void onError(String error) {
Log.e(TAG, "onError: " + error);
callCoordinator.reportError(error);
}
// Foreground Notification
public void startForegroundWithNotification(int id, Notification notification) {
// Always run on main thread
if (Looper.myLooper() != Looper.getMainLooper()) {
new Handler(Looper.getMainLooper())
.post(() -> startForegroundWithNotification(id, notification));
return;
}
Log.d(TAG, "Starting call FGS with notification id: " + id);
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
startForeground(id, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
int types =
ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
| ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
startForeground(id, notification, types);
} else {
startForeground(id, notification);
}
} catch (Exception e) {
Log.e(TAG, "startForeground failed", e);
if (callCoordinator != null) {
callCoordinator.reportError("Failed to activate call FGS: " + e.getMessage());
}
}
}
public void stopForegroundAndDismiss() {
if (Looper.myLooper() != Looper.getMainLooper()) {
new Handler(Looper.getMainLooper()).post(this::stopForegroundAndDismiss);
return;
}
Log.d(TAG, "Stopping call FGS and dismissing notification");
try {
stopForeground(STOP_FOREGROUND_REMOVE);
} catch (Exception e) {
Log.w(TAG, "stopForeground failed", e);
}
}
// Cleanup
private void disposeWebRTC() {
Log.d(TAG, "Disposing WebRTC resources");
if (mediaStreamManager != null) {
mediaStreamManager.dispose();
mediaStreamManager = null;
}
if (webRTCClient != null) {
webRTCClient.dispose();
webRTCClient = null;
}
}
public void stopService() {
Log.d(TAG, "Stopping CallService");
stopSelf();
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "CallService onDestroy");
stopRingtone();
disposeWebRTC();
Log.d(TAG, "CallService destroyed");
}
}
@@ -1,180 +1,62 @@
package org.thoughtcrime.securesms.calls;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.os.Build;
import android.util.Base64;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
import androidx.core.telecom.CallEndpointCompat;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContext;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.permissions.Permissions;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class CallUtil {
private static final String TAG = "CallUtil";
private static final String TAG = CallUtil.class.getSimpleName();
@RequiresApi(api = Build.VERSION_CODES.O)
public static void startAudioCall(Context context, int chatId) {
Log.d(TAG, "Starting audio call to " + chatId);
startCall(context, chatId, false);
public static void startCall(Activity activity, int chatId, boolean hasVideo) {
Permissions.with(activity)
.request(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
.ifNecessary()
.withPermanentDenialDialog(activity.getString(R.string.perm_explain_access_to_camera_denied))
.onAllGranted(() -> {
int accId = DcHelper.getContext(activity).getAccountId();
startCall(activity, accId, chatId, hasVideo);
})
.execute();
}
@RequiresApi(api = Build.VERSION_CODES.O)
public static void startVideoCall(Context context, int chatId) {
Log.d(TAG, "Starting video call to " + chatId);
startCall(context, chatId, true);
public static void startCall(Context context, int accId, int chatId, boolean hasVideo) {
Intent intent = new Intent(context, CallActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(CallActivity.EXTRA_ACCOUNT_ID, accId);
intent.putExtra(CallActivity.EXTRA_CHAT_ID, chatId);
intent.putExtra(CallActivity.EXTRA_HAS_VIDEO, hasVideo);
intent.putExtra(CallActivity.EXTRA_HASH, "#startCall");
context.startActivity(intent);
}
@RequiresApi(api = Build.VERSION_CODES.O)
private static void startCall(Context context, int chatId, boolean startsWithVideo) {
if (chatId < 0) {
Log.e(TAG, "Cannot start call: wrong chatId");
return;
}
CallCoordinator coordinator = CallCoordinator.getInstance(context);
if (coordinator.hasActiveCall()) {
Toast.makeText(context, R.string.already_in_call, Toast.LENGTH_SHORT).show();
Intent intent = new Intent(context, CallActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
return;
}
Runnable proceedWithCall =
() -> {
int accId = DcHelper.getContext(context).getAccountId();
coordinator.initiateOutgoingCall(accId, chatId, startsWithVideo);
};
if (!isNetworkAvailable(context)) {
new AlertDialog.Builder(context)
.setMessage(context.getString(R.string.call_requires_connection))
.setPositiveButton(R.string.perm_continue, (dialog, which) -> proceedWithCall.run())
.setNegativeButton(android.R.string.cancel, null)
.show();
return;
}
proceedWithCall.run();
}
@Nullable
@RequiresApi(api = Build.VERSION_CODES.M)
protected static Icon getIconFromChat(Context context, DcChat dcChat) {
Log.d(TAG, "getIconFromChat: thread=" + Thread.currentThread().getName());
public static void openCall(Context context, int accId, int chatId, int callId, String payload, boolean hasVideo) {
String base64 = Base64.encodeToString(payload.getBytes(StandardCharsets.UTF_8), Base64.NO_WRAP);
String hash = "";
try {
Recipient recipient = new Recipient(context, dcChat);
ContactPhoto contactPhoto = recipient.getContactPhoto(context);
int wh = context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size);
Bitmap bitmap;
if (contactPhoto != null) {
bitmap =
GlideApp.with(context)
.asBitmap()
.load(contactPhoto)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.circleCrop()
.submit(wh, wh)
.get();
} else {
Drawable drawable =
recipient
.getFallbackContactPhoto()
.asDrawable(context, recipient.getFallbackAvatarColor());
bitmap = BitmapUtil.createFromDrawable(drawable, wh, wh);
}
if (bitmap != null) {
Log.d(
TAG,
"Bitmap loaded: "
+ bitmap.getWidth()
+ "x"
+ bitmap.getHeight()
+ ", config="
+ bitmap.getConfig()
+ ", recycled="
+ bitmap.isRecycled());
Icon icon = Icon.createWithBitmap(bitmap);
Log.d(TAG, "Icon created successfully");
return icon;
}
} catch (Exception e) {
Log.e(TAG, "Failed to load caller icon", e);
hash = "#offerIncomingCall=" + URLEncoder.encode(base64, "UTF-8");
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Error", e);
}
Log.w(TAG, "Returning null icon");
return null;
Intent intent = new Intent(context, CallActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(CallActivity.EXTRA_ACCOUNT_ID, accId);
intent.putExtra(CallActivity.EXTRA_CHAT_ID, chatId);
intent.putExtra(CallActivity.EXTRA_CALL_ID, callId);
intent.putExtra(CallActivity.EXTRA_HAS_VIDEO, hasVideo);
intent.putExtra(CallActivity.EXTRA_HASH, hash);
context.startActivity(intent);
}
protected static String getNameFromChat(DcChat dcChat) {
return dcChat.getName();
}
@RequiresApi(api = Build.VERSION_CODES.O)
public static int getIconResByCallEndpoint(CallEndpointCompat endpoint) {
int iconRes;
switch (endpoint.getType()) {
case CallEndpointCompat.TYPE_EARPIECE:
iconRes = R.drawable.ic_phone_in_talk;
break;
case CallEndpointCompat.TYPE_SPEAKER:
iconRes = R.drawable.ic_volume_up;
break;
case CallEndpointCompat.TYPE_BLUETOOTH:
iconRes = R.drawable.ic_bluetooth_audio;
break;
case CallEndpointCompat.TYPE_WIRED_HEADSET:
iconRes = R.drawable.ic_headset;
break;
case CallEndpointCompat.TYPE_STREAMING:
iconRes = R.drawable.ic_cast;
break;
default:
iconRes = R.drawable.ic_volume_up;
break;
}
return iconRes;
}
@RequiresApi(api = Build.VERSION_CODES.M)
private static boolean isNetworkAvailable(Context context) {
ConnectivityManager manager =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (manager == null) return true;
boolean networkAvailable = false;
Network network = manager.getActiveNetwork();
if (network != null) {
NetworkCapabilities caps = manager.getNetworkCapabilities(network);
networkAvailable =
caps != null && caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
}
boolean serverConnected =
DcHelper.getContext(context).getConnectivity() >= DcContext.DC_CONNECTIVITY_WORKING;
return networkAvailable || serverConnected;
}
}
@@ -1,471 +0,0 @@
package org.thoughtcrime.securesms.calls;
import android.app.Application;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.telecom.DisconnectCause;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.telecom.CallEndpointCompat;
import androidx.lifecycle.AndroidViewModel;
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;
@RequiresApi(api = Build.VERSION_CODES.O)
public class CallViewModel extends AndroidViewModel {
private static final String TAG = "CallViewModel";
private final CallCoordinator callCoordinator;
// UI State LiveData (from Coordinator)
private final LiveData<VideoTrack> localVideoTrack;
private final LiveData<VideoTrack> remoteVideoTrack;
private final LiveData<Boolean> localAudioEnabled;
private final LiveData<Boolean> localVideoEnabled;
private final LiveData<Boolean> remoteAudioEnabled;
private final LiveData<Boolean> remoteVideoEnabled;
private final LiveData<Boolean> isRelayUsed;
private final LiveData<String> errorMessage;
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;
// Observer References for one-time observe
private Observer<Boolean> answerCallObserver;
private Observer<Boolean> startOutgoingCallObserver;
private final AtomicBoolean hasCallEnded = new AtomicBoolean(false);
// User-facing call states
public enum CallState {
INITIALIZING,
PROMPTING_USER_ACCEPT,
RINGING,
CONNECTING,
CONNECTED,
RECONNECTING,
ANSWERED_ELSEWHERE,
ENDED,
ERROR
}
public CallViewModel(@NonNull Application application) {
super(application);
this.callCoordinator = CallCoordinator.getInstance(application);
// Setup LiveData observations
this.localVideoTrack = callCoordinator.getLocalVideoTrack();
this.remoteVideoTrack = callCoordinator.getRemoteVideoTrack();
this.localAudioEnabled = callCoordinator.getLocalAudioEnabled();
this.localVideoEnabled = callCoordinator.getLocalVideoEnabled();
this.remoteAudioEnabled = callCoordinator.getRemoteAudioEnabled();
this.remoteVideoEnabled = callCoordinator.getRemoteVideoEnabled();
this.isRelayUsed = callCoordinator.getIsRelayUsed();
this.errorMessage = callCoordinator.getErrorMessage();
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);
setupConnectionStateObserver();
Log.d(TAG, "CallViewModel created");
}
public void initialize() {
Log.d(TAG, "Initializing CallViewModel");
callCoordinator.setActiveCallViewModel(this);
if (callCoordinator.isIncomingCall()) {
callState.setValue(CallState.PROMPTING_USER_ACCEPT);
} else {
callState.setValue(CallState.CONNECTING);
}
Log.d(TAG, "CallViewModel initialized");
}
private void setupConnectionStateObserver() {
callState.addSource(
callCoordinator.getConnectionState(),
state -> {
if (callState.getValue() == CallState.ANSWERED_ELSEWHERE) {
return;
}
CallState newState = translateConnectionState(state);
if (callState.getValue() != newState) {
callState.setValue(newState);
}
if (state == PeerConnection.PeerConnectionState.FAILED
|| state == PeerConnection.PeerConnectionState.CLOSED) {
hasCallEnded.set(true);
}
});
callState.addSource(
outgoingCallPlaced,
placed -> {
if (Boolean.TRUE.equals(placed) && !callCoordinator.isIncomingCall()) {
if (callState.getValue() == CallState.CONNECTING) {
callState.setValue(CallState.RINGING);
}
}
});
callState.addSource(
answeredElsewhere,
value -> {
if (Boolean.TRUE.equals(value)) {
hasCallEnded.set(true);
callState.setValue(CallState.ANSWERED_ELSEWHERE);
}
});
}
private CallState translateConnectionState(PeerConnection.PeerConnectionState state) {
switch (state) {
case NEW:
if (callCoordinator.isIncomingCall()) {
return CallState.PROMPTING_USER_ACCEPT;
} else {
return CallState.RINGING;
}
case CONNECTING:
if (callCoordinator.isIncomingCall()) {
return CallState.CONNECTING;
} else {
return Boolean.TRUE.equals(outgoingCallPlaced.getValue())
? CallState.RINGING
: CallState.CONNECTING;
}
case CONNECTED:
return CallState.CONNECTED;
case DISCONNECTED:
return CallState.RECONNECTING;
case FAILED:
return CallState.ERROR;
case CLOSED:
return CallState.ENDED;
default:
return CallState.INITIALIZING;
}
}
// Call Control
public void answerCall() {
Log.d(TAG, "answerCall");
if (!callCoordinator.isIncomingCall()) {
Log.w(TAG, "answerCall() called but this is not an incoming call");
return;
}
// System integration
callCoordinator.handleCallControlScopeAnswer();
answerCallWhenReady();
}
/** Answer incoming call (WebRTC only) Used when system answer already happened */
public void answerCallWhenReady() {
Log.d(TAG, "answerCallWhenReady");
if (callCoordinator.hasOngoingCall()) {
Log.w(TAG, "Call already ongoing, not starting duplicate");
return;
}
// Start media capture
callCoordinator.startMediaCapture();
// Create one-time observer
LiveData<Boolean> mediaReady = callCoordinator.getMediaCaptureReady();
answerCallObserver =
new Observer<Boolean>() {
@Override
public void onChanged(Boolean ready) {
if (Boolean.TRUE.equals(ready)) {
mediaReady.removeObserver(this);
answerCallObserver = null;
Log.d(TAG, "Media capture ready, answering call (WebRTC)");
callCoordinator.answerWebRTC();
}
}
};
mediaReady.observeForever(answerCallObserver);
}
/** Start outgoing call with media capture Called by Activity for outgoing calls */
public void startOutgoingCallWhenReady() {
Log.d(TAG, "startOutgoingCallWhenReady");
if (callCoordinator.hasOngoingCall()) {
Log.w(TAG, "Call already ongoing, not starting duplicate");
return;
}
callCoordinator.startMediaCapture();
// Create one-time observer
LiveData<Boolean> mediaReady = callCoordinator.getMediaCaptureReady();
if (Boolean.TRUE.equals(mediaReady.getValue())) {
Log.d(TAG, "Media already ready, starting call immediately");
callCoordinator.startOutgoingCall();
} else {
startOutgoingCallObserver =
new Observer<Boolean>() {
@Override
public void onChanged(Boolean ready) {
if (Boolean.TRUE.equals(ready)) {
mediaReady.removeObserver(this);
startOutgoingCallObserver = null;
Log.d(TAG, "Media capture ready, starting outgoing call");
callCoordinator.startOutgoingCall();
}
}
};
mediaReady.observeForever(startOutgoingCallObserver);
}
}
public void declineCall() {
Log.d(TAG, "declineCall");
if (!hasCallEnded.compareAndSet(false, true)) {
Log.w(TAG, "Call already ended");
return;
}
callCoordinator.declineCall();
}
public void hangUp() {
Log.d(TAG, "hangUp");
if (!hasCallEnded.compareAndSet(false, true)) {
Log.w(TAG, "Call already ended");
return;
}
callCoordinator.hangUp();
}
public void toggleAudio() {
Boolean current = localAudioEnabled.getValue();
boolean newState = current == null || !current;
Log.d(TAG, "toggleAudio: " + newState);
callCoordinator.setAudioEnabled(newState);
}
public void toggleVideo() {
Boolean current = localVideoEnabled.getValue();
boolean newState = current == null || !current;
Log.d(TAG, "toggleVideo: " + newState);
callCoordinator.setVideoEnabled(newState);
}
public void switchCamera() {
Log.d(TAG, "switchCamera");
callCoordinator.switchCamera();
}
public void selectAudioDevice(CallEndpointCompat endpoint) {
Log.d(TAG, "selectAudioDevice: " + endpoint.getName());
callCoordinator.requestAudioEndpointChange(endpoint);
}
public void switchToEarpiece(boolean shallUseEarpiece) {
Log.d(TAG, "switchToEarpiece: shallUseEarpiece: " + shallUseEarpiece);
List<CallEndpointCompat> endpoints = availableAudioEndpoints.getValue();
if (endpoints == null) {
Log.w(TAG, "No available audio endpoint");
return;
}
CallEndpointCompat targetEndpoint = null;
for (CallEndpointCompat endpoint : endpoints) {
boolean isEarpiece = endpoint.getType() == CallEndpointCompat.TYPE_EARPIECE;
if (isEarpiece == shallUseEarpiece) {
targetEndpoint = endpoint;
break;
}
}
if (targetEndpoint != null) {
selectAudioDevice(targetEndpoint);
}
}
public void setStartsWithVideo(boolean startsWithVideo) {
Log.d(TAG, "setStartsWithVideo: " + startsWithVideo);
callCoordinator.setStartsWithVideo(startsWithVideo);
}
// CallControlScope Callbacks
public void onCallAnswered() {
Log.d(TAG, "onCallAnswered callback from CallControlScope");
}
public void onCallActive() {
Log.d(TAG, "onCallActive callback from CallControlScope");
}
public void onCallInactive() {
Log.d(TAG, "onCallInactive callback from CallControlScope");
}
public void onCallDisconnected(DisconnectCause disconnectCause) {
Log.d(TAG, "onCallDisconnected callback from CallControlScope, cause: " + disconnectCause);
if (hasCallEnded.compareAndSet(false, true)) {
callState.postValue(CallState.ENDED);
}
}
// LiveData Getters
public LiveData<CallState> getCallState() {
return callState;
}
public LiveData<Boolean> getAudioEnabled() {
return localAudioEnabled;
}
public LiveData<Boolean> getVideoEnabled() {
return localVideoEnabled;
}
public LiveData<Boolean> getRemoteAudioEnabled() {
return remoteAudioEnabled;
}
public LiveData<Boolean> getRemoteVideoEnabled() {
return remoteVideoEnabled;
}
public LiveData<VideoTrack> getLocalVideoTrack() {
return localVideoTrack;
}
public LiveData<VideoTrack> getRemoteVideoTrack() {
return remoteVideoTrack;
}
public LiveData<String> getErrorMessage() {
return errorMessage;
}
public LiveData<Boolean> getIsRelayUsed() {
return isRelayUsed;
}
public LiveData<String> getDisplayName() {
return displayName;
}
public LiveData<Icon> getDisplayIcon() {
return displayIcon;
}
public LiveData<CallEndpointCompat> getCurrentAudioEndpoint() {
return currentAudioEndpoint;
}
public LiveData<List<CallEndpointCompat>> getAvailableAudioEndpoints() {
return availableAudioEndpoints;
}
public LiveData<Boolean> getIsFrontCamera() {
return isFrontCamera;
}
// Notification Action Handlers
public void handleNotificationAnswer() {
Log.d(TAG, "handleNotificationAnswer");
answerCall();
}
public void handleNotificationDecline() {
Log.d(TAG, "handleNotificationDecline");
declineCall();
}
public void handleNotificationHangup() {
Log.d(TAG, "handleNotificationHangup");
hangUp();
}
// Cleanup
@Override
protected void onCleared() {
super.onCleared();
Log.d(TAG, "CallViewModel cleared");
if (answerCallObserver != null) {
callCoordinator.getMediaCaptureReady().removeObserver(answerCallObserver);
answerCallObserver = null;
}
if (startOutgoingCallObserver != null) {
callCoordinator.getMediaCaptureReady().removeObserver(startOutgoingCallObserver);
startOutgoingCallObserver = null;
}
callCoordinator.clearActiveCallViewModel();
}
}
@@ -1,274 +0,0 @@
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;
import org.webrtc.Camera2Enumerator;
import org.webrtc.CameraVideoCapturer;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.SurfaceTextureHelper;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoSource;
import org.webrtc.VideoTrack;
public class MediaStreamManager {
private static final String TAG = "MediaStreamManager";
private static final String STREAM_ID = "local_stream";
private static final String AUDIO_TRACK_ID = "audio_track";
private static final String VIDEO_TRACK_ID = "video_track";
private static final int VIDEO_WIDTH = 1280;
private static final int VIDEO_HEIGHT = 720;
private static final int VIDEO_FPS = 30;
private final Context context;
private final PeerConnectionFactory peerConnectionFactory;
private VideoCapturer videoCapturer;
private VideoSource videoSource;
private AudioSource audioSource;
private SurfaceTextureHelper surfaceTextureHelper;
private volatile boolean isFrontCamera = true;
private volatile boolean isCapturing = false;
public interface Callback {
void onMediaStreamReady(MediaStream stream);
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();
this.peerConnectionFactory = peerConnectionFactory;
}
/** Create a media stream with an audio track and a video track. */
public synchronized void createMediaStream(Callback callback) {
try {
MediaStream mediaStream = peerConnectionFactory.createLocalMediaStream(STREAM_ID);
// Create audio track
MediaConstraints audioConstraints = new MediaConstraints();
audioSource = peerConnectionFactory.createAudioSource(audioConstraints);
AudioTrack audioTrack = peerConnectionFactory.createAudioTrack(AUDIO_TRACK_ID, audioSource);
mediaStream.addTrack(audioTrack);
// Create video source and track
videoSource = peerConnectionFactory.createVideoSource(false);
VideoTrack videoTrack = peerConnectionFactory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
mediaStream.addTrack(videoTrack);
callback.onMediaStreamReady(mediaStream);
} catch (Exception e) {
Log.e(TAG, "Failed to create media stream", e);
callback.onError("Failed to access camera/microphone: " + e.getMessage());
}
}
/**
* Open the camera and start sending frames to VideoSource.
*
* @return true if the camera is capturing, false if it could not be started
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public synchronized boolean startVideoCapture() {
if (isCapturing) {
return true;
}
if (videoSource == null) {
Log.e(TAG, "VideoSource not initialized");
return false;
}
if (videoCapturer == null) {
videoCapturer = createVideoCapturer();
if (videoCapturer == null) {
Log.w(TAG, "Cannot start video capture: no camera available");
return false;
}
if (surfaceTextureHelper == null) {
surfaceTextureHelper =
SurfaceTextureHelper.create("CaptureThread", EglUtils.getEglBase().getEglBaseContext());
}
videoCapturer.initialize(surfaceTextureHelper, context, videoSource.getCapturerObserver());
}
videoCapturer.startCapture(VIDEO_WIDTH, VIDEO_HEIGHT, VIDEO_FPS);
isCapturing = true;
Log.d(TAG, "Video capture started");
return true;
}
/** Stop the camera. The capturer is kept alive. */
public synchronized void stopVideoCapture() {
if (!isCapturing) {
return;
}
if (videoCapturer != null) {
try {
videoCapturer.stopCapture();
Log.d(TAG, "Video capture stopped");
} catch (InterruptedException e) {
Log.e(TAG, "Interrupted while stopping capture", e);
Thread.currentThread().interrupt();
}
}
isCapturing = false;
}
@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
String[] deviceNames = enumerator.getDeviceNames();
for (String deviceName : deviceNames) {
if (enumerator.isFrontFacing(deviceName)) {
VideoCapturer capturer = enumerator.createCapturer(deviceName, null);
if (capturer != null) {
isFrontCamera = true;
return capturer;
}
}
}
// Fall back to any camera
for (String deviceName : deviceNames) {
VideoCapturer capturer = enumerator.createCapturer(deviceName, null);
if (capturer != null) {
isFrontCamera = enumerator.isFrontFacing(deviceName);
return capturer;
}
}
return null;
}
public void switchCamera(@Nullable CameraSwitchCallback callback) {
if (!isCapturing) {
Log.w(TAG, "Cannot switch camera while not capturing");
if (callback != null) {
callback.onError("Camera not active");
}
return;
}
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 */
public synchronized void dispose() {
if (videoCapturer != null) {
try {
if (isCapturing) {
videoCapturer.stopCapture();
}
} catch (InterruptedException e) {
Log.e(TAG, "Error stopping capture", e);
}
videoCapturer.dispose();
videoCapturer = null;
}
isCapturing = false;
if (surfaceTextureHelper != null) {
surfaceTextureHelper.dispose();
surfaceTextureHelper = null;
}
if (videoSource != null) {
videoSource.dispose();
videoSource = null;
}
if (audioSource != null) {
audioSource.dispose();
audioSource = null;
}
}
}
@@ -26,7 +26,6 @@ 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 {
@@ -46,7 +45,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;
@@ -88,13 +87,11 @@ public class AttachmentTypeSelector extends PopupWindow {
this.webxdcButton.setOnClickListener(new PropagatingClickListener(ADD_WEBXDC));
this.recentRail.setListener(new RecentPhotoSelectedListener());
if (!Prefs.isLocationStreamingEnabled(context)) {
ViewUtil.findById(layout, R.id.location_linear_layout).setVisibility(View.GONE);
}
if (DcHelper.getContext(context).getChat(chatId).isOutBroadcast()) {
ViewUtil.findById(layout, R.id.webxdc_linear_layout).setVisibility(View.GONE);
}
// 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);
// }
setLocationButtonImage(context);
@@ -15,7 +15,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.DateUtils;
public class CallItemView extends FrameLayout {
private static final String TAG = "CallItemView";
private static final String TAG = CallItemView.class.getSimpleName();
private final @NonNull ImageView icon;
private final @NonNull TextView title;

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