Compare commits

..

147 Commits

Author SHA1 Message Date
adbenitez 0ac9bea63c Merge remote-tracking branch 'upstream/main' 2026-05-26 17:04:57 +02:00
adb 2bb3ad546e Merge pull request #4442 from deltachat/prep-2.50.0
prepare 2.50.0
2026-05-26 17:03:45 +02:00
adbenitez 41a6b9a6c9 prepare 2.50.0 2026-05-26 16:00:53 +02:00
adb fb7c119afc Merge pull request #4441 from deltachat/adb/update-translations-26-05-2026
update translations
2026-05-26 16:00:15 +02:00
adbenitez 5910fc245a update translations 2026-05-26 15:53:39 +02:00
adb 7d8887eddd Merge pull request #4440 from deltachat/adb/improve-changelog-26-05-2026
update changelog
2026-05-26 15:33:32 +02:00
adbenitez 37471ae9fa update changelog 2026-05-26 15:32:13 +02:00
adb 29a4a4712c Merge pull request #4439 from deltachat/adb/improve-conversationListFragment
Improve ConversationListFragment
2026-05-25 16:52:29 +02:00
adb 644bd1f594 Merge branch 'main' into adb/improve-conversationListFragment 2026-05-25 16:52:10 +02:00
adb ed6eca8920 Merge pull request #4431 from deltachat/adb/add-enforce-e2ee
add "Enforce e2ee" switch
2026-05-25 16:51:54 +02:00
adbenitez 47512f786e Improve ConversationListFragment
* remove unused queryFilter (always empty)
* add debugging information about the time DcContext.getChatlist() takes
2026-05-25 15:48:57 +02:00
adbenitez add51186e6 Merge remote-tracking branch 'upstream/main' 2026-05-25 15:35:22 +02:00
adb 52f63b28ba Merge branch 'main' into adb/add-enforce-e2ee 2026-05-24 00:11:32 +02:00
adb 889c9aab45 Merge pull request #4438 from deltachat/update-core-and-stuff-22-05-2026
Update core to 2.50.0
2026-05-24 00:10:36 +02:00
adbenitez de5940e709 update strings 2026-05-22 23:01:35 +02:00
adbenitez 6e184f735d update RPC bindings 2026-05-22 23:00:41 +02:00
adbenitez c33ff20d0a update changelog 2026-05-22 22:55:54 +02:00
adbenitez c67c3c8972 update deltachat-core-rust to 'chore(release): prepare for 2.50.0' of 'v2.50.0' 2026-05-22 22:30:13 +02:00
B. Petersen f98a5178b3 update advanced login hint 2026-05-22 17:55:27 +02:00
B. Petersen 6dfccdab84 update CHANGELOG 2026-05-22 17:52:34 +02:00
B. Petersen 40faa3d43e allow cancel without chaning encryption setting 2026-05-22 17:50:35 +02:00
B. Petersen 74f6853815 use correct flag from core 2026-05-22 17:32:58 +02:00
B. Petersen cef6817187 make clear, the 'encryption' setting affects all relays 2026-05-22 16:44:48 +02:00
B. Petersen 6f547dfb66 fix: hide 'encryption' switch if unexpanded 2026-05-22 16:41:10 +02:00
adbenitez d6e05f61ac add "Enforce e2ee" switch 2026-05-22 16:33:12 +02:00
wchen342 55649b0c4b Parse invitation link from search (#4432) 2026-05-15 18:10:32 +02:00
wchen342 4d9f8dd244 Update pinned commits; Remove separate PR comment workflow (#4434) 2026-05-15 17:42:18 +02:00
B. Petersen 3880da8a08 chore: "not supported by provider" is deprecated
the string will be removed at https://github.com/chatmail/core/pull/8247
2026-05-15 12:46:57 +02:00
adb 5e8666835d Merge pull request #4405 from deltachat/adb/issue-4401
remove legacy options
2026-05-13 22:44:19 +02:00
adbenitez 2f5619141e update changelog 2026-05-13 22:40:47 +02:00
adb 4a07d7fd9d Merge branch 'main' into adb/issue-4401 2026-05-13 21:31:17 +02:00
adb 1493583db7 Merge pull request #4426 from deltachat/adb/issue-4422
migrate to ViewPager2
2026-05-13 19:21:54 +02:00
adbenitez dc363a0281 Merge branch 'adb/issue-4422' of https://github.com/deltachat/deltachat-android into adb/issue-4422 2026-05-13 19:17:16 +02:00
adbenitez fcffe3922a fix createFragment 2026-05-13 19:17:00 +02:00
adb d546d8041f Merge branch 'main' into adb/issue-4422 2026-05-13 19:01:06 +02:00
adbenitez 267caf9b03 create a new QrShowFragment every time 2026-05-13 18:59:34 +02:00
wchen342 d55ad1f32a Call: Add notice when offline (#4425)
* Call: Add notice when offline

Co-authored-by: biörn <r10s@b44t.com>
2026-05-13 16:04:03 +02:00
B. Petersen be07043b47 prefer 'audio' over 'switch speaker', which is more correct, others are using that as well, and the translations are there already 2026-05-12 15:19:36 +02:00
adb cc3e6bcd9d Merge branch 'main' into adb/issue-4401 2026-05-12 15:18:17 +02:00
biörn 43654fdadb feat: tweak forward confirmation (#4427)
* cleanup: remove dead code

the special handling of single-chats was to show the email address in the past,
it was introduced at https://github.com/deltachat/deltachat-android/pull/1049 .
as we do no longer show the email address, this superfluous code now.

* feat: show impact of forwarding in confirmation dialog

* feat: show 'Forward' verb on button

* apply spotless
2026-05-12 13:00:42 +00:00
adbenitez 774add2380 update changelog 2026-05-11 23:52:12 +02:00
adbenitez 1133a6e624 migrate AllMediaActivity to ViewPager2 2026-05-11 23:43:17 +02:00
adbenitez 0aad1b3d76 migrate QrActivity to ViewPager2 2026-05-11 22:34:07 +02:00
adbenitez b58a9d0bab apply spotless 2026-05-11 17:58:47 +02:00
adbenitez 54a74a8586 migrate to ViewPager2 in MediaPreviewActivity 2026-05-11 17:48:19 +02:00
adb f25474947b Merge pull request #4421 from deltachat/adb/issue-4418
move requestPinShortcut to background
2026-05-08 17:14:10 +02:00
adbenitez 9ebd8e4a37 fix typos and other bugs 2026-05-08 17:09:01 +02:00
adbenitez 975ad2e149 wrap with try-catch 2026-05-08 17:01:47 +02:00
adbenitez e6350aaec2 move requestPinShortcut to background 2026-05-08 16:18:36 +02:00
B. Petersen d373537d6d avatars can be ignored by talkback, the name is in the title 2026-05-07 21:44:11 +02:00
B. Petersen 960581e5f2 make call buttons translatable 2026-05-07 21:44:11 +02:00
B. Petersen fccf8f402e use same theme colors as in camera, editor 2026-05-07 21:44:11 +02:00
B. Petersen e0459978f7 feat: use consistent colors and contrast 2026-05-07 21:44:11 +02:00
wchen342 72bd7376ca Make it possible to answer incoming calls from messages (#4415) 2026-05-07 16:29:27 +00:00
adb 94fbdcde05 Merge pull request #4408 from deltachat/adb/issue-4152
add a setting to enable/disable calls notification
2026-05-07 18:19:27 +02:00
wchen342 89d77e7638 Call: camera/audio permission related fixes (#4412)
* Call: camera/permission related fixes

Defer camera initialization until actually needed.

Fix missing camera permission which blocks call from initializing correctly.

Add dialog when audio permission is not granted.

Add redirection dialog when permissions are permanently denied.
2026-05-07 13:55:46 +02:00
adb d9edee117f Merge branch 'main' into adb/issue-4152 2026-05-06 15:04:57 +02:00
adbenitez 6cfdb87924 Merge branch 'adb/issue-4152' of https://github.com/deltachat/deltachat-android into adb/issue-4152 2026-05-06 15:04:40 +02:00
adbenitez 613940577c update changelog 2026-05-06 15:04:30 +02:00
B. Petersen 43a3e21495 add some context for translators, do not introduce another prefix 2026-05-06 15:03:46 +02:00
adb b5375b8c0e Merge branch 'main' into adb/issue-4152 2026-05-06 15:02:40 +02:00
B. Petersen 39aec04fea remove unhelpful channel description 2026-05-06 15:01:53 +02:00
B. Petersen 94e2c8dbed feat: simplify location streaming wording 2026-05-06 15:01:53 +02:00
adbenitez da45c7bc1c apply spotless 2026-05-06 15:01:51 +02:00
adbenitez d180704d52 allow to disable call notifications 2026-05-06 14:59:39 +02:00
B. Petersen 653c8688b7 mark deprecated strings as such 2026-05-06 14:42:49 +02:00
B. Petersen 9a382a4948 actually explain what the 'calls notification' option does 2026-05-06 14:42:49 +02:00
adbenitez dcaa19b29d Merge remote-tracking branch 'upstream/main' 2026-05-05 21:01:48 +02:00
adb 11845da65f Merge pull request #4407 from deltachat/adb/issue-4406
add "mark all as read" to account menu
2026-05-05 18:03:46 +02:00
adbenitez 82262cb166 update changelog 2026-05-05 15:41:55 +02:00
adbenitez e85ae5247a add "mark all as read" to account menu 2026-05-05 15:40:06 +02:00
adb 74e14bd0ea Merge branch 'main' into adb/issue-4401 2026-05-05 15:12:03 +02:00
adb 40cdeee47a Merge pull request #4404 from deltachat/adb/issue-4396
don't set draft if user can't send
2026-05-05 15:09:37 +02:00
adbenitez 844231e5e7 Merge branch 'adb/issue-4401' of https://github.com/deltachat/deltachat-android into adb/issue-4401 2026-05-04 22:05:52 +02:00
adbenitez 0ccf0a9309 apply spotless 2026-05-04 22:05:42 +02:00
adb 5040fb4634 Merge branch 'main' into adb/issue-4396 2026-05-04 22:04:51 +02:00
adb a57122a902 Merge branch 'main' into adb/issue-4401 2026-05-04 20:44:44 +02:00
adb 4313e8824b Merge pull request #4395 from deltachat/adb/issue-4200
allow to open message links via actions in TalkBack menu
2026-05-04 20:42:37 +02:00
adbenitez 464cba299a remove legacy options 2026-05-04 20:41:32 +02:00
B. Petersen 102c3fd82a make strings as deprecated 2026-05-04 20:14:19 +02:00
adbenitez dc31eee4bc Merge branch 'adb/issue-4200' of https://github.com/deltachat/deltachat-android into adb/issue-4200 2026-05-04 20:14:06 +02:00
adbenitez 875f0e5c86 remove unnecessary check 2026-05-04 20:13:37 +02:00
wchen342 2b36e2dc26 Hide webxdc attach button for channels (#4402) 2026-05-04 20:02:37 +02:00
adbenitez 6a9c3ab8ca update changelog 2026-05-04 19:57:10 +02:00
adbenitez 2ab544b18e don't set draft if user can't send 2026-05-04 19:55:13 +02:00
adbenitez 2630e22421 Merge remote-tracking branch 'upstream/main' 2026-05-04 19:20:04 +02:00
wchen342 1b7e32ab44 Get channels out of experimental (#4403)
* Get channels out of experimental

* Update CHANGELOG.md

Co-authored-by: adb <adb@merlinux.eu>
2026-05-04 18:28:51 +02:00
adb 8436909977 Merge branch 'main' into adb/issue-4200 2026-05-01 15:51:40 +02:00
adb 9dd44c24b3 Merge pull request #4394 from deltachat/adb/open-map-on-loc-streaming-enabled
open map if user clicks the "Location streaming enabled by Foo/you" message
2026-05-01 15:47:14 +02:00
adbenitez 5d0e85b30d update changelog 2026-05-01 15:41:06 +02:00
adbenitez 5193dfa412 allow to open message links via actions in TalkBack menu 2026-05-01 15:38:06 +02:00
adbenitez 96490e23f8 update changelog 2026-04-30 17:59:31 +02:00
adbenitez 7959b07d45 open map if user clicks the "Location streaming enabled by Foo/you" message 2026-04-30 17:57:35 +02:00
link2xt c4ae7927ad Setup zizmor workflow to check GitHub Actions workflows 2026-04-29 17:13:09 +00:00
wchen342 ba0515ebc5 Allow voice messages to be played continuously in a chat (#4376)
* Allow voice messages to be played continuously in a chat

---------

Co-authored-by: adb <adb@merlinux.eu>
2026-04-29 17:24:10 +02:00
adb 349bab9510 Merge pull request #4392 from deltachat/adb/issue-4391
change sticker sort order
2026-04-29 17:05:26 +02:00
adbenitez e26f803d1f update changelog 2026-04-29 17:01:36 +02:00
adbenitez 60ab7f2826 Merge remote-tracking branch 'upstream/main' 2026-04-29 16:48:31 +02:00
adbenitez 67d2c89bcc change sticker sort order 2026-04-29 15:58:11 +02:00
adb e511237e5e Merge pull request #4384 from deltachat/adb/issue-4345
add highlight to input panel buttons for keyboard navigation
2026-04-28 17:17:27 +02:00
adb f249fd6174 Merge pull request #4386 from deltachat/adb/issue-4342
add mapping.txt to symbols zip
2026-04-28 17:16:29 +02:00
B. Petersen 6db45c1911 add translator hints for new call strings 2026-04-28 17:01:47 +02:00
adb 06a1114744 Merge pull request #4388 from deltachat/adb/issue-4328
fix migration while upgrading from 1.10.1 and similar old versions
2026-04-28 16:54:19 +02:00
adb 3afff5625b Merge branch 'main' into adb/issue-4328 2026-04-28 16:40:53 +02:00
adbenitez cef8ddeedb update changelog 2026-04-28 16:32:33 +02:00
adbenitez 2f0ac973c0 fix migration from 1.10.1 and similar old versions 2026-04-28 16:28:25 +02:00
adbenitez 9465776715 add mapping.txt to symbols zip 2026-04-28 16:04:07 +02:00
B. Petersen 39685a575d feat: better wording for "Media Quality" option
this changes "Worse quality, small size" to "Worse quality, save data".

this makes it more clearer, what the advantage of that option is.

also, the recently added hint about sending as original also speaks about "data"
(https://github.com/deltachat/deltachat-android/pull/4259)
which is more known to the average user, knowing that of "mobile data" etc.
2026-04-28 15:51:37 +02:00
B. Petersen 8601736387 update translations 2026-04-28 15:49:37 +02:00
adbenitez 98d8572baf add highlight to input panel buttons for keyboard navigation 2026-04-28 15:16:36 +02:00
wchen342 917e764b6f Promote call FGS asap (#4377)
* Promote call FGS as soon as possible

* Add resource strings for call notification

* Launch with only phone call type on 14+
2026-04-27 20:34:51 +02:00
adb 52263150c8 Merge pull request #4374 from deltachat/adb/location-streaming-time-tweaks
allow to share location for 24 hours, remove 5 minutes
2026-04-27 17:27:12 +02:00
B. Petersen c2aba94b34 mark deprecated strings as such 2026-04-25 02:07:46 +02:00
adbenitez 1278dfd395 Merge remote-tracking branch 'upstream/main' 2026-04-24 19:55:16 +02:00
wchen342 44a688470f Merge pull request #4370 from deltachat/wch423/change-tag-names
Remove usages of `class.getSimpleName()`
2026-04-24 12:18:37 -04:00
wch423 e9a09d9c72 Correct tag name 2026-04-24 15:59:37 +02:00
wchen342 abc49a3b95 Update src/main/java/com/b44t/messenger/DcMsg.java
Co-authored-by: adb <adb@merlinux.eu>
2026-04-23 17:55:20 +02:00
wch423 cdd4e5eccd Code formatting 2026-04-23 17:55:18 +02:00
wch423 fa8ed2b881 Remove usages of class.getSimpleName() 2026-04-23 17:55:16 +02:00
B. Petersen 0731fe9447 fix warning string, make reuse of new explicit button strings 2026-04-23 15:57:42 +02:00
adbenitez d3567d9f0c fix arrays.xml 2026-04-23 14:51:48 +02:00
adbenitez 66ab1f8051 update changelog 2026-04-23 14:51:04 +02:00
adbenitez 872fd17f5e allow to share location for 24 hours, remove 5 minutes 2026-04-23 14:49:18 +02:00
B. Petersen af49018911 add strings from desktop, add some missing strings 2026-04-23 00:43:12 +02:00
adbenitez f870e9c8fd Merge remote-tracking branch 'upstream/main' 2026-04-22 21:48:58 +02:00
wchen342 d5982ba09f Merge pull request #4361 from deltachat/wch423/location-streaming
Add foreground service for location streaming
2026-04-18 17:30:56 -04:00
adbenitez 22d70d7bd4 Merge remote-tracking branch 'upstream/main' 2026-04-18 22:15:03 +02:00
adb 09bdc32a9c Merge pull request #4315 from deltachat/r10s/markfresh
feat: mark messages as "unread"
2026-04-18 21:09:18 +02:00
adb 7763ba17bf Merge branch 'main' into r10s/markfresh 2026-04-18 20:15:57 +02:00
adb 965204c46f Merge branch 'main' into wch423/location-streaming 2026-04-18 19:56:00 +02:00
adbenitez 815e4def6f Merge branch 'wch423/location-streaming' of https://github.com/deltachat/deltachat-android into wch423/location-streaming 2026-04-18 19:54:17 +02:00
adbenitez dc3ca8ff70 use "Location Streaming" for notification title and channel name 2026-04-18 19:48:39 +02:00
adb 3bccb3fb84 Apply suggestion from @adbenitez 2026-04-18 19:44:39 +02:00
adb 402d93b5a4 Merge pull request #4363 from deltachat/adb/allow-autoplay
allow webxdc to auto-play audio without requiring user gesture/touch
2026-04-18 17:10:48 +02:00
adbenitez 927dc46431 update changelog 2026-04-18 00:02:19 +02:00
adbenitez 87c32b1904 allow webxdc to auto-play audio without requiring user gesture/touch 2026-04-17 23:59:51 +02:00
adbenitez bb97213460 apply spotless 2026-04-17 23:52:22 +02:00
adbenitez 9ca9bf1acd remove unnecessary entry in src/main/res/xml/file_provider_paths.xml 2026-04-17 23:45:36 +02:00
wch423 885c902b14 Allow fallback to different providers 2026-04-17 21:11:56 +02:00
wch423 61bf5aaad4 Change how location sources work 2026-04-17 20:30:57 +02:00
wch423 5d76586ac1 Add foreground service for location streaming 2026-04-15 19:07:34 +02:00
B. Petersen beb45af440 prefer 'mark noticed' over 'mark fresh' 2026-04-02 15:50:40 +02:00
adbenitez 87a21eb0f2 apply spotless 2026-04-02 15:41:41 +02:00
adbenitez dea51cd356 use RPC API instead of C API 2026-04-02 15:41:41 +02:00
B. Petersen ed540e5584 update CHANGLOG 2026-04-02 15:41:38 +02:00
B. Petersen 9f80c9f35f make spotless formatter happy 2026-04-02 15:40:32 +02:00
B. Petersen 5dec1b24cd option to mark chat fresh 2026-04-02 15:40:32 +02:00
227 changed files with 7839 additions and 2208 deletions
-20
View File
@@ -1,20 +0,0 @@
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
+8 -5
View File
@@ -10,21 +10,24 @@ on:
- 'jni/dc_wrapper.c'
- 'scripts/ndk-make.sh'
permissions: {}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: recursive
persist-credentials: false
- uses: android-actions/setup-android@v3
- uses: nttld/setup-ndk@v1
- 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@v2
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
workspaces: jni/deltachat-core-rust
@@ -34,7 +37,7 @@ jobs:
- name: Cache compiled core
id: cache-core
uses: actions/cache@v4
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
jni/arm64-v8a
+18 -4
View File
@@ -5,6 +5,8 @@ on:
branches: [main]
pull_request:
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
@@ -16,12 +18,16 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v5
- uses: actions/setup-java@v5
- 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
- uses: actions/cache@v4
- name: Restore Gradle cache
id: gradle-cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
~/.gradle/caches
@@ -30,6 +36,14 @@ jobs:
restore-keys: |
${{ runner.os }}-gradle-
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@v4
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') }}
+32 -10
View File
@@ -6,24 +6,31 @@ 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@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: recursive
persist-credentials: false
- name: Validate Fastlane Metadata
uses: ashutoshgngwr/validate-fastlane-supply-metadata@v2
uses: ashutoshgngwr/validate-fastlane-supply-metadata@c8857fdbbd3e00f9a5cbe8604bcecfa95ce8fef8 # v2.1.0
- uses: actions/setup-java@v5
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
java-version: 17
distribution: 'temurin'
- uses: actions/cache@v4
- uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
~/.gradle/caches
@@ -33,15 +40,15 @@ jobs:
${{ runner.os }}-gradle-
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@v4
uses: gradle/actions/wrapper-validation@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
- uses: android-actions/setup-android@v3
- uses: nttld/setup-ndk@v1
- 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@v2
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
workspaces: jni/deltachat-core-rust
@@ -51,7 +58,7 @@ jobs:
- name: Restore compiled core
id: core-cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
jni/arm64-v8a
@@ -70,7 +77,22 @@ jobs:
run: ./gradlew --no-daemon -PABI_FILTER=arm64-v8a assembleFossDebug
- name: Upload APK
uses: actions/upload-artifact@v4
id: upload
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
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})`,
});
+6 -6
View File
@@ -23,18 +23,18 @@ jobs:
name: Upload Release APK
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: recursive
- uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
working-directory: jni/deltachat-core-rust
- uses: actions/setup-java@v3
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
java-version: 17
distribution: 'temurin'
- uses: android-actions/setup-android@v3
- uses: nttld/setup-ndk@v1
- uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4.0.1
- uses: nttld/setup-ndk@ed92fe6cadad69be94a966a7ee3271275e62f779 # v1.6.0
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@v1
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
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)'
+26
View File
@@ -0,0 +1,26 @@
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
@@ -0,0 +1,6 @@
rules:
unpinned-uses:
config:
policies:
actions/*: ref-pin
dependabot/*: ref-pin
+12
View File
@@ -243,3 +243,15 @@ $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/
+36 -1
View File
@@ -1,9 +1,44 @@
# Delta Chat Android Changelog
## Unreleased
## 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
+4 -3
View File
@@ -34,8 +34,8 @@ android {
useLibrary 'org.apache.http.legacy'
defaultConfig {
versionCode 30000742
versionName "2.49.0"
versionCode 30000743
versionName "2.50.0"
applicationId "chat.delta.lite"
multiDexEnabled true
@@ -223,7 +223,7 @@ spotless {
}
format 'xml', {
target 'src/*/res/**/*.xml', 'src/*/AndroidManifest.xml'
targetExclude 'src/*/res/values*/strings.xml' // line-break changes invalidate translations
targetExclude 'src/*/res/values*/strings*.xml' // line-break changes invalidate translations
eclipseWtp('xml').configFile('spotless/eclipse-wtp-xml.prefs')
trimTrailingWhitespace()
endWithNewline()
@@ -307,6 +307,7 @@ dependencies {
exclude group: 'com.google.firebase', module: 'firebase-analytics'
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
}
gplayImplementation 'com.google.android.gms:play-services-location:21.3.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.assertj:assertj-core:3.27.3'
@@ -25,7 +25,7 @@ ArcaneChat is a Delta Chat client and was created with a focus on usability, goo
<li>Multiple color themes/skins</li>
<li>It is possible to disable profiles to completely disconnect them saving data/bandwidth</li>
<li>You can easily see the connection status of all your profiles in the profile switcher</li>
<li>Extra option to share location for 12 hours</li>
<li>Extra option to share location for 24 hours</li>
<li>Clicking on a message with a POI location, will open the POI on the map</li>
<li>Last-seen status of contacts is shown in your contact list, like in WhatsApp, Telegram, etc.</li>
<li>Videos are played in loop, useful for short GIF videos</li>
+1 -6
View File
@@ -1393,6 +1393,7 @@ JNIEXPORT jint Java_com_b44t_messenger_DcMsg_getId(JNIEnv *env, jobject obj)
return dc_msg_get_id(get_dc_msg(env, obj));
}
JNIEXPORT jstring Java_com_b44t_messenger_DcMsg_getText(JNIEnv *env, jobject obj)
{
char* temp = dc_msg_get_text(get_dc_msg(env, obj));
@@ -1642,12 +1643,6 @@ 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));
+1
View File
@@ -34,6 +34,7 @@ cd ../..
SYMBOLS_ZIP="$APK-symbols.zip"
rm $SYMBOLS_ZIP
zip -r $SYMBOLS_ZIP obj
zip $SYMBOLS_ZIP build/outputs/mapping/gplayRelease/mapping.txt
ls -l $SYMBOLS_ZIP
rsync --progress $SYMBOLS_ZIP jekyll@download.delta.chat:/var/www/html/download/android/symbols/
@@ -39,7 +39,7 @@ public class EnterChatsBenchmark {
// PLEASE BACKUP YOUR ACCOUNT BEFORE RUNNING THIS!
// ==============================================================================================
private static final String TAG = EnterChatsBenchmark.class.getSimpleName();
private static final String TAG = "EnterChatsBenchmark";
@Rule
public ActivityScenarioRule<ConversationListActivity> activityRule =
@@ -0,0 +1,17 @@
package org.thoughtcrime.securesms.geolocation;
import android.content.Context;
import android.util.Log;
/** Non-GMS, always uses the platform LocationManager. */
public final class LocationSourceFactory {
private static final String TAG = "LocationSourceFactory";
private LocationSourceFactory() {}
public static LocationSource create(Context context) {
Log.i(TAG, "Non-GMS build, Using platform LocationManager");
return new PlatformLocationSource();
}
}
@@ -0,0 +1,61 @@
package org.thoughtcrime.securesms.geolocation;
import android.content.Context;
import android.location.Location;
import android.os.Looper;
import android.util.Log;
import androidx.annotation.NonNull;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.Priority;
public class GmsLocationSource implements LocationSource {
private static final String TAG = "GmsLocationSource";
private static final long UPDATE_INTERVAL_MS = 3_000;
private static final long FASTEST_INTERVAL_MS = 1_000;
private FusedLocationProviderClient client;
private LocationCallback locationCallback;
@Override
public void startUpdates(@NonNull Context context, @NonNull Callback callback) {
client = LocationServices.getFusedLocationProviderClient(context);
LocationRequest request =
new LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, UPDATE_INTERVAL_MS)
.setMinUpdateIntervalMillis(FASTEST_INTERVAL_MS)
.setMinUpdateDistanceMeters(0)
.setWaitForAccurateLocation(false)
.build();
locationCallback =
new LocationCallback() {
@Override
public void onLocationResult(@NonNull LocationResult result) {
Location loc = result.getLastLocation();
if (loc != null) {
callback.onLocationUpdate(loc);
}
}
};
try {
client.requestLocationUpdates(request, locationCallback, Looper.getMainLooper());
} catch (SecurityException e) {
Log.e(TAG, "Missing location permission", e);
}
}
@Override
public void stopUpdates() {
if (client != null && locationCallback != null) {
client.removeLocationUpdates(locationCallback);
client = null;
locationCallback = null;
}
}
}
@@ -0,0 +1,35 @@
package org.thoughtcrime.securesms.geolocation;
import android.content.Context;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
/**
* Prefers FusedLocationProviderClient, falls back to platform LocationManager if Play Services are
* somehow unavailable.
*/
public final class LocationSourceFactory {
private static final String TAG = "LocationSourceFactory";
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.class.getSimpleName();
private static final String TAG = "FcmReceiveService";
private static final Object INIT_LOCK = new Object();
private static boolean initialized;
private static volatile boolean triedRegistering;
+8 -6
View File
@@ -71,6 +71,8 @@
android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<!-- force compiling libs on older sdk than supported; runtime checks are required -->
@@ -81,7 +83,6 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
tools:replace="android:allowBackup"
android:allowBackup="false"
android:theme="@style/TextSecure.LightTheme"
android:largeHeap="true"
@@ -464,10 +465,6 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
</activity>
<service
android:name=".geolocation.LocationBackgroundService"
android:foregroundServiceType="location" />
<service
android:name=".service.GenericForegroundService"
android:foregroundServiceType="dataSync" />
@@ -476,6 +473,11 @@
android:name=".service.FetchForegroundService"
android:foregroundServiceType="dataSync" />
<service
android:name=".geolocation.LocationStreamingService"
android:foregroundServiceType="location"
android:exported="false" />
<service
android:name=".service.AudioPlaybackService"
android:foregroundServiceType="mediaPlayback"
@@ -489,7 +491,7 @@
android:name=".calls.CallService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="camera|microphone" />
android:foregroundServiceType="camera|microphone|phoneCall" />
<receiver
android:name=".notifications.MarkReadReceiver"
+216 -13
View File
@@ -29,6 +29,21 @@
<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>
@@ -628,6 +643,197 @@ 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">
@@ -1425,23 +1631,20 @@ can not be identified easily.</p>
</h3>
<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>
<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>IP Addresses are needed for connectivity and efficiency.
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>
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>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>
<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>
<h3 id="sealedsender">
+213 -14
View File
@@ -29,6 +29,21 @@
<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>
@@ -584,6 +599,197 @@ 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">
@@ -1329,23 +1535,16 @@ im Falle einer Beschlagnahmung des Geräts nicht ohne Weiteres identifiziert wer
</h3>
<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>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>IP-Adressen sind für Verbindungen und für Effizienz erforderlich.
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>
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>
<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>
<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>
<h3 id="sealedsender">
+216 -13
View File
@@ -29,6 +29,21 @@
<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>
@@ -627,6 +642,197 @@ 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">
@@ -1425,23 +1631,20 @@ can not be identified easily.</p>
</h3>
<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>
<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>IP Addresses are needed for connectivity and efficiency.
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>
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>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>
<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>
<h3 id="sealedsender">
+377 -176
View File
@@ -2,38 +2,53 @@
<html lang="es"><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="#qué-es-delta-chat">¿Qué es Delta Chat?</a>
<ul>
<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="#howtoe2ee">¿Cómo puedo encontrar gente para chatear?</a></li>
<li><a href="#por-qué-un-chat-aparece-marcado-como-solicitud">¿Por qué un chat aparece marcado como “Solicitud”?</a></li>
<li><a href="#cómo-puedo-poner-en-contacto-a-dos-de-mis-amigos-entre-ellos">¿Cómo puedo poner en contacto a dos de mis amigos entre ellos?</a></li>
<li><a href="#multiple-accounts">¿Qué son los perfiles? ¿Cómo puedo cambiar entre ellos?</a></li>
<li><a href="#quién-ve-mi-foto-de-perfil">¿Quién ve mi foto de perfil?</a></li>
<li><a href="#signature">Can I set a Bio/Status with Delta Chat?</a></li>
<li><a href="#signature">¿Puedo establecer una Bio/Estado en Delta Chat?</a></li>
<li><a href="#qué-significa-fijar-mutear-archivar">¿Qué significa fijar, mutear, archivar?</a></li>
<li><a href="#save">¿Cómo funcionan los “Mensajes guardados”?</a></li>
<li><a href="#qué-significa-el-punto-verde">¿Qué significa el punto verde?</a></li>
<li><a href="#qué-significan-las-marcas-que-se-muestran-junto-a-los-mensajes-salientes">¿Qué significan las marcas que se muestran junto a los mensajes salientes?</a></li>
<li><a href="#edit">Corregir errores y borrar mensajes después de enviar</a></li>
<li><a href="#mediaquality">How is media quality handled?</a></li>
<li><a href="#mediaquality">¿Cómo se gestiona la calidad de los archivos multimedia?</a></li>
<li><a href="#ephemeralmsgs">¿Cómo funciona la desaparición de mensajes?</a></li>
<li><a href="#delold">¿Qué pasa si activo “Borrar mensajes del dispositivo”?</a></li>
<li><a href="#remove-account">How can I delete my chat profile?</a></li>
<li><a href="#remove-account">¿Cómo puedo borrar mi perfil?</a></li>
</ul>
</li>
<li><a href="#groups">Groups</a>
<li><a href="#groups">Grupos</a>
<ul>
<li><a href="#creación-de-un-grupo">Creación de un grupo</a></li>
<li><a href="#addmembers">Add and remove members</a></li>
<li><a href="#addmembers">Añadir y eliminar miembros</a></li>
<li><a href="#me-he-eliminado-por-accidente">Me he eliminado por accidente.</a></li>
<li><a href="#no-quiero-recibir-más-los-mensajes-de-un-grupo">No quiero recibir más los mensajes de un grupo.</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>
<li><a href="#clonar-un-grupo">Clonar un grupo</a></li>
<li><a href="#cuántos-miembros-pueden-participar-en-un-solo-grupo">¿Cuántos miembros pueden participar en un solo grupo?</a></li>
</ul>
</li>
<li><a href="#webxdc">In-chat apps</a>
<li><a href="#channels">Channels</a>
<ul>
<li><a href="#where-can-i-get-in-chat-apps">Where can I get in-chat apps?</a></li>
<li><a href="#how-private-are-in-chat-apps">How private are in-chat apps?</a></li>
<li><a href="#create-xdc">How can I create my own in-chat apps?</a></li>
<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">Aplicaciones en chats</a>
<ul>
<li><a href="#dónde-puedo-conseguir-aplicaciones-para-el-chat">¿Dónde puedo conseguir aplicaciones para el chat?</a></li>
<li><a href="#cómo-de-privadas-son-las-aplicaciones-en-el-chat">¿Cómo de privadas son las aplicaciones en el chat?</a></li>
<li><a href="#create-xdc">¿Cómo puedo crear mis propias aplicaciones para el chat?</a></li>
</ul>
</li>
<li><a href="#instant-delivery">Entrega de mensajes instantáneos y notificaciones Push</a>
@@ -57,7 +72,7 @@
<ul>
<li><a href="#experiments">Experimental Features</a></li>
<li><a href="#relays">What are Relays?</a></li>
<li><a href="#can-i-use-a-classic-email-address-with-delta-chat">Can I use a classic email address with Delta Chat?</a></li>
<li><a href="#puedo-utilizar-una-dirección-de-email-clásico-con-delta-chat">¿Puedo utilizar una dirección de email clásico con Delta Chat?</a></li>
<li><a href="#classic-email">How can I configure a chat profile with a classic email address as relay?</a></li>
<li><a href="#i-want-to-manage-my-own-server-for-delta-chat-what-do-you-recommend">I want to manage my own server for Delta Chat. What do you recommend?</a></li>
<li><a href="#statssending">What is “Send statistics to Delta Chats developers”?</a></li>
@@ -67,8 +82,8 @@
<li><a href="#e2ee">Cifrado y seguridad</a>
<ul>
<li><a href="#qué-estándares-se-utilizan-para-el-cifrado-de-extremo-a-extremo">¿Qué estándares se utilizan para el cifrado de extremo a extremo?</a></li>
<li><a href="#whene2e">How can I know if messages are end-to-end encrypted?</a></li>
<li><a href="#can-i-still-receive-or-send-messages-without-end-to-end-encryption">Can I still receive or send messages without end-to-end encryption?</a></li>
<li><a href="#whene2e">¿Cómo puedo saber si los mensajes están cifrados de extremo a extremo?</a></li>
<li><a href="#puedo-enviar-o-recibir-mensajes-sin-cifrado-de-extremo-a-extremo">¿Puedo enviar o recibir mensajes sin cifrado de extremo a extremo?</a></li>
<li><a href="#e2eeguarantee">What does the green checkmark in a contact profile mean?</a></li>
<li><a href="#los-adjuntos-imágenes-archivos-audio-etc-están-cifrados-de-extremo-a-extremo">¿Los adjuntos (imágenes, archivos, audio, etc.) están cifrados de extremo a extremo?</a></li>
<li><a href="#openpgp-secure">¿Es OpenPGP seguro?</a></li>
@@ -77,11 +92,11 @@
<li><a href="#tls">Are messages marked with the mail icon exposed on the Internet?</a></li>
<li><a href="#message-metadata">¿Cómo Delta Chat protege los metadatos en los mensajes?</a></li>
<li><a href="#device-seizure">¿Cómo proteger los metadatos y los contactos cuando se incauta un dispositivo?</a></li>
<li><a href="#who-sees-my-ip-address">Who sees my IP Address?</a></li>
<li><a href="#sealedsender">Does Delta Chat support “Sealed Sender”?</a></li>
<li><a href="#quién-puede-ver-mi-dirección-ip">¿Quién puede ver mi dirección IP?</a></li>
<li><a href="#sealedsender">¿Soporta Delta Chat “Sealed Sender”?</a></li>
<li><a href="#pfs">¿Soporta Delta Chat Perfect Forward Secrecy?</a></li>
<li><a href="#pqc">Does Delta Chat support Post-Quantum-Cryptography?</a></li>
<li><a href="#how-can-i-manually-check-encryption-information">How can I manually check encryption information?</a></li>
<li><a href="#pqc">¿Soporta Delta Chat criptografía postcuántica?</a></li>
<li><a href="#cómo-puedo-comprobar-manualmente-la-información-de-cifrado">¿Cómo puedo comprobar manualmente la información de cifrado?</a></li>
<li><a href="#importkey">¿Puedo reutilizar mi clave privada existente?</a></li>
<li><a href="#security-audits">¿Se auditó Delta Chat de forma independiente en busca de vulnerabilidades de seguridad?</a></li>
</ul>
@@ -89,7 +104,7 @@
<li><a href="#miscelaneo">Miscelaneo</a>
<ul>
<li><a href="#qué-permisos-necesita-delta-chat">¿Qué permisos necesita Delta Chat?</a></li>
<li><a href="#where-can-my-friends-find-delta-chat">Where can my friends find Delta Chat?</a></li>
<li><a href="#dónde-pueden-encontrar-mis-amigos-delta-chat">¿Dónde pueden encontrar mis amigos Delta Chat?</a></li>
<li><a href="#cómo-se-financia-el-desarrollo-de-delta-chat">¿Cómo se financia el desarrollo de Delta Chat?</a></li>
</ul>
</li>
@@ -109,85 +124,85 @@
<ul>
<li>
<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>
<p>Creación instantánea de <strong>perfiles de chat privados</strong>
con <a href="https://chatmail.at/relays">servidores de chatmail</a> seguros e interoperables
que ofrecen un envío instantáneo de mensajes y Notificaciones Push en dispositivos Android e iOS.</p>
</li>
<li>
<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>
<p>Soporte generalizado <a href="#multiple-accounts">multiperfil</a> y
soporte <a href="#multiclient">multidispositivo</a> en todas las plataformas
y entre diferentes <a href="https://chatmail.at/clients">aplicaciones de chatmail</a>.</p>
</li>
<li>
<p>Interactive <a href="#webxdc">in-chat apps</a> for gaming and collaboration</p>
<p>Aplicaciones interactivas <a href="#webxdc">en el chat</a> para juegos y colaboración</p>
</li>
<li>
<p><a href="#security-audits">Audited end-to-end encryption</a>
safe against network and server attacks.</p>
<p><a href="#security-audits">Cifrado de extremo a extremo auditado</a>
seguro contra ataques a la red y al servidor.</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>
<p>Software gratuito y de código abierto, tanto las aplicaciones como los servidores,
construido sobre <a href="https://github.com/chatmail/core/blob/main/standards.md#standards-used-in-delta-chat">estándares de internet</a>.</p>
</li>
</ul>
<h3 id="howtoe2ee">
How can I find people to chat with? <a href="#howtoe2ee" class="anchor"></a>
¿Cómo puedo encontrar gente para chatear? <a href="#howtoe2ee" class="anchor"></a>
</h3>
<p>First, note that Delta Chat is a private messenger.
There is no public discovery, <em>you</em> decide about your contacts.</p>
<p>En primer lugar, ten en cuenta que Delta Chat es un sistema de mensajería privado.
No hay descubrimiento público, <em></em> decides sobre tus contactos.</p>
<ul>
<li>
<p>If you are <strong>face to face</strong> with your friend or family,
tap the <strong>QR Code</strong> icon <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" />
on the main screen.<br />
Ask your chat partner to <strong>scan</strong> the QR image
with their Delta Chat app.</p>
<p>Si estas <strong>cara a cara</strong> con tus amigos o familia,
pulsa el icono del <strong>Código QR</strong> <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" />
en la pantalla principal.
Pídele a la otra persona que <strong>escanee</strong> el código QR
con su aplicación de Delta Chat.</p>
</li>
<li>
<p>For a <strong>remote</strong> contact setup,
from the same screen,
click “Copy” orShare” and send the <strong>invite link</strong>
through another private chat.</p>
<p>Para configurar un contacto <strong>a distancia</strong>,
desde la misma pantalla,
haga clic en “Copiar” o “Compartir” y envíe el <strong>enlace de invitación</strong>
a través de otro chat privado.</p>
</li>
</ul>
<p>Now wait while connection gets established.</p>
<p>Espera mientras la conexión se establece.</p>
<ul>
<li>
<p>If both sides are online, they will soon see a chat
and can start messaging securely.</p>
<p>Si ambas partes están en línea, pronto verán un chat
y podrán empezar a mensajearse de forma segura.</p>
</li>
<li>
<p>If one side is offline or in bad network,
the ability to chat is delayed until connectivity is restored.</p>
<p>Si una de las partes no está conectada o tiene una mala conexión,
la capacidad de chatear se retrasará hasta que la conectividad se haya restaurado.</p>
</li>
</ul>
<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>
<p>¡Enhorabuena!
Ahora estás utilizando automáticamente el <a href="#e2ee">cifrado de extremo a extremo</a> con este contacto.
Si os añadís mutuamente a <a href="#groups">grupos</a>, el cifrado de extremo a extremo se establecerá entre todos los miembros.</p>
<h3 id="why-is-a-chat-marked-as-request">
<h3 id="por-qué-un-chat-aparece-marcado-como-solicitud">
Why is a chat marked as “Request”? <a href="#why-is-a-chat-marked-as-request" class="anchor"></a>
¿Por qué un chat aparece marcado como “Solicitud”? <a href="#por-qué-un-chat-aparece-marcado-como-solicitud" class="anchor"></a>
</h3>
<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>Al ser un sistema de mensajería privada
solo los amigos y familiares con los que <a href="#howtoe2ee">compartas tu código QR o enlace de invitación</a> pueden escribirte.</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>
<p>Tus amigos pueden compartir tu contacto con otros amigos,
esto aparecerá como <b style="border: 1px solid currentColor; padding: 0 3px; font-size:90%">Solicitud</b></p>
<ul>
<li>
@@ -201,10 +216,10 @@ this appears as <b style="border: 1px solid currentColor; padding: 0 3px; font-s
</li>
</ul>
<h3 id="how-can-i-put-two-of-my-friends-in-contact-with-each-other">
<h3 id="cómo-puedo-poner-en-contacto-a-dos-de-mis-amigos-entre-ellos">
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>
¿Cómo puedo poner en contacto a dos de mis amigos entre ellos? <a href="#cómo-puedo-poner-en-contacto-a-dos-de-mis-amigos-entre-ellos" class="anchor"></a>
</h3>
@@ -223,16 +238,16 @@ and can tap it to start chatting with the first contact.</p>
</h3>
<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>Un perfil es <strong>un nombre, una foto</strong> y alguna información adicional para cifrar los mensajes.
Un perfil se almacena únicamente en tu dispositivo (o dispositivos)
y solo utiliza el servidor para enviar los mensajes.</p>
<p>En la primera instalación de Delta Chat se crea un primer perfil.</p>
<p>Después, puedes tocar la imagen de tu perfil en la esquina superior izquierda para <strong>Añadir perfiles</strong>
o para <strong>Cambiar perfiles</strong>.</p>
<p>You may want to use separate profiles for political, family or work related activities.</p>
<p>Puede utilizar perfiles diferentes para actividades políticas, familiares o laborales.</p>
<p>Quizás quieres aprender <a href="#multiclient">cómo se usa el mismo perfil en múltiples dispositivos</a>.</p>
@@ -252,15 +267,15 @@ o los agrega a través de un código QR, ellos lo verán automáticamente como s
<h3 id="signature">
Can I set a Bio/Status with Delta Chat? <a href="#signature" class="anchor"></a>
¿Puedo establecer una Bio/Estado en Delta Chat? <a href="#signature" class="anchor"></a>
</h3>
<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>
<p>,
es posible hacerlo en <strong>Ajustes → Perfil → Texto de firma</strong>.
Una vez que hayas enviado un mensaje a un contacto,
este lo verá cuando consulte tus datos de contacto.</p>
<h3 id="qué-significa-fijar-mutear-archivar">
@@ -280,7 +295,7 @@ they will see it when they view your contact details.</p>
<p><strong>Chats muteados</strong> si no quieres recibir notificaciones de ellos. Chats muteados se mantienen en su lugar e inclusive puedes fijarlos.</p>
</li>
<li>
<p><strong>Archivar chats</strong> si no deseas verlos en tu lista de chats. Los chats archivados siguen siendo accesibles arriba de la lista de chats o a través de la búsqueda.</p>
<p><strong>Archivar chats</strong> si no deseas verlos en tu lista de chats. Los chats archivados siguen siendo accesibles arriba de la lista de chats o a través de la búsqueda y aparecen marcados como <b style="border: 1px solid currentColor; padding: 0 3px; font-size:90%">Archivado</b>.</p>
</li>
<li>
<p>Cuando un chat archivado recibe un nuevo mensaje, a menos que esté silenciado, <strong>saldrá del archivo</strong> y volverá a aparecer en tu lista de chats.
@@ -352,16 +367,16 @@ and others will as well not always see that you are “online”.</p>
<ul>
<li>
<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>
<p><strong>Un tick</strong> <img style="vertical-align:middle; width:1.5em; margin:1px" src="../tick1.png" alt="" />
significa que el mensaje se ha enviado correctamente al <a href="#relays">servidor</a>.</p>
</li>
<li>
<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>
<p><strong>Dos ticks</strong> <img style="vertical-align:middle; width:1.5em; margin:1px" src="../tick2.png" alt="" />
indican que tu contacto ha leído el mensaje.</p>
</li>
</ul>
<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>En los <a href="#groups">grupos</a> el segundo tick indica que, al menos, uno de los miembros ha leído el mensaje.</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>
@@ -396,13 +411,12 @@ who could have already replied, forwarded, saved, screenshotted or otherwise cop
<h3 id="mediaquality">
How is media quality handled? <a href="#mediaquality" class="anchor"></a>
¿Cómo se gestiona la calidad de los archivos multimedia? <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>
<p>Imágenes, vídeos, archivos, mensajes de voz, etc., pueden enviarse utilizando los botones de <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Adjuntar</strong> o <img style="vertical-align:middle; width:0.8em; margin:1px" src="../mic.png" alt="Microphone" /> <strong>Mensaje de Voz</strong>.</p>
<ul>
<li>
@@ -460,8 +474,8 @@ the (anyway encrypted) messages may take longer to get deleted from their server
</h3>
<p>If you want to save storage on your device, you can choose to delete old
messages automatically.</p>
<p>Si quieres ahorrar espacio en tu dispositivo, puedes elegir borrar los mensajes antiguos
automáticamente.</p>
<p>To turn it on, go to <strong>Settings → Chats → Delete Message from Device</strong>.
You can set a timeframe between “after an hour” and “after a year”;
@@ -471,7 +485,7 @@ older than that.</p>
<h3 id="remove-account">
How can I delete my chat profile? <a href="#remove-account" class="anchor"></a>
¿Cómo puedo borrar mi perfil? <a href="#remove-account" class="anchor"></a>
</h3>
@@ -490,12 +504,12 @@ or the respective page from your chosen <a href="https://chatmail.at/relays">3rd
<h2 id="groups">
Groups <a href="#groups" class="anchor"></a>
Grupos <a href="#groups" class="anchor"></a>
</h2>
<p>Groups let several people chat together privately with <strong>equal rights</strong>.</p>
<p>Los grupos permiten a varias personas chatear juntas en privado con <strong>igualdad de derechos</strong>.</p>
<p>Anyone can
change the group name or avatar,
@@ -503,7 +517,7 @@ change the group name or avatar,
set <a href="#ephemeralmsgs">disappearing messages</a>,
and <a href="#edit">delete their own messages</a> from all members devices.</p>
<p>Because all members have the same rights, groups work best among <strong>trusted friends and family</strong>.</p>
<p>Como todos los miembros tienen los mismos derechos, los grupos funcionan mejor entre <strong>amigos de confianza y familiares</strong>.</p>
<h3 id="creación-de-un-grupo">
@@ -528,17 +542,17 @@ and <a href="#edit">delete their own messages</a> from all members devices.</
<h3 id="addmembers">
Add and remove members <a href="#addmembers" class="anchor"></a>
Añadir y eliminar miembros <a href="#addmembers" class="anchor"></a>
</h3>
<p>All group members have the <strong>same rights</strong>.
For this reason, everyone can delete any member or add new ones.</p>
<p>Todos los miembros del grupo tienen los <strong>mismos derechos</strong>.
Por esta razón, todos pueden eliminar a cualquier miembro o añadir otros nuevos.</p>
<ul>
<li>
<p>To <strong>add or delete members</strong>, tap the group name in the chat and select the member to add or remove.</p>
<p>Para <strong>añadir o eliminar miembros</strong>, pulsa el nombre del grupo en el chat y selecciona el miembro a añadir o eliminar.</p>
</li>
<li>
<p>If the member is not yet in your contact list, but <strong>face to face</strong> with you,
@@ -583,84 +597,275 @@ Si desea unirse al grupo nuevamente más tarde, pídale a otro miembro del grupo
<p>Como alternativa, también puede “silenciar” a un grupo, lo que significa que recibirá todos los mensajes y
aún puede escribir, pero ya no se le notifican nuevos mensajes.</p>
<h3 id="cloning-a-group">
<h3 id="clonar-un-grupo">
Cloning a group <a href="#cloning-a-group" class="anchor"></a>
Clonar un grupo <a href="#clonar-un-grupo" class="anchor"></a>
</h3>
<p>You can duplicate a group to start a separate discussion
or to exclude members without them noticing.</p>
<p>Puedes duplicar un grupo para empezar una discusión separada
o para excluir a miembros sin notificárselo.</p>
<ul>
<li>
<p>Open the group profile and tap <strong>Clone Chat</strong> (Android/iOS),
or right-click the group in the chat list (Desktop).</p>
<p>Abre el perfil del grupo y pulsa <strong>Clonar Chat</strong> (Android/iOS),
o haz clic derecho en el grupo en la lista de chats (Escritorio).</p>
</li>
<li>
<p>Set a new name, choose an avatar, and adjust the member list if needed.</p>
<p>Establece un nuevo nombre, elige un avatar y ajusta la lista de miembros si es necesario.</p>
</li>
</ul>
<p>The new group is <strong>fully independent</strong> from the original,
which continues to work as before.</p>
<p>El nuevo grupo es <strong>completamente independiente</strong> del original,
que continúa funcionando como antes.</p>
<h3 id="how-many-members-can-participate-in-a-single-group">
<h3 id="cuántos-miembros-pueden-participar-en-un-solo-grupo">
How many members can participate in a single group? <a href="#how-many-members-can-participate-in-a-single-group" class="anchor"></a>
¿Cuántos miembros pueden participar en un solo grupo? <a href="#cuántos-miembros-pueden-participar-en-un-solo-grupo" class="anchor"></a>
</h3>
<p>There is no strict technical limit,
but more than 150 is not recommended.</p>
<p>No hay una limitación técnica,
pero no se recomiendan más de 150.</p>
<p>As groups get larger, they can become socially unstable and may need a hierarchy -
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="webxdc">
<h2 id="channels">
In-chat apps <a href="#webxdc" class="anchor"></a>
Channels <a href="#channels" class="anchor"></a>
</h2>
<p>You can send apps to a chat - games, editors, polls and other tools.
This makes Delta Chat a truly extensible messenger.</p>
<p>Channels are a one-to-many tool for broadcasting messages.</p>
<h3 id="where-can-i-get-in-chat-apps">
<h3 id="subscribe-to-a-channel">
Where can I get in-chat apps? <a href="#where-can-i-get-in-chat-apps" class="anchor"></a>
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>In a chat, using <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attachment Button → Apps</strong></p>
<p>Tap <strong>New Chat</strong> and choose <strong>New Channel</strong>.</p>
</li>
<li>
<p>You can also <a href="#create-xdc">create your own app</a> and attach it using <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attachment Button → File</strong></p>
<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="how-private-are-in-chat-apps">
<h3 id="accept-or-reject-a-call">
How private are in-chat apps? <a href="#how-private-are-in-chat-apps" class="anchor"></a>
Accept or reject a call <a href="#accept-or-reject-a-call" class="anchor"></a>
</h3>
<ul>
<li>
<p>In-chat apps can not send data to the Internet, or download anything.</p>
<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">
Aplicaciones en chats <a href="#webxdc" class="anchor"></a>
</h2>
<p>Puedes enviar aplicaciones a un chat: juegos, editores, encuestas y otras herramientas.
Esto convierte a Delta Chat en una aplicación de mensajería realmente ampliable.</p>
<h3 id="dónde-puedo-conseguir-aplicaciones-para-el-chat">
¿Dónde puedo conseguir aplicaciones para el chat? <a href="#dónde-puedo-conseguir-aplicaciones-para-el-chat" class="anchor"></a>
</h3>
<ul>
<li>
<p>En un chat, utilizando <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Botón Adjuntar → Aplicaciones</strong>.</p>
</li>
<li>
<p>También puedes <a href="#create-xdc">crear tu propia aplicación</a> y adjuntarla utilizando <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Botón Adjuntar → Archivo</strong></p>
</li>
</ul>
<h3 id="cómo-de-privadas-son-las-aplicaciones-en-el-chat">
¿Cómo de privadas son las aplicaciones en el chat? <a href="#cómo-de-privadas-son-las-aplicaciones-en-el-chat" class="anchor"></a>
</h3>
<ul>
<li>
<p>Las aplicaciones en el chat no pueden enviar datos a Internet ni descargar nada.</p>
</li>
<li>
<p>An in-chat app can only exchange data within a chat, with its
@@ -672,29 +877,28 @@ isolated from the Internet.</p>
trust the people you chat with, you can trust the in-chat app as well.</p>
</li>
<li>
<p>This also means: Just like for web links, do not open apps from untrusted contacts.</p>
<p>Esto también significa que, al igual que con los enlaces web, no debes abrir aplicaciones de contactos en los que no confíes.</p>
</li>
</ul>
<h3 id="create-xdc">
How can I create my own in-chat apps? <a href="#create-xdc" class="anchor"></a>
¿Cómo puedo crear mis propias aplicaciones para el chat? <a href="#create-xdc" class="anchor"></a>
</h3>
<ul>
<li>
<p>In-chat apps are zip files with <code class="language-plaintext highlighter-rouge">.xdc</code> extension containing html, css, and javascript code.</p>
<p>Las aplicaciones de chat son archivos zip con extensión <code class="language-plaintext highlighter-rouge">.xdc</code> que contienen código html, css y javascript.</p>
</li>
<li>
<p>You can extend the <a href="https://github.com/webxdc/hello">Hello World example app</a>
to get started.</p>
<p>Para empezar, puedes ampliar la <a href="https://github.com/webxdc/hello">aplicación de ejemplo Hola, Mundo</a></p>
</li>
<li>
<p>All else you need to know is written in the
<a href="https://webxdc.org/docs">Webxdc documentation</a>.</p>
<p>Todo lo que necesitas saber lo puedes encontrar en
la <a href="https://webxdc.org/docs">documentación de Webxdc</a></p>
</li>
<li>
<p>If you have question, you can ask others with experience
@@ -871,21 +1075,21 @@ Welcome to the power of the interoperable chatmail relay network :)</p>
</h3>
<p>Yes. You can use the same profile on different devices:</p>
<p>Sí. Puedes utilizar el mismo perfil en diferentes dispositivos.</p>
<ul>
<li>
<p>Asegurate que ambos dispositivos estén en la misma Wi-Fi o red</p>
</li>
<li>
<p>On the first device, go to <strong>Settings → Add Second Device</strong>, unlock the screen if needed
and wait a moment until a QR code is shown</p>
<p>En el primer dispositivo, ve a <strong>Ajustes → Añadir segundo dispositivo</strong>, desbloquea la pantalla si es necesario,
y espera un momento hasta que aparezca un código QR</p>
</li>
<li>
<p>En el otro dispositivo, <a href="https://get.delta.chat">instala Delta Chat</a></p>
</li>
<li>
<p>On the second device, start Delta Chat, select <strong>Add as Second Device</strong>, and scan the QR code from the old device</p>
<p>En el segundo dispositivo, abre Delta Chat, selecciona **Añadir desde otro dispositivo” y escanea el código QR del primer dispositivo</p>
</li>
<li>
<p>La transferencia debería comenzar después de unos segundos y durante la transferencia ambos dispositivos mostrarán el <strong>progreso</strong>.
@@ -946,8 +1150,8 @@ Do not exit Delta Chat.
You can use multiple profiles per device, just <a href="#multiple-accounts">add another profile</a></p>
</li>
<li>
<p>If you still have problems or if you <strong>cannot scan a QR code</strong>
try the <strong>manual transfer</strong> described below</p>
<p>Si todavía tienes problemas o si <strong>no puedes escanear el código QR</strong>
utiliza la <strong>transferencia manual</strong> descrita debajo</p>
</li>
</ul>
@@ -1048,7 +1252,7 @@ If you have multiple relays, you will receive messages on all of them.
Contacts learn your current relays automatically when you message them.</p>
</li>
<li>
<p>Tap on a relay to set it as <strong>used for sending</strong>.</p>
<p>Pulsa en un retransmisor para establecerlo como <strong>usado para enviar</strong>.</p>
</li>
<li>
<p>If you <strong>remove</strong> a relay,
@@ -1057,17 +1261,17 @@ To stay reachable in the meantime, choose <strong>Hide from Contacts</strong> in
instead of removing it right away.</p>
</li>
<li>
<p>To <strong>show</strong> a hidden relay again, tap on it.</p>
<p>Para <strong>mostrar</strong> de nuevo un retransmisor oculto, pulsa en él.</p>
</li>
</ul>
<p>For more details and future possibilities of relays,
you can follow discussions in the <a href="https://support.delta.chat">Forum</a>.</p>
<h3 id="can-i-use-a-classic-email-address-with-delta-chat">
<h3 id="puedo-utilizar-una-dirección-de-email-clásico-con-delta-chat">
Can I use a classic email address with Delta Chat? <a href="#can-i-use-a-classic-email-address-with-delta-chat" class="anchor"></a>
¿Puedo utilizar una dirección de email clásico con Delta Chat? <a href="#puedo-utilizar-una-dirección-de-email-clásico-con-delta-chat" class="anchor"></a>
</h3>
@@ -1141,27 +1345,27 @@ that power <a href="https://chatmail.at/clients">chatmail clients</a> of which D
which is why Delta Chat for Android asks whether you want
to send anonymous usage statistics.</p>
<p>You can turn it on and off at
<strong>Settings → Advanced → Send statistics to Delta Chats developers</strong>.</p>
<p>Puedes activarlo y desactivarlo en
<strong>Ajustes → Avanzado → Enviar estasticas a los desarrolladores de Delta Chat</strong>.</p>
<p>When you turn it on,
weekly statistics will be automatically sent to a bot.</p>
<p>Cuando lo activas,
las estasticas semanales se envían automáticamente a un bot.</p>
<p>We are interested e.g. in statistics like:</p>
<p>Estamos interesados en estasticas como, p. ej.:</p>
<ul>
<li>
<p>How many contacts are introduced by personally scanning a QR code?</p>
</li>
<li>
<p>Which versions of Delta Chat are being used?</p>
<p>¿Qué versiones de Delta Chat están siendo utilizadas?</p>
</li>
<li>
<p>What errors occur for users?</p>
<p>¿Qué errores se producen para los usuarios?</p>
</li>
</ul>
<p>We will <em>not</em> collect any personally identifiable information about you.</p>
<p>Nosotros <em>no</em> recopilaremos ninguna información de carácter personal sobre ti.</p>
<h3 id="estoy-interesado-en-los-detalles-técnicos-pueden-decirme-más">
@@ -1212,12 +1416,12 @@ enables receivers to use end-to-end encryption with the contact.</p>
</li>
</ul>
<p>Delta Chat does not query, publish or interact with any OpenPGP key servers.</p>
<p>Delta Chat no consulta, publica o interacciona con ningún servidor de claves OpenPGP.</p>
<h3 id="whene2e">
How can I know if messages are end-to-end encrypted? <a href="#whene2e" class="anchor"></a>
¿Cómo puedo saber si los mensajes están cifrados de extremo a extremo? <a href="#whene2e" class="anchor"></a>
</h3>
@@ -1226,10 +1430,10 @@ enables receivers to use end-to-end encryption with the contact.</p>
Since the Delta Chat Version 2 release series (July 2025)
there are no lock or similar markers on end-to-end encrypted messages, anymore.</p>
<h3 id="can-i-still-receive-or-send-messages-without-end-to-end-encryption">
<h3 id="puedo-enviar-o-recibir-mensajes-sin-cifrado-de-extremo-a-extremo">
Can I still receive or send messages without end-to-end encryption? <a href="#can-i-still-receive-or-send-messages-without-end-to-end-encryption" class="anchor"></a>
¿Puedo enviar o recibir mensajes sin cifrado de extremo a extremo? <a href="#puedo-enviar-o-recibir-mensajes-sin-cifrado-de-extremo-a-extremo" class="anchor"></a>
</h3>
@@ -1409,41 +1613,38 @@ with the knowledge that all their data, along with all metadata, will be deleted
Moreover, if a device is seized then chat contacts using short-lived profiles
can not be identified easily.</p>
<h3 id="who-sees-my-ip-address">
<h3 id="quién-puede-ver-mi-dirección-ip">
Who sees my IP Address? <a href="#who-sees-my-ip-address" class="anchor"></a>
¿Quién puede ver mi dirección IP? <a href="#quién-puede-ver-mi-dirección-ip" class="anchor"></a>
</h3>
<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>
<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>IP Addresses are needed for connectivity and efficiency.
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>
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>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>
<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>
<h3 id="sealedsender">
Does Delta Chat support “Sealed Sender”? <a href="#sealedsender" class="anchor"></a>
¿Soporta Delta Chat “Sealed Sender”? <a href="#sealedsender" class="anchor"></a>
</h3>
<p>No, not yet.</p>
<p>No, todavía no.</p>
<p>The Signal messenger introduced <a href="https://signal.org/blog/sealed-sender/">“Sealed Sender” in 2018</a>
to keep their server infrastructure ignorant of who is sending a message to a set of recipients.
@@ -1464,7 +1665,7 @@ but an implementation has not been agreed as a priority yet.</p>
</h3>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Todavía no, pero llegará con <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Delta Chat today doesnt support Perfect Forward Secrecy (PFS).
This means that if your private decryption key is leaked,
@@ -1482,12 +1683,12 @@ This approach is specified in the <a href="https://datatracker.ietf.org/doc/draf
<h3 id="pqc">
Does Delta Chat support Post-Quantum-Cryptography? <a href="#pqc" class="anchor"></a>
¿Soporta Delta Chat criptografía postcuántica? <a href="#pqc" class="anchor"></a>
</h3>
<p>Not yet, but its coming with <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p>Todavía no, pero llegará con <a href="https://autocrypt2.org">Autocrypt v2</a>.</p>
<p><a href="https://autocrypt2.org">Autocrypt v2</a>, scheduled for full implementation in 2026,
will bring post-quantum resistant encryption to protect against quantum computer attacks.
@@ -1495,10 +1696,10 @@ Delta Chat uses the Rust OpenPGP library <a href="https://github.com/rpgp/rpgp">
which supports the latest <a href="https://datatracker.ietf.org/doc/draft-ietf-openpgp-pqc/">IETF Post-Quantum-Cryptography OpenPGP draft</a>.
The implementation is specified in the <a href="https://datatracker.ietf.org/doc/draft-autocrypt-openpgp-v2-cert/">Autocrypt v2 OpenPGP Certificates</a> draft.</p>
<h3 id="how-can-i-manually-check-encryption-information">
<h3 id="cómo-puedo-comprobar-manualmente-la-información-de-cifrado">
How can I manually check encryption information? <a href="#how-can-i-manually-check-encryption-information" class="anchor"></a>
¿Cómo puedo comprobar manualmente la información de cifrado? <a href="#cómo-puedo-comprobar-manualmente-la-información-de-cifrado" class="anchor"></a>
</h3>
@@ -1614,37 +1815,37 @@ You can read the <a href="https://delta.chat/assets/blog/2019-first-security-rev
</h3>
<p>Some features require certain permissions,
e.g. you need to grant camera permission if you want to <a href="#howtoe2ee">scan an invite QR code</a>.</p>
<p>Algunas características requieren ciertos permisos,
p. ej., tienes que conceder permiso de acceso a la cámara si quieres <a href="#howtoe2ee">escanear un código QR de invitación</a>.</p>
<p>See <a href="https://delta.chat/en/gdpr#24-app-permissions">Privacy Policy</a> for a detailed overview.</p>
<p>Consulta la <a href="https://delta.chat/en/gdpr#24-app-permissions">Política de privacidad</a> para obtener información detallada.</p>
<h3 id="where-can-my-friends-find-delta-chat">
<h3 id="dónde-pueden-encontrar-mis-amigos-delta-chat">
Where can my friends find Delta Chat? <a href="#where-can-my-friends-find-delta-chat" class="anchor"></a>
¿Dónde pueden encontrar mis amigos Delta Chat? <a href="#dónde-pueden-encontrar-mis-amigos-delta-chat" class="anchor"></a>
</h3>
<p>Delta Chat is available for all major and some minor platforms:</p>
<p>Delta Chat está disponible para todas las plataformas principales y algunas secundarias:</p>
<ul>
<li>
<p>The <strong>official website</strong>, <a href="https://delta.chat/download">https://delta.chat/download</a> shows all options in detail</p>
<p>La <strong>página web oficial</strong>, <a href="https://delta.chat/download">https://delta.chat/download</a>, muestra todas las opciones en detalle.</p>
</li>
<li>
<p>If unavailable, use the <strong>mirror</strong> at <a href="https://deltachat.github.io/deltachat-pages">https://deltachat.github.io/deltachat-pages</a></p>
<p>Si no está disponible, utiliza el <strong>mirror</strong> en <a href="https://deltachat.github.io/deltachat-pages">https://deltachat.github.io/deltachat-pages</a></p>
</li>
<li>
<p>Open one of the following <strong>app stores and search for “Delta Chat”:</strong>
Google Play Store, F-Droid, Huawei App Gallery, iOS and macOS App Store, Microsoft Store</p>
<p>Abre una de las siguientes <strong>tiendas de aplicaciones y busca “Delta Chat”:</strong>
Google Play Store, F-Droid, Huawei App Gallery, iOS y macOS App Store, Microsoft Store</p>
</li>
<li>
<p>Check the <strong>package manager</strong> of your Linux distributions</p>
<p>Comprueba el <strong>gestor de paquetes</strong> de tu distribución de Linux</p>
</li>
<li>
<p><strong>Android APKs</strong> are also available on <a href="https://github.com/deltachat/deltachat-android/releases">https://github.com/deltachat/deltachat-android/releases</a></p>
<p>Las <strong>Android APKs</strong> están también disponibles en <a href="https://github.com/deltachat/deltachat-android/releases">https://github.com/deltachat/deltachat-android/releases</a></p>
</li>
</ul>
+218 -18
View File
@@ -29,6 +29,21 @@
<li><a href="#zenbat-kide-izan-ditzake-talde-batek">Zenbat kide izan ditzake talde batek?</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">Txat barruko aplikazioak</a>
<ul>
<li><a href="#non-lortu-ditzaket-txat-barruko-aplikazioak">Non lortu ditzaket txat barruko aplikazioak?</a></li>
@@ -624,6 +639,197 @@ baina ez da komeni 150 baino gehiago izatea.</p>
Delta Chat <a href="#groups">eskubide berberekin</a> txateatzeko aukera ematen duen mezularitza pribatuko zerbitzu bat da.
Informazio gehiago nahi baduzu, kontsultatu <a href="https://en.wikipedia.org/wiki/Dunbar%27s_number">Dunbarren zenbakia</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">
@@ -1431,26 +1637,20 @@ txat-kontaktuak ezin izango dira erraz identifikatu.</p>
</h3>
<p>Erabiltzen duzun <a href="#relays">erreleak</a> zure IP helbidea jakin behar du,
eta, batzuetan, baita zure kontaktuen gailuena ere; adibidez,
kontaktu horiekin <a href="#experiments">deiak</a> egiten badituzu edo
<a href="#webxdc">aplikazioak</a> erabiltzen badituzu.</p>
<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>IP helbideak ezinbestekoak dira konektibitaterako eta eraginkortasunerako.
Ez dira gordetzen, ez argitara ematen.
Kontuan izan IP helbidea ez dela mezularitzako zerbitzuei-eta eman ohi zaiena
bezalako helbide zehatz bat; askoz orokorragoa da, eta askotan eskualdea edo
herrialdea baizik ez du identifikatzen.</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>Internetek eta beste mezularitza-aplikazioek horrela funtzionatzen dutenez,
ez dugu horren gaineko bestelako aukerarik eskaintzen, ez galderarik egiten.</p>
<p>Uste baduzu zure IP helbidea jakiteak zure segurtasuna edo pribatutasuna
arriskuan jar ditzakeela, zera gomendatuko genizuke, VPN bat erabiltzea,
sistema blokeatzeko moduarekin batera. Zure sistemako aplikazio guztietan
alternatibak bilatzeak segurtasun-zuloak utziko ditu; adibidez, esteka batean klik egindakoan,
IP helbideak agerian geratzen zaizkie hirugarren ezezagunei, eta
hori askoz arriskutsuagoa da.</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>
<h3 id="sealedsender">
+216 -13
View File
@@ -29,6 +29,21 @@
<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>
@@ -547,6 +562,197 @@ 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">
@@ -1338,23 +1544,20 @@ can not be identified easily.</p>
</h3>
<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>
<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>IP Addresses are needed for connectivity and efficiency.
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>
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>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>
<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>
<h3 id="sealedsender">
+216 -13
View File
@@ -29,6 +29,21 @@
<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>
@@ -627,6 +642,197 @@ 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">
@@ -1425,23 +1631,20 @@ can not be identified easily.</p>
</h3>
<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>
<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>IP Addresses are needed for connectivity and efficiency.
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>
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>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>
<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>
<h3 id="sealedsender">
+218 -15
View File
@@ -29,6 +29,21 @@
<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>
@@ -620,6 +635,197 @@ 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">
@@ -1412,23 +1618,20 @@ non possono essere identificati facilmente.</p>
</h3>
<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>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>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>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>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>
<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>
<h3 id="sealedsender">
+216 -13
View File
@@ -29,6 +29,21 @@
<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>
@@ -622,6 +637,197 @@ 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">
@@ -1419,23 +1625,20 @@ can not be identified easily.</p>
</h3>
<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>
<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>IP Addresses are needed for connectivity and efficiency.
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>
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>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>
<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>
<h3 id="sealedsender">
+275 -124
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">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="#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="#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">Can I set a Bio/Status with Delta Chat?</a></li>
<li><a href="#signature">Czy w Delta Chat mogę ustawić biografię/status?</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">How is media quality handled?</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,6 +29,21 @@
<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>
@@ -107,87 +122,67 @@
<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>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>
<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>
</li>
<li>
<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>
<p>Interaktywne <a href="#webxdc">aplikacje do czatu</a> w grach i do współpracy</p>
</li>
<li>
<p>Interactive <a href="#webxdc">in-chat apps</a> for gaming and collaboration</p>
<p><a href="#security-audits">Audytowne szyfrowanie end-to-end</a> zabezpieczające przed atakami sieciowymi i serwerowymi.</p>
</li>
<li>
<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>
<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>
</li>
</ul>
<h3 id="howtoe2ee">
How can I find people to chat with? <a href="#howtoe2ee" class="anchor"></a>
Jak znaleźć osoby do czatu? <a href="#howtoe2ee" class="anchor"></a>
</h3>
<p>First, note that Delta Chat is a private messenger.
There is no public discovery, <em>you</em> decide about your contacts.</p>
<p>Najpierw pamiętaj, że Delta Chat to prywatny komunikator. Nie ma możliwości publicznego wyszukiwania, sam decydujesz o swoich kontaktach.</p>
<ul>
<li>
<p>If you are <strong>face to face</strong> with your friend or family,
tap the <strong>QR Code</strong> icon <img style="vertical-align:middle; height:1.3em; margin:1px" src="../qr-icon.png" />
on the main screen.<br />
Ask your chat partner to <strong>scan</strong> the QR image
with their Delta Chat app.</p>
<p>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>
</li>
<li>
<p>For a <strong>remote</strong> contact setup,
from the same screen,
click “Copy” or “Share” and send the <strong>invite link</strong>
through another private chat.</p>
<p>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>
</li>
</ul>
<p>Now wait while connection gets established.</p>
<p>Poczekaj, aż połączenie zostanie nawiązane.</p>
<ul>
<li>
<p>If both sides are online, they will soon see a chat
and can start messaging securely.</p>
<p>Jeśli obie strony są online, wkrótce zobaczą czat i będą mogły bezpiecznie wysyłać wiadomości.</p>
</li>
<li>
<p>If one side is offline or in bad network,
the ability to chat is delayed until connectivity is restored.</p>
<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>
</li>
</ul>
<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>
<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>
<h3 id="why-is-a-chat-marked-as-request">
<h3 id="dlaczego-czat-jest-oznaczony-jako-prośba">
Why is a chat marked as “Request”? <a href="#why-is-a-chat-marked-as-request" class="anchor"></a>
Dlaczego czat jest oznaczony jako „Prośba”? <a href="#dlaczego-czat-jest-oznaczony-jako-prośba" class="anchor"></a>
</h3>
<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>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>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>
<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>
<ul>
<li>
@@ -201,19 +196,17 @@ this appears as <b style="border: 1px solid currentColor; padding: 0 3px; font-s
</li>
</ul>
<h3 id="how-can-i-put-two-of-my-friends-in-contact-with-each-other">
<h3 id="jak-mogę-skontaktować-ze-sobą-dwóch-znajomych">
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>
Jak mogę skontaktować ze sobą dwóch znajomych? <a href="#jak-mogę-skontaktować-ze-sobą-dwóch-znajomych" class="anchor"></a>
</h3>
<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>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>The second contact will receive a <strong>card</strong> then
and can tap it to start chatting with the first contact.</p>
<p>Drugi kontakt otrzyma wtedy <strong>kartkę</strong> i może ją nacisnąć, aby rozpocząć czat z pierwszym kontaktem.</p>
<h3 id="multiple-accounts">
@@ -223,15 +216,13 @@ and can tap it to start chatting with the first contact.</p>
</h3>
<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>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>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>You may want to use separate profiles for political, family or work related activities.</p>
<p>Możesz używać osobnych profili dla aktywności politycznych, rodzinnych lub zawodowych.</p>
<p>Możesz także dowiedzieć się, <a href="#multiclient">jak używać tego samego profilu na wielu urządzeniach</a>.</p>
@@ -243,22 +234,19 @@ and uses the server only to relay messages.</p>
</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">
Can I set a Bio/Status with Delta Chat? <a href="#signature" class="anchor"></a>
Czy w Delta Chat mogę ustawić biografię/status? <a href="#signature" class="anchor"></a>
</h3>
<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>
<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>
<h3 id="co-oznacza-przypinanie-wyciszanie-i-archiwizowanie">
@@ -278,7 +266,7 @@ they will see it when they view your contact details.</p>
<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. Zarchiwizowane czaty pozostają dostępne nad listą czatów lub poprzez wyszukiwanie.</p>
<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>
</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.
@@ -309,7 +297,7 @@ they will see it when they view your contact details.</p>
<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ć „Zapisz 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ć „Zapisanych 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>
@@ -326,13 +314,9 @@ they will see it when they view your contact details.</p>
</h3>
<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>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>So this is not a real time online status
and others will as well not always see that you are “online”.</p>
<p>Nie jest to więc status online w czasie rzeczywistym i inni również nie zawsze zobaczą, że jesteś „online”.</p>
<h3 id="co-oznaczają-znaczniki-wyświetlane-obok-wiadomości-wychodzących">
@@ -344,19 +328,16 @@ and others will as well not always see that you are “online”.</p>
<ul>
<li>
<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>
<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>
</li>
<li>
<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>
<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>
</li>
</ul>
<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>W <a href="#groups">grupach</a> drugi znacznik oznacza, że co najmniej jeden członek potwierdził przeczytanie wiadomości.</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>
<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>
<h3 id="edit">
@@ -382,26 +363,22 @@ has <strong>Settings → Chats → Read Receipts</strong> enabled.</p>
<h3 id="mediaquality">
How is media quality handled? <a href="#mediaquality" class="anchor"></a>
Jak obsługiwana jest jakość multimediów? <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>
<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>By default, compression ensures <strong>fast, efficient delivery</strong> that respects everyones data limits and storage.
This is ideal for everyday communication.</p>
<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>In regions with worse connectivity,
you can choose higher compression at <strong>Settings → Chats → Outgoing Media Quality</strong>.</p>
<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>If you specifically need to send media in its <strong>original quality</strong>, use <img style="vertical-align:middle; width:1.0em; margin:1px" src="../paperclip.png" alt="Paperclip" /> <strong>Attach → File</strong> in the chat.
Please use this method sparingly, as sending original files will significantly increase data usage for you and all recipients in the chat.</p>
<p>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>
@@ -415,21 +392,11 @@ Please use this method sparingly, as sending original files will significantly i
<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>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>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>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>Apart from that,
if one chat partner uninstalls Delta Chat,
the (anyway encrypted) messages may take longer to get deleted from their server.</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>
<h3 id="delold">
@@ -439,9 +406,9 @@ the (anyway encrypted) messages may take longer to get deleted from their server
</h3>
<p>Jeśli chcesz zaoszczędzić miejsce na urządzeniu, możesz wybrać opcję automatycznego usuwania starych wiadomości.</p>
<p>Jeśli chcesz zaoszczędzić miejsce na swoim urządzeniu, możesz wybrać opcję automatycznego usuwania starych wiadomości.</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>
<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>
<h3 id="remove-account">
@@ -493,10 +460,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ć awatar <strong>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ć <strong>awatar grupy</strong>.</p>
</li>
<li>
<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>
<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>
</li>
</ul>
@@ -539,8 +506,7 @@ 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">
@@ -555,8 +521,7 @@ 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">
@@ -597,6 +562,197 @@ 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">
@@ -849,8 +1005,7 @@ 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>
@@ -894,15 +1049,14 @@ Welcome to the power of the interoperable chatmail relay network :)</p>
<ul>
<li>
<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>
<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>
</li>
<li>
<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>
<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>
</li>
</ul>
<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>
<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>
<h3 id="czy-są-jakieś-plany-wprowadzenia-klienta-web-delta-chat">
@@ -1289,23 +1443,20 @@ can not be identified easily.</p>
</h3>
<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>
<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>IP Addresses are needed for connectivity and efficiency.
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>
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>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>
<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>
<h3 id="sealedsender">
+216 -13
View File
@@ -29,6 +29,21 @@
<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>
@@ -622,6 +637,197 @@ 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">
@@ -1420,23 +1626,20 @@ can not be identified easily.</p>
</h3>
<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>
<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>IP Addresses are needed for connectivity and efficiency.
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>
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>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>
<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>
<h3 id="sealedsender">
+218 -17
View File
@@ -29,6 +29,21 @@
<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>
@@ -622,6 +637,197 @@
в то время как 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">
@@ -1418,25 +1624,20 @@ Delta Chat вместо этого использует реализацию Ope
</h3>
<p>Используемый <a href="#relays">релей</a> должен знать ваш IP-адрес,
а также иногда устройства ваших контактов, если вы проводите совместные <a href="#experiments">звонки</a>
или используете <a href="#webxdc">приложения</a>.</p>
<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>IP-адреса необходимы для обеспечения соединения и эффективности.
Они не сохраняются и не передаются третьим лицам.
Обратите внимание, что IP-адрес</p>
<ul>
<li>это не подробный адрес, который вы указываете службе доставки,
а скорее приблизительный, обычно определяющий регион или страну.</li>
</ul>
<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>Поскольку именно так по умолчанию работает интернет и другие мессенджеры,
мы не предлагаем здесь никаких настроек и не задаём предварительных вопросов</p>
<p>Если вы считаете свой IP-адрес угрозой безопасности или конфиденциальности,
мы рекомендуем использовать VPN в сочетании с режимом блокировки системы.
Поиск настроек во всех приложениях на вашем устройстве оставит уязвимости.
Например, нажатие на ссылку раскрывает IP-адрес неизвестным лицам и представляет собой гораздо больший риск в данном случае.</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>
<h3 id="sealedsender">
+216 -13
View File
@@ -29,6 +29,21 @@
<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>
@@ -627,6 +642,197 @@ 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">
@@ -1425,23 +1631,20 @@ can not be identified easily.</p>
</h3>
<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>
<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>IP Addresses are needed for connectivity and efficiency.
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>
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>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>
<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>
<h3 id="sealedsender">
+216 -13
View File
@@ -29,6 +29,21 @@
<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>
@@ -627,6 +642,197 @@ 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">
@@ -1427,23 +1633,20 @@ can not be identified easily.</p>
</h3>
<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>
<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>IP Addresses are needed for connectivity and efficiency.
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>
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>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>
<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>
<h3 id="sealedsender">
+216 -13
View File
@@ -28,6 +28,21 @@
<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>
@@ -582,6 +597,197 @@ 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">
@@ -1268,23 +1474,20 @@ can not be identified easily.</p>
</h3>
<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>
<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>IP Addresses are needed for connectivity and efficiency.
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>
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>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>
<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>
<h3 id="sealedsender">
+218 -14
View File
@@ -32,6 +32,21 @@
</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">Webxdc 应用</a>
<ul>
<li><a href="#我在哪里可以获得-webxdc-应用">我在哪里可以获得 Webxdc 应用?</a></li>
@@ -621,6 +636,197 @@ Please use this method sparingly, as sending original files will significantly i
其中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">
@@ -1414,22 +1620,20 @@ Delta Chat 应用程序不会在服务器上存储任何有关联系人或群组
</h4>
<p>使用的 <a href="#relays">中继服务器</a> 需要知道您的 IP 地址、
有时还需要知道联系人的设备(如果你们有 <a href="#experiments">通话</a>),或一起使用 <a href="#webxdc">Webxdc应用程序</a></p>
<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>IP 地址是连接和提高效率所必需的。
它们既不会持久存在,也不会暴露。
请注意,IP 地址
不像你给快递服务的详细地址、
而是更粗略,通常只定义地区或国家。</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>这只是互联网和其他信使的默认工作方式、
我们在此不提供选项,也不预先提问。</p>
<p>如果你认为你的 IP 地址存在安全或隐私风险、
我们建议使用 VPN 并结合系统锁定模式。
在系统的所有应用程序中查找选项会留下漏洞。
例如,点击链接会将 IP 地址暴露给未知方,这是目前最大的风险。</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>###Delta Chat 是否支持 “密封发件人”?{#sealedsender}</p>
+77 -13
View File
@@ -443,10 +443,19 @@ public class Rpc {
}
/**
* Estimate the number of messages that will be deleted
* by the set_config()-options `delete_device_after` or `delete_server_after`.
* Estimates the number of messages that will be deleted
* by the `set_config()`-option `delete_device_after`.
* <p>
* 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));
@@ -913,8 +922,22 @@ public class Rpc {
}
/**
* Returns all messages of a particular chat.
* Get all message IDs belonging to a 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.
@@ -932,6 +955,14 @@ 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));
}
@@ -1181,11 +1212,6 @@ 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>
@@ -1328,10 +1354,47 @@ 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));
}
@@ -1467,17 +1530,18 @@ 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));
}
/**
* Send a reaction to message.
* Sends a reaction to message.
* <p>
* 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.
* 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.
*/
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,6 +12,13 @@ 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;
@@ -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. */
/** 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. */
public java.util.Map<String, java.util.List<String>> reactionsByContact;
}
@@ -14,7 +14,7 @@ public enum Viewtype {
Gif,
/**
* 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.
* Message containing a sticker, similar to image.
* <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,6 +15,10 @@ 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>
@@ -59,8 +59,6 @@ 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);
}
@@ -196,8 +194,6 @@ public class DcMsg {
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);
@@ -4,7 +4,6 @@ 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;
@@ -12,21 +11,23 @@ import androidx.appcompat.view.ActionMode;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.ViewModelProvider;
import androidx.media3.session.MediaController;
import androidx.media3.session.SessionCommand;
import androidx.media3.session.SessionToken;
import androidx.viewpager.widget.ViewPager;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
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;
@@ -35,7 +36,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class AllMediaActivity extends PassphraseRequiredActionBarActivity
implements DcEventCenter.DcEventDelegate {
private static final String TAG = AllMediaActivity.class.getSimpleName();
private static final String TAG = "AllMediaActivity";
public static final String CHAT_ID_EXTRA = "chat_id";
public static final String CONTACT_ID_EXTRA = "contact_id";
@@ -54,7 +55,6 @@ 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 ViewPager viewPager;
private ViewPager2 viewPager;
private @Nullable MediaController mediaController;
private ListenableFuture<MediaController> mediaControllerFuture;
@@ -97,8 +97,20 @@ public class AllMediaActivity extends PassphraseRequiredActionBarActivity
isGlobalGallery() ? R.string.menu_all_media : R.string.apps_and_media);
}
this.tabLayout.setupWithViewPager(viewPager);
this.viewPager.setAdapter(new AllMediaPagerAdapter(getSupportFragmentManager()));
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();
if (getIntent().getBooleanExtra(FORCE_GALLERY, false)) {
this.viewPager.setCurrentItem(1, false);
}
@@ -107,7 +119,9 @@ 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();
}
@@ -119,6 +133,7 @@ public class AllMediaActivity extends PassphraseRequiredActionBarActivity
mediaController = null;
playbackViewModel.setMediaController(null);
}
playbackViewModel.setQueueProvider(null);
super.onDestroy();
}
@@ -182,31 +197,16 @@ public class AllMediaActivity extends PassphraseRequiredActionBarActivity
return contactId == 0 && chatId == 0;
}
private class AllMediaPagerAdapter extends FragmentStatePagerAdapter {
private Object currentFragment = null;
private class AllMediaPagerAdapter extends FragmentStateAdapter {
private int currentPosition = -1;
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;
AllMediaPagerAdapter(FragmentActivity activity) {
super(activity);
}
@NonNull
@Override
public Fragment getItem(int position) {
public Fragment createFragment(int position) {
TabData data = tabs.get(position);
Fragment fragment;
Bundle args = new Bundle();
@@ -229,13 +229,24 @@ public class AllMediaActivity extends PassphraseRequiredActionBarActivity
}
@Override
public int getCount() {
public int getItemCount() {
return tabs.size();
}
@Override
public CharSequence getPageTitle(int position) {
return getString(tabs.get(position).title);
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;
}
}
@@ -40,7 +40,6 @@ import org.thoughtcrime.securesms.connect.KeepAliveService;
import org.thoughtcrime.securesms.connect.NetworkStateReceiver;
import org.thoughtcrime.securesms.crypto.DatabaseSecret;
import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider;
import org.thoughtcrime.securesms.geolocation.DcLocationManager;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.notifications.FcmReceiveService;
import org.thoughtcrime.securesms.notifications.InChatSounds;
@@ -53,7 +52,7 @@ import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.webxdc.WebxdcGarbageCollectionWorker;
public class ApplicationContext extends MultiDexApplication {
private static final String TAG = ApplicationContext.class.getSimpleName();
private static final String TAG = "ApplicationContext";
private static final Object initLock = new Object();
private static volatile boolean isInitialized = false;
@@ -61,7 +60,6 @@ public class ApplicationContext extends MultiDexApplication {
private Rpc rpc;
private DcContext dcContext;
private DcLocationManager dcLocationManager;
private DcEventCenter eventCenter;
private NotificationCenter notificationCenter;
private JobManager jobManager;
@@ -126,15 +124,6 @@ public class ApplicationContext extends MultiDexApplication {
}
}
/**
* Get DcLocationManager instance, waiting for initialization if necessary. This method is
* thread-safe and will block until initialization is complete.
*/
public DcLocationManager getLocationManager() {
ensureInitialized();
return dcLocationManager;
}
/**
* Get DcEventCenter instance, waiting for initialization if necessary. This method is thread-safe
* and will block until initialization is complete.
@@ -229,7 +218,7 @@ public class ApplicationContext extends MultiDexApplication {
Log.i(TAG, "DcAccounts created");
rpc = new Rpc(new FFITransport(dcAccounts.getJsonrpcInstance()));
Log.i(TAG, "Rpc created");
AccountManager.getInstance().migrateToDcAccounts(this);
AccountManager.getInstance().migrateToDcAccounts(this, dcAccounts);
int[] allAccounts = dcAccounts.getAll();
Log.i(TAG, "Number of profiles: " + allAccounts.length);
@@ -266,12 +255,6 @@ public class ApplicationContext extends MultiDexApplication {
// 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 {
@@ -282,7 +265,6 @@ public class ApplicationContext extends MultiDexApplication {
}
dcContext = dcAccounts.getSelectedAccount();
notificationCenter = new NotificationCenter(this);
dcLocationManager = new DcLocationManager(this, dcContext);
isInitialized = true;
initLock.notifyAll();
@@ -20,7 +20,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public abstract class BaseActionBarActivity extends AppCompatActivity {
private static final String TAG = BaseActionBarActivity.class.getSimpleName();
private static final String TAG = "BaseActionBarActivity";
protected DynamicTheme dynamicTheme = new DynamicTheme();
protected void onPreCreate() {
@@ -13,6 +13,7 @@ import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -26,6 +27,8 @@ import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.core.graphics.drawable.IconCompat;
import androidx.fragment.app.Fragment;
import chat.delta.rpc.Rpc;
import chat.delta.rpc.RpcException;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContact;
import com.b44t.messenger.DcContext;
@@ -44,6 +47,7 @@ import org.thoughtcrime.securesms.util.task.SnackbarAsyncTask;
import org.thoughtcrime.securesms.util.views.ProgressDialog;
public abstract class BaseConversationListFragment extends Fragment implements ActionMode.Callback {
private static final String TAG = "BaseConversationListFragment";
protected ActionMode actionMode;
protected PulsingFloatingActionButton fab;
@@ -170,6 +174,17 @@ public abstract class BaseConversationListFragment extends Fragment implements A
return false;
}
private boolean areSomeSelectedChatsFresh() {
DcContext dcContext = DcHelper.getContext(requireActivity());
final Set<Long> selectedChats = getListAdapter().getBatchSelections();
for (long chatId : selectedChats) {
if (dcContext.getFreshMsgCount((int) chatId) > 0) {
return true;
}
}
return false;
}
private void handlePinAllSelected() {
final DcContext dcContext = DcHelper.getContext(requireActivity());
final Set<Long> selectedConversations =
@@ -229,6 +244,26 @@ public abstract class BaseConversationListFragment extends Fragment implements A
}
}
private void handleMarkfreshSelected() {
final Set<Long> selectedConversations =
new HashSet<Long>(getListAdapter().getBatchSelections());
final Rpc rpc = DcHelper.getRpc(requireActivity());
try {
int accId = rpc.getSelectedAccountId();
for (long chatId : selectedConversations) {
rpc.markfreshChat(accId, (int) chatId);
}
} catch (RpcException e) {
Log.e(TAG, "RPC error", e);
}
if (actionMode != null) {
actionMode.finish();
actionMode = null;
}
}
@SuppressLint("StaticFieldLeak")
private void handleArchiveAllSelected() {
final DcContext dcContext = DcHelper.getContext(requireActivity());
@@ -388,9 +423,18 @@ 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 (!ShortcutManagerCompat.requestPinShortcut(activity, shortcutInfoCompat, null)) {
if (!finalSuccess) {
Toast.makeText(
activity,
"ErrAddToHomescreen: requestPinShortcut() failed",
@@ -409,6 +453,7 @@ public abstract class BaseConversationListFragment extends Fragment implements A
if (!isRelayingMessageContent(requireActivity())) {
final int selectedCount = getListAdapter().getBatchSelections().size();
menu.findItem(R.id.menu_add_to_home_screen).setVisible(selectedCount == 1);
MenuItem archiveItem = menu.findItem(R.id.menu_archive_selected);
if (offerToArchive()) {
archiveItem.setIcon(R.drawable.ic_archive_white_24dp);
@@ -417,6 +462,7 @@ public abstract class BaseConversationListFragment extends Fragment implements A
archiveItem.setIcon(R.drawable.ic_unarchive_white_24dp);
archiveItem.setTitle(R.string.menu_unarchive_chat);
}
MenuItem pinItem = menu.findItem(R.id.menu_pin_selected);
if (areSomeSelectedChatsUnpinned()) {
pinItem.setIcon(R.drawable.ic_pin_white);
@@ -425,12 +471,17 @@ public abstract class BaseConversationListFragment extends Fragment implements A
pinItem.setIcon(R.drawable.ic_unpin_white);
pinItem.setTitle(R.string.unpin_chat);
}
MenuItem muteItem = menu.findItem(R.id.menu_mute_selected);
if (areSomeSelectedChatsUnmuted()) {
muteItem.setTitle(R.string.menu_mute);
} else {
muteItem.setTitle(R.string.menu_unmute);
}
final boolean someAreFresh = areSomeSelectedChatsFresh();
menu.findItem(R.id.menu_marknoticed_selected).setVisible(someAreFresh);
menu.findItem(R.id.menu_markfresh_selected).setVisible(!someAreFresh);
}
}
@@ -484,6 +535,9 @@ public abstract class BaseConversationListFragment extends Fragment implements A
} else if (itemId == R.id.menu_marknoticed_selected) {
handleMarknoticedSelected();
return true;
} else if (itemId == R.id.menu_markfresh_selected) {
handleMarkfreshSelected();
return true;
} else if (itemId == R.id.menu_add_to_home_screen) {
handleAddToHomeScreen();
return true;
@@ -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.class.getSimpleName();
private static final String TAG = "ContactSelectionActivity";
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.class.getSimpleName();
private static final String TAG = "ContactSelectionListFragment";
public static final String MULTI_SELECT = "multi_select";
public static final String SELECT_UNENCRYPTED_EXTRA = "select_unencrypted_extra";
@@ -101,6 +101,7 @@ import org.thoughtcrime.securesms.components.ScaleStableImageView;
import org.thoughtcrime.securesms.components.SendButton;
import org.thoughtcrime.securesms.components.audioplay.AudioPlaybackViewModel;
import org.thoughtcrime.securesms.components.audioplay.AudioView;
import org.thoughtcrime.securesms.components.audioplay.ChatAudioQueueProvider;
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
import org.thoughtcrime.securesms.connect.AccountManager;
import org.thoughtcrime.securesms.connect.DcEventCenter;
@@ -148,7 +149,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
InputPanel.Listener,
InputPanel.MediaListener,
AudioView.OnActionListener {
private static final String TAG = ConversationActivity.class.getSimpleName();
private static final String TAG = "ConversationActivity";
public static final String ACCOUNT_ID_EXTRA = "account_id";
public static final String CHAT_ID_EXTRA = "chat_id";
@@ -199,7 +200,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private final boolean isSecureText = true;
private boolean isDefaultSms = true;
private boolean isSecurityInitialized = false;
private boolean successfulForwardingAttempt = false;
private boolean isEditing = false;
private boolean switchedProfile = false;
@@ -223,7 +223,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
initializeViews();
initializeResources();
int accountId = DcHelper.getAccounts(this).getSelectedAccount().getAccountId();
playbackViewModel = new ViewModelProvider(this).get(AudioPlaybackViewModel.class);
playbackViewModel.setQueueProvider(new ChatAudioQueueProvider(this, chatId, accountId));
initializeMediaController();
initializeSecurity(false, isDefaultSms)
@@ -413,6 +415,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
mediaController = null;
playbackViewModel.setMediaController(null);
}
playbackViewModel.setQueueProvider(null);
super.onDestroy();
}
@@ -632,12 +635,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
return true;
} else if (itemId == R.id.menu_start_audio_call) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CallUtil.startAudioCall(context, chatId);
CallUtil.startAudioCall(this, chatId);
}
return true;
} else if (itemId == R.id.menu_start_video_call) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CallUtil.startVideoCall(context, chatId);
CallUtil.startVideoCall(this, chatId);
}
return true;
} else if (itemId == R.id.menu_all_media) {
@@ -672,8 +675,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
public void setDraftText(String txt) {
composeText.setText(txt);
composeText.setSelection(composeText.getText().length());
try {
if (rpc.canSend(rpc.getSelectedAccountId(), chatId)) {
composeText.setText(txt);
composeText.setSelection(composeText.getText().length());
}
} catch (RpcException e) {
Log.e(TAG, "Rpc error", e);
}
}
public void hideSoftKeyboard() {
@@ -836,20 +845,17 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (dcChat.isSelfTalk()) {
SendRelayedMessageUtil.immediatelyRelay(this, chatId);
} else {
String name = dcChat.getName();
if (!dcChat.isMultiUser()) {
int[] contactIds = dcContext.getChatContacts(chatId);
if (contactIds.length == 1 || contactIds.length == 2) {
name = dcContext.getContact(contactIds[0]).getDisplayName();
}
}
int messageIds[] = ShareUtil.getForwardedMessageIDs(this);
int messageCount = messageIds == null ? 0 : messageIds.length;
new AlertDialog.Builder(this)
.setMessage(getString(R.string.ask_forward, name))
.setMessage(
getResources()
.getQuantityString(
R.plurals.ask_forward_messages, messageCount, messageCount, dcChat.getName()))
.setPositiveButton(
R.string.ok,
R.string.forward,
(dialogInterface, i) -> {
SendRelayedMessageUtil.immediatelyRelay(this, chatId);
successfulForwardingAttempt = true;
})
.setNegativeButton(R.string.cancel, (dialogInterface, i) -> finish())
.setOnCancelListener(dialog -> finish())
@@ -1119,6 +1125,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (chatId == DcChat.DC_CHAT_NO_CHAT)
throw new IllegalStateException("can't display a conversation for no chat.");
dcChat = DcHelper.getContext(context).getChat(chatId);
attachmentTypeSelector = null;
recipient = new Recipient(this, dcChat);
glideRequests = GlideApp.with(this);
@@ -1267,6 +1274,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
DcContext dcContext = DcHelper.getContext(context);
final int currentChatId = dcChat.getId();
final boolean canSend = dcChat.canSend();
Util.runOnAnyBackgroundThread(
() -> {
DcMsg msg = null;
@@ -1389,7 +1397,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
}
} else {
dcContext.setDraft(currentChatId, msg);
// set or clear draft if user can't send in chat since they can't delete it otherwise
dcContext.setDraft(currentChatId, canSend ? msg : null);
}
future.set(currentChatId);
});
@@ -1611,7 +1620,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
msg.setQuote(quote.get().getQuotedMsg());
}
msg.setFileAndDeduplicate(path, null, null);
msg.forceSticker();
dcContext.sendMsg(chatId, msg);
}
@@ -80,7 +80,7 @@ import org.thoughtcrime.securesms.util.views.ConversationAdaptiveActionsToolbar;
@SuppressLint("StaticFieldLeak")
public class ConversationFragment extends MessageSelectorFragment {
private static final String TAG = ConversationFragment.class.getSimpleName();
private static final String TAG = "ConversationFragment";
private static final int SCROLL_ANIMATION_THRESHOLD = 50;
@@ -944,6 +944,8 @@ 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);
@@ -24,6 +24,7 @@ import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.os.Build;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
@@ -36,6 +37,7 @@ import androidx.annotation.DimenRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.view.ViewCompat;
import chat.delta.rpc.RpcException;
import chat.delta.rpc.types.CallInfo;
import chat.delta.rpc.types.CallState;
@@ -44,6 +46,7 @@ import chat.delta.rpc.types.VcardContact;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContact;
import com.b44t.messenger.DcMsg;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.thoughtcrime.securesms.calls.CallCoordinator;
@@ -72,6 +75,7 @@ import org.thoughtcrime.securesms.mms.VcardSlide;
import org.thoughtcrime.securesms.reactions.ReactionsConversationView;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Linkifier;
import org.thoughtcrime.securesms.util.LongClickCopySpan;
import org.thoughtcrime.securesms.util.LongClickMovementMethod;
import org.thoughtcrime.securesms.util.MarkdownUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
@@ -86,7 +90,7 @@ import org.thoughtcrime.securesms.util.views.Stub;
* @author Moxie Marlinspike
*/
public class ConversationItem extends BaseConversationItem {
private static final String TAG = ConversationItem.class.getSimpleName();
private static final String TAG = "ConversationItem";
private static final Rect SWIPE_RECT = new Rect();
@@ -120,6 +124,9 @@ public class ConversationItem extends BaseConversationItem {
private Stub<CallItemView> callViewStub;
private @Nullable EventListener eventListener;
// IDs of accessibility actions registered via ViewCompat.addAccessibilityAction
private final List<Integer> linkActionIds = new ArrayList<>();
private int measureCalls;
private int incomingBubbleColor;
@@ -424,6 +431,12 @@ public class ConversationItem extends BaseConversationItem {
bodyText.setClickable(false);
bodyText.setFocusable(false);
// Remove any link actions registered for the previous message binding
for (int id : linkActionIds) {
ViewCompat.removeAccessibilityAction(this, id);
}
linkActionIds.clear();
String subject = messageRecord.getSubject();
String text = messageRecord.getText();
@@ -433,12 +446,33 @@ public class ConversationItem extends BaseConversationItem {
if (messageRecord.getType() == DcMsg.DC_MSG_CALL || text.isEmpty()) {
bodyText.setVisibility(View.GONE);
} else {
Spannable spannable = (Spannable) MarkdownUtil.toMarkdown(context, text);
Spannable spannable = MarkdownUtil.toMarkdown(context, text);
if (batchSelected.isEmpty()) {
spannable = Linkifier.linkify(spannable);
Linkifier.linkify(spannable);
}
bodyText.setText(spannable);
bodyText.setVisibility(View.VISIBLE);
// Register a TalkBack "Actions" entry for each link in the message
Spanned spanned = (Spanned) spannable;
final TextView tv = bodyText;
for (LongClickCopySpan span :
spanned.getSpans(0, spanned.length(), LongClickCopySpan.class)) {
int start = spanned.getSpanStart(span);
int end = spanned.getSpanEnd(span);
if (start >= 0 && end > start && end <= spanned.length()) {
String linkText = spanned.subSequence(start, end).toString();
String label = context.getString(R.string.open_link, linkText);
linkActionIds.add(
ViewCompat.addAccessibilityAction(
this,
label,
(v, args) -> {
span.onClick(tv);
return true;
}));
}
}
}
int downloadState = messageRecord.getDownloadState();
@@ -1062,7 +1096,18 @@ public class ConversationItem extends BaseConversationItem {
if (!messageRecord.isOutgoing() && callInfo.state instanceof CallState.Alerting) {
int callId = messageRecord.getId();
CallCoordinator coordinator = CallCoordinator.getInstance(context);
coordinator.showIncomingCallScreen(callId);
if (coordinator.hasActiveCall()) {
coordinator.showIncomingCallScreen(callId);
} else {
if (callInfo.sdpOffer == null) {
Toast.makeText(context, R.string.error, Toast.LENGTH_SHORT).show();
return;
}
int accId = dcContext.getAccountId();
coordinator.handleIncomingCallFromConversation(
accId, callId, callInfo.sdpOffer, callInfo.hasVideo);
}
} else {
if (callInfo.hasVideo) {
CallUtil.startVideoCall(getContext(), chatId);
@@ -69,6 +69,7 @@ import org.thoughtcrime.securesms.components.SearchToolbar;
import org.thoughtcrime.securesms.connect.AccountManager;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.connect.DirectShareUtil;
import org.thoughtcrime.securesms.geolocation.LocationStreamingService;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
@@ -90,7 +91,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class ConversationListActivity extends PassphraseRequiredActionBarActivity
implements ConversationListFragment.ConversationSelectedListener {
private static final String TAG = ConversationListActivity.class.getSimpleName();
private static final String TAG = "ConversationListActivity";
private static final String OPENPGP4FPR = "openpgp4fpr";
private static final String NDK_ARCH_WARNED = "ndk_arch_warned";
public static final String CLEAR_NOTIFICATIONS = "clear_notifications";
@@ -449,6 +450,9 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
refreshTitle();
invalidateOptionsMenu();
DirectShareUtil.triggerRefreshDirectShare(this);
if (DcHelper.getContext(this).isSendingLocationsToChat(0)) {
LocationStreamingService.ensureRunning(this);
}
}
@Override
@@ -603,6 +607,13 @@ 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();
@@ -688,7 +699,11 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
// Util.copy(inputStream, new FileOutputStream(outputFile));
// msg.setFile(outputFile, "image/jpeg");
msg.setText(getString(R.string.update_2_46ac, "https://arcanechat.me/#contribute", "https://i.delta.chat/#0A45953086F0C166D3BAF1D4BB2025496E4C2704&x=MVPi07rQBEmHO4FRb3brpwDe&j=n8mkKqu42WAKKUCx1bQOVh23&s=AM1YCd_2hKuiSiTEb-2177On&a=arcanechat%40arcanechat.me&n=ArcaneChat&b=ArcaneChat+Channel"));
msg.setText(
getString(
R.string.update_2_46ac,
"https://arcanechat.me/#contribute",
"https://i.delta.chat/#0A45953086F0C166D3BAF1D4BB2025496E4C2704&x=MVPi07rQBEmHO4FRb3brpwDe&j=n8mkKqu42WAKKUCx1bQOVh23&s=AM1YCd_2hKuiSiTEb-2177On&a=arcanechat%40arcanechat.me&n=ArcaneChat&b=ArcaneChat+Channel"));
}
dcContext.addDeviceMsg(deviceMsgLabel, msg);
@@ -23,7 +23,6 @@ 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;
@@ -56,12 +55,11 @@ 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.class.getSimpleName();
private static final String TAG = "ConversationListFragment";
private RecyclerView list;
private View emptyState;
private TextView emptySearch;
private final String queryFilter = "";
private boolean archive;
private Timer reloadTimer;
private boolean chatlistJustLoaded;
@@ -285,22 +283,17 @@ public class ConversationListFragment extends BaseConversationListFragment
Log.w(TAG, "Ignoring call to loadChatlist()");
return;
}
DcChatlist chatlist =
DcHelper.getContext(context)
.getChatlist(listflags, queryFilter.isEmpty() ? null : queryFilter, 0);
long startMs = System.currentTimeMillis();
DcChatlist chatlist = DcHelper.getContext(context).getChatlist(listflags, null, 0);
Log.i(TAG, "⏰ getChatlist(): " + (System.currentTimeMillis() - startMs) + "ms");
Util.runOnMain(
() -> {
if (chatlist.getCnt() <= 0 && TextUtils.isEmpty(queryFilter)) {
if (chatlist.getCnt() <= 0) {
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,9 +44,11 @@ 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;
@@ -222,6 +224,40 @@ 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.class.getSimpleName();
private static final String TAG = "CreateProfileActivity";
private static final int REQUEST_CODE_AVATAR = 1;
@@ -15,7 +15,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class EphemeralMessagesDialog {
private static final String TAG = EphemeralMessagesDialog.class.getSimpleName();
private static final String TAG = "EphemeralMessagesDialog";
public static void show(
final Context context,
@@ -46,7 +46,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
implements ItemClickListener {
private static final String TAG = GroupCreateActivity.class.getSimpleName();
private static final String TAG = "GroupCreateActivity";
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,8 +307,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private boolean showGroupNameEmptyToast(String groupName) {
if (groupName == null) {
Toast.makeText(this, getString(R.string.group_please_enter_group_name), Toast.LENGTH_LONG)
.show();
Toast.makeText(this, getString(R.string.please_enter_chat_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.class.getSimpleName();
private static final String TAG = "InstantOnboardingActivity";
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.class.getSimpleName();
private static final String TAG = "LogViewActivity";
LogViewFragment logViewFragment;
@@ -33,14 +33,13 @@ 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.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.ViewPager2;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcMediaGalleryElement;
@@ -48,7 +47,6 @@ 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;
@@ -69,7 +67,7 @@ import org.thoughtcrime.securesms.util.Util;
public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
implements RecipientModifiedListener, LoaderManager.LoaderCallbacks<DcMediaGalleryElement> {
private static final String TAG = MediaPreviewActivity.class.getSimpleName();
private static final String TAG = "MediaPreviewActivity";
public static final String ACTIVITY_TITLE_EXTRA = "activity_title";
public static final String EDIT_AVATAR_CHAT_ID = "avatar_for_chat_id";
@@ -88,7 +86,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
@Nullable private DcMsg messageRecord;
private DcContext dcContext;
private MediaItem initialMedia;
private ViewPager mediaPager;
private ViewPager2 mediaPager;
private Recipient conversationRecipient;
private boolean leftIsRecent;
@@ -190,7 +188,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
private void initializeViews() {
mediaPager = findViewById(R.id.media_pager);
mediaPager.setOffscreenPageLimit(1);
mediaPager.addOnPageChangeListener(new ViewPagerListener());
mediaPager.registerOnPageChangeCallback(new ViewPagerListener());
}
private void initializeResources() {
@@ -260,10 +258,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
private int cleanupMedia() {
int restartItem = mediaPager.getCurrentItem();
mediaPager.removeAllViews();
mediaPager.setAdapter(null);
return restartItem;
}
@@ -483,22 +478,25 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
@SuppressWarnings("ConstantConditions")
DcMediaPagerAdapter adapter =
new DcMediaPagerAdapter(this, GlideApp.with(this), getWindow(), data, leftIsRecent);
mediaPager.setAdapter(adapter);
adapter.setActive(true);
mediaPager.setAdapter(adapter);
if (restartItem < 0) mediaPager.setCurrentItem(data.getPosition());
else mediaPager.setCurrentItem(restartItem);
if (restartItem < 0) mediaPager.setCurrentItem(data.getPosition(), false);
else mediaPager.setCurrentItem(restartItem, false);
}
}
@Override
public void onLoaderReset(Loader<DcMediaGalleryElement> loader) {}
private class ViewPagerListener extends ExtendedOnPageChangedListener {
private class ViewPagerListener extends ViewPager2.OnPageChangeCallback {
private Integer currentPage = null;
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
if (currentPage != null && currentPage != position) onPageUnselected(currentPage);
currentPage = position;
MediaItemAdapter adapter = (MediaItemAdapter) mediaPager.getAdapter();
@@ -510,8 +508,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
}
}
@Override
public void onPageUnselected(int position) {
private void onPageUnselected(int position) {
MediaItemAdapter adapter = (MediaItemAdapter) mediaPager.getAdapter();
if (adapter != null) {
@@ -526,7 +523,9 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
}
}
private static class SingleItemPagerAdapter extends PagerAdapter implements MediaItemAdapter {
private static class SingleItemPagerAdapter
extends RecyclerView.Adapter<SingleItemPagerAdapter.MediaViewHolder>
implements MediaItemAdapter {
private final GlideRequests glideRequests;
private final Window window;
@@ -555,37 +554,29 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
}
@Override
public int getCount() {
public int getItemCount() {
return 1;
}
@NonNull
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
public MediaViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new MediaViewHolder(inflater.inflate(R.layout.media_view_page, parent, false));
}
@Override
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);
public void onBindViewHolder(@NonNull MediaViewHolder holder, int position) {
try {
mediaView.set(glideRequests, window, uri, name, mediaType, size, true);
holder.mediaView.set(glideRequests, window, uri, name, mediaType, size, true);
} catch (IOException e) {
Log.w(TAG, e);
}
container.addView(itemView);
return itemView;
}
@Override
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);
public void onViewRecycled(@NonNull MediaViewHolder holder) {
super.onViewRecycled(holder);
holder.mediaView.cleanup();
}
@Override
@@ -595,9 +586,20 @@ 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 PagerAdapter implements MediaItemAdapter {
private static class DcMediaPagerAdapter
extends RecyclerView.Adapter<DcMediaPagerAdapter.MediaViewHolder>
implements MediaItemAdapter {
private final WeakHashMap<Integer, MediaView> mediaViews = new WeakHashMap<>();
@@ -630,21 +632,20 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
}
@Override
public int getCount() {
public int getItemCount() {
if (!active) return 0;
else return gallery.getCount();
}
@NonNull
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
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);
}
@Override
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);
public void onBindViewHolder(@NonNull MediaViewHolder holder, int position) {
boolean autoplay = position == autoPlayPosition;
int cursorPosition = getCursorPosition(position);
@@ -656,7 +657,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
try {
//noinspection ConstantConditions
mediaView.set(
holder.mediaView.set(
glideRequests,
window,
Uri.fromFile(msg.getFileAsFile()),
@@ -668,19 +669,17 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
Log.w(TAG, e);
}
mediaViews.put(position, mediaView);
container.addView(itemView);
return itemView;
mediaViews.put(position, holder.mediaView);
}
@Override
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 void onViewRecycled(@NonNull MediaViewHolder holder) {
super.onViewRecycled(holder);
int pos = holder.getBindingAdapterPosition();
if (pos != RecyclerView.NO_POSITION) {
mediaViews.remove(pos);
}
holder.mediaView.cleanup();
}
public MediaItem getMediaItemFor(int position) {
@@ -710,6 +709,15 @@ 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 {
@@ -742,7 +750,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
}
}
interface MediaItemAdapter {
private 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.class.getSimpleName();
private static final String TAG = "NewConversationActivity";
@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.class.getSimpleName();
private static final String TAG = "PassphraseRequiredActionBarActivity";
@Override
protected final void onCreate(Bundle savedInstanceState) {
@@ -29,7 +29,7 @@ import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.util.Util;
public class ProfileAdapter extends RecyclerView.Adapter {
private static final String TAG = ProfileAdapter.class.getSimpleName();
private static final String TAG = "ProfileAdapter";
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.class.getSimpleName();
private static final String TAG = "ProfileFragment";
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.class.getSimpleName();
private static final String TAG = "ResolveMediaTask";
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.class.getSimpleName();
private static final String TAG = "ShareActivity";
public static final String EXTRA_ACC_ID = "acc_id";
public static final String EXTRA_CHAT_ID = "chat_id";
@@ -18,22 +18,19 @@ public class ShareLocationDialog {
switch (which) {
default:
case 0:
shareLocationUnit = 5 * 60;
break;
case 1:
shareLocationUnit = 30 * 60;
break;
case 2:
case 1:
shareLocationUnit = 60 * 60;
break;
case 3:
case 2:
shareLocationUnit = 2 * 60 * 60;
break;
case 4:
case 3:
shareLocationUnit = 6 * 60 * 60;
break;
case 5:
shareLocationUnit = 12 * 60 * 60;
case 4:
shareLocationUnit = 24 * 60 * 60;
break;
}
@@ -32,7 +32,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
public class WebViewActivity extends PassphraseRequiredActionBarActivity
implements SearchView.OnQueryTextListener, WebView.FindListener {
private static final String TAG = WebViewActivity.class.getSimpleName();
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]+://)?)([^/?#]+)(.*)$");
@@ -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.class.getSimpleName();
private static final String TAG = "WebxdcActivity";
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,6 +240,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setMediaPlaybackRequiresUserGesture(false);
webSettings.setAllowFileAccess(false);
webSettings.setBlockNetworkLoads(!internetAccess);
webSettings.setAllowContentAccess(false);
@@ -542,11 +543,25 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
.build();
Toast.makeText(context, R.string.one_moment, Toast.LENGTH_SHORT).show();
if (!ShortcutManagerCompat.requestPinShortcut(context, shortcutInfoCompat, null)) {
Toast.makeText(
context, "ErrAddToHomescreen: requestPinShortcut() failed", Toast.LENGTH_LONG)
.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());
}
});
} 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.class.getSimpleName();
private static final String TAG = "WebxdcStoreActivity";
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.class.getSimpleName();
private static final String TAG = "WelcomeActivity";
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.class.getSimpleName();
private static final String TAG = "AccountSelectionListFragment";
private static final String ARG_SELECT_ONLY = "select_only";
private RecyclerView recyclerView;
private AccountSelectionListAdapter adapter;
@@ -157,6 +157,12 @@ 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);
}
}
}
@@ -15,7 +15,7 @@ import org.thoughtcrime.securesms.util.Prefs;
public class AudioCodec {
private static final String TAG = AudioCodec.class.getSimpleName();
private static final String TAG = "AudioCodec";
private static final int SAMPLE_RATE_BALANCED = 44100;
private static final int BIT_RATE_BALANCED = 32000;
@@ -196,7 +196,7 @@ public class AudioCodec {
setFinished();
}
},
AudioCodec.class.getSimpleName())
"AudioCodec")
.start();
}
@@ -18,7 +18,7 @@ import org.thoughtcrime.securesms.util.Util;
public class AudioRecorder {
private static final String TAG = AudioRecorder.class.getSimpleName();
private static final String TAG = "AudioRecorder";
private static final ExecutorService executor = ThreadUtil.newDynamicSingleThreadedExecutor();
@@ -20,7 +20,7 @@ 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.class.getSimpleName();
private static final String TAG = "AudioDevicePickerDialog";
public interface OnDeviceSelectedListener {
void onDeviceSelected(CallEndpointCompat endpoint);
@@ -6,11 +6,13 @@ import android.app.PictureInPictureParams;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.provider.Settings;
import android.util.Log;
import android.util.Rational;
import android.view.View;
@@ -22,7 +24,9 @@ import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.cardview.widget.CardView;
import androidx.constraintlayout.widget.ConstraintLayout;
@@ -51,9 +55,10 @@ import org.webrtc.VideoTrack;
@RequiresApi(api = Build.VERSION_CODES.O)
public class CallActivity extends AppCompatActivity {
private static final String TAG = CallActivity.class.getSimpleName();
private static final String TAG = "CallActivity";
private static final int MIC_PERMISSION_REQUEST_CODE = 1001;
private static final int CAMERA_PERMISSION_REQUEST_CODE = 1002;
private static final int CAMERA_MID_CALL_PERMISSION_REQUEST_CODE = 1003;
public static final String ACTION_ANSWER_CALL = BuildConfig.APPLICATION_ID + ".ANSWER_CALL";
public static final String ACTION_DECLINE_CALL = BuildConfig.APPLICATION_ID + ".DECLINE_CALL";
@@ -99,6 +104,7 @@ public class CallActivity extends AppCompatActivity {
private boolean awaitingPermissionResult = false;
private boolean pausedWhileAwaitingPermission = false;
private boolean intentHandled = false;
private boolean doNotAutoFinish = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -227,6 +233,7 @@ public class CallActivity extends AppCompatActivity {
Log.d(TAG, "Resuming existing call");
} else if (!coordinator.isIncomingCall()) {
Log.d(TAG, "Starting outgoing call");
coordinator.ensureServiceStarted();
viewModel.startOutgoingCallWhenReady();
}
}
@@ -374,6 +381,18 @@ public class CallActivity extends AppCompatActivity {
videoButton.setOnClickListener(
v -> {
if (viewModel != null) {
Boolean currentEnabled = viewModel.getVideoEnabled().getValue();
boolean needToEnable = currentEnabled == null || !currentEnabled;
if (needToEnable && !hasCameraPermission()) {
awaitingPermissionResult = true;
ActivityCompat.requestPermissions(
this,
new String[] {Manifest.permission.CAMERA},
CAMERA_MID_CALL_PERMISSION_REQUEST_CODE);
return;
}
viewModel.toggleVideo();
}
});
@@ -634,7 +653,7 @@ public class CallActivity extends AppCompatActivity {
new Handler(Looper.getMainLooper())
.postDelayed(
() -> {
if (!isFinishing()) {
if (!isFinishing() && !doNotAutoFinish) {
finish();
}
},
@@ -643,7 +662,9 @@ public class CallActivity extends AppCompatActivity {
case ENDED:
statusText.setText(R.string.call_ended);
finish();
if (!doNotAutoFinish) {
finish();
}
break;
case ERROR:
@@ -656,7 +677,7 @@ public class CallActivity extends AppCompatActivity {
new Handler(Looper.getMainLooper())
.postDelayed(
() -> {
if (!isFinishing()) {
if (!isFinishing() && !doNotAutoFinish) {
finish();
}
},
@@ -842,16 +863,28 @@ public class CallActivity extends AppCompatActivity {
}
private void handleMicPermissionDenied() {
Toast.makeText(this, R.string.call_requires_mic_permission, Toast.LENGTH_LONG).show();
CallCoordinator coordinator = CallCoordinator.getInstance(getApplication());
if (coordinator.hasActiveCall()
&& coordinator.isIncomingCall()
&& !coordinator.hasOngoingCall()) {
coordinator.declineCall();
if (coordinator.hasActiveCall() && !coordinator.hasOngoingCall()) {
if (coordinator.isIncomingCall()) {
coordinator.declineCall();
} else {
coordinator.hangUp();
}
}
finish();
if (!shouldShowRequestPermissionRationale(Manifest.permission.RECORD_AUDIO)) {
doNotAutoFinish = true;
showPermissionSettingsDialog(
getString(R.string.call_requires_mic_permission),
() -> {
doNotAutoFinish = false;
if (!isFinishing()) finish();
});
} else {
Toast.makeText(this, R.string.call_requires_mic_permission, Toast.LENGTH_LONG).show();
finish();
}
}
private void proceedAfterPermissions() {
@@ -878,6 +911,22 @@ public class CallActivity extends AppCompatActivity {
CallCoordinator coordinator = CallCoordinator.getInstance(getApplication());
if (requestCode == CAMERA_MID_CALL_PERMISSION_REQUEST_CODE) {
boolean cameraGranted =
grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
if (cameraGranted && viewModel != null) {
viewModel.toggleVideo();
} else {
if (!shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
showPermissionSettingsDialog(getString(R.string.call_requires_camera_permission), null);
} else {
Toast.makeText(this, R.string.call_requires_camera_permission, Toast.LENGTH_SHORT).show();
}
}
return;
}
if (requestCode == MIC_PERMISSION_REQUEST_CODE) {
boolean micGranted =
grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
@@ -893,9 +942,17 @@ public class CallActivity extends AppCompatActivity {
if (!cameraGranted) {
Log.w(TAG, "Camera permission denied, switching to audio-only");
Toast.makeText(
this, "Starting audio-only call (camera permission denied)", Toast.LENGTH_SHORT)
.show();
if (!shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
Toast.makeText(
this,
"Camera permission permanently denied. Enable in app settings for video calls.",
Toast.LENGTH_LONG)
.show();
} else {
Toast.makeText(
this, "Starting audio-only call (camera permission denied)", Toast.LENGTH_SHORT)
.show();
}
coordinator.setStartsWithVideo(false);
}
}
@@ -903,6 +960,34 @@ public class CallActivity extends AppCompatActivity {
proceedAfterPermissions();
}
private void showPermissionSettingsDialog(String message, @Nullable Runnable onDismissAction) {
if (isFinishing() || isDestroyed()) {
if (onDismissAction != null) onDismissAction.run();
return;
}
new AlertDialog.Builder(this)
.setTitle("Permission Required")
.setMessage(message)
.setPositiveButton(
"Open Settings",
(dialog, which) -> {
Intent intent =
new Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", getPackageName(), null));
startActivity(intent);
})
.setNegativeButton(android.R.string.cancel, null)
.setOnDismissListener(
dialog -> {
if (onDismissAction != null) {
onDismissAction.run();
}
})
.show();
}
// Picture-in-Picture
@Override
@@ -23,6 +23,7 @@ import android.os.Looper;
import android.telecom.DisconnectCause;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
@@ -32,6 +33,7 @@ import androidx.core.telecom.CallControlScope;
import androidx.core.telecom.CallEndpointCompat;
import androidx.core.telecom.CallException;
import androidx.core.telecom.CallsManager;
import androidx.core.util.Pair;
import androidx.lifecycle.FlowLiveDataConversions;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
@@ -63,7 +65,7 @@ import org.webrtc.VideoTrack;
@RequiresApi(api = Build.VERSION_CODES.O)
public class CallCoordinator implements DcEventCenter.DcEventDelegate {
private static final String TAG = CallCoordinator.class.getSimpleName();
private static final String TAG = "CallCoordinator";
// Notification channels
private static final String CHANNEL_ID_INCOMING = "voip_incoming_calls";
@@ -97,6 +99,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
private final MutableLiveData<Boolean> outgoingCallPlaced = new MutableLiveData<>(false);
private final MutableLiveData<Boolean> answeredElsewhere = new MutableLiveData<>(false);
private final MutableLiveData<Boolean> isFrontCamera = new MutableLiveData<>(true);
private final MutableLiveData<Boolean> mediaCaptureReady = new MutableLiveData<>(false);
// Audio Routing Support
private final MediatorLiveData<CallEndpointCompat> currentAudioEndpoint =
@@ -119,6 +122,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
private String pendingOfferSdp;
private boolean hasNotifiedBackend = false;
private boolean hasAutoSelectedEarpiece = false;
private boolean pendingMediaCapture = false;
private CallControlScope activeCallControlScope;
private CallViewModel activeCallViewModel;
@@ -215,7 +219,8 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
calleeName = "Unknown";
}
showOrUpdateOngoingNotification("Calling " + calleeName + "...");
showOrUpdateOngoingNotification(
appContext.getString(R.string.calling_person, calleeName));
}
// Initialize call
@@ -227,6 +232,11 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
mainHandler.removeCallbacks(outgoingRingtoneRunnable);
mainHandler.postDelayed(outgoingRingtoneRunnable, 1500);
}
if (pendingMediaCapture) {
pendingMediaCapture = false;
callService.startMediaCapture();
}
}
}
@@ -335,6 +345,10 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
return isFrontCamera;
}
public LiveData<Boolean> getMediaCaptureReady() {
return mediaCaptureReady;
}
// State Update Methods (CallService)
public void updateConnectionState(PeerConnection.PeerConnectionState state) {
@@ -374,6 +388,11 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
isFrontCamera.postValue(front);
}
public void updateMediaCaptureReady(boolean ready) {
Log.d(TAG, "updateMediaCaptureReady: " + ready);
mediaCaptureReady.postValue(ready);
}
public void reportError(String error) {
Log.e(TAG, "reportError: " + error);
errorMessage.postValue(error);
@@ -386,7 +405,8 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
if (callService != null) {
callService.startMediaCapture();
} else {
Log.w(TAG, "Cannot start media capture, service not ready");
Log.d(TAG, "Service not ready, deferring media capture");
pendingMediaCapture = true;
}
}
@@ -402,6 +422,15 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
callService.stopRingtone();
}
// Promote the service to foreground immediately. Waiting until onIncomingCallAccepted
// on executor pool thread is too late on stricter OEM.
//
// Do not cancel() but use showOrUpdateOngoingNotification to replace incoming
// notification without a gap.
String callerName = displayName.getValue();
if (callerName == null) callerName = "Unknown";
showOrUpdateOngoingNotification(appContext.getString(R.string.call_with, callerName));
// Notify Android system with CallControlScope
CallControlScope scope = activeCallControlScope;
if (scope != null) {
@@ -425,8 +454,6 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
}
});
}
notificationManager.cancel(NOTIFICATION_ID_CALL);
}
public void answerWebRTC() {
@@ -643,11 +670,17 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
public synchronized void setVideoEnabled(boolean enabled) {
Log.d(TAG, "setVideoEnabled: " + enabled);
if (callService != null) {
boolean success = callService.setVideoEnabled(enabled);
if (!success && enabled) {
enabled = false;
reportError("Camera unavailable");
}
}
localVideoEnabled.postValue(enabled);
if (callService != null) {
callService.setVideoEnabled(enabled);
callService.sendMutedState(Boolean.TRUE.equals(localAudioEnabled.getValue()), enabled);
}
}
@@ -867,32 +900,15 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
int accId, int callId, String offerSdp, boolean startsWithVideo) {
Log.d(TAG, "onIncomingCall: accId=" + accId + ", callId=" + callId);
if (hasActiveCall()) {
Log.w(TAG, "Already have an active call, ignoring incoming call");
return;
}
Pair<DcChat, String> result = setupIncomingCallState(accId, callId, offerSdp, startsWithVideo);
if (result == null) return;
resetLiveDataForNewCall();
this.activeAccId = accId;
this.activeCallId = callId;
this.isIncomingCall = true;
this.startsWithVideo = startsWithVideo;
this.pendingOfferSdp = offerSdp;
// Get caller info
DcContext dcContext = ApplicationContext.getDcAccounts().getAccount(accId);
int chatId = dcContext.getMsg(callId).getChatId();
this.activeChatId = chatId;
DcChat dcChat = dcContext.getChat(chatId);
String callerName = getNameFromChat(dcChat);
DcChat dcChat = result.first;
String callerName = result.second;
Icon callerIcon = getIconFromChat(this.appContext, dcChat);
displayName.postValue(callerName);
displayIcon.postValue(callerIcon);
this.preferredStartingEndpoint = getPreferredStartingEndpoint(startsWithVideo);
// Add to CallsManager
CallAttributesCompat callAttributes = createCallAttributes(callerName, callId, true);
addCallToTelecom(callAttributes, callerName, callerIcon);
@@ -903,6 +919,37 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
startAndBindService();
}
public synchronized void handleIncomingCallFromConversation(
int accId, int callId, String offerSdp, boolean hasVideo) {
Log.d(TAG, "handleIncomingCallFromConversation: accId=" + accId + ", callId=" + callId);
if (offerSdp == null || offerSdp.isEmpty()) {
Log.e(TAG, "Cannot start incoming call: no SDP offer");
return;
}
Pair<DcChat, String> result = setupIncomingCallState(accId, callId, offerSdp, hasVideo);
if (result == null) return;
DcChat dcChat = result.first;
String callerName = result.second;
new Thread(
() -> {
Icon callerIcon = getIconFromChat(this.appContext, dcChat);
displayIcon.postValue(callerIcon);
})
.start();
// Add to CallsManager
CallAttributesCompat callAttributes = createCallAttributes(callerName, callId, true);
addCallToTelecom(callAttributes, callerName, null);
startAndBindService();
launchCallActivity();
}
private synchronized void onIncomingCallAccepted(int callId, boolean fromThisDevice) {
Log.d(TAG, "onIncomingCallAccepted: callId=" + callId + ", fromThisDevice=" + fromThisDevice);
@@ -921,7 +968,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
callerName = "Unknown";
}
showOrUpdateOngoingNotification("Call with " + callerName);
showOrUpdateOngoingNotification(appContext.getString(R.string.call_with, callerName));
}
private synchronized void onCallAnsweredOnOtherDevice() {
@@ -1008,7 +1055,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
if (calleeName == null) {
calleeName = "Unknown";
}
showOrUpdateOngoingNotification("Call with " + calleeName);
showOrUpdateOngoingNotification(appContext.getString(R.string.call_with, calleeName));
}
private synchronized void onCallEnded(int accId, int callId) {
@@ -1101,6 +1148,14 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
return;
}
if (callService != null) {
try {
callService.stopForegroundAndDismiss();
} catch (Exception e) {
Log.w(TAG, "stopForegroundAndDismiss failed", e);
}
}
// Clear state
this.activeAccId = null;
this.activeCallId = null;
@@ -1113,6 +1168,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
this.pendingOfferSdp = null;
this.hasNotifiedBackend = false;
this.hasAutoSelectedEarpiece = false;
this.pendingMediaCapture = false;
mainHandler.removeCallbacks(outgoingRingtoneRunnable);
@@ -1178,6 +1234,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
private void resetLiveDataForNewCall() {
connectionState.postValue(PeerConnection.PeerConnectionState.NEW);
answeredElsewhere.postValue(false); // clearLiveData() must not reset answeredElsewhere
mediaCaptureReady.postValue(false);
clearLiveData();
}
@@ -1193,6 +1250,7 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
outgoingCallPlaced.postValue(false);
currentAudioEndpoint.postValue(null);
availableAudioEndpoints.postValue(null);
mediaCaptureReady.postValue(false);
}
public synchronized void initiateOutgoingCall(int accId, int chatId, boolean startsWithVideo) {
@@ -1203,16 +1261,6 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
return;
}
// Check microphone permission
if (!hasMicrophonePermission()) {
Log.e(TAG, "Microphone permission not granted");
Intent intent = new Intent(appContext, CallActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
appContext.startActivity(intent);
return;
}
resetLiveDataForNewCall();
this.activeCallId = -1; // Placeholder call ID for Intent
@@ -1238,7 +1286,9 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
this.preferredStartingEndpoint = getPreferredStartingEndpoint(startsWithVideo);
startAndBindService();
if (hasMicrophonePermission()) {
startAndBindService();
}
launchCallActivity();
}
@@ -1270,6 +1320,43 @@ public class CallCoordinator implements DcEventCenter.DcEventDelegate {
addCallToTelecom(callAttributes, calleeName, calleeIcon);
}
@Nullable
private Pair<DcChat, String> setupIncomingCallState(
int accId, int callId, String offerSdp, boolean startsWithVideo) {
if (hasActiveCall()) {
Log.w(TAG, "Already have an active call, ignoring incoming call");
return null;
}
resetLiveDataForNewCall();
this.activeAccId = accId;
this.activeCallId = callId;
this.isIncomingCall = true;
this.startsWithVideo = startsWithVideo;
this.pendingOfferSdp = offerSdp;
DcContext dcContext = ApplicationContext.getDcAccounts().getAccount(accId);
int chatId = dcContext.getMsg(callId).getChatId();
this.activeChatId = chatId;
DcChat dcChat = dcContext.getChat(chatId);
String callerName = getNameFromChat(dcChat);
displayName.postValue(callerName);
this.preferredStartingEndpoint = getPreferredStartingEndpoint(startsWithVideo);
return new Pair<>(dcChat, callerName);
}
public synchronized void ensureServiceStarted() {
if (isServiceBound || !hasActiveCall()) {
return;
}
Log.d(TAG, "Starting service after permission grant");
startAndBindService();
}
private void addCallToTelecom(
CallAttributesCompat callAttributes, String displayName, Icon icon) {
try {
@@ -4,6 +4,7 @@ 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;
@@ -13,7 +14,9 @@ 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;
@@ -33,7 +36,7 @@ import org.webrtc.VideoTrack;
@RequiresApi(api = Build.VERSION_CODES.O)
public class CallService extends Service implements WebRTCClient.Callbacks {
private static final String TAG = CallService.class.getSimpleName();
private static final String TAG = "CallService";
private final IBinder binder = new LocalBinder();
@@ -123,13 +126,13 @@ public class CallService extends Service implements WebRTCClient.Callbacks {
}
/**
* Start camera/microphone capture
* 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 (Camera/Microphone)");
Log.d(TAG, "startMediaCapture");
if (webRTCClient != null && webRTCClient.hasLocalMediaStream()) {
Log.w(TAG, "Media already initialized, skipping");
@@ -150,7 +153,7 @@ public class CallService extends Service implements WebRTCClient.Callbacks {
boolean startsWithVideo = callCoordinator.isStartsWithVideo();
Log.d(TAG, "Creating media stream with video: " + startsWithVideo);
Log.d(TAG, "Creating media stream");
mediaStreamManager.createMediaStream(
new MediaStreamManager.Callback() {
@@ -160,31 +163,22 @@ public class CallService extends Service implements WebRTCClient.Callbacks {
webRTCClient.setLocalMediaStream(stream);
callCoordinator.updateFrontCamera(mediaStreamManager.isFrontCamera());
callCoordinator.setVideoEnabled(startsWithVideo);
if (!stream.videoTracks.isEmpty()) {
VideoTrack localTrack = stream.videoTracks.get(0);
callCoordinator.updateLocalVideoTrack(localTrack);
} else {
Log.w(TAG, "No video track in stream, call will be audio-only");
if (startsWithVideo) {
callCoordinator.reportError("Camera unavailable, using audio only");
}
callCoordinator.setVideoEnabled(false);
}
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);
if (startsWithVideo) {
callCoordinator.reportError("Camera/microphone error: " + error);
}
callCoordinator.setVideoEnabled(false);
callCoordinator.reportError("Camera/microphone error: " + error);
}
});
}
@@ -392,12 +386,30 @@ public class CallService extends Service implements WebRTCClient.Callbacks {
}
}
public void setVideoEnabled(boolean enabled) {
public boolean setVideoEnabled(boolean enabled) {
Log.d(TAG, "setVideoEnabled: " + enabled);
if (webRTCClient != null) {
webRTCClient.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) {
@@ -432,6 +444,12 @@ public class CallService extends Service implements WebRTCClient.Callbacks {
disposeWebRTC();
try {
stopForeground(STOP_FOREGROUND_REMOVE);
} catch (Exception e) {
Log.w(TAG, "stopForeground failed", e);
}
stopService();
}
@@ -494,13 +512,44 @@ public class CallService extends Service implements WebRTCClient.Callbacks {
// Foreground Notification
public void startForegroundWithNotification(int id, Notification notification) {
Log.d(TAG, "Starting foreground with notification id: " + id);
startForeground(id, 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() {
Log.d(TAG, "Stopping foreground and dismissing notification");
stopForeground(STOP_FOREGROUND_REMOVE);
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
@@ -5,13 +5,18 @@ 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.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;
@@ -21,7 +26,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.BitmapUtil;
public class CallUtil {
private static final String TAG = CallUtil.class.getSimpleName();
private static final String TAG = "CallUtil";
@RequiresApi(api = Build.VERSION_CODES.O)
public static void startAudioCall(Context context, int chatId) {
@@ -52,8 +57,22 @@ public class CallUtil {
return;
}
int accId = DcHelper.getContext(context).getAccountId();
coordinator.initiateOutgoingCall(accId, chatId, startsWithVideo);
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
@@ -138,4 +157,24 @@ public class CallUtil {
}
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;
}
}
@@ -20,7 +20,7 @@ import org.webrtc.VideoTrack;
@RequiresApi(api = Build.VERSION_CODES.O)
public class CallViewModel extends AndroidViewModel {
private static final String TAG = CallViewModel.class.getSimpleName();
private static final String TAG = "CallViewModel";
private final CallCoordinator callCoordinator;
@@ -46,8 +46,8 @@ public class CallViewModel extends AndroidViewModel {
private final MediatorLiveData<CallState> callState;
// Observer References for one-time observe
private Observer<VideoTrack> answerCallObserver;
private Observer<VideoTrack> startOutgoingCallObserver;
private Observer<Boolean> answerCallObserver;
private Observer<Boolean> startOutgoingCallObserver;
private final AtomicBoolean hasCallEnded = new AtomicBoolean(false);
@@ -211,25 +211,24 @@ public class CallViewModel extends AndroidViewModel {
callCoordinator.startMediaCapture();
// Create one-time observer
LiveData<VideoTrack> localTrack = callCoordinator.getLocalVideoTrack();
LiveData<Boolean> mediaReady = callCoordinator.getMediaCaptureReady();
answerCallObserver =
new Observer<VideoTrack>() {
new Observer<Boolean>() {
@Override
public void onChanged(VideoTrack videoTrack) {
if (videoTrack != null) {
// Media is ready, remove observer
localTrack.removeObserver(this);
public void onChanged(Boolean ready) {
if (Boolean.TRUE.equals(ready)) {
mediaReady.removeObserver(this);
answerCallObserver = null;
Log.d(TAG, "Local video ready, answering call (WebRTC)");
Log.d(TAG, "Media capture ready, answering call (WebRTC)");
callCoordinator.answerWebRTC();
}
}
};
localTrack.observeForever(answerCallObserver);
mediaReady.observeForever(answerCallObserver);
}
/** Start outgoing call with media capture Called by Activity for outgoing calls */
@@ -244,30 +243,28 @@ public class CallViewModel extends AndroidViewModel {
callCoordinator.startMediaCapture();
// Create one-time observer
LiveData<VideoTrack> localTrack = callCoordinator.getLocalVideoTrack();
VideoTrack currentValue = localTrack.getValue();
LiveData<Boolean> mediaReady = callCoordinator.getMediaCaptureReady();
if (currentValue != null) {
if (Boolean.TRUE.equals(mediaReady.getValue())) {
Log.d(TAG, "Media already ready, starting call immediately");
callCoordinator.startOutgoingCall();
} else {
startOutgoingCallObserver =
new Observer<VideoTrack>() {
new Observer<Boolean>() {
@Override
public void onChanged(VideoTrack videoTrack) {
if (videoTrack != null) {
// Media is ready, remove observer
localTrack.removeObserver(this);
public void onChanged(Boolean ready) {
if (Boolean.TRUE.equals(ready)) {
mediaReady.removeObserver(this);
startOutgoingCallObserver = null;
Log.d(TAG, "Local video ready, starting outgoing call");
Log.d(TAG, "Media capture ready, starting outgoing call");
callCoordinator.startOutgoingCall();
}
}
};
localTrack.observeForever(startOutgoingCallObserver);
mediaReady.observeForever(startOutgoingCallObserver);
}
}
@@ -460,12 +457,12 @@ public class CallViewModel extends AndroidViewModel {
Log.d(TAG, "CallViewModel cleared");
if (answerCallObserver != null) {
callCoordinator.getLocalVideoTrack().removeObserver(answerCallObserver);
callCoordinator.getMediaCaptureReady().removeObserver(answerCallObserver);
answerCallObserver = null;
}
if (startOutgoingCallObserver != null) {
callCoordinator.getLocalVideoTrack().removeObserver(startOutgoingCallObserver);
callCoordinator.getMediaCaptureReady().removeObserver(startOutgoingCallObserver);
startOutgoingCallObserver = null;
}
@@ -24,11 +24,15 @@ import org.webrtc.VideoTrack;
public class MediaStreamManager {
private static final String TAG = MediaStreamManager.class.getSimpleName();
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;
@@ -37,6 +41,7 @@ public class MediaStreamManager {
private AudioSource audioSource;
private SurfaceTextureHelper surfaceTextureHelper;
private volatile boolean isFrontCamera = true;
private volatile boolean isCapturing = false;
public interface Callback {
void onMediaStreamReady(MediaStream stream);
@@ -56,9 +61,8 @@ public class MediaStreamManager {
this.peerConnectionFactory = peerConnectionFactory;
}
/** Create media stream with audio and optionally video */
@RequiresApi(api = Build.VERSION_CODES.M)
public void createMediaStream(Callback callback) {
/** 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);
@@ -68,24 +72,11 @@ public class MediaStreamManager {
AudioTrack audioTrack = peerConnectionFactory.createAudioTrack(AUDIO_TRACK_ID, audioSource);
mediaStream.addTrack(audioTrack);
// Create video track
videoCapturer = createVideoCapturer();
if (videoCapturer == null) {
callback.onError("No camera available");
callback.onMediaStreamReady(mediaStream);
return;
}
videoSource = peerConnectionFactory.createVideoSource(videoCapturer.isScreencast());
// Create video source and track
videoSource = peerConnectionFactory.createVideoSource(false);
VideoTrack videoTrack = peerConnectionFactory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
mediaStream.addTrack(videoTrack);
// Start capturing
surfaceTextureHelper =
SurfaceTextureHelper.create("CaptureThread", EglUtils.getEglBase().getEglBaseContext());
videoCapturer.initialize(surfaceTextureHelper, context, videoSource.getCapturerObserver());
videoCapturer.startCapture(1280, 720, 30);
callback.onMediaStreamReady(mediaStream);
} catch (Exception e) {
@@ -94,6 +85,62 @@ public class MediaStreamManager {
}
}
/**
* 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)
@@ -129,6 +176,14 @@ public class MediaStreamManager {
}
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;
@@ -186,10 +241,12 @@ public class MediaStreamManager {
}
/** Cleanup resources */
public void dispose() {
public synchronized void dispose() {
if (videoCapturer != null) {
try {
videoCapturer.stopCapture();
if (isCapturing) {
videoCapturer.stopCapture();
}
} catch (InterruptedException e) {
Log.e(TAG, "Error stopping capture", e);
}
@@ -197,6 +254,8 @@ public class MediaStreamManager {
videoCapturer = null;
}
isCapturing = false;
if (surfaceTextureHelper != null) {
surfaceTextureHelper.dispose();
surfaceTextureHelper = null;
@@ -26,6 +26,7 @@ import androidx.loader.app.LoaderManager;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.util.Prefs;
import org.thoughtcrime.securesms.util.ViewUtil;
public class AttachmentTypeSelector extends PopupWindow {
@@ -45,7 +46,7 @@ public class AttachmentTypeSelector extends PopupWindow {
private final @NonNull ImageView imageButton;
private final @NonNull ImageView documentButton;
private final @NonNull ImageView contactButton;
// private final @NonNull ImageView cameraButton;
// private final @NonNull ImageView cameraButton;
private final @NonNull ImageView videoButton;
private final @NonNull ImageView locationButton;
private final @NonNull ImageView webxdcButton;
@@ -87,11 +88,13 @@ public class AttachmentTypeSelector extends PopupWindow {
this.webxdcButton.setOnClickListener(new PropagatingClickListener(ADD_WEBXDC));
this.recentRail.setListener(new RecentPhotoSelectedListener());
// disable location streaming button for now
// if (!Prefs.isLocationStreamingEnabled(context)) {
this.locationButton.setVisibility(View.GONE);
ViewUtil.findById(layout, R.id.location_button_label).setVisibility(View.GONE);
// }
if (!Prefs.isLocationStreamingEnabled(context)) {
ViewUtil.findById(layout, R.id.location_linear_layout).setVisibility(View.GONE);
}
if (DcHelper.getContext(context).getChat(chatId).isOutBroadcast()) {
ViewUtil.findById(layout, R.id.webxdc_linear_layout).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.class.getSimpleName();
private static final String TAG = "CallItemView";
private final @NonNull ImageView icon;
private final @NonNull TextView title;
@@ -1,31 +0,0 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewpager.widget.ViewPager;
import org.thoughtcrime.securesms.components.viewpager.HackyViewPager;
/** An implementation of {@link ViewPager} that disables swiping when the view is disabled. */
public class ControllableViewPager extends HackyViewPager {
public ControllableViewPager(@NonNull Context context) {
super(context);
}
public ControllableViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return isEnabled() && super.onTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return isEnabled() && super.onInterceptTouchEvent(ev);
}
}
@@ -43,7 +43,7 @@ public class InputPanel extends ConstraintLayout
KeyboardAwareLinearLayout.OnKeyboardShownListener,
MediaKeyboard.MediaKeyboardListener {
private static final String TAG = InputPanel.class.getSimpleName();
private static final String TAG = "InputPanel";
private static final int FADE_TIME = 150;
@@ -38,7 +38,7 @@ import org.thoughtcrime.securesms.util.ViewUtil;
* been opened and what its height would be.
*/
public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
private static final String TAG = KeyboardAwareLinearLayout.class.getSimpleName();
private static final String TAG = "KeyboardAwareLinearLayout";
private static final long KEYBOARD_DEBOUNCE = 150;
@@ -38,7 +38,7 @@ import org.thoughtcrime.securesms.util.Util;
public class QuoteView extends FrameLayout implements RecipientForeverObserver {
private static final String TAG = QuoteView.class.getSimpleName();
private static final String TAG = "QuoteView";
private static final int MESSAGE_TYPE_PREVIEW = 0;
@@ -30,7 +30,7 @@ public class ScaleStableImageView extends AppCompatImageView
implements KeyboardAwareLinearLayout.OnKeyboardShownListener,
KeyboardAwareLinearLayout.OnKeyboardHiddenListener {
private static final String TAG = ScaleStableImageView.class.getSimpleName();
private static final String TAG = "ScaleStableImageView";
private Drawable defaultDrawable;
private Drawable currentDrawable;
@@ -22,7 +22,7 @@ import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
public class SearchToolbar extends LinearLayout {
private static final String TAG = SearchToolbar.class.getSimpleName();
private static final String TAG = "SearchToolbar";
private float x, y;
private MenuItem searchItem;
private EditText searchText;
@@ -30,7 +30,7 @@ import org.thoughtcrime.securesms.util.Util;
public class ThumbnailView extends FrameLayout {
private static final String TAG = ThumbnailView.class.getSimpleName();
private static final String TAG = "ThumbnailView";
private static final int WIDTH = 0;
private static final int HEIGHT = 1;
private static final int MIN_WIDTH = 0;
@@ -17,7 +17,7 @@ import org.thoughtcrime.securesms.mms.VcardSlide;
import org.thoughtcrime.securesms.recipients.Recipient;
public class VcardView extends FrameLayout {
private static final String TAG = VcardView.class.getSimpleName();
private static final String TAG = "VcardView";
private final @NonNull AvatarView avatar;
private final @NonNull TextView name;
@@ -6,22 +6,26 @@ import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import androidx.media3.common.MediaItem;
import androidx.media3.common.PlaybackException;
import androidx.media3.common.Player;
import androidx.media3.session.MediaController;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AudioPlaybackViewModel extends ViewModel {
private static final String TAG = AudioPlaybackViewModel.class.getSimpleName();
private static final String TAG = "AudioPlaybackViewModel";
private static final int NON_MESSAGE_AUDIO_MSG_ID =
0; // Audios not attached to a message doesn't have message id.
@@ -34,6 +38,8 @@ public class AudioPlaybackViewModel extends ViewModel {
private final ExecutorService extractionExecutor = Executors.newFixedThreadPool(2);
private @Nullable MediaController mediaController;
private @Nullable ChatAudioQueueProvider queueProvider;
private @Nullable Player.Listener playerListener;
private final Handler handler;
private boolean isUserSeeking = false;
@@ -47,6 +53,11 @@ public class AudioPlaybackViewModel extends ViewModel {
}
public void setMediaController(@Nullable MediaController controller) {
if (this.mediaController != null && playerListener != null) {
this.mediaController.removeListener(playerListener);
}
playerListener = null;
this.mediaController = controller;
if (mediaController != null && mediaController.isPlaying()) {
startUpdateProgress();
@@ -59,30 +70,41 @@ public class AudioPlaybackViewModel extends ViewModel {
public void loadAudioAndPlay(int msgId, Uri audioUri) {
if (mediaController == null) return;
// Set media item if we have a different audio.
if (isDifferentAudio(msgId, audioUri)) {
updateState(msgId, audioUri, AudioPlaybackState.PlaybackStatus.LOADING, 0, 0);
String mediaId = String.valueOf(msgId);
MediaItem mediaItem =
new MediaItem.Builder().setMediaId(String.valueOf(msgId)).setUri(audioUri).build();
mediaController.setMediaItem(mediaItem);
mediaController.prepare();
MediaItem current = mediaController.getCurrentMediaItem();
if (current != null && mediaId.equals(current.mediaId)) {
mediaController.play();
return;
}
play(msgId, audioUri);
updateState(msgId, audioUri, AudioPlaybackState.PlaybackStatus.LOADING, 0, 0);
List<MediaItem> items = null;
int startIndex = -1;
if (queueProvider != null) {
items = queueProvider.buildAudioQueue();
startIndex = indexOfMediaId(items, mediaId);
}
if (startIndex < 0) {
items =
Collections.singletonList(
new MediaItem.Builder().setMediaId(mediaId).setUri(audioUri).build());
startIndex = 0;
}
mediaController.setMediaItems(items, startIndex, 0);
mediaController.prepare();
mediaController.play();
}
private boolean isSameAudio(int msgId, Uri audioUri) {
return !isDifferentAudio(msgId, audioUri);
}
private boolean isDifferentAudio(int msgId, Uri audioUri) {
AudioPlaybackState currentState = playbackState.getValue();
return currentState != null
&& (msgId != currentState.getMsgId()
|| currentState.getAudioUri() == null
|| currentState.getAudioUri() != null && !currentState.getAudioUri().equals(audioUri));
private static int indexOfMediaId(List<MediaItem> items, String mediaId) {
for (int i = 0; i < items.size(); i++) {
if (mediaId.equals(items.get(i).mediaId)) return i;
}
return -1;
}
public LiveData<Map<Integer, Long>> getDurations() {
@@ -138,46 +160,72 @@ public class AudioPlaybackViewModel extends ViewModel {
}
}
public void pause(int msgId, Uri audioUri) {
if (mediaController != null && isSameAudio(msgId, audioUri)) {
public void pause(int msgId) {
if (isCurrentItem(msgId)) {
mediaController.pause();
}
}
public void play(int msgId, Uri audioUri) {
if (mediaController != null && isSameAudio(msgId, audioUri)) {
public void play(int msgId) {
if (isCurrentItem(msgId)) {
mediaController.play();
}
}
public void seekTo(long position, int msgId, Uri audioUri) {
if (mediaController != null && isSameAudio(msgId, audioUri)) {
public void seekTo(long position, int msgId) {
if (isCurrentItem(msgId)) {
mediaController.seekTo(position);
}
}
public void stop(int msgId, Uri audioUri) {
if (mediaController != null && isSameAudio(msgId, audioUri)) {
public void stop(int msgId) {
if (isCurrentItem(msgId)) {
mediaController.stop();
mediaController.clearMediaItems();
stopUpdateProgress();
playbackState.setValue(AudioPlaybackState.idle());
}
}
private boolean isCurrentItem(int msgId) {
if (mediaController == null) return false;
MediaItem current = mediaController.getCurrentMediaItem();
return current != null && String.valueOf(msgId).equals(current.mediaId);
}
public void stopNonMessageAudioPlayback() {
stopByIds(NON_MESSAGE_AUDIO_MSG_ID);
}
// A special method for deleting message, where we only use message Ids
public void stopByIds(int... msgIds) {
AudioPlaybackState currentState = playbackState.getValue();
if (mediaController == null) return;
if (mediaController != null && currentState != null) {
AudioPlaybackState currentState = playbackState.getValue();
boolean stoppedCurrent = false;
if (currentState != null) {
for (int msgId : msgIds) {
if (msgId == currentState.getMsgId()) {
mediaController.stop();
mediaController.clearMediaItems();
stopUpdateProgress();
playbackState.setValue(AudioPlaybackState.idle());
stoppedCurrent = true;
break;
}
}
}
if (!stoppedCurrent) {
Set<String> deletedMediaIds = new HashSet<>();
for (int msgId : msgIds) {
deletedMediaIds.add(String.valueOf(msgId));
}
for (int i = mediaController.getMediaItemCount() - 1; i >= 0; i--) {
MediaItem item = mediaController.getMediaItemAt(i);
if (deletedMediaIds.contains(item.mediaId)) {
mediaController.removeMediaItem(i);
}
}
}
@@ -191,10 +239,10 @@ public class AudioPlaybackViewModel extends ViewModel {
private void setupPlayerListener() {
if (mediaController == null) return;
mediaController.addListener(
playerListener =
new Player.Listener() {
@Override
public void onEvents(Player player, Player.Events events) {
public void onEvents(@NonNull Player player, @NonNull Player.Events events) {
if (events.containsAny(Player.EVENT_IS_PLAYING_CHANGED)) {
if (player.isPlaying()) {
startUpdateProgress();
@@ -203,20 +251,40 @@ public class AudioPlaybackViewModel extends ViewModel {
}
updateCurrentState(false);
}
if (events.containsAny(Player.EVENT_MEDIA_ITEM_TRANSITION)) {
updateCurrentState(true);
}
if (events.containsAny(Player.EVENT_PLAYBACK_STATE_CHANGED)) {
if (player.getPlaybackState() == Player.STATE_READY) {
updateCurrentState(false);
} else if (player.getPlaybackState() == Player.STATE_ENDED) {
// This is to prevent automatically playing after the audio
// has been play to the end once, then user dragged the seek bar again
} else if (player.getPlaybackState() == Player.STATE_ENDED
&& !player.hasNextMediaItem()) {
mediaController.setPlayWhenReady(false);
}
}
if (events.containsAny(Player.EVENT_PLAYER_ERROR)) {
}
@Override
public void onPlayerError(@NonNull PlaybackException error) {
Log.w(
TAG,
"Playback error on msgId="
+ (mediaController.getCurrentMediaItem() != null
? mediaController.getCurrentMediaItem().mediaId
: "null"),
error);
if (mediaController.hasNextMediaItem()) {
mediaController.seekToNextMediaItem();
mediaController.prepare();
mediaController.play();
} else {
updateCurrentAudioState(AudioPlaybackState.PlaybackStatus.ERROR, 0, 0);
mediaController.clearMediaItems();
}
}
});
};
mediaController.addListener(playerListener);
}
private void updateCurrentState(boolean queryPlaying) {
@@ -285,6 +353,11 @@ public class AudioPlaybackViewModel extends ViewModel {
}
}
// Playing Queue
public void setQueueProvider(@Nullable ChatAudioQueueProvider provider) {
this.queueProvider = provider;
}
// Progress tracking
private final Runnable progressRunnable =
new Runnable() {
@@ -23,7 +23,7 @@ import org.thoughtcrime.securesms.util.DateUtils;
public class AudioView extends FrameLayout {
private static final String TAG = AudioView.class.getSimpleName();
private static final String TAG = "AudioView";
private final @NonNull ImageView playPauseButton;
private final AnimatedVectorDrawableCompat playToPauseDrawable;
@@ -109,12 +109,12 @@ public class AudioView extends FrameLayout {
AudioPlaybackState state = viewModel.getPlaybackState().getValue();
if (state != null && msgId == state.getMsgId() && audioUri.equals(state.getAudioUri())) {
if (state != null && msgId == state.getMsgId()) {
// Same audio
if (state.getStatus() == AudioPlaybackState.PlaybackStatus.PLAYING) {
viewModel.pause(msgId, audioUri);
viewModel.pause(msgId);
} else {
viewModel.play(msgId, audioUri);
viewModel.play(msgId);
}
} else {
// Different audio
@@ -145,7 +145,7 @@ public class AudioView extends FrameLayout {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
viewModel.setUserSeeking(false);
viewModel.seekTo(seekBar.getProgress(), msgId, audioUri);
viewModel.seekTo(seekBar.getProgress(), msgId);
}
});
@@ -305,7 +305,7 @@ public class AudioView extends FrameLayout {
if (audioUri == null || state == null) return;
// Check if this state is about this message
boolean isThisMessage = msgId == state.getMsgId() && audioUri.equals(state.getAudioUri());
boolean isThisMessage = msgId == state.getMsgId();
if (isThisMessage) {
updateUIForPlaybackState(state);
@@ -340,13 +340,9 @@ public class AudioView extends FrameLayout {
private void onDurationsChanged(Map<Integer, Long> durations) {
AudioPlaybackState state = viewModel.getPlaybackState().getValue();
// When there is no playback happening, msgId can be -1 and audioUri is null
if (state != null
&& msgId >= 0
&& msgId == state.getMsgId()
&& audioUri != null
&& audioUri.equals(state.getAudioUri())) {
return; // Is playing this message
// When there is no playback happening, msgId can be -1
if (state != null && msgId >= 0 && msgId == state.getMsgId()) {
return;
}
Long duration = durations.get(msgId);
@@ -0,0 +1,41 @@
package org.thoughtcrime.securesms.components.audioplay;
import android.content.Context;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.media3.common.MediaItem;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcMsg;
import java.util.ArrayList;
import java.util.List;
import org.thoughtcrime.securesms.connect.DcHelper;
public class ChatAudioQueueProvider {
private final Context context;
private final int chatId;
private final int accountId;
public ChatAudioQueueProvider(@NonNull Context context, int chatId, int accountId) {
this.context = context.getApplicationContext();
this.chatId = chatId;
this.accountId = accountId;
}
@NonNull
public List<MediaItem> buildAudioQueue() {
DcContext dcContext = DcHelper.getContext(context);
int[] msgIds = dcContext.getChatMedia(chatId, DcMsg.DC_MSG_AUDIO, DcMsg.DC_MSG_VOICE, 0);
List<MediaItem> items = new ArrayList<>(msgIds.length);
for (int msgId : msgIds) {
String id = String.valueOf(msgId);
items.add(
new MediaItem.Builder()
.setMediaId(id)
.setUri(Uri.parse("dcmsg://" + accountId + "/" + id))
.build());
}
return items;
}
}
@@ -22,7 +22,7 @@ import org.thoughtcrime.securesms.util.ResUtil;
public class MediaKeyboard extends LinearLayout
implements InputView, Consumer<EmojiViewItem>, StickerPickerView.StickerPickerListener {
private static final String TAG = MediaKeyboard.class.getSimpleName();
private static final String TAG = "MediaKeyboard";
@Nullable private MediaKeyboardListener keyboardListener;
private EmojiPickerView emojiPicker;
@@ -71,7 +71,7 @@ public class StickerPickerView extends RecyclerView {
}
// Sort stickers just to provide consistent order
Collections.sort(stickerFiles, (f1, f2) -> f1.getName().compareToIgnoreCase(f2.getName()));
Collections.sort(stickerFiles, (f1, f2) -> f2.getName().compareToIgnoreCase(f1.getName()));
return stickerFiles;
}
@@ -23,7 +23,7 @@ import org.thoughtcrime.securesms.util.Prefs;
@SuppressLint("BatteryLife")
public class DozeReminder {
private static final String TAG = DozeReminder.class.getSimpleName();
private static final String TAG = "DozeReminder";
public static boolean isEligible(Context context) {
if (context == null) {
@@ -1,22 +0,0 @@
package org.thoughtcrime.securesms.components.viewpager;
import androidx.viewpager.widget.ViewPager;
public abstract class ExtendedOnPageChangedListener implements ViewPager.OnPageChangeListener {
private Integer currentPage = null;
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
@Override
public void onPageSelected(int position) {
if (currentPage != null && currentPage != position) onPageUnselected(currentPage);
currentPage = position;
}
public abstract void onPageUnselected(int position);
@Override
public void onPageScrollStateChanged(int state) {}
}
@@ -1,41 +0,0 @@
package org.thoughtcrime.securesms.components.viewpager;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import androidx.viewpager.widget.ViewPager;
/**
* Hacky fix for http://code.google.com/p/android/issues/detail?id=18990
*
* <p>ScaleGestureDetector seems to mess up the touch events, which means that ViewGroups which make
* use of onInterceptTouchEvent throw a lot of IllegalArgumentException: pointerIndex out of range.
*
* <p>There's not much I can do in my code for now, but we can mask the result by just catching the
* problem and ignoring it.
*
* @author Chris Banes
*/
public class HackyViewPager extends ViewPager {
private static final String TAG = HackyViewPager.class.getSimpleName();
public HackyViewPager(Context context) {
super(context);
}
public HackyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
try {
return super.onInterceptTouchEvent(ev);
} catch (IllegalArgumentException e) {
Log.w(TAG, e);
return false;
}
}
}
@@ -18,7 +18,7 @@ import org.thoughtcrime.securesms.accounts.AccountSelectionListFragment;
public class AccountManager {
private static final String TAG = AccountManager.class.getSimpleName();
private static final String TAG = "AccountManager";
private static final String LAST_ACCOUNT_ID = "last_account_id";
private static AccountManager self;
@@ -38,7 +38,7 @@ public class AccountManager {
return self;
}
public void migrateToDcAccounts(ApplicationContext context) {
public void migrateToDcAccounts(Context context, DcAccounts dcAccounts) {
try {
int selectAccountId = 0;
@@ -49,8 +49,7 @@ public class AccountManager {
if (!file.isDirectory()
&& file.getName().startsWith("messenger")
&& file.getName().endsWith(".db")) {
int accountId =
ApplicationContext.getDcAccounts().migrateAccount(file.getAbsolutePath());
int accountId = dcAccounts.migrateAccount(file.getAbsolutePath());
if (accountId != 0) {
String selName =
PreferenceManager.getDefaultSharedPreferences(context)
@@ -67,7 +66,7 @@ public class AccountManager {
}
if (selectAccountId != 0) {
ApplicationContext.getDcAccounts().selectAccount(selectAccountId);
dcAccounts.selectAccount(selectAccountId);
}
} catch (Exception e) {
Log.e(TAG, "Error in migrateToDcAccounts()", e);

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