mirror of
https://github.com/nextcloud/talk-android
synced 2025-07-07 12:59:55 +01:00
Compare commits
No commits in common. "master" and "alpha-220000003" have entirely different histories.
master
...
alpha-2200
.devcontainer
.drone.yml.github/workflows
analysis.ymlassembleFlavors.ymlcodeql.ymlpr-feedback.ymlrenovate-approve-merge.ymlreuse.ymlscorecard.ymlunit-tests.yml
CHANGELOG.mdapp
build.gradle
schemas/com.nextcloud.talk.data.source.local.TalkDatabase
src/main
java/com/nextcloud/talk
account
AccountVerificationActivity.ktServerSelectionActivity.ktSwitchAccountActivity.ktWebViewLoginActivity.kt
activities
adapters
items
messages
api
bottomsheet/items
call/components
chat
components
contacts
conversationcreation
conversationinfo
conversationinfoedit
conversationlist
data
database
dao
mappers
model
source/local
diagnose
fullscreenfile
invitation
location
messagesearch
models
openconversations
profile
receivers
repositories/conversations
settings
shareditems/activities
translate/ui
ui/dialog
utils
res
drawable-night
drawable
@ -1,4 +1,4 @@
|
||||
FROM ubuntu:noble@sha256:440dcf6a5640b2ae5c77724e68787a906afb8ddee98bf86db94eea8528c2c076
|
||||
FROM ubuntu:noble@sha256:6015f66923d7afbc53558d7ccffd325d43b4e249f41a6e93eef074c9505d2233
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ENV ANDROID_HOME=/usr/lib/android-sdk
|
||||
|
10
.drone.yml
10
.drone.yml
@ -8,7 +8,7 @@ name: generic
|
||||
|
||||
steps:
|
||||
- name: generic
|
||||
image: ghcr.io/nextcloud/continuous-integration-android8:4
|
||||
image: ghcr.io/nextcloud/continuous-integration-android8:3
|
||||
commands:
|
||||
- ./gradlew --console=plain assembleGeneric
|
||||
|
||||
@ -27,7 +27,7 @@ name: gplay
|
||||
|
||||
steps:
|
||||
- name: gplay
|
||||
image: ghcr.io/nextcloud/continuous-integration-android8:4
|
||||
image: ghcr.io/nextcloud/continuous-integration-android8:3
|
||||
commands:
|
||||
- ./gradlew --console=plain assembleGplay
|
||||
|
||||
@ -46,7 +46,7 @@ name: tests
|
||||
|
||||
steps:
|
||||
- name: all
|
||||
image: ghcr.io/nextcloud/continuous-integration-android8:4
|
||||
image: ghcr.io/nextcloud/continuous-integration-android8:3
|
||||
privileged: true
|
||||
commands:
|
||||
- emulator -avd android -no-snapshot -gpu swiftshader_indirect -no-window -no-audio -skin 500x833 &
|
||||
@ -81,6 +81,4 @@ trigger:
|
||||
- pull_request
|
||||
---
|
||||
kind: signature
|
||||
hmac: cf0c19e54fa45d1ee226f5f05202a32329b90aaf46711ea073c566a4c4a8a6c5
|
||||
|
||||
...
|
||||
hmac: cdce3f7eea46ef85c0223f62f66d1fe53d7dad007ef095c9f70fa063450d8c75
|
||||
|
2
.github/workflows/analysis.yml
vendored
2
.github/workflows/analysis.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Disabled on forks
|
||||
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository }}
|
||||
if: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
|
||||
run: |
|
||||
echo 'Can not analyze PRs from forks'
|
||||
exit 1
|
||||
|
2
.github/workflows/assembleFlavors.yml
vendored
2
.github/workflows/assembleFlavors.yml
vendored
@ -34,7 +34,7 @@ jobs:
|
||||
java-version: 17
|
||||
|
||||
- name: Gradle validate
|
||||
uses: gradle/actions/wrapper-validation@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
|
||||
uses: gradle/actions/wrapper-validation@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
|
||||
|
||||
- name: Build ${{ matrix.flavor }}
|
||||
run: |
|
||||
|
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@ -43,7 +43,7 @@ jobs:
|
||||
with:
|
||||
swap-size-gb: 10
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Set up JDK 17
|
||||
@ -57,4 +57,4 @@ jobs:
|
||||
echo "org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError" > "$HOME/.gradle/gradle.properties"
|
||||
./gradlew assembleDebug
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||
|
2
.github/workflows/pr-feedback.yml
vendored
2
.github/workflows/pr-feedback.yml
vendored
@ -36,7 +36,7 @@ jobs:
|
||||
blocklist=$(curl https://raw.githubusercontent.com/nextcloud/.github/master/non-community-usernames.txt | paste -s -d, -)
|
||||
echo "blocklist=$blocklist" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- uses: nextcloud/pr-feedback-action@e397f3c7e655092b746e3610d121545530c6a90e # main
|
||||
- uses: nextcloud/pr-feedback-action@1883b38a033fb16f576875e0cf45f98b857655c4 # main
|
||||
with:
|
||||
feedback-message: |
|
||||
Hello there,
|
||||
|
19
.github/workflows/renovate-approve-merge.yml
vendored
19
.github/workflows/renovate-approve-merge.yml
vendored
@ -29,6 +29,8 @@ jobs:
|
||||
permissions:
|
||||
# for hmarr/auto-approve-action to approve PRs
|
||||
pull-requests: write
|
||||
# for alexwilson/enable-github-automerge-action to approve PRs
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Disabled on forks
|
||||
@ -44,18 +46,13 @@ jobs:
|
||||
|
||||
# GitHub actions bot approve
|
||||
- uses: hmarr/auto-approve-action@f0939ea97e9205ef24d872e76833fa908a770363 # v4.0.0
|
||||
if: github.actor == 'renovate[bot]'
|
||||
if: startsWith(steps.branchname.outputs.branch, 'renovate/')
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
|
||||
# Enable GitHub auto merge
|
||||
- name: Enable Pull Request Automerge
|
||||
if: github.actor == 'renovate[bot]'
|
||||
run: gh pr merge --merge --auto
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.AUTOMERGE }}
|
||||
|
||||
- name: Auto merge
|
||||
uses: alexwilson/enable-github-automerge-action@56e3117d1ae1540309dc8f7a9f2825bc3c5f06ff # v2.0.0
|
||||
if: startsWith(steps.branchname.outputs.branch, 'renovate/')
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
4
.github/workflows/reuse.yml
vendored
4
.github/workflows/reuse.yml
vendored
@ -12,11 +12,11 @@ name: REUSE Compliance Check
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
reuse-compliance-check:
|
||||
runs-on: ubuntu-latest-low
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
4
.github/workflows/scorecard.yml
vendored
4
.github/workflows/scorecard.yml
vendored
@ -34,7 +34,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
|
||||
uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
@ -42,6 +42,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
|
||||
uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
2
.github/workflows/unit-tests.yml
vendored
2
.github/workflows/unit-tests.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
||||
java-version: 17
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1
|
||||
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
|
||||
|
||||
- name: Run unit tests with coverage
|
||||
run: ./gradlew testGplayDebugUnit
|
||||
|
30
CHANGELOG.md
30
CHANGELOG.md
@ -9,36 +9,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
Types of changes can be: Added/Changed/Deprecated/Removed/Fixed/Security
|
||||
|
||||
## [21.1.0] - 2025-06-05
|
||||
|
||||
### Added
|
||||
- Allow adding participants to one-to-one chats creating a new conversation
|
||||
- Handling of event conversations
|
||||
- Show info about participant (organization, role, timezone, ...) in 1:1 conversation info screen
|
||||
- Added gallery option in chat attachment menu (access photos and videos with one click without giving permissions)
|
||||
- Add self-test button for push notifications in diagnosis screen
|
||||
- Edit checkbox in chat messages
|
||||
- Team mentions in chat
|
||||
- Add option to mark a conversation as sensitive
|
||||
- Allow bluetooth headset to be discovered and used during a call (@gavine99)
|
||||
|
||||
### Changed
|
||||
- Design of participants grid in call
|
||||
- Improve subline in conversations screen when last message is attachment
|
||||
- Improve message search
|
||||
- In search window, show messages at last
|
||||
- Switch video capture for calls between 4:3 and 16:9 ratio depending on portrait/ landscape mode
|
||||
|
||||
### Fixed
|
||||
- Crashes
|
||||
- Videos in videocall lost after app comes back to foreground
|
||||
- Open conversations not being shown in search
|
||||
- Minor bugs (@MmAaXx500)
|
||||
|
||||
Minimum: NC 17 Server, Android 8.0 Oreo
|
||||
|
||||
For a full list, please see https://github.com/nextcloud/talk-android/milestone/94?closed=1
|
||||
|
||||
## [21.0.1] - 2025-04-15
|
||||
|
||||
### Fixed
|
||||
|
@ -13,9 +13,9 @@ import com.github.spotbugs.snom.Effort
|
||||
import com.github.spotbugs.snom.SpotBugsTask
|
||||
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.plugin.compose" version "2.2.0"
|
||||
id "org.jetbrains.kotlin.plugin.compose" version "2.1.21"
|
||||
id "org.jetbrains.kotlin.kapt"
|
||||
id 'com.google.devtools.ksp' version '2.2.0-2.0.2'
|
||||
id 'com.google.devtools.ksp' version '2.1.21-2.0.1'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
@ -28,19 +28,19 @@ apply plugin: "org.jlleitschuh.gradle.ktlint"
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
android {
|
||||
compileSdk 35
|
||||
compileSdk 34
|
||||
|
||||
namespace 'com.nextcloud.talk'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 26
|
||||
targetSdkVersion 35
|
||||
targetSdkVersion 34
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
// mayor.minor.hotfix.increment (for increment: 01-50=Alpha / 51-89=RC / 90-99=stable)
|
||||
// xx .xxx .xx .xx
|
||||
versionCode 220000009
|
||||
versionName "22.0.0 Alpha 09"
|
||||
versionCode 220000003
|
||||
versionName "22.0.0 Alpha 03"
|
||||
|
||||
flavorDimensions "default"
|
||||
renderscriptTargetApi 19
|
||||
@ -155,18 +155,18 @@ ext {
|
||||
daggerVersion = "2.56.2"
|
||||
emojiVersion = "1.5.0"
|
||||
fidoVersion = "4.1.0-patch2"
|
||||
lifecycleVersion = '2.9.1'
|
||||
lifecycleVersion = '2.8.7'
|
||||
okhttpVersion = "4.12.0"
|
||||
markwonVersion = "4.6.2"
|
||||
materialDialogsVersion = "3.3.0"
|
||||
parcelerVersion = "1.1.13"
|
||||
prismVersion = "2.0.0"
|
||||
retrofit2Version = "3.0.0"
|
||||
roomVersion = "2.7.2"
|
||||
workVersion = "2.10.2"
|
||||
retrofit2Version = "2.11.0"
|
||||
roomVersion = "2.7.1"
|
||||
workVersion = "2.9.1"
|
||||
espressoVersion = "3.6.1"
|
||||
androidxTestVersion = "1.5.0"
|
||||
media3_version = "1.7.1"
|
||||
media3_version = "1.4.1"
|
||||
coroutines_version = "1.10.2"
|
||||
mockitoKotlinVersion = "5.4.0"
|
||||
}
|
||||
@ -180,10 +180,10 @@ configurations.configureEach {
|
||||
|
||||
dependencies {
|
||||
spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.14.0'
|
||||
spotbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.6.11'
|
||||
spotbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.6.9'
|
||||
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.8")
|
||||
|
||||
implementation("androidx.compose.runtime:runtime:1.8.3")
|
||||
implementation("androidx.compose.runtime:runtime:1.7.8")
|
||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||
implementation 'androidx.datastore:datastore-core:1.1.7'
|
||||
implementation 'androidx.datastore:datastore-preferences:1.1.7'
|
||||
@ -191,9 +191,9 @@ dependencies {
|
||||
|
||||
implementation fileTree(include: ['*'], dir: 'libs')
|
||||
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1"
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.7.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'com.google.android.material:material:1.12.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
|
||||
implementation "com.vanniktech:emoji-google:0.21.0"
|
||||
@ -236,7 +236,7 @@ dependencies {
|
||||
implementation "com.squareup.okhttp3:logging-interceptor:${okhttpVersion}"
|
||||
|
||||
implementation 'com.bluelinelabs:logansquare:1.3.7'
|
||||
implementation 'com.fasterxml.jackson.core:jackson-core:2.19.1'
|
||||
implementation 'com.fasterxml.jackson.core:jackson-core:2.14.3'
|
||||
kapt 'com.bluelinelabs:logansquare-compiler:1.3.7'
|
||||
|
||||
implementation "com.squareup.retrofit2:retrofit:${retrofit2Version}"
|
||||
@ -301,24 +301,24 @@ dependencies {
|
||||
exclude group: 'org.apache.httpcomponents', module: 'httpclient'
|
||||
})
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.16.0'
|
||||
implementation 'androidx.activity:activity-ktx:1.10.1'
|
||||
implementation 'com.github.nextcloud.android-common:ui:0.26.0'
|
||||
implementation 'androidx.core:core-ktx:1.13.1'
|
||||
implementation 'androidx.activity:activity-ktx:1.9.3'
|
||||
implementation 'com.github.nextcloud.android-common:ui:0.23.2'
|
||||
implementation 'com.github.nextcloud-deps:android-talk-webrtc:132.6834.0'
|
||||
|
||||
gplayImplementation 'com.google.android.gms:play-services-base:18.6.0'
|
||||
gplayImplementation "com.google.firebase:firebase-messaging:24.1.2"
|
||||
gplayImplementation "com.google.firebase:firebase-messaging:24.1.1"
|
||||
|
||||
//compose
|
||||
implementation(platform("androidx.compose:compose-bom:2025.06.01"))
|
||||
implementation(platform("androidx.compose:compose-bom:2025.04.00"))
|
||||
implementation("androidx.compose.ui:ui")
|
||||
implementation 'androidx.compose.material3:material3:1.3.2'
|
||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||
implementation 'androidx.activity:activity-compose:1.10.1'
|
||||
implementation 'androidx.activity:activity-compose:1.9.3'
|
||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||
|
||||
//tests
|
||||
androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.8.3")
|
||||
androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.7.8")
|
||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
@ -343,11 +343,11 @@ dependencies {
|
||||
|
||||
androidTestImplementation('com.android.support.test.espresso:espresso-intents:3.0.2')
|
||||
|
||||
androidTestImplementation(platform("androidx.compose:compose-bom:2025.06.01"))
|
||||
androidTestImplementation(platform("androidx.compose:compose-bom:2025.04.00"))
|
||||
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion"
|
||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
|
||||
|
||||
testImplementation 'org.junit.vintage:junit-vintage-engine:5.13.3'
|
||||
testImplementation 'org.junit.vintage:junit-vintage-engine:5.12.2'
|
||||
}
|
||||
|
||||
tasks.register('installGitHooks', Copy) {
|
||||
|
@ -1,731 +0,0 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 16,
|
||||
"identityHash": "bbf526d5c78a99eb951635cc46f4c59f",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "User",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` TEXT, `username` TEXT, `baseUrl` TEXT, `token` TEXT, `displayName` TEXT, `pushConfigurationState` TEXT, `capabilities` TEXT, `serverVersion` TEXT DEFAULT '', `clientCertificate` TEXT, `externalSignalingServer` TEXT, `current` INTEGER NOT NULL, `scheduledForDeletion` INTEGER NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userId",
|
||||
"columnName": "userId",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "username",
|
||||
"columnName": "username",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "baseUrl",
|
||||
"columnName": "baseUrl",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "token",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushConfigurationState",
|
||||
"columnName": "pushConfigurationState",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "capabilities",
|
||||
"columnName": "capabilities",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "serverVersion",
|
||||
"columnName": "serverVersion",
|
||||
"affinity": "TEXT",
|
||||
"defaultValue": "''"
|
||||
},
|
||||
{
|
||||
"fieldPath": "clientCertificate",
|
||||
"columnName": "clientCertificate",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "externalSignalingServer",
|
||||
"columnName": "externalSignalingServer",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "current",
|
||||
"columnName": "current",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "scheduledForDeletion",
|
||||
"columnName": "scheduledForDeletion",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "ArbitraryStorage",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountIdentifier` INTEGER NOT NULL, `key` TEXT NOT NULL, `object` TEXT, `value` TEXT, PRIMARY KEY(`accountIdentifier`, `key`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "accountIdentifier",
|
||||
"columnName": "accountIdentifier",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "key",
|
||||
"columnName": "key",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "storageObject",
|
||||
"columnName": "object",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "value",
|
||||
"columnName": "value",
|
||||
"affinity": "TEXT"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"accountIdentifier",
|
||||
"key"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "Conversations",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`internalId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `token` TEXT NOT NULL, `displayName` TEXT NOT NULL, `actorId` TEXT NOT NULL, `actorType` TEXT NOT NULL, `avatarVersion` TEXT NOT NULL, `callFlag` INTEGER NOT NULL, `callRecording` INTEGER NOT NULL, `callStartTime` INTEGER NOT NULL, `canDeleteConversation` INTEGER NOT NULL, `canLeaveConversation` INTEGER NOT NULL, `canStartCall` INTEGER NOT NULL, `description` TEXT NOT NULL, `hasCall` INTEGER NOT NULL, `hasPassword` INTEGER NOT NULL, `isCustomAvatar` INTEGER NOT NULL, `isFavorite` INTEGER NOT NULL, `lastActivity` INTEGER NOT NULL, `lastCommonReadMessage` INTEGER NOT NULL, `lastMessage` TEXT, `lastPing` INTEGER NOT NULL, `lastReadMessage` INTEGER NOT NULL, `lobbyState` TEXT NOT NULL, `lobbyTimer` INTEGER NOT NULL, `messageExpiration` INTEGER NOT NULL, `name` TEXT NOT NULL, `notificationCalls` INTEGER NOT NULL, `notificationLevel` TEXT NOT NULL, `objectType` TEXT NOT NULL, `objectId` TEXT NOT NULL, `participantType` TEXT NOT NULL, `permissions` INTEGER NOT NULL, `readOnly` TEXT NOT NULL, `recordingConsent` INTEGER NOT NULL, `remoteServer` TEXT, `remoteToken` TEXT, `sessionId` TEXT NOT NULL, `status` TEXT, `statusClearAt` INTEGER, `statusIcon` TEXT, `statusMessage` TEXT, `type` TEXT NOT NULL, `unreadMention` INTEGER NOT NULL, `unreadMentionDirect` INTEGER NOT NULL, `unreadMessages` INTEGER NOT NULL, `hasArchived` INTEGER NOT NULL, `hasSensitive` INTEGER NOT NULL, `hasImportant` INTEGER NOT NULL, PRIMARY KEY(`internalId`), FOREIGN KEY(`accountId`) REFERENCES `User`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "internalId",
|
||||
"columnName": "internalId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountId",
|
||||
"columnName": "accountId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "token",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "actorId",
|
||||
"columnName": "actorId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "actorType",
|
||||
"columnName": "actorType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "avatarVersion",
|
||||
"columnName": "avatarVersion",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "callFlag",
|
||||
"columnName": "callFlag",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "callRecording",
|
||||
"columnName": "callRecording",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "callStartTime",
|
||||
"columnName": "callStartTime",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "canDeleteConversation",
|
||||
"columnName": "canDeleteConversation",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "canLeaveConversation",
|
||||
"columnName": "canLeaveConversation",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "canStartCall",
|
||||
"columnName": "canStartCall",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "description",
|
||||
"columnName": "description",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "hasCall",
|
||||
"columnName": "hasCall",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "hasPassword",
|
||||
"columnName": "hasPassword",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "hasCustomAvatar",
|
||||
"columnName": "isCustomAvatar",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "favorite",
|
||||
"columnName": "isFavorite",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastActivity",
|
||||
"columnName": "lastActivity",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastCommonReadMessage",
|
||||
"columnName": "lastCommonReadMessage",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastMessage",
|
||||
"columnName": "lastMessage",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastPing",
|
||||
"columnName": "lastPing",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastReadMessage",
|
||||
"columnName": "lastReadMessage",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lobbyState",
|
||||
"columnName": "lobbyState",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lobbyTimer",
|
||||
"columnName": "lobbyTimer",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "messageExpiration",
|
||||
"columnName": "messageExpiration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationCalls",
|
||||
"columnName": "notificationCalls",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationLevel",
|
||||
"columnName": "notificationLevel",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "objectType",
|
||||
"columnName": "objectType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "objectId",
|
||||
"columnName": "objectId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "participantType",
|
||||
"columnName": "participantType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "permissions",
|
||||
"columnName": "permissions",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "conversationReadOnlyState",
|
||||
"columnName": "readOnly",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "recordingConsentRequired",
|
||||
"columnName": "recordingConsent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "remoteServer",
|
||||
"columnName": "remoteServer",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "remoteToken",
|
||||
"columnName": "remoteToken",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "sessionId",
|
||||
"columnName": "sessionId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "status",
|
||||
"columnName": "status",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "statusClearAt",
|
||||
"columnName": "statusClearAt",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "statusIcon",
|
||||
"columnName": "statusIcon",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "statusMessage",
|
||||
"columnName": "statusMessage",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unreadMention",
|
||||
"columnName": "unreadMention",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unreadMentionDirect",
|
||||
"columnName": "unreadMentionDirect",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unreadMessages",
|
||||
"columnName": "unreadMessages",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "hasArchived",
|
||||
"columnName": "hasArchived",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "hasSensitive",
|
||||
"columnName": "hasSensitive",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "hasImportant",
|
||||
"columnName": "hasImportant",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"internalId"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_Conversations_accountId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"accountId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_Conversations_accountId` ON `${TABLE_NAME}` (`accountId`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "User",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "CASCADE",
|
||||
"columns": [
|
||||
"accountId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "ChatMessages",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`internalId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `token` TEXT NOT NULL, `id` INTEGER NOT NULL, `internalConversationId` TEXT NOT NULL, `actorDisplayName` TEXT NOT NULL, `message` TEXT NOT NULL, `actorId` TEXT NOT NULL, `actorType` TEXT NOT NULL, `deleted` INTEGER NOT NULL, `expirationTimestamp` INTEGER NOT NULL, `isReplyable` INTEGER NOT NULL, `isTemporary` INTEGER NOT NULL, `lastEditActorDisplayName` TEXT, `lastEditActorId` TEXT, `lastEditActorType` TEXT, `lastEditTimestamp` INTEGER, `markdown` INTEGER, `messageParameters` TEXT, `messageType` TEXT NOT NULL, `parent` INTEGER, `reactions` TEXT, `reactionsSelf` TEXT, `referenceId` TEXT, `sendingFailed` INTEGER NOT NULL, `silent` INTEGER NOT NULL, `systemMessage` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, PRIMARY KEY(`internalId`), FOREIGN KEY(`internalConversationId`) REFERENCES `Conversations`(`internalId`) ON UPDATE CASCADE ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "internalId",
|
||||
"columnName": "internalId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountId",
|
||||
"columnName": "accountId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "token",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "internalConversationId",
|
||||
"columnName": "internalConversationId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "actorDisplayName",
|
||||
"columnName": "actorDisplayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "message",
|
||||
"columnName": "message",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "actorId",
|
||||
"columnName": "actorId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "actorType",
|
||||
"columnName": "actorType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "deleted",
|
||||
"columnName": "deleted",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "expirationTimestamp",
|
||||
"columnName": "expirationTimestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "replyable",
|
||||
"columnName": "isReplyable",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTemporary",
|
||||
"columnName": "isTemporary",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastEditActorDisplayName",
|
||||
"columnName": "lastEditActorDisplayName",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastEditActorId",
|
||||
"columnName": "lastEditActorId",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastEditActorType",
|
||||
"columnName": "lastEditActorType",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastEditTimestamp",
|
||||
"columnName": "lastEditTimestamp",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "renderMarkdown",
|
||||
"columnName": "markdown",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "messageParameters",
|
||||
"columnName": "messageParameters",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "messageType",
|
||||
"columnName": "messageType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentMessageId",
|
||||
"columnName": "parent",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "reactions",
|
||||
"columnName": "reactions",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "reactionsSelf",
|
||||
"columnName": "reactionsSelf",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "referenceId",
|
||||
"columnName": "referenceId",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "sendingFailed",
|
||||
"columnName": "sendingFailed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "silent",
|
||||
"columnName": "silent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "systemMessageType",
|
||||
"columnName": "systemMessage",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"internalId"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_ChatMessages_internalId",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"internalId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ChatMessages_internalId` ON `${TABLE_NAME}` (`internalId`)"
|
||||
},
|
||||
{
|
||||
"name": "index_ChatMessages_internalConversationId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"internalConversationId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_ChatMessages_internalConversationId` ON `${TABLE_NAME}` (`internalConversationId`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "Conversations",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "CASCADE",
|
||||
"columns": [
|
||||
"internalConversationId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"internalId"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "ChatBlocks",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `internalConversationId` TEXT NOT NULL, `accountId` INTEGER, `token` TEXT, `oldestMessageId` INTEGER NOT NULL, `newestMessageId` INTEGER NOT NULL, `hasHistory` INTEGER NOT NULL, FOREIGN KEY(`internalConversationId`) REFERENCES `Conversations`(`internalId`) ON UPDATE CASCADE ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "internalConversationId",
|
||||
"columnName": "internalConversationId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountId",
|
||||
"columnName": "accountId",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "token",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "oldestMessageId",
|
||||
"columnName": "oldestMessageId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "newestMessageId",
|
||||
"columnName": "newestMessageId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "hasHistory",
|
||||
"columnName": "hasHistory",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_ChatBlocks_internalConversationId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"internalConversationId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_ChatBlocks_internalConversationId` ON `${TABLE_NAME}` (`internalConversationId`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "Conversations",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "CASCADE",
|
||||
"columns": [
|
||||
"internalConversationId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"internalId"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'bbf526d5c78a99eb951635cc46f4c59f')"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,730 +0,0 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 17,
|
||||
"identityHash": "5bc4247e179307faa995552da5d34324",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "User",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` TEXT, `username` TEXT, `baseUrl` TEXT, `token` TEXT, `displayName` TEXT, `pushConfigurationState` TEXT, `capabilities` TEXT, `serverVersion` TEXT DEFAULT '', `clientCertificate` TEXT, `externalSignalingServer` TEXT, `current` INTEGER NOT NULL, `scheduledForDeletion` INTEGER NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "userId",
|
||||
"columnName": "userId",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "username",
|
||||
"columnName": "username",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "baseUrl",
|
||||
"columnName": "baseUrl",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "token",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushConfigurationState",
|
||||
"columnName": "pushConfigurationState",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "capabilities",
|
||||
"columnName": "capabilities",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "serverVersion",
|
||||
"columnName": "serverVersion",
|
||||
"affinity": "TEXT",
|
||||
"defaultValue": "''"
|
||||
},
|
||||
{
|
||||
"fieldPath": "clientCertificate",
|
||||
"columnName": "clientCertificate",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "externalSignalingServer",
|
||||
"columnName": "externalSignalingServer",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "current",
|
||||
"columnName": "current",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "scheduledForDeletion",
|
||||
"columnName": "scheduledForDeletion",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "ArbitraryStorage",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountIdentifier` INTEGER NOT NULL, `key` TEXT NOT NULL, `object` TEXT, `value` TEXT, PRIMARY KEY(`accountIdentifier`, `key`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "accountIdentifier",
|
||||
"columnName": "accountIdentifier",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "key",
|
||||
"columnName": "key",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "storageObject",
|
||||
"columnName": "object",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "value",
|
||||
"columnName": "value",
|
||||
"affinity": "TEXT"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"accountIdentifier",
|
||||
"key"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "Conversations",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`internalId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `token` TEXT NOT NULL, `displayName` TEXT NOT NULL, `actorId` TEXT NOT NULL, `actorType` TEXT NOT NULL, `avatarVersion` TEXT NOT NULL, `callFlag` INTEGER NOT NULL, `callRecording` INTEGER NOT NULL, `callStartTime` INTEGER NOT NULL, `canDeleteConversation` INTEGER NOT NULL, `canLeaveConversation` INTEGER NOT NULL, `canStartCall` INTEGER NOT NULL, `description` TEXT NOT NULL, `hasCall` INTEGER NOT NULL, `hasPassword` INTEGER NOT NULL, `isCustomAvatar` INTEGER NOT NULL, `isFavorite` INTEGER NOT NULL, `lastActivity` INTEGER NOT NULL, `lastCommonReadMessage` INTEGER NOT NULL, `lastMessage` TEXT, `lastPing` INTEGER NOT NULL, `lastReadMessage` INTEGER NOT NULL, `lobbyState` TEXT NOT NULL, `lobbyTimer` INTEGER NOT NULL, `messageExpiration` INTEGER NOT NULL, `name` TEXT NOT NULL, `notificationCalls` INTEGER NOT NULL, `notificationLevel` TEXT NOT NULL, `objectType` TEXT NOT NULL, `objectId` TEXT NOT NULL, `participantType` TEXT NOT NULL, `permissions` INTEGER NOT NULL, `readOnly` TEXT NOT NULL, `recordingConsent` INTEGER NOT NULL, `remoteServer` TEXT, `remoteToken` TEXT, `sessionId` TEXT NOT NULL, `status` TEXT, `statusClearAt` INTEGER, `statusIcon` TEXT, `statusMessage` TEXT, `type` TEXT NOT NULL, `unreadMention` INTEGER NOT NULL, `unreadMentionDirect` INTEGER NOT NULL, `unreadMessages` INTEGER NOT NULL, `hasArchived` INTEGER NOT NULL, `hasSensitive` INTEGER NOT NULL, `hasImportant` INTEGER NOT NULL, PRIMARY KEY(`internalId`), FOREIGN KEY(`accountId`) REFERENCES `User`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "internalId",
|
||||
"columnName": "internalId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountId",
|
||||
"columnName": "accountId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "token",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "actorId",
|
||||
"columnName": "actorId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "actorType",
|
||||
"columnName": "actorType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "avatarVersion",
|
||||
"columnName": "avatarVersion",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "callFlag",
|
||||
"columnName": "callFlag",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "callRecording",
|
||||
"columnName": "callRecording",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "callStartTime",
|
||||
"columnName": "callStartTime",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "canDeleteConversation",
|
||||
"columnName": "canDeleteConversation",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "canLeaveConversation",
|
||||
"columnName": "canLeaveConversation",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "canStartCall",
|
||||
"columnName": "canStartCall",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "description",
|
||||
"columnName": "description",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "hasCall",
|
||||
"columnName": "hasCall",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "hasPassword",
|
||||
"columnName": "hasPassword",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "hasCustomAvatar",
|
||||
"columnName": "isCustomAvatar",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "favorite",
|
||||
"columnName": "isFavorite",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastActivity",
|
||||
"columnName": "lastActivity",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastCommonReadMessage",
|
||||
"columnName": "lastCommonReadMessage",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastMessage",
|
||||
"columnName": "lastMessage",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastPing",
|
||||
"columnName": "lastPing",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastReadMessage",
|
||||
"columnName": "lastReadMessage",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lobbyState",
|
||||
"columnName": "lobbyState",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lobbyTimer",
|
||||
"columnName": "lobbyTimer",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "messageExpiration",
|
||||
"columnName": "messageExpiration",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationCalls",
|
||||
"columnName": "notificationCalls",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationLevel",
|
||||
"columnName": "notificationLevel",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "objectType",
|
||||
"columnName": "objectType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "objectId",
|
||||
"columnName": "objectId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "participantType",
|
||||
"columnName": "participantType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "permissions",
|
||||
"columnName": "permissions",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "conversationReadOnlyState",
|
||||
"columnName": "readOnly",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "recordingConsentRequired",
|
||||
"columnName": "recordingConsent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "remoteServer",
|
||||
"columnName": "remoteServer",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "remoteToken",
|
||||
"columnName": "remoteToken",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "sessionId",
|
||||
"columnName": "sessionId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "status",
|
||||
"columnName": "status",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "statusClearAt",
|
||||
"columnName": "statusClearAt",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "statusIcon",
|
||||
"columnName": "statusIcon",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "statusMessage",
|
||||
"columnName": "statusMessage",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unreadMention",
|
||||
"columnName": "unreadMention",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unreadMentionDirect",
|
||||
"columnName": "unreadMentionDirect",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unreadMessages",
|
||||
"columnName": "unreadMessages",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "hasArchived",
|
||||
"columnName": "hasArchived",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "hasSensitive",
|
||||
"columnName": "hasSensitive",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "hasImportant",
|
||||
"columnName": "hasImportant",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"internalId"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_Conversations_accountId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"accountId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_Conversations_accountId` ON `${TABLE_NAME}` (`accountId`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "User",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "CASCADE",
|
||||
"columns": [
|
||||
"accountId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "ChatMessages",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`internalId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `token` TEXT NOT NULL, `id` INTEGER NOT NULL, `internalConversationId` TEXT NOT NULL, `actorDisplayName` TEXT NOT NULL, `message` TEXT NOT NULL, `actorId` TEXT NOT NULL, `actorType` TEXT NOT NULL, `deleted` INTEGER NOT NULL, `expirationTimestamp` INTEGER NOT NULL, `isReplyable` INTEGER NOT NULL, `isTemporary` INTEGER NOT NULL, `lastEditActorDisplayName` TEXT, `lastEditActorId` TEXT, `lastEditActorType` TEXT, `lastEditTimestamp` INTEGER, `markdown` INTEGER, `messageParameters` TEXT, `messageType` TEXT NOT NULL, `parent` INTEGER, `reactions` TEXT, `reactionsSelf` TEXT, `referenceId` TEXT, `sendStatus` TEXT, `silent` INTEGER NOT NULL, `systemMessage` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, PRIMARY KEY(`internalId`), FOREIGN KEY(`internalConversationId`) REFERENCES `Conversations`(`internalId`) ON UPDATE CASCADE ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "internalId",
|
||||
"columnName": "internalId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountId",
|
||||
"columnName": "accountId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "token",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "internalConversationId",
|
||||
"columnName": "internalConversationId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "actorDisplayName",
|
||||
"columnName": "actorDisplayName",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "message",
|
||||
"columnName": "message",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "actorId",
|
||||
"columnName": "actorId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "actorType",
|
||||
"columnName": "actorType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "deleted",
|
||||
"columnName": "deleted",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "expirationTimestamp",
|
||||
"columnName": "expirationTimestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "replyable",
|
||||
"columnName": "isReplyable",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isTemporary",
|
||||
"columnName": "isTemporary",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastEditActorDisplayName",
|
||||
"columnName": "lastEditActorDisplayName",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastEditActorId",
|
||||
"columnName": "lastEditActorId",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastEditActorType",
|
||||
"columnName": "lastEditActorType",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastEditTimestamp",
|
||||
"columnName": "lastEditTimestamp",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "renderMarkdown",
|
||||
"columnName": "markdown",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "messageParameters",
|
||||
"columnName": "messageParameters",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "messageType",
|
||||
"columnName": "messageType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentMessageId",
|
||||
"columnName": "parent",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "reactions",
|
||||
"columnName": "reactions",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "reactionsSelf",
|
||||
"columnName": "reactionsSelf",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "referenceId",
|
||||
"columnName": "referenceId",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "sendStatus",
|
||||
"columnName": "sendStatus",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "silent",
|
||||
"columnName": "silent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "systemMessageType",
|
||||
"columnName": "systemMessage",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"internalId"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_ChatMessages_internalId",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"internalId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ChatMessages_internalId` ON `${TABLE_NAME}` (`internalId`)"
|
||||
},
|
||||
{
|
||||
"name": "index_ChatMessages_internalConversationId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"internalConversationId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_ChatMessages_internalConversationId` ON `${TABLE_NAME}` (`internalConversationId`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "Conversations",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "CASCADE",
|
||||
"columns": [
|
||||
"internalConversationId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"internalId"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "ChatBlocks",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `internalConversationId` TEXT NOT NULL, `accountId` INTEGER, `token` TEXT, `oldestMessageId` INTEGER NOT NULL, `newestMessageId` INTEGER NOT NULL, `hasHistory` INTEGER NOT NULL, FOREIGN KEY(`internalConversationId`) REFERENCES `Conversations`(`internalId`) ON UPDATE CASCADE ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "internalConversationId",
|
||||
"columnName": "internalConversationId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountId",
|
||||
"columnName": "accountId",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "token",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "oldestMessageId",
|
||||
"columnName": "oldestMessageId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "newestMessageId",
|
||||
"columnName": "newestMessageId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "hasHistory",
|
||||
"columnName": "hasHistory",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_ChatBlocks_internalConversationId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"internalConversationId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_ChatBlocks_internalConversationId` ON `${TABLE_NAME}` (`internalConversationId`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "Conversations",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "CASCADE",
|
||||
"columns": [
|
||||
"internalConversationId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"internalId"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5bc4247e179307faa995552da5d34324')"
|
||||
]
|
||||
}
|
||||
}
|
@ -91,7 +91,7 @@ class AccountVerificationActivity : BaseActivity() {
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
setContentView(binding.root)
|
||||
actionBar?.hide()
|
||||
initSystemBars()
|
||||
setupSystemColors()
|
||||
|
||||
handleIntent()
|
||||
}
|
||||
@ -490,9 +490,9 @@ class AccountVerificationActivity : BaseActivity() {
|
||||
WorkManager.getInstance(applicationContext).enqueue(accountRemovalWork)
|
||||
|
||||
WorkManager.getInstance(context).getWorkInfoByIdLiveData(accountRemovalWork.id)
|
||||
.observeForever { workInfo: WorkInfo? ->
|
||||
.observeForever { workInfo: WorkInfo ->
|
||||
|
||||
when (workInfo?.state) {
|
||||
when (workInfo.state) {
|
||||
WorkInfo.State.SUCCEEDED -> {
|
||||
val intent = Intent(this, ServerSelectionActivity::class.java)
|
||||
startActivity(intent)
|
||||
|
@ -78,7 +78,7 @@ class ServerSelectionActivity : BaseActivity() {
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
setContentView(binding.root)
|
||||
actionBar?.hide()
|
||||
initSystemBars()
|
||||
setupSystemColors()
|
||||
|
||||
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ class SwitchAccountActivity : BaseActivity() {
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
setContentView(binding.root)
|
||||
setupActionBar()
|
||||
initSystemBars()
|
||||
setupSystemColors()
|
||||
|
||||
Configuration.getInstance().load(context, PreferenceManager.getDefaultSharedPreferences(context))
|
||||
|
||||
|
@ -114,7 +114,7 @@ class WebViewLoginActivity : BaseActivity() {
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
setContentView(binding.root)
|
||||
actionBar?.hide()
|
||||
initSystemBars()
|
||||
setupSystemColors()
|
||||
|
||||
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
|
||||
handleIntent()
|
||||
@ -286,7 +286,6 @@ class WebViewLoginActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("DiscouragedPrivateApi")
|
||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||
override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) {
|
||||
try {
|
||||
@ -389,9 +388,9 @@ class WebViewLoginActivity : BaseActivity() {
|
||||
WorkManager.getInstance(applicationContext).enqueue(accountRemovalWork)
|
||||
|
||||
WorkManager.getInstance(context).getWorkInfoByIdLiveData(accountRemovalWork.id)
|
||||
.observeForever { workInfo: WorkInfo? ->
|
||||
.observeForever { workInfo: WorkInfo ->
|
||||
|
||||
when (workInfo?.state) {
|
||||
when (workInfo.state) {
|
||||
WorkInfo.State.SUCCEEDED, WorkInfo.State.FAILED, WorkInfo.State.CANCELLED -> {
|
||||
restartApp()
|
||||
}
|
||||
|
@ -11,13 +11,11 @@ package com.nextcloud.talk.activities
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowInsets
|
||||
import android.view.WindowManager
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.webkit.SslErrorHandler
|
||||
@ -39,7 +37,6 @@ import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.FileViewerUtils
|
||||
import com.nextcloud.talk.utils.UriUtils
|
||||
import com.nextcloud.talk.utils.adjustUIForAPILevel35
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||
@ -84,7 +81,6 @@ open class BaseActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
||||
adjustUIForAPILevel35()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
cleanTempCertPreference()
|
||||
@ -115,22 +111,9 @@ open class BaseActivity : AppCompatActivity() {
|
||||
eventBus.unregister(this)
|
||||
}
|
||||
|
||||
/*
|
||||
* May be aligned with android-common lib in the future: .../ui/util/extensions/AppCompatActivityExtensions.kt
|
||||
*/
|
||||
fun initSystemBars() {
|
||||
window.decorView.setOnApplyWindowInsetsListener { view, insets ->
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
val statusBarHeight = insets.getInsets(WindowInsets.Type.statusBars()).top
|
||||
view.setPadding(0, statusBarHeight, 0, 0)
|
||||
val color = ResourcesCompat.getColor(resources, R.color.bg_default, context.theme)
|
||||
view.setBackgroundColor(color)
|
||||
} else {
|
||||
colorizeStatusBar()
|
||||
colorizeNavigationBar()
|
||||
}
|
||||
insets
|
||||
}
|
||||
fun setupSystemColors() {
|
||||
colorizeStatusBar()
|
||||
colorizeNavigationBar()
|
||||
}
|
||||
|
||||
open fun colorizeStatusBar() {
|
||||
|
@ -376,8 +376,6 @@ class CallActivity : CallBaseActivity() {
|
||||
Log.d(TAG, "onCreate")
|
||||
super.onCreate(savedInstanceState)
|
||||
sharedApplication!!.componentApplication.inject(this)
|
||||
|
||||
rootEglBase = EglBase.create()
|
||||
binding = CallActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding!!.root)
|
||||
hideNavigationIfNoPipAvailable()
|
||||
@ -767,6 +765,7 @@ class CallActivity : CallBaseActivity() {
|
||||
}
|
||||
|
||||
private fun basicInitialization() {
|
||||
rootEglBase = EglBase.create()
|
||||
createCameraEnumerator()
|
||||
|
||||
// Create a new PeerConnectionFactory instance.
|
||||
@ -948,7 +947,8 @@ class CallActivity : CallBaseActivity() {
|
||||
ParticipantGrid(
|
||||
participantUiStates = participantUiStates,
|
||||
eglBase = rootEglBase!!,
|
||||
isVoiceOnlyCall = isVoiceOnlyCall
|
||||
isVoiceOnlyCall = isVoiceOnlyCall,
|
||||
isInPipMode = isInPipMode
|
||||
) {
|
||||
animateCallControls(true, 0)
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ import android.text.TextUtils
|
||||
import android.text.format.DateUtils
|
||||
import android.text.style.ImageSpan
|
||||
import android.view.View
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import com.nextcloud.talk.R
|
||||
@ -156,30 +155,6 @@ class ConversationItem(
|
||||
} else {
|
||||
holder.binding.userStatusImage.visibility = View.GONE
|
||||
}
|
||||
|
||||
val dialogNameParams = holder.binding.dialogName.layoutParams as RelativeLayout.LayoutParams
|
||||
val unreadBubbleParams = holder.binding.dialogUnreadBubble.layoutParams as RelativeLayout.LayoutParams
|
||||
val relativeLayoutParams = holder.binding.relativeLayout.layoutParams as RelativeLayout.LayoutParams
|
||||
|
||||
if (model.hasSensitive == true) {
|
||||
dialogNameParams.addRule(RelativeLayout.CENTER_VERTICAL)
|
||||
relativeLayoutParams.addRule(RelativeLayout.ALIGN_TOP, R.id.dialogAvatarFrameLayout)
|
||||
dialogNameParams.marginEnd =
|
||||
context.resources.getDimensionPixelSize(R.dimen.standard_double_padding)
|
||||
unreadBubbleParams.topMargin =
|
||||
context.resources.getDimensionPixelSize(R.dimen.double_margin_between_elements)
|
||||
unreadBubbleParams.addRule(RelativeLayout.CENTER_VERTICAL)
|
||||
} else {
|
||||
dialogNameParams.removeRule(RelativeLayout.CENTER_VERTICAL)
|
||||
relativeLayoutParams.removeRule(RelativeLayout.ALIGN_TOP)
|
||||
dialogNameParams.marginEnd = 0
|
||||
unreadBubbleParams.topMargin = 0
|
||||
unreadBubbleParams.removeRule(RelativeLayout.CENTER_VERTICAL)
|
||||
}
|
||||
holder.binding.relativeLayout.layoutParams = relativeLayoutParams
|
||||
holder.binding.dialogUnreadBubble.layoutParams = unreadBubbleParams
|
||||
holder.binding.dialogName.layoutParams = dialogNameParams
|
||||
|
||||
setLastMessage(holder, appContext)
|
||||
showAvatar(holder)
|
||||
}
|
||||
@ -431,9 +406,9 @@ class ConversationItem(
|
||||
)
|
||||
return lastMessage
|
||||
} else if (MessageType.SINGLE_NC_ATTACHMENT_MESSAGE == chatMessage?.getCalculateMessageType()) {
|
||||
var attachmentName = chatMessage.text
|
||||
var attachmentName = chatMessage.message
|
||||
if (attachmentName == "{file}") {
|
||||
attachmentName = chatMessage.messageParameters?.get("file")?.get("name") ?: ""
|
||||
attachmentName = chatMessage.messageParameters?.get("file")?.get("name")
|
||||
}
|
||||
val author = authorName(chatMessage)
|
||||
|
||||
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.adapters.items
|
||||
|
||||
import android.view.View
|
||||
import com.nextcloud.talk.R
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
|
||||
class SpacerItem(private val height: Int) : AbstractFlexibleItem<SpacerItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes(): Int = R.layout.item_spacer
|
||||
|
||||
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>?>?): ViewHolder {
|
||||
return ViewHolder(view!!, adapter!!)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(
|
||||
adapter: FlexibleAdapter<IFlexible<*>?>?,
|
||||
holder: ViewHolder,
|
||||
position: Int,
|
||||
payloads: MutableList<Any>?
|
||||
) {
|
||||
holder.itemView.layoutParams.height = height
|
||||
}
|
||||
|
||||
override fun equals(other: Any?) = other is SpacerItem
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) :
|
||||
FlexibleViewHolder(view, adapter)
|
||||
}
|
@ -20,7 +20,6 @@ import autodagger.AutoInjector
|
||||
import coil.load
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.nextcloud.android.common.ui.theme.utils.ColorRole
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
@ -158,7 +157,7 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
|
||||
binding.messageEditIndicator.visibility = View.GONE
|
||||
binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp)
|
||||
}
|
||||
viewThemeUtils.platform.colorTextView(binding.messageTime, ColorRole.ON_SURFACE_VARIANT)
|
||||
binding.messageTime.setTextColor(ContextCompat.getColor(context, R.color.no_emphasis_text))
|
||||
// parent message handling
|
||||
if (!message.isDeleted && message.parentMessageId != null) {
|
||||
processParentMessage(message)
|
||||
|
@ -29,7 +29,6 @@ import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedA
|
||||
import com.nextcloud.talk.chat.ChatActivity
|
||||
import com.nextcloud.talk.chat.data.ChatMessageRepository
|
||||
import com.nextcloud.talk.chat.data.model.ChatMessage
|
||||
import com.nextcloud.talk.data.database.model.SendStatus
|
||||
import com.nextcloud.talk.data.network.NetworkMonitor
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.ItemCustomOutcomingTextMessageBinding
|
||||
@ -106,6 +105,7 @@ class OutcomingTextMessageViewHolder(itemView: View) :
|
||||
if (!hasCheckboxes) {
|
||||
realView.isSelected = false
|
||||
layoutParams.isWrapBefore = false
|
||||
viewThemeUtils.platform.colorTextView(binding.messageTime, ColorRole.ON_SURFACE_VARIANT)
|
||||
|
||||
binding.messageText.visibility = View.VISIBLE
|
||||
binding.checkboxContainer.visibility = View.GONE
|
||||
@ -172,7 +172,7 @@ class OutcomingTextMessageViewHolder(itemView: View) :
|
||||
binding.messageEditIndicator.visibility = View.GONE
|
||||
binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp)
|
||||
}
|
||||
viewThemeUtils.platform.colorTextView(binding.messageTime, ColorRole.ON_SURFACE_VARIANT)
|
||||
binding.messageTime.setTextColor(ContextCompat.getColor(context, R.color.no_emphasis_text))
|
||||
setBubbleOnChatMessage(message)
|
||||
// parent message handling
|
||||
if (!message.isDeleted && message.parentMessageId != null) {
|
||||
@ -185,7 +185,7 @@ class OutcomingTextMessageViewHolder(itemView: View) :
|
||||
binding.checkMark.visibility = View.INVISIBLE
|
||||
binding.sendingProgress.visibility = View.GONE
|
||||
|
||||
if (message.sendStatus == SendStatus.FAILED) {
|
||||
if (message.sendingFailed) {
|
||||
updateStatus(R.drawable.baseline_error_outline_24, context.resources?.getString(R.string.nc_message_failed))
|
||||
} else if (message.isTemporary) {
|
||||
updateStatus(R.drawable.baseline_schedule_24, context.resources?.getString(R.string.nc_message_sending))
|
||||
|
@ -179,18 +179,6 @@ interface NcApiCoroutines {
|
||||
@Url url: String
|
||||
): GenericOverall
|
||||
|
||||
@POST
|
||||
suspend fun markConversationAsImportant(
|
||||
@Header("Authorization") authorization: String,
|
||||
@Url url: String
|
||||
): GenericOverall
|
||||
|
||||
@DELETE
|
||||
suspend fun markConversationAsUnimportant(
|
||||
@Header("Authorization") authorization: String,
|
||||
@Url url: String
|
||||
): GenericOverall
|
||||
|
||||
@DELETE
|
||||
suspend fun removeConversationFromFavorites(
|
||||
@Header("Authorization") authorization: String,
|
||||
|
@ -6,7 +6,6 @@
|
||||
*/
|
||||
package com.nextcloud.talk.bottomsheet.items
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
@ -66,7 +65,6 @@ internal class ListIconDialogAdapter<IT : ListItemWithImage>(
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListItemViewHolder {
|
||||
val listItemView: View = parent.inflate(dialog.windowContext, R.layout.menu_item_sheet)
|
||||
val viewHolder = ListItemViewHolder(
|
||||
|
@ -9,15 +9,14 @@
|
||||
|
||||
package com.nextcloud.talk.call.components
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
@ -30,7 +29,6 @@ import com.nextcloud.talk.adapters.ParticipantUiState
|
||||
import org.webrtc.EglBase
|
||||
import kotlin.math.ceil
|
||||
|
||||
@SuppressLint("UnusedBoxWithConstraintsScope")
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
fun ParticipantGrid(
|
||||
@ -38,6 +36,7 @@ fun ParticipantGrid(
|
||||
eglBase: EglBase?,
|
||||
participantUiStates: List<ParticipantUiState>,
|
||||
isVoiceOnlyCall: Boolean,
|
||||
isInPipMode: Boolean,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
val configuration = LocalConfiguration.current
|
||||
@ -45,61 +44,63 @@ fun ParticipantGrid(
|
||||
|
||||
val minItemHeight = 100.dp
|
||||
|
||||
if (participantUiStates.isEmpty()) return
|
||||
|
||||
val columns = if (isPortrait) {
|
||||
when (participantUiStates.size) {
|
||||
1, 2, 3 -> 1
|
||||
else -> 2
|
||||
val columns =
|
||||
if (isPortrait) {
|
||||
when (participantUiStates.size) {
|
||||
1, 2, 3 -> 1
|
||||
else -> 2
|
||||
}
|
||||
} else {
|
||||
when (participantUiStates.size) {
|
||||
1 -> 1
|
||||
2, 4 -> 2
|
||||
else -> 3
|
||||
}
|
||||
}
|
||||
|
||||
val rows = ceil(participantUiStates.size / columns.toFloat()).toInt()
|
||||
|
||||
val heightForNonGridComponents = if (isVoiceOnlyCall && !isInPipMode) {
|
||||
// this is a workaround for now. It should ~summarize the height of callInfosLinearLayout and callControls
|
||||
// Once everything is migrated to jetpack, this workaround should be obsolete or solved in a better way
|
||||
240.dp
|
||||
} else {
|
||||
when (participantUiStates.size) {
|
||||
1 -> 1
|
||||
2, 4 -> 2
|
||||
else -> 3
|
||||
}
|
||||
}.coerceAtLeast(1) // Prevent 0
|
||||
|
||||
val rows = ceil(participantUiStates.size / columns.toFloat()).toInt().coerceAtLeast(1)
|
||||
0.dp
|
||||
}
|
||||
|
||||
val gridHeight = LocalConfiguration.current.screenHeightDp.dp - heightForNonGridComponents
|
||||
val itemSpacing = 8.dp
|
||||
val edgePadding = 8.dp
|
||||
|
||||
val totalVerticalSpacing = itemSpacing * (rows - 1)
|
||||
val totalVerticalPadding = edgePadding * 2
|
||||
val availableHeight = gridHeight - totalVerticalSpacing - totalVerticalPadding
|
||||
|
||||
BoxWithConstraints(
|
||||
modifier = modifier
|
||||
val rawItemHeight = availableHeight / rows
|
||||
val itemHeight = maxOf(rawItemHeight, minItemHeight)
|
||||
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(columns),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clickable { onClick() }
|
||||
.padding(horizontal = edgePadding)
|
||||
.clickable { onClick() },
|
||||
verticalArrangement = Arrangement.spacedBy(itemSpacing),
|
||||
horizontalArrangement = Arrangement.spacedBy(itemSpacing),
|
||||
contentPadding = PaddingValues(vertical = edgePadding)
|
||||
) {
|
||||
val availableHeight = maxHeight
|
||||
|
||||
val gridAvailableHeight = availableHeight - totalVerticalSpacing - totalVerticalPadding
|
||||
val rawItemHeight = gridAvailableHeight / rows
|
||||
val itemHeight = maxOf(rawItemHeight, minItemHeight)
|
||||
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(columns),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(availableHeight),
|
||||
verticalArrangement = Arrangement.spacedBy(itemSpacing),
|
||||
horizontalArrangement = Arrangement.spacedBy(itemSpacing),
|
||||
contentPadding = PaddingValues(vertical = edgePadding, horizontal = edgePadding)
|
||||
) {
|
||||
items(
|
||||
participantUiStates,
|
||||
key = { it.sessionKey }
|
||||
) { participant ->
|
||||
ParticipantTile(
|
||||
participantUiState = participant,
|
||||
modifier = Modifier
|
||||
.height(itemHeight)
|
||||
.fillMaxWidth(),
|
||||
eglBase = eglBase,
|
||||
isVoiceOnlyCall = isVoiceOnlyCall
|
||||
)
|
||||
}
|
||||
items(
|
||||
participantUiStates,
|
||||
key = { it.sessionKey }
|
||||
) { participant ->
|
||||
ParticipantTile(
|
||||
participantUiState = participant,
|
||||
modifier = Modifier
|
||||
.height(itemHeight)
|
||||
.fillMaxWidth(),
|
||||
eglBase = eglBase,
|
||||
isVoiceOnlyCall = isVoiceOnlyCall
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -110,7 +111,8 @@ fun ParticipantGridPreview() {
|
||||
ParticipantGrid(
|
||||
participantUiStates = getTestParticipants(1),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
isVoiceOnlyCall = false,
|
||||
isInPipMode = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -120,7 +122,8 @@ fun TwoParticipants() {
|
||||
ParticipantGrid(
|
||||
participantUiStates = getTestParticipants(2),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
isVoiceOnlyCall = false,
|
||||
isInPipMode = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -130,7 +133,8 @@ fun ThreeParticipants() {
|
||||
ParticipantGrid(
|
||||
participantUiStates = getTestParticipants(3),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
isVoiceOnlyCall = false,
|
||||
isInPipMode = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -140,7 +144,8 @@ fun FourParticipants() {
|
||||
ParticipantGrid(
|
||||
participantUiStates = getTestParticipants(4),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
isVoiceOnlyCall = false,
|
||||
isInPipMode = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -150,7 +155,8 @@ fun FiveParticipants() {
|
||||
ParticipantGrid(
|
||||
participantUiStates = getTestParticipants(5),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
isVoiceOnlyCall = false,
|
||||
isInPipMode = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -160,7 +166,8 @@ fun SevenParticipants() {
|
||||
ParticipantGrid(
|
||||
participantUiStates = getTestParticipants(7),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
isVoiceOnlyCall = false,
|
||||
isInPipMode = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -170,7 +177,8 @@ fun FiftyParticipants() {
|
||||
ParticipantGrid(
|
||||
participantUiStates = getTestParticipants(50),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
isVoiceOnlyCall = false,
|
||||
isInPipMode = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -184,7 +192,8 @@ fun OneParticipantLandscape() {
|
||||
ParticipantGrid(
|
||||
participantUiStates = getTestParticipants(1),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
isVoiceOnlyCall = false,
|
||||
isInPipMode = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -198,7 +207,8 @@ fun TwoParticipantsLandscape() {
|
||||
ParticipantGrid(
|
||||
participantUiStates = getTestParticipants(2),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
isVoiceOnlyCall = false,
|
||||
isInPipMode = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -212,7 +222,8 @@ fun ThreeParticipantsLandscape() {
|
||||
ParticipantGrid(
|
||||
participantUiStates = getTestParticipants(3),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
isVoiceOnlyCall = false,
|
||||
isInPipMode = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -226,7 +237,8 @@ fun FourParticipantsLandscape() {
|
||||
ParticipantGrid(
|
||||
participantUiStates = getTestParticipants(4),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
isVoiceOnlyCall = false,
|
||||
isInPipMode = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -240,7 +252,8 @@ fun SevenParticipantsLandscape() {
|
||||
ParticipantGrid(
|
||||
participantUiStates = getTestParticipants(7),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
isVoiceOnlyCall = false,
|
||||
isInPipMode = false
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -254,7 +267,8 @@ fun FiftyParticipantsLandscape() {
|
||||
ParticipantGrid(
|
||||
participantUiStates = getTestParticipants(50),
|
||||
eglBase = null,
|
||||
isVoiceOnlyCall = false
|
||||
isVoiceOnlyCall = false,
|
||||
isInPipMode = false
|
||||
) {}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
package com.nextcloud.talk.call.components
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
@ -41,7 +40,6 @@ const val NICK_OFFSET = 4f
|
||||
const val NICK_BLUR_RADIUS = 4f
|
||||
const val AVATAR_SIZE_FACTOR = 0.6f
|
||||
|
||||
@SuppressLint("UnusedBoxWithConstraintsScope")
|
||||
@Suppress("Detekt.LongMethod")
|
||||
@Composable
|
||||
fun ParticipantTile(
|
||||
|
@ -16,6 +16,7 @@ package com.nextcloud.talk.chat
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
@ -66,8 +67,6 @@ import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.text.bold
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.emoji2.text.EmojiCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.commit
|
||||
@ -277,8 +276,6 @@ class ChatActivity :
|
||||
lateinit var conversationInfoViewModel: ConversationInfoViewModel
|
||||
lateinit var messageInputViewModel: MessageInputViewModel
|
||||
|
||||
private var chatMenu: Menu? = null
|
||||
|
||||
private val startSelectContactForResult = registerForActivityResult(
|
||||
ActivityResultContracts
|
||||
.StartActivityForResult()
|
||||
@ -310,7 +307,12 @@ class ChatActivity :
|
||||
runBlocking {
|
||||
val id = intent?.getStringExtra(MessageSearchActivity.RESULT_KEY_MESSAGE_ID)
|
||||
id?.let {
|
||||
startContextChatWindowForMessage(id)
|
||||
val isSaved = chatViewModel.isMessageSaved(id.toLong())
|
||||
if (isSaved) {
|
||||
onMessageSearchResult(intent)
|
||||
} else {
|
||||
startContextChatWindowForMessage(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -364,7 +366,6 @@ class ChatActivity :
|
||||
var startCallFromRoomSwitch: Boolean = false
|
||||
|
||||
var voiceOnly: Boolean = true
|
||||
var focusInput: Boolean = false
|
||||
private lateinit var path: String
|
||||
|
||||
var myFirstMessage: CharSequence? = null
|
||||
@ -460,28 +461,7 @@ class ChatActivity :
|
||||
binding = ActivityChatBinding.inflate(layoutInflater)
|
||||
setupActionBar()
|
||||
setContentView(binding.root)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.chat_container)) { view, insets ->
|
||||
val statusBarInsets = insets.getInsets(WindowInsetsCompat.Type.statusBars())
|
||||
val navBarInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||
val imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime())
|
||||
|
||||
val isKeyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
|
||||
val bottomPadding = if (isKeyboardVisible) imeInsets.bottom else navBarInsets.bottom
|
||||
|
||||
view.setPadding(
|
||||
view.paddingLeft,
|
||||
statusBarInsets.top,
|
||||
view.paddingRight,
|
||||
bottomPadding
|
||||
)
|
||||
WindowInsetsCompat.CONSUMED
|
||||
}
|
||||
} else {
|
||||
colorizeStatusBar()
|
||||
colorizeNavigationBar()
|
||||
}
|
||||
setupSystemColors()
|
||||
|
||||
conversationUser = currentUserProvider.currentUser.blockingGet()
|
||||
handleIntent(intent)
|
||||
@ -509,7 +489,7 @@ class ChatActivity :
|
||||
initObservers()
|
||||
|
||||
pickMultipleMedia = registerForActivityResult(
|
||||
ActivityResultContracts.PickMultipleVisualMedia(MAX_AMOUNT_MEDIA_FILE_PICKER)
|
||||
ActivityResultContracts.PickMultipleVisualMedia(5)
|
||||
) { uris ->
|
||||
if (uris.isNotEmpty()) {
|
||||
onChooseFileResult(uris)
|
||||
@ -569,8 +549,6 @@ class ChatActivity :
|
||||
startCallFromRoomSwitch = extras?.getBoolean(KEY_START_CALL_AFTER_ROOM_SWITCH, false) == true
|
||||
|
||||
voiceOnly = extras?.getBoolean(KEY_CALL_VOICE_ONLY, false) == true
|
||||
|
||||
focusInput = extras?.getBoolean(BundleKeys.KEY_FOCUS_INPUT) == true
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
@ -662,17 +640,12 @@ class ChatActivity :
|
||||
supportFragmentManager.commit {
|
||||
setReorderingAllowed(true) // optimizes out redundant replace operations
|
||||
replace(R.id.fragment_container_activity_chat, messageInputFragment)
|
||||
runOnCommit {
|
||||
if (focusInput) {
|
||||
messageInputFragment.binding.fragmentMessageInputView.requestFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
joinRoomWithPassword()
|
||||
|
||||
if (conversationUser?.userId != "?" &&
|
||||
hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MENTION_FLAG)
|
||||
CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MENTION_FLAG)
|
||||
) {
|
||||
binding.chatToolbar.setOnClickListener { _ -> showConversationInfoScreen() }
|
||||
}
|
||||
@ -707,7 +680,7 @@ class ChatActivity :
|
||||
?.split("#")
|
||||
?.getOrNull(1)
|
||||
?.toLongOrNull()
|
||||
val currentTimeStamp = (System.currentTimeMillis() / ONE_SECOND_IN_MILLIS).toLong()
|
||||
val currentTimeStamp = (System.currentTimeMillis() / 1000).toLong()
|
||||
val retentionPeriod = retentionOfEventRooms(spreedCapabilities)
|
||||
val isPastEvent = eventEndTimeStamp?.let { it < currentTimeStamp }
|
||||
if (isPastEvent == true && retentionPeriod != 0) {
|
||||
@ -1129,8 +1102,6 @@ class ChatActivity :
|
||||
context.getString(R.string.nc_room_retention),
|
||||
Snackbar.LENGTH_LONG
|
||||
).show()
|
||||
|
||||
chatMenu?.removeItem(R.id.conversation_event)
|
||||
}
|
||||
is ChatViewModel.UnbindRoomUiState.Error -> {
|
||||
Snackbar.make(
|
||||
@ -1266,17 +1237,11 @@ class ChatActivity :
|
||||
bringToFront()
|
||||
}
|
||||
val deleteNoticeText = binding.conversationDeleteNotice.findViewById<TextView>(R.id.deletion_message)
|
||||
viewThemeUtils.material.themeCardView(binding.conversationDeleteNotice)
|
||||
|
||||
deleteNoticeText.text = resources.getQuantityString(
|
||||
R.plurals.nc_conversation_auto_delete_info,
|
||||
retentionPeriod,
|
||||
deleteNoticeText.text = String.format(
|
||||
resources.getString(R.string.nc_conversation_auto_delete_notice),
|
||||
retentionPeriod
|
||||
)
|
||||
viewThemeUtils.material.colorMaterialButtonPrimaryTonal(
|
||||
binding.conversationDeleteNotice
|
||||
.findViewById<MaterialButton>(R.id.keep_button)
|
||||
)
|
||||
|
||||
if (ConversationUtils.isParticipantOwnerOrModerator(currentConversation!!)) {
|
||||
binding.conversationDeleteNotice.findViewById<MaterialButton>(R.id.delete_now_button).visibility =
|
||||
@ -1993,8 +1958,8 @@ class ChatActivity :
|
||||
WorkManager.getInstance().enqueue(downloadWorker)
|
||||
|
||||
WorkManager.getInstance(context).getWorkInfoByIdLiveData(downloadWorker.id)
|
||||
.observeForever { workInfo: WorkInfo? ->
|
||||
if (workInfo?.state == WorkInfo.State.SUCCEEDED) {
|
||||
.observeForever { workInfo: WorkInfo ->
|
||||
if (workInfo.state == WorkInfo.State.SUCCEEDED) {
|
||||
funToCallWhenDownloadSuccessful()
|
||||
}
|
||||
}
|
||||
@ -2077,7 +2042,7 @@ class ChatActivity :
|
||||
|
||||
private fun shouldShowLobby(): Boolean {
|
||||
if (currentConversation != null) {
|
||||
return hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.WEBINARY_LOBBY) &&
|
||||
return CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.WEBINARY_LOBBY) &&
|
||||
currentConversation?.lobbyState == ConversationEnums.LobbyState.LOBBY_STATE_MODERATORS_ONLY &&
|
||||
!ConversationUtils.canModerate(currentConversation!!, spreedCapabilities) &&
|
||||
!participantPermissions.canIgnoreLobby()
|
||||
@ -2332,8 +2297,15 @@ class ChatActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun onMessageSearchResult(intent: Intent?) {
|
||||
val messageId = intent?.getStringExtra(MessageSearchActivity.RESULT_KEY_MESSAGE_ID)
|
||||
messageId?.let { id ->
|
||||
scrollToAndCenterMessageWithId(id)
|
||||
}
|
||||
}
|
||||
|
||||
private fun executeIfResultOk(result: ActivityResult, onResult: (intent: Intent?) -> Unit) {
|
||||
if (result.resultCode == RESULT_OK) {
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
onResult(result.data)
|
||||
} else {
|
||||
Log.e(TAG, "resultCode for received intent was != ok")
|
||||
@ -2347,7 +2319,7 @@ class ChatActivity :
|
||||
if (position != null && position >= 0) {
|
||||
binding.messagesListView.scrollToPosition(position)
|
||||
} else {
|
||||
Log.d(TAG, "message $messageId that should be scrolled to was not found (scrollToMessageWithId)")
|
||||
startContextChatWindowForMessage(messageId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2360,12 +2332,10 @@ class ChatActivity :
|
||||
binding.messagesListView.height / 2
|
||||
)
|
||||
} else {
|
||||
Log.d(
|
||||
TAG,
|
||||
"message $messageId that should be scrolled " +
|
||||
"to was not found (scrollToAndCenterMessageWithId)"
|
||||
)
|
||||
startContextChatWindowForMessage(messageId)
|
||||
}
|
||||
} ?: run {
|
||||
startContextChatWindowForMessage(messageId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2827,7 +2797,7 @@ class ChatActivity :
|
||||
}
|
||||
|
||||
if (this::spreedCapabilities.isInitialized) {
|
||||
if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MESSAGE_EXPIRATION)) {
|
||||
if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MESSAGE_EXPIRATION)) {
|
||||
deleteExpiredMessages()
|
||||
}
|
||||
} else {
|
||||
@ -3074,7 +3044,6 @@ class ChatActivity :
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
super.onCreateOptionsMenu(menu)
|
||||
menuInflater.inflate(R.menu.menu_conversation, menu)
|
||||
chatMenu = menu
|
||||
|
||||
if (currentConversation?.objectType == ConversationEnums.ObjectType.EVENT) {
|
||||
eventConversationMenuItem = menu.findItem(R.id.conversation_event)
|
||||
@ -3088,6 +3057,7 @@ class ChatActivity :
|
||||
loadAvatarForStatusBar()
|
||||
setActionBarTitle()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -3095,7 +3065,7 @@ class ChatActivity :
|
||||
super.onPrepareOptionsMenu(menu)
|
||||
|
||||
if (this::spreedCapabilities.isInitialized) {
|
||||
if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.READ_ONLY_ROOMS)) {
|
||||
if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.READ_ONLY_ROOMS)) {
|
||||
checkShowCallButtons()
|
||||
}
|
||||
|
||||
@ -3116,7 +3086,7 @@ class ChatActivity :
|
||||
}.collect()
|
||||
}
|
||||
|
||||
if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.SILENT_CALL)) {
|
||||
if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.SILENT_CALL)) {
|
||||
Handler().post {
|
||||
findViewById<View?>(R.id.conversation_voice_call)?.setOnLongClickListener {
|
||||
showCallButtonMenu(true)
|
||||
@ -3175,10 +3145,10 @@ class ChatActivity :
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
private fun showPopupWindow(anchorView: View) {
|
||||
val popupView = layoutInflater.inflate(R.layout.item_event_schedule, null)
|
||||
|
||||
val titleTextView = popupView.findViewById<TextView>(R.id.event_scheduled)
|
||||
val subtitleTextView = popupView.findViewById<TextView>(R.id.meetingTime)
|
||||
|
||||
val popupWindow = PopupWindow(
|
||||
@ -3627,7 +3597,7 @@ class ChatActivity :
|
||||
|
||||
fun copyMessage(message: IMessage?) {
|
||||
val clipboardManager =
|
||||
getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||
getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clipData = ClipData.newPlainText(
|
||||
resources?.getString(R.string.nc_app_product_name),
|
||||
message?.text
|
||||
@ -3941,7 +3911,7 @@ class ChatActivity :
|
||||
val isOlderThanSixHours = message
|
||||
.createdAt
|
||||
.before(Date(System.currentTimeMillis() - AGE_THRESHOLD_FOR_DELETE_MESSAGE))
|
||||
val hasDeleteMessagesUnlimitedCapability = hasSpreedFeatureCapability(
|
||||
val hasDeleteMessagesUnlimitedCapability = CapabilitiesUtil.hasSpreedFeatureCapability(
|
||||
spreedCapabilities,
|
||||
SpreedFeatures.DELETE_MESSAGES_UNLIMITED
|
||||
)
|
||||
@ -3951,7 +3921,7 @@ class ChatActivity :
|
||||
!hasDeleteMessagesUnlimitedCapability && isOlderThanSixHours -> false
|
||||
message.systemMessageType != ChatMessage.SystemMessageType.DUMMY -> false
|
||||
message.isDeleted -> false
|
||||
!hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.DELETE_MESSAGES) -> false
|
||||
!CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.DELETE_MESSAGES) -> false
|
||||
!participantPermissions.hasChatPermission() -> false
|
||||
hasDeleteMessagesUnlimitedCapability -> true
|
||||
else -> true
|
||||
@ -4216,6 +4186,5 @@ class ChatActivity :
|
||||
const val OUT_OF_OFFICE_ALPHA = 76
|
||||
const val ZERO_INDEX = 0
|
||||
const val ONE_INDEX = 1
|
||||
const val MAX_AMOUNT_MEDIA_FILE_PICKER = 10
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +200,7 @@ class MessageInputFragment : Fragment() {
|
||||
val connectionGained = (!wasOnline && isOnline)
|
||||
Log.d(TAG, "isOnline: $isOnline\nwasOnline: $wasOnline\nconnectionGained: $connectionGained")
|
||||
if (connectionGained) {
|
||||
chatActivity.messageInputViewModel.sendUnsentMessages(
|
||||
chatActivity.messageInputViewModel.sendTempMessages(
|
||||
chatActivity.conversationUser!!.getCredentials(),
|
||||
ApiUtils.getUrlForChat(
|
||||
chatActivity.chatApiVersion,
|
||||
|
@ -76,6 +76,8 @@ interface ChatMessageRepository : LifecycleAwareManager {
|
||||
*/
|
||||
suspend fun getMessage(messageId: Long, bundle: Bundle): Flow<ChatMessage>
|
||||
|
||||
suspend fun checkIfMessageIsSaved(messageId: Long): Boolean
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
suspend fun sendChatMessage(
|
||||
credentials: String,
|
||||
@ -110,7 +112,7 @@ interface ChatMessageRepository : LifecycleAwareManager {
|
||||
|
||||
suspend fun editTempChatMessage(message: ChatMessage, editedMessageText: String): Flow<Boolean>
|
||||
|
||||
suspend fun sendUnsentChatMessages(credentials: String, url: String)
|
||||
suspend fun sendTempChatMessages(credentials: String, url: String)
|
||||
|
||||
suspend fun deleteTempMessage(chatMessage: ChatMessage)
|
||||
}
|
||||
|
@ -183,26 +183,19 @@ class MediaPlayerManager : LifecycleAwareManager {
|
||||
continue
|
||||
}
|
||||
|
||||
mediaPlayer?.let { player ->
|
||||
try {
|
||||
if (!player.isPlaying) return@let
|
||||
} catch (e: IllegalStateException) {
|
||||
Log.e(TAG, "Seekbar updated during an improper state: $e")
|
||||
return@let
|
||||
}
|
||||
|
||||
val pos = player.currentPosition
|
||||
if (mediaPlayer != null && mediaPlayer?.isPlaying == true) {
|
||||
val pos = mediaPlayer!!.currentPosition
|
||||
mediaPlayerPosition = pos
|
||||
val progress = (pos.toFloat() / mediaPlayerDuration) * DIVIDER
|
||||
val progressI = ceil(progress).toInt()
|
||||
val seconds = (pos / ONE_SEC)
|
||||
_mediaPlayerSeekBarPosition.emit(progressI)
|
||||
currentCycledMessage?.let { msg ->
|
||||
msg.isPlayingVoiceMessage = true
|
||||
msg.voiceMessageSeekbarProgress = progressI
|
||||
msg.voiceMessagePlayedSeconds = seconds
|
||||
if (progressI >= IS_PLAYED_CUTOFF) msg.wasPlayedVoiceMessage = true
|
||||
_mediaPlayerSeekBarPositionMsg.emit(msg)
|
||||
currentCycledMessage?.let {
|
||||
it.isPlayingVoiceMessage = true
|
||||
it.voiceMessageSeekbarProgress = progressI
|
||||
it.voiceMessagePlayedSeconds = seconds
|
||||
if (progressI >= IS_PLAYED_CUTOFF) it.wasPlayedVoiceMessage = true
|
||||
_mediaPlayerSeekBarPositionMsg.emit(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,6 @@ import android.util.Log
|
||||
import com.bluelinelabs.logansquare.annotation.JsonIgnore
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.data.database.model.SendStatus
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.json.chat.ChatUtils.Companion.getParsedMessage
|
||||
import com.nextcloud.talk.models.json.chat.ReadStatus
|
||||
@ -120,7 +119,7 @@ data class ChatMessage(
|
||||
|
||||
var referenceId: String? = null,
|
||||
|
||||
var sendStatus: SendStatus? = null,
|
||||
var sendingFailed: Boolean = true,
|
||||
|
||||
var silent: Boolean = false
|
||||
|
||||
|
@ -19,7 +19,6 @@ import com.nextcloud.talk.data.database.mappers.asEntity
|
||||
import com.nextcloud.talk.data.database.mappers.asModel
|
||||
import com.nextcloud.talk.data.database.model.ChatBlockEntity
|
||||
import com.nextcloud.talk.data.database.model.ChatMessageEntity
|
||||
import com.nextcloud.talk.data.database.model.SendStatus
|
||||
import com.nextcloud.talk.data.network.NetworkMonitor
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.extensions.toIntOrZero
|
||||
@ -215,8 +214,7 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
// this call could be deleted when we have a worker to send messages..
|
||||
sendUnsentChatMessages(credentials, urlForChatting)
|
||||
sendTempChatMessages(credentials, urlForChatting)
|
||||
|
||||
// delay is a dirty workaround to make sure messages are added to adapter on initial load before dealing
|
||||
// with them (otherwise there is a race condition).
|
||||
@ -367,18 +365,11 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
lookIntoFuture: Boolean,
|
||||
showUnreadMessagesMarker: Boolean
|
||||
) {
|
||||
receivedChatMessages.forEach {
|
||||
Log.d(TAG, "receivedChatMessage: " + it.message)
|
||||
}
|
||||
|
||||
// remove all temp messages from UI
|
||||
val oldTempMessages = chatDao.getTempMessagesForConversation(internalConversationId)
|
||||
.first()
|
||||
.map(ChatMessageEntity::asModel)
|
||||
oldTempMessages.forEach {
|
||||
Log.d(TAG, "oldTempMessage to be removed from UI: " + it.message)
|
||||
_removeMessageFlow.emit(it)
|
||||
}
|
||||
oldTempMessages.forEach { _removeMessageFlow.emit(it) }
|
||||
|
||||
// add new messages to UI
|
||||
val tripleChatMessages = Triple(lookIntoFuture, showUnreadMessagesMarker, receivedChatMessages)
|
||||
@ -387,9 +378,6 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
// remove temp messages from DB that are now found in the new messages
|
||||
val chatMessagesReferenceIds = receivedChatMessages.mapTo(HashSet(receivedChatMessages.size)) { it.referenceId }
|
||||
val tempChatMessagesThatCanBeReplaced = oldTempMessages.filter { it.referenceId in chatMessagesReferenceIds }
|
||||
tempChatMessagesThatCanBeReplaced.forEach {
|
||||
Log.d(TAG, "oldTempMessage that was identified in newMessages: " + it.message)
|
||||
}
|
||||
chatDao.deleteTempChatMessages(
|
||||
internalConversationId,
|
||||
tempChatMessagesThatCanBeReplaced.map { it.referenceId!! }
|
||||
@ -401,10 +389,6 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
.sortedBy { it.internalId }
|
||||
.map(ChatMessageEntity::asModel)
|
||||
|
||||
remainingTempMessages.forEach {
|
||||
Log.d(TAG, "remainingTempMessage: " + it.message)
|
||||
}
|
||||
|
||||
val triple = Triple(true, false, remainingTempMessages)
|
||||
_messageFlow.emit(triple)
|
||||
}
|
||||
@ -491,6 +475,15 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
.map(ChatMessageEntity::asModel)
|
||||
}
|
||||
|
||||
override suspend fun checkIfMessageIsSaved(messageId: Long): Boolean {
|
||||
try {
|
||||
chatDao.getChatMessageForConversation(internalConversationId, messageId)
|
||||
return true
|
||||
} catch (_: Exception) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST", "MagicNumber", "Detekt.TooGenericExceptionCaught")
|
||||
private fun getMessagesFromServer(bundle: Bundle): Pair<Int, List<ChatMessageJson>>? {
|
||||
val fieldMap = bundle.getSerializable(BundleKeys.KEY_FIELD_MAP) as HashMap<String, Int>
|
||||
@ -859,17 +852,6 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
|
||||
val chatMessageModel = response.ocs?.data?.asModel()
|
||||
|
||||
val sentMessage = chatDao.getTempMessageForConversation(
|
||||
internalConversationId,
|
||||
referenceId
|
||||
).firstOrNull()
|
||||
|
||||
sentMessage?.let {
|
||||
it.sendStatus = SendStatus.SENT_PENDING_ACK
|
||||
chatDao.updateChatMessage(it)
|
||||
}
|
||||
|
||||
Log.d(TAG, "sending chat message succeeded: " + message)
|
||||
emit(Result.success(chatMessageModel))
|
||||
}
|
||||
.catch { e ->
|
||||
@ -880,7 +862,7 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
referenceId
|
||||
).firstOrNull()
|
||||
failedMessage?.let {
|
||||
it.sendStatus = SendStatus.FAILED
|
||||
it.sendingFailed = true
|
||||
chatDao.updateChatMessage(it)
|
||||
|
||||
val failedMessageModel = it.asModel()
|
||||
@ -900,28 +882,22 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
sendWithoutNotification: Boolean,
|
||||
referenceId: String
|
||||
): Flow<Result<ChatMessage?>> {
|
||||
val messageToResend = chatDao.getTempMessageForConversation(internalConversationId, referenceId).firstOrNull()
|
||||
return if (messageToResend != null) {
|
||||
messageToResend.sendStatus = SendStatus.PENDING
|
||||
chatDao.updateChatMessage(messageToResend)
|
||||
val messageToResend = chatDao.getTempMessageForConversation(internalConversationId, referenceId).first()
|
||||
messageToResend.sendingFailed = false
|
||||
chatDao.updateChatMessage(messageToResend)
|
||||
|
||||
val messageToResendModel = messageToResend.asModel()
|
||||
_updateMessageFlow.emit(messageToResendModel)
|
||||
val messageToResendModel = messageToResend.asModel()
|
||||
_updateMessageFlow.emit(messageToResendModel)
|
||||
|
||||
sendChatMessage(
|
||||
credentials,
|
||||
url,
|
||||
message,
|
||||
displayName,
|
||||
replyTo,
|
||||
sendWithoutNotification,
|
||||
referenceId
|
||||
)
|
||||
} else {
|
||||
flow {
|
||||
emit(Result.failure(IllegalStateException("No temporary message found to resend")))
|
||||
}
|
||||
}
|
||||
return sendChatMessage(
|
||||
credentials,
|
||||
url,
|
||||
message,
|
||||
displayName,
|
||||
replyTo,
|
||||
sendWithoutNotification,
|
||||
referenceId
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||
@ -963,8 +939,8 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendUnsentChatMessages(credentials: String, url: String) {
|
||||
val tempMessages = chatDao.getTempUnsentMessagesForConversation(internalConversationId).first()
|
||||
override suspend fun sendTempChatMessages(credentials: String, url: String) {
|
||||
val tempMessages = chatDao.getTempMessagesForConversation(internalConversationId).first()
|
||||
tempMessages.sortedBy { it.internalId }.onEach {
|
||||
sendChatMessage(
|
||||
credentials,
|
||||
@ -1058,7 +1034,7 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
actorDisplayName = currentUser.displayName!!,
|
||||
referenceId = referenceId,
|
||||
isTemporary = true,
|
||||
sendStatus = SendStatus.PENDING,
|
||||
sendingFailed = false,
|
||||
silent = sendWithoutNotification
|
||||
)
|
||||
return entity
|
||||
|
@ -285,6 +285,10 @@ class ChatViewModel @Inject constructor(
|
||||
conversationRepository.getRoom(token)
|
||||
}
|
||||
|
||||
suspend fun isMessageSaved(messageId: Long): Boolean {
|
||||
return chatRepository.checkIfMessageIsSaved(messageId)
|
||||
}
|
||||
|
||||
fun getCapabilities(user: User, token: String, conversationModel: ConversationModel) {
|
||||
Log.d(TAG, "Remote server ${conversationModel.remoteServer}")
|
||||
if (conversationModel.remoteServer.isNullOrEmpty()) {
|
||||
|
@ -169,9 +169,9 @@ class MessageInputViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun sendUnsentMessages(credentials: String, url: String) {
|
||||
fun sendTempMessages(credentials: String, url: String) {
|
||||
viewModelScope.launch {
|
||||
chatRepository.sendUnsentChatMessages(
|
||||
chatRepository.sendTempChatMessages(
|
||||
credentials,
|
||||
url
|
||||
)
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
@ -8,41 +9,16 @@
|
||||
package com.nextcloud.talk.components
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.windowInsetsTopHeight
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
|
||||
@Composable
|
||||
fun ColoredStatusBar() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Box(
|
||||
Modifier
|
||||
.windowInsetsTopHeight(WindowInsets.statusBars)
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
ColorLegacyStatusBar()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColorLegacyStatusBar() {
|
||||
fun SetupSystemBars() {
|
||||
val view = LocalView.current
|
||||
val isDarkMode = isSystemInDarkTheme()
|
||||
val statusBarColor = MaterialTheme.colorScheme.surface.toArgb()
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.components
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
@Composable
|
||||
fun VerticallyCenteredRow(content: @Composable RowScope.() -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
content = content
|
||||
)
|
||||
}
|
@ -18,9 +18,9 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import autodagger.AutoInjector
|
||||
import com.nextcloud.talk.activities.BaseActivity
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.components.ColoredStatusBar
|
||||
import com.nextcloud.talk.contacts.CompanionClass.Companion.KEY_HIDE_ALREADY_EXISTING_PARTICIPANTS
|
||||
import com.nextcloud.talk.extensions.getParcelableArrayListExtraProvider
|
||||
import com.nextcloud.talk.components.SetupSystemBars
|
||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import javax.inject.Inject
|
||||
@ -64,11 +64,11 @@ class ContactsActivity : BaseActivity() {
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme
|
||||
) {
|
||||
ColoredStatusBar()
|
||||
ContactsScreen(
|
||||
contactsViewModel = contactsViewModel,
|
||||
uiState = uiState.value
|
||||
)
|
||||
SetupSystemBars()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,18 +11,16 @@ package com.nextcloud.talk.contacts
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.contacts.components.ContactsAppBar
|
||||
import com.nextcloud.talk.contacts.components.AppBar
|
||||
import com.nextcloud.talk.contacts.components.ContactsList
|
||||
import com.nextcloud.talk.contacts.components.ContactsSearchAppBar
|
||||
import com.nextcloud.talk.contacts.components.ConversationCreationOptions
|
||||
|
||||
@Composable
|
||||
@ -31,40 +29,33 @@ fun ContactsScreen(contactsViewModel: ContactsViewModel, uiState: ContactsUiStat
|
||||
val isSearchActive by contactsViewModel.isSearchActive.collectAsStateWithLifecycle()
|
||||
val isAddParticipants by contactsViewModel.isAddParticipantsView.collectAsStateWithLifecycle()
|
||||
val autocompleteUsers by contactsViewModel.selectedParticipantsList.collectAsStateWithLifecycle()
|
||||
val enableAddButton by contactsViewModel.enableAddButton.collectAsStateWithLifecycle()
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.statusBarsPadding(),
|
||||
topBar = {
|
||||
if (isSearchActive) {
|
||||
ContactsSearchAppBar(
|
||||
searchQuery = searchQuery,
|
||||
onTextChange = {
|
||||
contactsViewModel.updateSearchQuery(it)
|
||||
contactsViewModel.getContactsFromSearchParams()
|
||||
},
|
||||
onCloseSearch = {
|
||||
contactsViewModel.updateSearchQuery("")
|
||||
contactsViewModel.setSearchActive(false)
|
||||
contactsViewModel.getContactsFromSearchParams()
|
||||
},
|
||||
enableAddButton = enableAddButton,
|
||||
isAddParticipants = isAddParticipants,
|
||||
clickAddButton = { contactsViewModel.modifyClickAddButton(true) }
|
||||
)
|
||||
} else {
|
||||
ContactsAppBar(
|
||||
isAddParticipants = isAddParticipants,
|
||||
autocompleteUsers = autocompleteUsers,
|
||||
onStartSearch = { contactsViewModel.setSearchActive(true) }
|
||||
)
|
||||
}
|
||||
AppBar(
|
||||
title = stringResource(R.string.nc_app_product_name),
|
||||
searchQuery = searchQuery,
|
||||
isSearchActive = isSearchActive,
|
||||
isAddParticipants = isAddParticipants,
|
||||
autocompleteUsers = autocompleteUsers,
|
||||
onEnableSearch = {
|
||||
contactsViewModel.setSearchActive(true)
|
||||
},
|
||||
onDisableSearch = {
|
||||
contactsViewModel.setSearchActive(false)
|
||||
},
|
||||
onUpdateSearchQuery = {
|
||||
contactsViewModel.updateSearchQuery(query = it)
|
||||
},
|
||||
onUpdateAutocompleteUsers = {
|
||||
contactsViewModel.getContactsFromSearchParams()
|
||||
}
|
||||
)
|
||||
},
|
||||
content = { paddingValues ->
|
||||
content = {
|
||||
Column(
|
||||
Modifier
|
||||
.padding(0.dp, paddingValues.calculateTopPadding(), 0.dp, 0.dp)
|
||||
.padding(it)
|
||||
.background(colorResource(id = R.color.bg_default))
|
||||
) {
|
||||
if (!isAddParticipants) {
|
||||
|
@ -36,15 +36,6 @@ class ContactsViewModel @Inject constructor(
|
||||
private val _isAddParticipantsView = MutableStateFlow(false)
|
||||
val isAddParticipantsView: StateFlow<Boolean> = _isAddParticipantsView
|
||||
|
||||
private val _enableAddButton = MutableStateFlow(false)
|
||||
val enableAddButton: StateFlow<Boolean> = _enableAddButton
|
||||
|
||||
@Suppress("PropertyName")
|
||||
private val _selectedContacts = MutableStateFlow<List<AutocompleteUser>>(emptyList())
|
||||
|
||||
@Suppress("PropertyName")
|
||||
private val _clickAddButton = MutableStateFlow(false)
|
||||
|
||||
private var hideAlreadyAddedParticipants: Boolean = false
|
||||
|
||||
init {
|
||||
@ -55,28 +46,14 @@ class ContactsViewModel @Inject constructor(
|
||||
_searchQuery.value = query
|
||||
}
|
||||
|
||||
fun modifyClickAddButton(value: Boolean) {
|
||||
_clickAddButton.value = value
|
||||
}
|
||||
|
||||
fun selectContact(contact: AutocompleteUser) {
|
||||
val updatedParticipants = selectedParticipants.value + contact
|
||||
selectedParticipants.value = updatedParticipants
|
||||
_selectedContacts.value = _selectedContacts.value + contact
|
||||
}
|
||||
|
||||
fun updateAddButtonState() {
|
||||
if (_selectedContacts.value.isEmpty()) {
|
||||
_enableAddButton.value = false
|
||||
} else {
|
||||
_enableAddButton.value = true
|
||||
}
|
||||
}
|
||||
|
||||
fun deselectContact(contact: AutocompleteUser) {
|
||||
val updatedParticipants = selectedParticipants.value - contact
|
||||
selectedParticipants.value = updatedParticipants
|
||||
_selectedContacts.value = _selectedContacts.value - contact
|
||||
}
|
||||
|
||||
fun updateSelectedParticipants(participants: List<AutocompleteUser>) {
|
||||
@ -99,23 +76,20 @@ class ContactsViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||
fun getContactsFromSearchParams(query: String = "") {
|
||||
fun getContactsFromSearchParams() {
|
||||
_contactsViewState.value = ContactsUiState.Loading
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val contacts = repository.getContacts(
|
||||
if (query != "") query else searchQuery.value,
|
||||
searchQuery.value,
|
||||
shareTypeList
|
||||
)
|
||||
val contactsList: MutableList<AutocompleteUser>? = contacts.ocs!!.data?.toMutableList()
|
||||
|
||||
if (hideAlreadyAddedParticipants && !_clickAddButton.value) {
|
||||
if (hideAlreadyAddedParticipants) {
|
||||
contactsList?.removeAll(selectedParticipants.value)
|
||||
}
|
||||
if (_clickAddButton.value) {
|
||||
contactsList?.removeAll(selectedParticipants.value)
|
||||
contactsList?.addAll(_selectedContacts.value)
|
||||
}
|
||||
|
||||
_contactsViewState.value = ContactsUiState.Success(contactsList)
|
||||
} catch (exception: Exception) {
|
||||
_contactsViewState.value = ContactsUiState.Error(exception.message ?: "")
|
||||
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.contacts.components
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
|
||||
|
||||
@SuppressLint("UnrememberedMutableState")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AppBar(
|
||||
title: String,
|
||||
searchQuery: String,
|
||||
isSearchActive: Boolean,
|
||||
isAddParticipants: Boolean,
|
||||
autocompleteUsers: List<AutocompleteUser>,
|
||||
onEnableSearch: () -> Unit,
|
||||
onDisableSearch: () -> Unit,
|
||||
onUpdateSearchQuery: (String) -> Unit,
|
||||
onUpdateAutocompleteUsers: () -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
TopAppBar(
|
||||
title = { Text(text = title) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
(context as? Activity)?.finish()
|
||||
}) {
|
||||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back_button))
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = onEnableSearch) {
|
||||
Icon(Icons.Filled.Search, contentDescription = stringResource(R.string.search_icon))
|
||||
}
|
||||
if (isAddParticipants) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.nc_contacts_done),
|
||||
modifier = Modifier.clickable {
|
||||
val resultIntent = Intent().apply {
|
||||
putParcelableArrayListExtra(
|
||||
"selectedParticipants",
|
||||
ArrayList(autocompleteUsers)
|
||||
)
|
||||
}
|
||||
(context as? Activity)?.setResult(Activity.RESULT_OK, resultIntent)
|
||||
(context as? Activity)?.finish()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
if (isSearchActive) {
|
||||
Row {
|
||||
SearchComponent(
|
||||
text = searchQuery,
|
||||
onTextChange = { searchQuery ->
|
||||
onUpdateSearchQuery(searchQuery)
|
||||
onUpdateAutocompleteUsers()
|
||||
},
|
||||
onDisableSearch = onDisableSearch
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -65,10 +65,8 @@ fun ContactItemRow(contact: AutocompleteUser, contactsViewModel: ContactsViewMod
|
||||
isSelected = !isSelected
|
||||
if (isSelected) {
|
||||
contactsViewModel.selectContact(contact)
|
||||
contactsViewModel.updateAddButtonState()
|
||||
} else {
|
||||
contactsViewModel.deselectContact(contact)
|
||||
contactsViewModel.updateAddButtonState()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.contacts.components
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.components.VerticallyCenteredRow
|
||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ContactsAppBar(isAddParticipants: Boolean, autocompleteUsers: List<AutocompleteUser>, onStartSearch: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
TopAppBar(
|
||||
modifier = Modifier
|
||||
.height(60.dp),
|
||||
title = {
|
||||
VerticallyCenteredRow {
|
||||
Text(
|
||||
text = if (isAddParticipants) {
|
||||
stringResource(R.string.nc_participants_add)
|
||||
} else {
|
||||
stringResource(R.string.nc_new_conversation)
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
VerticallyCenteredRow {
|
||||
IconButton(onClick = { (context as? Activity)?.finish() }) {
|
||||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back_button))
|
||||
}
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
VerticallyCenteredRow {
|
||||
IconButton(onClick = onStartSearch) {
|
||||
Icon(Icons.Filled.Search, contentDescription = stringResource(R.string.search_icon))
|
||||
}
|
||||
if (isAddParticipants) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.nc_contacts_done),
|
||||
modifier = Modifier.clickable {
|
||||
val resultIntent = Intent().apply {
|
||||
putParcelableArrayListExtra("selectedParticipants", ArrayList(autocompleteUsers))
|
||||
}
|
||||
(context as? Activity)?.setResult(Activity.RESULT_OK, resultIntent)
|
||||
(context as? Activity)?.finish()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
@ -43,13 +44,9 @@ fun ContactsItem(contacts: List<AutocompleteUser>, contactsViewModel: ContactsVi
|
||||
}
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(8.dp)
|
||||
.fillMaxWidth(),
|
||||
contentPadding = PaddingValues(
|
||||
top = 10.dp,
|
||||
bottom = 40.dp,
|
||||
start = 10.dp,
|
||||
end = 10.dp
|
||||
),
|
||||
contentPadding = PaddingValues(all = 10.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
groupedContacts.forEach { (initial, contactsForInitial) ->
|
||||
|
@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.contacts.components
|
||||
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.platform.SoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.components.VerticallyCenteredRow
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ContactsSearchAppBar(
|
||||
searchQuery: String,
|
||||
onTextChange: (String) -> Unit,
|
||||
onCloseSearch: () -> Unit,
|
||||
enableAddButton: Boolean,
|
||||
isAddParticipants: Boolean,
|
||||
clickAddButton: (Boolean) -> Unit
|
||||
) {
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
Surface(
|
||||
modifier = Modifier.height(60.dp)
|
||||
) {
|
||||
VerticallyCenteredRow {
|
||||
IconButton(
|
||||
modifier = Modifier.padding(start = 4.dp),
|
||||
onClick = onCloseSearch
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = stringResource(R.string.back_button)
|
||||
)
|
||||
}
|
||||
|
||||
TextField(
|
||||
value = searchQuery,
|
||||
onValueChange = onTextChange,
|
||||
placeholder = { Text(text = stringResource(R.string.nc_search)) },
|
||||
singleLine = true,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = 8.dp),
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
|
||||
keyboardActions = searchKeyboardActions(searchQuery, keyboardController),
|
||||
colors = searchTextFieldColors(),
|
||||
trailingIcon = {
|
||||
if (searchQuery.isNotEmpty()) {
|
||||
IconButton(onClick = { onTextChange("") }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = stringResource(R.string.nc_search_clear)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (isAddParticipants) {
|
||||
TextButton(
|
||||
onClick = {
|
||||
onCloseSearch()
|
||||
clickAddButton(true)
|
||||
},
|
||||
enabled = enableAddButton
|
||||
) {
|
||||
Text(text = stringResource(R.string.add_participants))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun searchTextFieldColors() =
|
||||
TextFieldDefaults.colors(
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
disabledIndicatorColor = Color.Transparent
|
||||
)
|
||||
|
||||
fun searchKeyboardActions(text: String, keyboardController: SoftwareKeyboardController?) =
|
||||
KeyboardActions(
|
||||
onSearch = {
|
||||
if (text.trim().isNotEmpty()) {
|
||||
keyboardController?.hide()
|
||||
}
|
||||
}
|
||||
)
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2025 Julius Linus <juliuslinus1@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.contacts.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.platform.SoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.nextcloud.talk.R
|
||||
|
||||
@Composable
|
||||
fun SearchComponent(text: String, onTextChange: (String) -> Unit, onDisableSearch: () -> Unit) {
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
TextField(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.fillMaxWidth()
|
||||
.height(60.dp),
|
||||
value = text,
|
||||
onValueChange = { onTextChange(it) },
|
||||
placeholder = { Text(text = stringResource(R.string.nc_search)) },
|
||||
textStyle = TextStyle(fontSize = 16.sp),
|
||||
singleLine = true,
|
||||
leadingIcon = { LeadingIcon(onTextChange, onDisableSearch) },
|
||||
trailingIcon = { TrailingIcon(text, onTextChange) },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
|
||||
keyboardActions = searchKeyboardActions(text, keyboardController),
|
||||
colors = searchTextFieldColors(),
|
||||
maxLines = 1
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun searchTextFieldColors() =
|
||||
TextFieldDefaults.colors(
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
disabledIndicatorColor = Color.Transparent
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun LeadingIcon(onTextChange: (String) -> Unit, onDisableSearch: () -> Unit) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
onTextChange("")
|
||||
onDisableSearch()
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Default.ArrowBack,
|
||||
contentDescription = stringResource(R.string.back_button)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TrailingIcon(text: String, onTextChange: (String) -> Unit) {
|
||||
if (text.isNotEmpty()) {
|
||||
IconButton(
|
||||
onClick = { onTextChange("") }
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = stringResource(R.string.close_icon)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun searchKeyboardActions(text: String, keyboardController: SoftwareKeyboardController?) =
|
||||
KeyboardActions(
|
||||
onSearch = {
|
||||
if (text.trim().isNotEmpty()) {
|
||||
keyboardController?.hide()
|
||||
}
|
||||
}
|
||||
)
|
@ -84,8 +84,8 @@ import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.activities.BaseActivity
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.chat.ChatActivity
|
||||
import com.nextcloud.talk.components.ColoredStatusBar
|
||||
import com.nextcloud.talk.contacts.ContactsActivity
|
||||
import com.nextcloud.talk.components.SetupSystemBars
|
||||
import com.nextcloud.talk.contacts.loadImage
|
||||
import com.nextcloud.talk.extensions.getParcelableArrayListExtraProvider
|
||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
|
||||
@ -117,6 +117,7 @@ class ConversationCreationActivity : BaseActivity() {
|
||||
colorScheme = colorScheme
|
||||
) {
|
||||
ConversationCreationScreen(conversationCreationViewModel, context, pickImage)
|
||||
SetupSystemBars()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -171,7 +172,6 @@ fun ConversationCreationScreen(
|
||||
}
|
||||
)
|
||||
|
||||
ColoredStatusBar()
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
@ -191,7 +191,7 @@ fun ConversationCreationScreen(
|
||||
content = { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(0.dp, paddingValues.calculateTopPadding(), 0.dp, 0.dp)
|
||||
.padding(paddingValues)
|
||||
.background(colorResource(id = R.color.bg_default))
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
@ -289,7 +289,7 @@ fun UploadAvatar(
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_folder),
|
||||
painter = painterResource(id = R.drawable.ic_mimetype_folder),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
|
@ -190,7 +190,7 @@ class ConversationInfoActivity :
|
||||
binding = ActivityConversationInfoBinding.inflate(layoutInflater)
|
||||
setupActionBar()
|
||||
setContentView(binding.root)
|
||||
initSystemBars()
|
||||
setupSystemColors()
|
||||
|
||||
viewModel =
|
||||
ViewModelProvider(this, viewModelFactory)[ConversationInfoViewModel::class.java]
|
||||
@ -252,8 +252,6 @@ class ConversationInfoActivity :
|
||||
initClearChatHistoryObserver()
|
||||
initMarkConversationAsSensitiveObserver()
|
||||
initMarkConversationAsInsensitiveObserver()
|
||||
initMarkConversationAsImportantObserver()
|
||||
initMarkConversationAsUnimportantObserver()
|
||||
}
|
||||
|
||||
private fun initMarkConversationAsSensitiveObserver() {
|
||||
@ -383,47 +381,7 @@ class ConversationInfoActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun initMarkConversationAsImportantObserver() {
|
||||
viewModel.markAsImportantResult.observe(this) { uiState ->
|
||||
when (uiState) {
|
||||
is ConversationInfoViewModel.MarkConversationAsImportantViewState.Success -> {
|
||||
Snackbar.make(
|
||||
binding.root,
|
||||
context.getString(R.string.nc_mark_conversation_as_important),
|
||||
Snackbar.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
is ConversationInfoViewModel.MarkConversationAsImportantViewState.Error -> {
|
||||
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
|
||||
Log.e(TAG, "failed to mark conversation as important", uiState.exception)
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initMarkConversationAsUnimportantObserver() {
|
||||
viewModel.markAsUnimportantResult.observe(this) { uiState ->
|
||||
when (uiState) {
|
||||
is ConversationInfoViewModel.MarkConversationAsUnimportantViewState.Success -> {
|
||||
Snackbar.make(
|
||||
binding.root,
|
||||
context.getString(R.string.nc_mark_conversation_as_unimportant),
|
||||
Snackbar.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
is ConversationInfoViewModel.MarkConversationAsUnimportantViewState.Error -> {
|
||||
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
|
||||
Log.e(TAG, "failed to mark conversation as unimportant", uiState.exception)
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun initViewStateObserver() {
|
||||
viewModel.viewState.observe(this) { state ->
|
||||
when (state) {
|
||||
@ -444,10 +402,8 @@ class ConversationInfoActivity :
|
||||
)
|
||||
}
|
||||
|
||||
conversation?.let {
|
||||
if (it.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) {
|
||||
viewModel.getProfileData(conversationUser, it.name)
|
||||
}
|
||||
if (conversation!!.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) {
|
||||
viewModel.getProfileData(conversationUser, conversation!!.name)
|
||||
}
|
||||
}
|
||||
|
||||
@ -462,48 +418,28 @@ class ConversationInfoActivity :
|
||||
viewModel.getProfileViewState.observe(this) { state ->
|
||||
when (state) {
|
||||
is ConversationInfoViewModel.GetProfileSuccessState -> {
|
||||
try {
|
||||
// Pronouns
|
||||
val profile = state.profile
|
||||
val pronouns = profile.pronouns ?: ""
|
||||
binding.pronouns.text = pronouns
|
||||
val profile = state.profile
|
||||
val pronouns = profile.pronouns ?: ""
|
||||
binding.pronouns.text = pronouns
|
||||
|
||||
// Role @ Organization
|
||||
val concat1 = if (profile.role != null && profile.company != null) " @ " else ""
|
||||
val role = profile.role ?: ""
|
||||
val company = profile.company ?: ""
|
||||
val professionCompanyText = "$role$concat1$company"
|
||||
binding.professionCompany.text = professionCompanyText
|
||||
val concat1 = if (profile.role != null && profile.company != null) " @ " else ""
|
||||
val role = profile.role ?: ""
|
||||
val company = profile.company ?: ""
|
||||
val professionCompanyText = "$role$concat1$company"
|
||||
binding.professionCompany.text = professionCompanyText
|
||||
|
||||
// Local Time: xX:xX · Address
|
||||
val profileZoneOffset = ZoneOffset.ofTotalSeconds(0)
|
||||
val secondsToAdd = profile.timezoneOffset?.toLong() ?: 0
|
||||
val localTime = ZonedDateTime.ofInstant(
|
||||
Instant.now().plusSeconds(secondsToAdd),
|
||||
profileZoneOffset
|
||||
)
|
||||
val localTimeString = localTime.format(
|
||||
DateTimeFormatter
|
||||
.ofLocalizedTime(FormatStyle.SHORT)
|
||||
.withLocale(Locale.getDefault())
|
||||
)
|
||||
val concat2 = if (profile.address != null) " · " else ""
|
||||
val address = profile.address ?: ""
|
||||
val localTimeLocation = "$localTimeString$concat2$address"
|
||||
binding.locationTime.text = resources.getString(R.string.local_time, localTimeLocation)
|
||||
val profileZoneOffset = ZoneOffset.ofTotalSeconds(0)
|
||||
val secondsToAdd = profile.timezoneOffset?.toLong() ?: 0
|
||||
val localTime = ZonedDateTime.ofInstant(Instant.now().plusSeconds(secondsToAdd), profileZoneOffset)
|
||||
val localTimeString = localTime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT))
|
||||
val concat2 = if (profile.address != null) " · " else ""
|
||||
val address = profile.address ?: ""
|
||||
val localTimeLocation = "$localTimeString$concat2$address"
|
||||
binding.locationTime.text = resources.getString(R.string.local_time, localTimeLocation)
|
||||
|
||||
binding.pronouns.visibility = VISIBLE
|
||||
binding.professionCompany.visibility = if (professionCompanyText.isNotEmpty()) VISIBLE else GONE
|
||||
binding.locationTime.visibility = VISIBLE
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Exception getting profile information", e)
|
||||
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
is ConversationInfoViewModel.GetProfileErrorState -> {
|
||||
Log.e(TAG, "Network error occurred getting profile information")
|
||||
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
|
||||
binding.pronouns.visibility = VISIBLE
|
||||
binding.professionCompany.visibility = if (professionCompanyText.isNotEmpty()) VISIBLE else GONE
|
||||
binding.locationTime.visibility = VISIBLE
|
||||
}
|
||||
|
||||
else -> {}
|
||||
@ -568,8 +504,7 @@ class ConversationInfoActivity :
|
||||
binding.guestAccessView.allowGuestsSwitch,
|
||||
binding.guestAccessView.passwordProtectionSwitch,
|
||||
binding.recordingConsentView.recordingConsentForConversationSwitch,
|
||||
binding.lockConversationSwitch,
|
||||
binding.notificationSettingsView.sensitiveConversationSwitch
|
||||
binding.lockConversationSwitch
|
||||
).forEach(viewThemeUtils.talk::colorSwitch)
|
||||
}
|
||||
}
|
||||
@ -882,13 +817,16 @@ class ConversationInfoActivity :
|
||||
private fun selectParticipantsToAdd() {
|
||||
val bundle = Bundle()
|
||||
val existingParticipants = ArrayList<AutocompleteUser>()
|
||||
|
||||
for (userItem in userItems) {
|
||||
val user = AutocompleteUser(
|
||||
userItem.model.calculatedActorId!!,
|
||||
userItem.model.displayName,
|
||||
userItem.model.calculatedActorType.name.lowercase()
|
||||
)
|
||||
existingParticipants.add(user)
|
||||
if (userItem.model.calculatedActorType == USERS) {
|
||||
val user = AutocompleteUser(
|
||||
userItem.model.calculatedActorId!!,
|
||||
userItem.model.displayName,
|
||||
userItem.model.calculatedActorType.name.lowercase()
|
||||
)
|
||||
existingParticipants.add(user)
|
||||
}
|
||||
}
|
||||
|
||||
bundle.putBoolean(BundleKeys.KEY_ADD_PARTICIPANTS, true)
|
||||
@ -1065,13 +1003,21 @@ class ConversationInfoActivity :
|
||||
) {
|
||||
binding.addParticipantsAction.visibility = GONE
|
||||
binding.startGroupChat.visibility = VISIBLE
|
||||
showDeleteAllMessagesOption(conversationCopy)
|
||||
} else if (ConversationUtils.canModerate(conversationCopy, spreedCapabilities)) {
|
||||
binding.addParticipantsAction.visibility = VISIBLE
|
||||
showDeleteAllMessagesOption(conversationCopy)
|
||||
if (hasSpreedFeatureCapability(
|
||||
spreedCapabilities,
|
||||
SpreedFeatures.CLEAR_HISTORY
|
||||
) && conversationCopy.canDeleteConversation
|
||||
) {
|
||||
binding.clearConversationHistory.visibility = VISIBLE
|
||||
} else {
|
||||
binding.clearConversationHistory.visibility = GONE
|
||||
}
|
||||
showOptionsMenu()
|
||||
} else {
|
||||
binding.addParticipantsAction.visibility = GONE
|
||||
|
||||
if (ConversationUtils.isNoteToSelfConversation(conversation)) {
|
||||
binding.notificationSettingsView.notificationSettings.visibility = VISIBLE
|
||||
} else {
|
||||
@ -1079,31 +1025,6 @@ class ConversationInfoActivity :
|
||||
}
|
||||
}
|
||||
|
||||
binding.notificationSettingsView.importantConversationSwitch.isChecked = conversation!!.hasImportant
|
||||
|
||||
binding.notificationSettingsView.notificationSettingsImportantConversation.setOnClickListener {
|
||||
val isChecked = binding.notificationSettingsView.importantConversationSwitch.isChecked
|
||||
binding.notificationSettingsView.importantConversationSwitch.isChecked = !isChecked
|
||||
if (!isChecked) {
|
||||
viewModel.markConversationAsImportant(
|
||||
credentials,
|
||||
conversationUser.baseUrl!!,
|
||||
conversation?.token!!
|
||||
)
|
||||
} else {
|
||||
viewModel.markConversationAsUnimportant(
|
||||
credentials,
|
||||
conversationUser.baseUrl!!,
|
||||
conversation?.token!!
|
||||
)
|
||||
}
|
||||
}
|
||||
if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.IMPORTANT_CONVERSATIONS)) {
|
||||
binding.notificationSettingsView.notificationSettingsImportantConversation.visibility = VISIBLE
|
||||
} else {
|
||||
binding.notificationSettingsView.notificationSettingsImportantConversation.visibility = GONE
|
||||
}
|
||||
|
||||
if (!hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.ARCHIVE_CONVERSATIONS)) {
|
||||
binding.archiveConversationBtn.visibility = GONE
|
||||
binding.archiveConversationTextHint.visibility = GONE
|
||||
@ -1303,18 +1224,6 @@ class ConversationInfoActivity :
|
||||
}
|
||||
}
|
||||
|
||||
fun showDeleteAllMessagesOption(conversationCopy: ConversationModel) {
|
||||
if (hasSpreedFeatureCapability(
|
||||
spreedCapabilities,
|
||||
SpreedFeatures.CLEAR_HISTORY
|
||||
) && conversationCopy.canDeleteConversation
|
||||
) {
|
||||
binding.clearConversationHistory.visibility = VISIBLE
|
||||
} else {
|
||||
binding.clearConversationHistory.visibility = GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun submitRecordingConsentChanges() {
|
||||
val state = if (binding.recordingConsentView.recordingConsentForConversationSwitch.isChecked) {
|
||||
RECORDING_CONSENT_REQUIRED_FOR_CONVERSATION
|
||||
@ -1847,6 +1756,13 @@ class ConversationInfoActivity :
|
||||
}
|
||||
|
||||
private fun setUpNotificationSettings(module: DatabaseStorageModule) {
|
||||
binding.notificationSettingsView.notificationSettingsImportantConversation.setOnClickListener {
|
||||
val isChecked = binding.notificationSettingsView.importantConversationSwitch.isChecked
|
||||
binding.notificationSettingsView.importantConversationSwitch.isChecked = !isChecked
|
||||
lifecycleScope.launch {
|
||||
module.saveBoolean("important_conversation_switch", !isChecked)
|
||||
}
|
||||
}
|
||||
binding.notificationSettingsView.notificationSettingsCallNotifications.setOnClickListener {
|
||||
val isChecked = binding.notificationSettingsView.callNotificationsSwitch.isChecked
|
||||
binding.notificationSettingsView.callNotificationsSwitch.isChecked = !isChecked
|
||||
@ -1864,6 +1780,9 @@ class ConversationInfoActivity :
|
||||
}
|
||||
}
|
||||
|
||||
binding.notificationSettingsView.importantConversationSwitch.isChecked = module
|
||||
.getBoolean("important_conversation_switch", false)
|
||||
|
||||
if (conversation!!.remoteServer.isNullOrEmpty()) {
|
||||
binding.notificationSettingsView.notificationSettingsCallNotifications.visibility = VISIBLE
|
||||
binding.notificationSettingsView.callNotificationsSwitch.isChecked = module
|
||||
|
@ -124,18 +124,6 @@ class ConversationInfoViewModel @Inject constructor(
|
||||
val getConversationReadOnlyState: LiveData<SetConversationReadOnlyViewState>
|
||||
get() = _getConversationReadOnlyState
|
||||
|
||||
@Suppress("PropertyName")
|
||||
private val _markConversationAsImportantResult =
|
||||
MutableLiveData<MarkConversationAsImportantViewState>(MarkConversationAsImportantViewState.None)
|
||||
val markAsImportantResult: LiveData<MarkConversationAsImportantViewState>
|
||||
get() = _markConversationAsImportantResult
|
||||
|
||||
@Suppress("PropertyName")
|
||||
private val _markConversationAsUnimportantResult =
|
||||
MutableLiveData<MarkConversationAsUnimportantViewState>(MarkConversationAsUnimportantViewState.None)
|
||||
val markAsUnimportantResult: LiveData<MarkConversationAsUnimportantViewState>
|
||||
get() = _markConversationAsUnimportantResult
|
||||
|
||||
private val _createRoomViewState = MutableLiveData<CreateRoomUIState>(CreateRoomUIState.None)
|
||||
val createRoomViewState: LiveData<CreateRoomUIState>
|
||||
get() = _createRoomViewState
|
||||
@ -319,19 +307,14 @@ class ConversationInfoViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||
fun getProfileData(user: User, userId: String) {
|
||||
val url = ApiUtils.getUrlForProfile(user.baseUrl!!, userId)
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val profile = conversationsRepository.getProfile(user.getCredentials(), url)
|
||||
if (profile != null) {
|
||||
_getProfileViewState.value = GetProfileSuccessState(profile)
|
||||
} else {
|
||||
_getProfileViewState.value = GetProfileErrorState
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get profile data (if not supported there wil be http405)", e)
|
||||
val profile = conversationsRepository.getProfile(user.getCredentials(), url)
|
||||
if (profile != null) {
|
||||
_getProfileViewState.value = GetProfileSuccessState(profile)
|
||||
} else {
|
||||
_getProfileViewState.value = GetProfileErrorState
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -373,34 +356,6 @@ class ConversationInfoViewModel @Inject constructor(
|
||||
conversationsRepository.unarchiveConversation(user.getCredentials(), url)
|
||||
}
|
||||
|
||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||
fun markConversationAsImportant(credentials: String, baseUrl: String, roomToken: String) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val response = conversationsRepository.markConversationAsImportant(credentials, baseUrl, roomToken)
|
||||
_markConversationAsImportantResult.value =
|
||||
MarkConversationAsImportantViewState.Success(response.ocs?.meta?.statusCode!!)
|
||||
} catch (exception: Exception) {
|
||||
_markConversationAsImportantResult.value =
|
||||
MarkConversationAsImportantViewState.Error(exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||
fun markConversationAsUnimportant(credentials: String, baseUrl: String, roomToken: String) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val response = conversationsRepository.markConversationAsUnImportant(credentials, baseUrl, roomToken)
|
||||
_markConversationAsUnimportantResult.value =
|
||||
MarkConversationAsUnimportantViewState.Success(response.ocs?.meta?.statusCode!!)
|
||||
} catch (exception: Exception) {
|
||||
_markConversationAsUnimportantResult.value =
|
||||
MarkConversationAsUnimportantViewState.Error(exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||
fun clearChatHistory(apiVersion: Int, roomToken: String) {
|
||||
viewModelScope.launch {
|
||||
@ -525,16 +480,4 @@ class ConversationInfoViewModel @Inject constructor(
|
||||
data object Success : PasswordUiState()
|
||||
data class Error(val exception: Exception) : PasswordUiState()
|
||||
}
|
||||
|
||||
sealed class MarkConversationAsImportantViewState {
|
||||
data object None : MarkConversationAsImportantViewState()
|
||||
data class Success(val statusCode: Int) : MarkConversationAsImportantViewState()
|
||||
data class Error(val exception: Exception) : MarkConversationAsImportantViewState()
|
||||
}
|
||||
|
||||
sealed class MarkConversationAsUnimportantViewState {
|
||||
data object None : MarkConversationAsUnimportantViewState()
|
||||
data class Success(val statusCode: Int) : MarkConversationAsUnimportantViewState()
|
||||
data class Error(val exception: Exception) : MarkConversationAsUnimportantViewState()
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ class ConversationInfoEditActivity : BaseActivity() {
|
||||
binding = ActivityConversationInfoEditBinding.inflate(layoutInflater)
|
||||
setupActionBar()
|
||||
setContentView(binding.root)
|
||||
initSystemBars()
|
||||
setupSystemColors()
|
||||
|
||||
val extras: Bundle? = intent.extras
|
||||
|
||||
|
@ -16,6 +16,7 @@ import android.animation.AnimatorInflater
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.SearchManager
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Drawable
|
||||
@ -40,9 +41,6 @@ import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.core.content.pm.ShortcutInfoCompat
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.os.bundleOf
|
||||
@ -81,7 +79,6 @@ import com.nextcloud.talk.adapters.items.GenericTextHeaderItem
|
||||
import com.nextcloud.talk.adapters.items.LoadMoreResultsItem
|
||||
import com.nextcloud.talk.adapters.items.MessageResultItem
|
||||
import com.nextcloud.talk.adapters.items.MessagesTextHeaderItem
|
||||
import com.nextcloud.talk.adapters.items.SpacerItem
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager
|
||||
@ -214,7 +211,7 @@ class ConversationsListActivity :
|
||||
private var adapter: FlexibleAdapter<AbstractFlexibleItem<*>>? = null
|
||||
private var conversationItems: MutableList<AbstractFlexibleItem<*>> = ArrayList()
|
||||
private var conversationItemsWithHeader: MutableList<AbstractFlexibleItem<*>> = ArrayList()
|
||||
private var searchableConversationItems: MutableList<AbstractFlexibleItem<*>> = ArrayList()
|
||||
private val searchableConversationItems: MutableList<AbstractFlexibleItem<*>> = ArrayList()
|
||||
private var filterableConversationItems: MutableList<AbstractFlexibleItem<*>> = ArrayList()
|
||||
private var nearFutureEventConversationItems: MutableList<AbstractFlexibleItem<*>> = ArrayList()
|
||||
private var searchItem: MenuItem? = null
|
||||
@ -238,9 +235,9 @@ class ConversationsListActivity :
|
||||
private var searchViewDisposable: Disposable? = null
|
||||
private var filterState =
|
||||
mutableMapOf(
|
||||
MENTION to false,
|
||||
UNREAD to false,
|
||||
ARCHIVE to false,
|
||||
FilterConversationFragment.MENTION to false,
|
||||
FilterConversationFragment.UNREAD to false,
|
||||
FilterConversationFragment.ARCHIVE to false,
|
||||
FilterConversationFragment.DEFAULT to true
|
||||
)
|
||||
val searchBehaviorSubject = BehaviorSubject.createDefault(false)
|
||||
@ -267,7 +264,7 @@ class ConversationsListActivity :
|
||||
binding = ActivityConversationsBinding.inflate(layoutInflater)
|
||||
setupActionBar()
|
||||
setContentView(binding.root)
|
||||
initSystemBars()
|
||||
setupSystemColors()
|
||||
viewThemeUtils.material.themeCardView(binding.searchToolbar)
|
||||
viewThemeUtils.material.themeSearchBarText(binding.searchText)
|
||||
|
||||
@ -294,13 +291,14 @@ class ConversationsListActivity :
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
// actionBar?.show()
|
||||
if (adapter == null) {
|
||||
adapter = FlexibleAdapter(conversationItems, this, true)
|
||||
addEmptyItemForEdgeToEdgeIfNecessary()
|
||||
} else {
|
||||
binding.loadingContent.visibility = View.GONE
|
||||
}
|
||||
adapter?.addListener(this)
|
||||
adapter!!.addListener(this)
|
||||
prepareViews()
|
||||
|
||||
showNotificationWarning()
|
||||
@ -316,10 +314,8 @@ class ConversationsListActivity :
|
||||
showServerEOLDialog()
|
||||
return
|
||||
}
|
||||
currentUser?.capabilities?.spreedCapability?.let { spreedCapabilities ->
|
||||
if (isUnifiedSearchAvailable(spreedCapabilities)) {
|
||||
searchHelper = MessageSearchHelper(unifiedSearchRepository)
|
||||
}
|
||||
if (isUnifiedSearchAvailable(currentUser!!.capabilities!!.spreedCapability!!)) {
|
||||
searchHelper = MessageSearchHelper(unifiedSearchRepository)
|
||||
}
|
||||
credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
|
||||
|
||||
@ -344,14 +340,6 @@ class ConversationsListActivity :
|
||||
showSearchOrToolbar()
|
||||
}
|
||||
|
||||
// if edge to edge is used, add an empty item at the bottom of the list
|
||||
@Suppress("MagicNumber")
|
||||
private fun addEmptyItemForEdgeToEdgeIfNecessary() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
adapter?.addScrollableFooter(SpacerItem(200))
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
private fun initObservers() {
|
||||
this.lifecycleScope.launch {
|
||||
@ -421,10 +409,6 @@ class ConversationsListActivity :
|
||||
conversationsListViewModel.getRoomsFlow
|
||||
.onEach { list ->
|
||||
setConversationList(list)
|
||||
val noteToSelf = list
|
||||
.firstOrNull { ConversationUtils.isNoteToSelfConversation(it) }
|
||||
val isNoteToSelfAvailable = noteToSelf != null
|
||||
handleNoteToSelfShortcut(isNoteToSelfAvailable, noteToSelf?.token ?: "")
|
||||
}.collect()
|
||||
}
|
||||
|
||||
@ -477,13 +461,7 @@ class ConversationsListActivity :
|
||||
userItems.add(contactItem)
|
||||
}
|
||||
|
||||
val list = searchableConversationItems.filter {
|
||||
it !is ContactItem
|
||||
}.toMutableList()
|
||||
|
||||
list.addAll(userItems)
|
||||
|
||||
searchableConversationItems = list
|
||||
searchableConversationItems.addAll(userItems)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
@ -541,29 +519,6 @@ class ConversationsListActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleNoteToSelfShortcut(noteToSelfAvailable: Boolean, noteToSelfToken: String) {
|
||||
if (noteToSelfAvailable) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_ROOM_TOKEN, noteToSelfToken)
|
||||
bundle.putBoolean(BundleKeys.KEY_FOCUS_INPUT, true)
|
||||
val intent = Intent(context, ChatActivity::class.java)
|
||||
intent.putExtras(bundle)
|
||||
intent.action = Intent.ACTION_VIEW
|
||||
val openNotesString = resources.getString(R.string.open_notes)
|
||||
|
||||
val shortcut = ShortcutInfoCompat.Builder(context, NOTE_TO_SELF_SHORTCUT_ID)
|
||||
.setShortLabel(openNotesString)
|
||||
.setLongLabel(openNotesString)
|
||||
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_pencil_grey600_24dp))
|
||||
.setIntent(intent)
|
||||
.build()
|
||||
|
||||
ShortcutManagerCompat.pushDynamicShortcut(context, shortcut)
|
||||
} else {
|
||||
ShortcutManagerCompat.removeDynamicShortcuts(context, listOf(NOTE_TO_SELF_SHORTCUT_ID))
|
||||
}
|
||||
}
|
||||
|
||||
private fun setConversationList(list: List<ConversationModel>) {
|
||||
// Update Conversations
|
||||
conversationItems.clear()
|
||||
@ -571,7 +526,7 @@ class ConversationsListActivity :
|
||||
nearFutureEventConversationItems.clear()
|
||||
|
||||
for (conversation in list) {
|
||||
if (!isFutureEvent(conversation) && !conversation.hasArchived) {
|
||||
if (!isFutureEvent(conversation)) {
|
||||
addToNearFutureEventConversationItems(conversation)
|
||||
}
|
||||
addToConversationItems(conversation)
|
||||
@ -602,6 +557,9 @@ class ConversationsListActivity :
|
||||
intArrayOf(ApiUtils.API_V4, ApiUtils.API_V3, 1)
|
||||
)
|
||||
fetchOpenConversations(apiVersion)
|
||||
|
||||
// Get users
|
||||
fetchUsers()
|
||||
}
|
||||
|
||||
fun applyFilter() {
|
||||
@ -679,7 +637,7 @@ class ConversationsListActivity :
|
||||
}
|
||||
}
|
||||
|
||||
val archiveFilterOn = filterState[ARCHIVE] == true
|
||||
val archiveFilterOn = filterState[FilterConversationFragment.ARCHIVE] ?: false
|
||||
if (archiveFilterOn && newItems.isEmpty()) {
|
||||
binding.noArchivedConversationLayout.visibility = View.VISIBLE
|
||||
} else {
|
||||
@ -701,7 +659,7 @@ class ConversationsListActivity :
|
||||
for ((k, v) in filterState) {
|
||||
if (v) {
|
||||
when (k) {
|
||||
MENTION -> result = (result && conversation.unreadMention) ||
|
||||
FilterConversationFragment.MENTION -> result = (result && conversation.unreadMention) ||
|
||||
(
|
||||
result &&
|
||||
(
|
||||
@ -711,10 +669,10 @@ class ConversationsListActivity :
|
||||
(conversation.unreadMessages > 0)
|
||||
)
|
||||
|
||||
UNREAD -> result = result && (conversation.unreadMessages > 0)
|
||||
FilterConversationFragment.UNREAD -> result = result && (conversation.unreadMessages > 0)
|
||||
|
||||
FilterConversationFragment.DEFAULT -> {
|
||||
result = if (filterState[ARCHIVE] == true) {
|
||||
result = if (filterState[FilterConversationFragment.ARCHIVE] == true) {
|
||||
result && conversation.hasArchived
|
||||
} else {
|
||||
result && !conversation.hasArchived
|
||||
@ -794,7 +752,7 @@ class ConversationsListActivity :
|
||||
}
|
||||
|
||||
private fun initSearchView() {
|
||||
val searchManager = getSystemService(SEARCH_SERVICE) as SearchManager?
|
||||
val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager?
|
||||
if (searchItem != null) {
|
||||
searchView = MenuItemCompat.getActionView(searchItem) as SearchView
|
||||
viewThemeUtils.talk.themeSearchView(searchView!!)
|
||||
@ -973,7 +931,8 @@ class ConversationsListActivity :
|
||||
} else {
|
||||
showToolbar()
|
||||
}
|
||||
initSystemBars()
|
||||
colorizeStatusBar()
|
||||
colorizeNavigationBar()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1209,8 +1168,8 @@ class ConversationsListActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchUsers(query: String = "") {
|
||||
contactsViewModel.getContactsFromSearchParams(query)
|
||||
private fun fetchUsers() {
|
||||
contactsViewModel.getContactsFromSearchParams()
|
||||
}
|
||||
|
||||
private fun handleHttpExceptions(throwable: Throwable) {
|
||||
@ -1255,7 +1214,7 @@ class ConversationsListActivity :
|
||||
})
|
||||
binding.recyclerView.setOnTouchListener { v: View, _: MotionEvent? ->
|
||||
if (!isDestroyed) {
|
||||
val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.hideSoftInputFromWindow(v.windowToken, 0)
|
||||
}
|
||||
false
|
||||
@ -1410,15 +1369,12 @@ class ConversationsListActivity :
|
||||
clearMessageSearchResults()
|
||||
binding.noArchivedConversationLayout.visibility = View.GONE
|
||||
|
||||
fetchUsers(filter)
|
||||
|
||||
if (hasFilterEnabled()) {
|
||||
adapter?.updateDataSet(conversationItems)
|
||||
adapter?.setFilter(filter)
|
||||
adapter?.filterItems()
|
||||
adapter?.updateDataSet(filterableConversationItems)
|
||||
} else {
|
||||
adapter?.updateDataSet(searchableConversationItems)
|
||||
adapter?.setFilter(filter)
|
||||
adapter?.filterItems()
|
||||
}
|
||||
@ -1433,10 +1389,9 @@ class ConversationsListActivity :
|
||||
|
||||
private fun resetSearchResults() {
|
||||
clearMessageSearchResults()
|
||||
adapter?.updateDataSet(conversationItems)
|
||||
adapter?.setFilter("")
|
||||
adapter?.filterItems()
|
||||
val archiveFilterOn = filterState[ARCHIVE] == true
|
||||
val archiveFilterOn = filterState[FilterConversationFragment.ARCHIVE] ?: false
|
||||
if (archiveFilterOn && adapter!!.isEmpty) {
|
||||
binding.noArchivedConversationLayout.visibility = View.VISIBLE
|
||||
} else {
|
||||
@ -1847,7 +1802,7 @@ class ConversationsListActivity :
|
||||
val callsChannelNotEnabled = !NotificationUtils.isCallsNotificationChannelEnabled(this)
|
||||
|
||||
val serverNotificationAppInstalled =
|
||||
currentUser?.capabilities?.notificationsCapability?.features?.isNotEmpty() == true
|
||||
currentUser?.capabilities?.notificationsCapability?.features?.isNotEmpty() ?: false
|
||||
|
||||
val settingsOfUserAreWrong = notificationPermissionNotGranted ||
|
||||
batteryOptimizationNotIgnored ||
|
||||
@ -1976,9 +1931,9 @@ class ConversationsListActivity :
|
||||
WorkManager.getInstance(applicationContext).enqueue(accountRemovalWork)
|
||||
|
||||
WorkManager.getInstance(context).getWorkInfoByIdLiveData(accountRemovalWork.id)
|
||||
.observeForever { workInfo: WorkInfo? ->
|
||||
.observeForever { workInfo: WorkInfo ->
|
||||
|
||||
when (workInfo?.state) {
|
||||
when (workInfo.state) {
|
||||
WorkInfo.State.SUCCEEDED -> {
|
||||
val text = String.format(
|
||||
context.resources.getString(R.string.nc_deleted_user),
|
||||
@ -2179,8 +2134,8 @@ class ConversationsListActivity :
|
||||
}
|
||||
|
||||
fun updateFilterState(mention: Boolean, unread: Boolean) {
|
||||
filterState[MENTION] = mention
|
||||
filterState[UNREAD] = unread
|
||||
filterState[FilterConversationFragment.MENTION] = mention
|
||||
filterState[FilterConversationFragment.UNREAD] = unread
|
||||
}
|
||||
|
||||
fun setFilterableItems(items: MutableList<AbstractFlexibleItem<*>>) {
|
||||
@ -2221,6 +2176,5 @@ class ConversationsListActivity :
|
||||
const val ROOM_TYPE_ONE_ONE = "1"
|
||||
private const val SIXTEEN_HOURS_IN_SECONDS: Long = 57600
|
||||
const val LONG_1000: Long = 1000
|
||||
private const val NOTE_TO_SELF_SHORTCUT_ID = "NOTE_TO_SELF_SHORTCUT_ID"
|
||||
}
|
||||
}
|
||||
|
@ -50,18 +50,6 @@ interface ChatMessagesDao {
|
||||
)
|
||||
fun getTempMessagesForConversation(internalConversationId: String): Flow<List<ChatMessageEntity>>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT *
|
||||
FROM ChatMessages
|
||||
WHERE internalConversationId = :internalConversationId
|
||||
AND isTemporary = 1
|
||||
AND sendStatus != 'SENT_PENDING_ACK'
|
||||
ORDER BY timestamp DESC, id DESC
|
||||
"""
|
||||
)
|
||||
fun getTempUnsentMessagesForConversation(internalConversationId: String): Flow<List<ChatMessageEntity>>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT *
|
||||
@ -72,7 +60,7 @@ interface ChatMessagesDao {
|
||||
ORDER BY timestamp DESC, id DESC
|
||||
"""
|
||||
)
|
||||
fun getTempMessageForConversation(internalConversationId: String, referenceId: String): Flow<ChatMessageEntity?>
|
||||
fun getTempMessageForConversation(internalConversationId: String, referenceId: String): Flow<ChatMessageEntity>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun upsertChatMessages(chatMessages: List<ChatMessageEntity>)
|
||||
|
@ -68,7 +68,7 @@ fun ChatMessageEntity.asModel() =
|
||||
isDeleted = deleted,
|
||||
referenceId = referenceId,
|
||||
isTemporary = isTemporary,
|
||||
sendStatus = sendStatus,
|
||||
sendingFailed = sendingFailed,
|
||||
readStatus = ReadStatus.NONE,
|
||||
silent = silent
|
||||
)
|
||||
|
@ -62,8 +62,7 @@ fun ConversationModel.asEntity() =
|
||||
remoteServer = remoteServer,
|
||||
remoteToken = remoteToken,
|
||||
hasArchived = hasArchived,
|
||||
hasSensitive = hasSensitive,
|
||||
hasImportant = hasImportant
|
||||
hasSensitive = hasSensitive
|
||||
)
|
||||
|
||||
fun ConversationEntity.asModel() =
|
||||
@ -116,8 +115,7 @@ fun ConversationEntity.asModel() =
|
||||
remoteServer = remoteServer,
|
||||
remoteToken = remoteToken,
|
||||
hasArchived = hasArchived,
|
||||
hasSensitive = hasSensitive,
|
||||
hasImportant = hasImportant
|
||||
hasSensitive = hasSensitive
|
||||
)
|
||||
|
||||
fun Conversation.asEntity(accountId: Long) =
|
||||
@ -169,6 +167,5 @@ fun Conversation.asEntity(accountId: Long) =
|
||||
remoteServer = remoteServer,
|
||||
remoteToken = remoteToken,
|
||||
hasArchived = hasArchived,
|
||||
hasSensitive = hasSensitive,
|
||||
hasImportant = hasImportant
|
||||
hasSensitive = hasSensitive
|
||||
)
|
||||
|
@ -64,7 +64,7 @@ data class ChatMessageEntity(
|
||||
@ColumnInfo(name = "reactions") var reactions: LinkedHashMap<String, Int>? = null,
|
||||
@ColumnInfo(name = "reactionsSelf") var reactionsSelf: ArrayList<String>? = null,
|
||||
@ColumnInfo(name = "referenceId") var referenceId: String? = null,
|
||||
@ColumnInfo(name = "sendStatus") var sendStatus: SendStatus? = null,
|
||||
@ColumnInfo(name = "sendingFailed") var sendingFailed: Boolean = false,
|
||||
@ColumnInfo(name = "silent") var silent: Boolean = false,
|
||||
@ColumnInfo(name = "systemMessage") var systemMessageType: ChatMessage.SystemMessageType,
|
||||
@ColumnInfo(name = "timestamp") var timestamp: Long = 0
|
||||
|
@ -95,8 +95,7 @@ data class ConversationEntity(
|
||||
@ColumnInfo(name = "unreadMentionDirect") var unreadMentionDirect: Boolean,
|
||||
@ColumnInfo(name = "unreadMessages") var unreadMessages: Int = 0,
|
||||
@ColumnInfo(name = "hasArchived") var hasArchived: Boolean = false,
|
||||
@ColumnInfo(name = "hasSensitive") var hasSensitive: Boolean = false,
|
||||
@ColumnInfo(name = "hasImportant") var hasImportant: Boolean = false
|
||||
@ColumnInfo(name = "hasSensitive") var hasSensitive: Boolean = false
|
||||
// missing/not needed: attendeeId
|
||||
// missing/not needed: attendeePin
|
||||
// missing/not needed: attendeePermissions
|
||||
|
@ -1,14 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.data.database.model
|
||||
|
||||
enum class SendStatus {
|
||||
PENDING,
|
||||
SENT_PENDING_ACK,
|
||||
FAILED
|
||||
}
|
@ -1,31 +1,18 @@
|
||||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-FileCopyrightText: 2022 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.data.source.local
|
||||
|
||||
import android.util.Log
|
||||
import androidx.room.DeleteColumn
|
||||
import androidx.room.migration.AutoMigrationSpec
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import java.sql.SQLException
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
object Migrations {
|
||||
|
||||
//region Auto migrations
|
||||
|
||||
@DeleteColumn(tableName = "ChatMessages", columnName = "sendingFailed")
|
||||
class AutoMigration16To17 : AutoMigrationSpec
|
||||
|
||||
//endregion
|
||||
|
||||
//region Manual migrations
|
||||
|
||||
val MIGRATION_6_8 = object : Migration(6, 8) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
Log.i("Migrations", "Migrating 6 to 8")
|
||||
@ -78,19 +65,10 @@ object Migrations {
|
||||
val MIGRATION_14_15 = object : Migration(14, 15) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
Log.i("Migrations", "Migrating 14 to 15")
|
||||
addIsSensitive(db)
|
||||
addisSensitive(db)
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_15_16 = object : Migration(15, 16) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
Log.i("Migrations", "Migrating 15 to 16")
|
||||
addIsImportant(db)
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
fun migrateToRoom(db: SupportSQLiteDatabase) {
|
||||
db.execSQL(
|
||||
"CREATE TABLE User_new (" +
|
||||
@ -312,7 +290,7 @@ object Migrations {
|
||||
}
|
||||
}
|
||||
|
||||
fun addIsSensitive(db: SupportSQLiteDatabase) {
|
||||
fun addisSensitive(db: SupportSQLiteDatabase) {
|
||||
try {
|
||||
db.execSQL(
|
||||
"ALTER TABLE Conversations " +
|
||||
@ -323,17 +301,6 @@ object Migrations {
|
||||
}
|
||||
}
|
||||
|
||||
fun addIsImportant(db: SupportSQLiteDatabase) {
|
||||
try {
|
||||
db.execSQL(
|
||||
"ALTER TABLE Conversations " +
|
||||
"ADD COLUMN hasImportant INTEGER NOT NULL DEFAULT 0;"
|
||||
)
|
||||
} catch (e: SQLException) {
|
||||
Log.i("Migrations", "Something went wrong when adding column hasImportant to table Conversations")
|
||||
}
|
||||
}
|
||||
|
||||
fun addTempMessagesSupport(db: SupportSQLiteDatabase) {
|
||||
try {
|
||||
db.execSQL(
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023-2025 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-FileCopyrightText: 2023-2024 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-FileCopyrightText: 2022 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
* SPDX-FileCopyrightText: 2017-2020 Mario Danic <mario@lovelyhq.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
@ -23,14 +23,12 @@ import com.nextcloud.talk.data.database.dao.ConversationsDao
|
||||
import com.nextcloud.talk.data.database.model.ChatBlockEntity
|
||||
import com.nextcloud.talk.data.database.model.ChatMessageEntity
|
||||
import com.nextcloud.talk.data.database.model.ConversationEntity
|
||||
import com.nextcloud.talk.data.source.local.Migrations.AutoMigration16To17
|
||||
import com.nextcloud.talk.data.source.local.converters.ArrayListConverter
|
||||
import com.nextcloud.talk.data.source.local.converters.CapabilitiesConverter
|
||||
import com.nextcloud.talk.data.source.local.converters.ExternalSignalingServerConverter
|
||||
import com.nextcloud.talk.data.source.local.converters.HashMapHashMapConverter
|
||||
import com.nextcloud.talk.data.source.local.converters.LinkedHashMapConverter
|
||||
import com.nextcloud.talk.data.source.local.converters.PushConfigurationConverter
|
||||
import com.nextcloud.talk.data.source.local.converters.SendStatusConverter
|
||||
import com.nextcloud.talk.data.source.local.converters.ServerVersionConverter
|
||||
import com.nextcloud.talk.data.source.local.converters.SignalingSettingsConverter
|
||||
import com.nextcloud.talk.data.storage.ArbitraryStoragesDao
|
||||
@ -51,10 +49,9 @@ import java.util.Locale
|
||||
ChatMessageEntity::class,
|
||||
ChatBlockEntity::class
|
||||
],
|
||||
version = 17,
|
||||
version = 15,
|
||||
autoMigrations = [
|
||||
AutoMigration(from = 9, to = 10),
|
||||
AutoMigration(from = 16, to = 17, spec = AutoMigration16To17::class)
|
||||
AutoMigration(from = 9, to = 10)
|
||||
],
|
||||
exportSchema = true
|
||||
)
|
||||
@ -66,8 +63,7 @@ import java.util.Locale
|
||||
SignalingSettingsConverter::class,
|
||||
HashMapHashMapConverter::class,
|
||||
LinkedHashMapConverter::class,
|
||||
ArrayListConverter::class,
|
||||
SendStatusConverter::class
|
||||
ArrayListConverter::class
|
||||
)
|
||||
abstract class TalkDatabase : RoomDatabase() {
|
||||
|
||||
@ -121,8 +117,7 @@ abstract class TalkDatabase : RoomDatabase() {
|
||||
Migrations.MIGRATION_11_12,
|
||||
Migrations.MIGRATION_12_13,
|
||||
Migrations.MIGRATION_13_14,
|
||||
Migrations.MIGRATION_14_15,
|
||||
Migrations.MIGRATION_15_16
|
||||
Migrations.MIGRATION_14_15
|
||||
)
|
||||
.allowMainThreadQueries()
|
||||
.addCallback(
|
||||
|
@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.data.source.local.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import com.nextcloud.talk.data.database.model.SendStatus
|
||||
|
||||
class SendStatusConverter {
|
||||
@TypeConverter
|
||||
fun fromStatus(value: SendStatus): String {
|
||||
return value.name
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toStatus(value: String): SendStatus {
|
||||
return SendStatus.valueOf(value)
|
||||
}
|
||||
}
|
@ -21,7 +21,6 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.collectAsState
|
||||
@ -29,9 +28,8 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.core.net.toUri
|
||||
import autodagger.AutoInjector
|
||||
import com.nextcloud.talk.BuildConfig
|
||||
import com.nextcloud.talk.R
|
||||
@ -39,8 +37,8 @@ import com.nextcloud.talk.activities.BaseActivity
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager
|
||||
import com.nextcloud.talk.components.ColoredStatusBar
|
||||
import com.nextcloud.talk.components.StandardAppBar
|
||||
import com.nextcloud.talk.components.SetupSystemBars
|
||||
import com.nextcloud.talk.users.UserManager
|
||||
import com.nextcloud.talk.utils.BrandingUtils
|
||||
import com.nextcloud.talk.utils.ClosedInterfaceImpl
|
||||
@ -111,22 +109,18 @@ class DiagnoseActivity : BaseActivity() {
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme
|
||||
) {
|
||||
ColoredStatusBar()
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.statusBarsPadding(),
|
||||
topBar = {
|
||||
StandardAppBar(
|
||||
title = stringResource(R.string.nc_settings_diagnose_title),
|
||||
menuItems
|
||||
)
|
||||
},
|
||||
content = { paddingValues ->
|
||||
content = {
|
||||
val viewState = diagnoseViewModel.notificationViewState.collectAsState().value
|
||||
|
||||
Column(
|
||||
Modifier
|
||||
.padding(0.dp, paddingValues.calculateTopPadding(), 0.dp, 0.dp)
|
||||
.padding(it)
|
||||
.background(backgroundColor)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
@ -142,6 +136,7 @@ class DiagnoseActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
)
|
||||
SetupSystemBars()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,12 +65,7 @@ fun DiagnoseContentComposable(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
start = 16.dp,
|
||||
top = 0.dp,
|
||||
end = 16.dp,
|
||||
bottom = 0.dp
|
||||
)
|
||||
.padding(16.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
data.value.forEach { element ->
|
||||
@ -105,7 +100,6 @@ fun DiagnoseContentComposable(
|
||||
ShowTestPushButton(onTestPushClick)
|
||||
}
|
||||
ShowNotificationData(isLoading, showDialog, context, viewState, onDismissDialog)
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,7 +190,6 @@ class FullScreenMediaActivity : AppCompatActivity() {
|
||||
supportActionBar?.show()
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
private fun applyWindowInsets() {
|
||||
val playerView = binding.playerView
|
||||
val exoControls = playerView.findViewById<FrameLayout>(R.id.exo_bottom_bar)
|
||||
|
@ -65,7 +65,7 @@ class InvitationsActivity : BaseActivity() {
|
||||
binding = ActivityInvitationsBinding.inflate(layoutInflater)
|
||||
setupActionBar()
|
||||
setContentView(binding.root)
|
||||
initSystemBars()
|
||||
setupSystemColors()
|
||||
|
||||
adapter = InvitationsAdapter(currentUser) { invitation, action ->
|
||||
handleInvitation(invitation, action)
|
||||
|
@ -67,7 +67,7 @@ class GeocodingActivity :
|
||||
binding = ActivityGeocodingBinding.inflate(layoutInflater)
|
||||
setupActionBar()
|
||||
setContentView(binding.root)
|
||||
initSystemBars()
|
||||
setupSystemColors()
|
||||
|
||||
Configuration.getInstance().load(context, PreferenceManager.getDefaultSharedPreferences(context))
|
||||
|
||||
|
@ -129,7 +129,7 @@ class LocationPickerActivity :
|
||||
binding = ActivityLocationBinding.inflate(layoutInflater)
|
||||
setupActionBar()
|
||||
setContentView(binding.root)
|
||||
initSystemBars()
|
||||
setupSystemColors()
|
||||
|
||||
getInstance().load(context, PreferenceManager.getDefaultSharedPreferences(context))
|
||||
|
||||
|
@ -70,7 +70,7 @@ class MessageSearchActivity : BaseActivity() {
|
||||
binding = ActivityMessageSearchBinding.inflate(layoutInflater)
|
||||
setupActionBar()
|
||||
setContentView(binding.root)
|
||||
initSystemBars()
|
||||
setupSystemColors()
|
||||
|
||||
viewModel = ViewModelProvider(this, viewModelFactory)[MessageSearchViewModel::class.java]
|
||||
user = currentUserProvider.currentUser.blockingGet()
|
||||
|
@ -62,10 +62,10 @@ class ConversationModel(
|
||||
var remoteToken: String? = null,
|
||||
var hasArchived: Boolean = false,
|
||||
var hasSensitive: Boolean = false,
|
||||
var hasImportant: Boolean = false,
|
||||
|
||||
// attributes that don't come from API. This should be changed?!
|
||||
var password: String? = null
|
||||
|
||||
) {
|
||||
|
||||
companion object {
|
||||
@ -128,8 +128,7 @@ class ConversationModel(
|
||||
remoteServer = conversation.remoteServer,
|
||||
remoteToken = conversation.remoteToken,
|
||||
hasArchived = conversation.hasArchived,
|
||||
hasSensitive = conversation.hasSensitive,
|
||||
hasImportant = conversation.hasImportant
|
||||
hasSensitive = conversation.hasSensitive
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ class ChatUtils {
|
||||
return message
|
||||
}
|
||||
|
||||
@Suppress("Detekt.ComplexMethod", "Detekt.ComplexCondition")
|
||||
@Suppress("Detekt.ComplexMethod")
|
||||
private fun parse(messageParameters: HashMap<String?, HashMap<String?, String?>>, message: String?): String? {
|
||||
var resultMessage = message
|
||||
for (key in messageParameters.keys) {
|
||||
@ -29,9 +29,7 @@ class ChatUtils {
|
||||
|
||||
if (individualHashMap != null) {
|
||||
val type = individualHashMap["type"]
|
||||
resultMessage = if (type == "user" || type == "guest" || type == "call" || type == "email" ||
|
||||
type == "user-group" || type == "circle"
|
||||
) {
|
||||
resultMessage = if (type == "user" || type == "guest" || type == "call" || type == "email") {
|
||||
resultMessage?.replace("{$key}", "@" + individualHashMap["name"])
|
||||
} else if (type == "geo-location") {
|
||||
individualHashMap["name"]
|
||||
|
@ -168,8 +168,5 @@ data class Conversation(
|
||||
var hasArchived: Boolean = false,
|
||||
|
||||
@JsonField(name = ["isSensitive"])
|
||||
var hasSensitive: Boolean = false,
|
||||
|
||||
@JsonField(name = ["isImportant"])
|
||||
var hasImportant: Boolean = false
|
||||
var hasSensitive: Boolean = false
|
||||
) : Parcelable
|
||||
|
@ -57,7 +57,7 @@ class ListOpenConversationsActivity : BaseActivity() {
|
||||
binding = ActivityOpenConversationsBinding.inflate(layoutInflater)
|
||||
setupActionBar()
|
||||
setContentView(binding.root)
|
||||
initSystemBars()
|
||||
setupSystemColors()
|
||||
viewThemeUtils.platform.colorImageView(binding.searchOpenConversations, ColorRole.ON_SURFACE)
|
||||
viewThemeUtils.material.colorTextInputLayout(binding.textInputLayout)
|
||||
|
||||
|
@ -119,7 +119,7 @@ class ProfileActivity : BaseActivity() {
|
||||
binding = ActivityProfileBinding.inflate(layoutInflater)
|
||||
setupActionBar()
|
||||
setContentView(binding.root)
|
||||
initSystemBars()
|
||||
setupSystemColors()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
@ -6,17 +6,14 @@
|
||||
*/
|
||||
package com.nextcloud.talk.receivers
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Notification
|
||||
import android.app.NotificationManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.util.Log
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.app.Person
|
||||
@ -165,15 +162,9 @@ class DirectReplyReceiver : BroadcastReceiver() {
|
||||
// Set the updated style
|
||||
previousBuilder.setStyle(previousStyle)
|
||||
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.POST_NOTIFICATIONS
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
// Check if notification still exists
|
||||
if (findActiveNotification(systemNotificationId!!) != null) {
|
||||
NotificationManagerCompat.from(context).notify(systemNotificationId!!, previousBuilder.build())
|
||||
}
|
||||
// Check if notification still exists
|
||||
if (findActiveNotification(systemNotificationId!!) != null) {
|
||||
NotificationManagerCompat.from(context).notify(systemNotificationId!!, previousBuilder.build())
|
||||
}
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
|
@ -53,8 +53,4 @@ interface ConversationsRepository {
|
||||
suspend fun markConversationAsSensitive(credentials: String, baseUrl: String, roomToken: String): GenericOverall
|
||||
|
||||
suspend fun markConversationAsInsensitive(credentials: String, baseUrl: String, roomToken: String): GenericOverall
|
||||
|
||||
suspend fun markConversationAsImportant(credentials: String, baseUrl: String, roomToken: String): GenericOverall
|
||||
|
||||
suspend fun markConversationAsUnImportant(credentials: String, baseUrl: String, roomToken: String): GenericOverall
|
||||
}
|
||||
|
@ -139,24 +139,6 @@ class ConversationsRepositoryImpl(
|
||||
return coroutineApi.markConversationAsInsensitive(credentials, url)
|
||||
}
|
||||
|
||||
override suspend fun markConversationAsImportant(
|
||||
credentials: String,
|
||||
baseUrl: String,
|
||||
roomToken: String
|
||||
): GenericOverall {
|
||||
val url = ApiUtils.getUrlForImportantConversation(baseUrl, roomToken)
|
||||
return coroutineApi.markConversationAsImportant(credentials, url)
|
||||
}
|
||||
|
||||
override suspend fun markConversationAsUnImportant(
|
||||
credentials: String,
|
||||
baseUrl: String,
|
||||
roomToken: String
|
||||
): GenericOverall {
|
||||
val url = ApiUtils.getUrlForImportantConversation(baseUrl, roomToken)
|
||||
return coroutineApi.markConversationAsUnimportant(credentials, url)
|
||||
}
|
||||
|
||||
override suspend fun banActor(
|
||||
credentials: String,
|
||||
url: String,
|
||||
|
@ -145,7 +145,7 @@ class SettingsActivity :
|
||||
binding = ActivitySettingsBinding.inflate(layoutInflater)
|
||||
setupActionBar()
|
||||
setContentView(binding.root)
|
||||
initSystemBars()
|
||||
setupSystemColors()
|
||||
|
||||
binding.avatarImage.let { ViewCompat.setTransitionName(it, "userAvatar.transitionTag") }
|
||||
|
||||
@ -681,9 +681,9 @@ class SettingsActivity :
|
||||
WorkManager.getInstance(applicationContext).enqueue(accountRemovalWork)
|
||||
|
||||
WorkManager.getInstance(context).getWorkInfoByIdLiveData(accountRemovalWork.id)
|
||||
.observeForever { workInfo: WorkInfo? ->
|
||||
.observeForever { workInfo: WorkInfo ->
|
||||
|
||||
when (workInfo?.state) {
|
||||
when (workInfo.state) {
|
||||
WorkInfo.State.SUCCEEDED -> {
|
||||
val text = String.format(
|
||||
context.resources.getString(R.string.nc_deleted_user),
|
||||
|
@ -13,6 +13,7 @@ import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
@ -27,6 +28,7 @@ import com.nextcloud.talk.databinding.ActivitySharedItemsBinding
|
||||
import com.nextcloud.talk.shareditems.adapters.SharedItemsAdapter
|
||||
import com.nextcloud.talk.shareditems.model.SharedItemType
|
||||
import com.nextcloud.talk.shareditems.viewmodels.SharedItemsViewModel
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||
import javax.inject.Inject
|
||||
@ -55,11 +57,15 @@ class SharedItemsActivity : BaseActivity() {
|
||||
setSupportActionBar(binding.sharedItemsToolbar)
|
||||
setContentView(binding.root)
|
||||
|
||||
initSystemBars()
|
||||
|
||||
viewThemeUtils.platform.themeStatusBar(this)
|
||||
viewThemeUtils.material.themeToolbar(binding.sharedItemsToolbar)
|
||||
viewThemeUtils.material.themeTabLayoutOnSurface(binding.sharedItemsTabs)
|
||||
|
||||
DisplayUtils.applyColorToNavigationBar(
|
||||
this.window,
|
||||
ResourcesCompat.getColor(resources, R.color.bg_default, null)
|
||||
)
|
||||
|
||||
supportActionBar?.title = conversationName
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
|
@ -86,7 +86,7 @@ class TranslateActivity : BaseActivity() {
|
||||
}
|
||||
setupActionBar()
|
||||
setContentView(binding.root)
|
||||
initSystemBars()
|
||||
setupSystemColors()
|
||||
setupTextViews()
|
||||
viewModel.getLanguages()
|
||||
setupCopyButton()
|
||||
|
@ -118,6 +118,7 @@ class ConversationsListBottomDialog(
|
||||
currentUser.capabilities?.spreedCapability!!,
|
||||
SpreedFeatures.FAVORITES
|
||||
)
|
||||
val canModerate = ConversationUtils.canModerate(conversation, currentUser.capabilities?.spreedCapability!!)
|
||||
|
||||
binding.conversationRemoveFromFavorites.visibility = setVisibleIf(
|
||||
hasFavoritesCapability && conversation.favorite
|
||||
@ -148,11 +149,14 @@ class ConversationsListBottomDialog(
|
||||
)
|
||||
|
||||
binding.conversationOperationDelete.visibility = setVisibleIf(
|
||||
conversation.canDeleteConversation
|
||||
canModerate
|
||||
)
|
||||
|
||||
binding.conversationOperationLeave.visibility = setVisibleIf(
|
||||
conversation.canLeaveConversation
|
||||
conversation.canLeaveConversation &&
|
||||
// leaving is by api not possible for the last user with moderator permissions.
|
||||
// for now, hide this option for all moderators.
|
||||
!ConversationUtils.canModerate(conversation, currentUser.capabilities!!.spreedCapability!!)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@ import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.chat.ChatActivity
|
||||
import com.nextcloud.talk.chat.data.model.ChatMessage
|
||||
import com.nextcloud.talk.data.database.model.SendStatus
|
||||
import com.nextcloud.talk.data.network.NetworkMonitor
|
||||
import com.nextcloud.talk.databinding.DialogTempMessageActionsBinding
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
@ -59,10 +58,9 @@ class TempMessageActionsDialog(
|
||||
|
||||
private fun initMenuItems() {
|
||||
this.lifecycleScope.launch {
|
||||
val sendingFailed = message.sendStatus == SendStatus.FAILED
|
||||
initResendMessage(sendingFailed && networkMonitor.isOnline.value)
|
||||
initMenuEditMessage(sendingFailed || !networkMonitor.isOnline.value)
|
||||
initMenuDeleteMessage(sendingFailed || !networkMonitor.isOnline.value)
|
||||
initResendMessage(message.sendingFailed && networkMonitor.isOnline.value)
|
||||
initMenuEditMessage(message.sendingFailed || !networkMonitor.isOnline.value)
|
||||
initMenuDeleteMessage(message.sendingFailed || !networkMonitor.isOnline.value)
|
||||
initMenuItemCopy()
|
||||
}
|
||||
}
|
||||
|
@ -205,10 +205,6 @@ object ApiUtils {
|
||||
return getUrlForParticipants(version, baseUrl, token) + "/active"
|
||||
}
|
||||
|
||||
fun getUrlForImportantConversation(baseUrl: String, roomToken: String): String {
|
||||
return "$baseUrl$OCS_API_VERSION/apps/spreed/api/v4/room/$roomToken/important"
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getUrlForParticipantsSelf(version: Int, baseUrl: String?, token: String?): String {
|
||||
return getUrlForParticipants(version, baseUrl, token) + "/self"
|
||||
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.utils
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
||||
/**
|
||||
* This method is similar to "adjustUIForAPILevel35" in
|
||||
* AppCompatActivityExtensions.kt in https://github.com/nextcloud/android-common/
|
||||
* Only window.addSystemBarPaddings() had to be removed. This could be unified again at some point.
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun AppCompatActivity.adjustUIForAPILevel35(
|
||||
statusBarStyle: SystemBarStyle = SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT),
|
||||
// It may make sense to change navigationBarStyle to "SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT)"
|
||||
// For now, it is set to "light" to have a fully transparent navigation bar to align with the XML screens.
|
||||
// It may be wanted to have a semi transparent navigation bar in the future. Then set it to "auto" and try to
|
||||
// migrate the XML screens to Compose (having semi transparent navigation bar for XML did not work out. In
|
||||
// general, supporting both XML and Compose system bar handling is a pain and we will have it easier without XML)
|
||||
// So in short: migrate all screens to Compose. Then it's easier to decide if navigation bar should be semi
|
||||
// transparent or not for all screens.
|
||||
navigationBarStyle: SystemBarStyle = SystemBarStyle.light(Color.TRANSPARENT, Color.TRANSPARENT)
|
||||
) {
|
||||
val isApiLevel35OrHigher = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM)
|
||||
if (!isApiLevel35OrHigher) {
|
||||
return
|
||||
}
|
||||
enableEdgeToEdge(statusBarStyle, navigationBarStyle)
|
||||
}
|
@ -59,8 +59,7 @@ enum class SpreedFeatures(val value: String) {
|
||||
ARCHIVE_CONVERSATIONS("archived-conversations-v2"),
|
||||
CONVERSATION_CREATION_ALL("conversation-creation-all"),
|
||||
UNBIND_CONVERSATION("unbind-conversation"),
|
||||
SENSITIVE_CONVERSATIONS("sensitive-conversations"),
|
||||
IMPORTANT_CONVERSATIONS("important-conversations")
|
||||
SENSITIVE_CONVERSATIONS("sensitive-conversations")
|
||||
}
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
|
@ -81,5 +81,4 @@ object BundleKeys {
|
||||
const val KEY_FIELD_MAP: String = "KEY_FIELD_MAP"
|
||||
const val KEY_CHAT_URL: String = "KEY_CHAT_URL"
|
||||
const val KEY_SCROLL_TO_NOTIFICATION_CATEGORY: String = "KEY_SCROLL_TO_NOTIFICATION_CATEGORY"
|
||||
const val KEY_FOCUS_INPUT: String = "KEY_FOCUS_INPUT"
|
||||
}
|
||||
|
@ -30,11 +30,6 @@ class DummyChatMessagesDaoImpl : ChatMessagesDao {
|
||||
override fun getTempMessagesForConversation(internalConversationId: String): Flow<List<ChatMessageEntity>> =
|
||||
flowOf()
|
||||
|
||||
override fun getTempUnsentMessagesForConversation(internalConversationId: String): Flow<List<ChatMessageEntity>> {
|
||||
// nothing to return here as long this class is only used for the Search window
|
||||
return flowOf()
|
||||
}
|
||||
|
||||
override fun getTempMessageForConversation(
|
||||
internalConversationId: String,
|
||||
referenceId: String
|
||||
|
@ -1,7 +1,7 @@
|
||||
<!--
|
||||
~ Nextcloud Talk - Android Client
|
||||
~
|
||||
~ SPDX-FileCopyrightText: 2018-2025 Google LLC
|
||||
~ SPDX-FileCopyrightText: 2018-2024 Google LLC
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
@ -11,6 +11,5 @@
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M280,920Q247,920 223.5,896.5Q200,873 200,840L200,120Q200,87 223.5,63.5Q247,40 280,40L680,40Q713,40 736.5,63.5Q760,87 760,120L760,244Q778,251 789,266Q800,281 800,300L800,380Q800,399 789,414Q778,429 760,436L760,840Q760,873 736.5,896.5Q713,920 680,920L280,920ZM280,840L680,840Q680,840 680,840Q680,840 680,840L680,120Q680,120 680,120Q680,120 680,120L280,120Q280,120 280,120Q280,120 280,120L280,840Q280,840 280,840Q280,840 280,840ZM280,840Q280,840 280,840Q280,840 280,840L280,120Q280,120 280,120Q280,120 280,120L280,120Q280,120 280,120Q280,120 280,120L280,840Q280,840 280,840Q280,840 280,840ZM480,240Q497,240 508.5,228.5Q520,217 520,200Q520,183 508.5,171.5Q497,160 480,160Q463,160 451.5,171.5Q440,183 440,200Q440,217 451.5,228.5Q463,240 480,240Z" />
|
||||
android:pathData="M280,920Q247,920 223.5,896.5Q200,873 200,840L200,120Q200,87 223.5,63.5Q247,40 280,40L680,40Q713,40 736.5,63.5Q760,87 760,120L760,840Q760,873 736.5,896.5Q713,920 680,920L280,920ZM280,720L680,720L680,240L280,240L280,720Z" />
|
||||
</vector>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<!--
|
||||
~ Nextcloud Talk - Android Client
|
||||
~
|
||||
~ SPDX-FileCopyrightText: 2018-2025 Google LLC
|
||||
~ SPDX-FileCopyrightText: 2018-2024 Google LLC
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
@ -12,5 +12,5 @@
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M40,800L40,688Q40,654 57.5,625.5Q75,597 104,582Q166,551 230,535.5Q294,520 360,520Q426,520 490,535.5Q554,551 616,582Q645,597 662.5,625.5Q680,654 680,688L680,800L40,800ZM760,800L760,680Q760,636 735.5,595.5Q711,555 666,526Q717,532 762,546.5Q807,561 846,582Q882,602 901,626.5Q920,651 920,680L920,800L760,800ZM360,480Q294,480 247,433Q200,386 200,320Q200,254 247,207Q294,160 360,160Q426,160 473,207Q520,254 520,320Q520,386 473,433Q426,480 360,480ZM760,320Q760,386 713,433Q666,480 600,480Q589,480 572,477.5Q555,475 544,472Q571,440 585.5,401Q600,362 600,320Q600,278 585.5,239Q571,200 544,168Q558,163 572,161.5Q586,160 600,160Q666,160 713,207Q760,254 760,320ZM120,720L600,720L600,688Q600,677 594.5,668Q589,659 580,654Q526,627 471,613.5Q416,600 360,600Q304,600 249,613.5Q194,627 140,654Q131,659 125.5,668Q120,677 120,688L120,720ZM360,400Q393,400 416.5,376.5Q440,353 440,320Q440,287 416.5,263.5Q393,240 360,240Q327,240 303.5,263.5Q280,287 280,320Q280,353 303.5,376.5Q327,400 360,400ZM360,720L360,720L360,720Q360,720 360,720Q360,720 360,720Q360,720 360,720Q360,720 360,720Q360,720 360,720Q360,720 360,720Q360,720 360,720Q360,720 360,720ZM360,320Q360,320 360,320Q360,320 360,320Q360,320 360,320Q360,320 360,320Q360,320 360,320Q360,320 360,320Q360,320 360,320Q360,320 360,320Z" />
|
||||
android:pathData="M-19,861L-19,718Q-19,677 0.69,644.15Q20.38,611.29 53,594Q119,560 189.27,543Q259.54,526 332,526Q404.12,526 474.06,543Q544,560 610,594Q642.63,611.29 662.31,644.15Q682,677 682,718L682,861L-19,861ZM767,861L767,706Q767,664 744,614Q721,564 672,529Q735,535 790.5,551.5Q846,568 893,594Q934.84,617.77 956.92,646.88Q979,676 979,706L979,861L767,861ZM331,473Q253.45,473 198.23,417.78Q143,362.55 143,285Q143,207.45 198.23,152.22Q253.45,97 331,97Q408.55,97 463.77,152.22Q519,207.45 519,285Q519,362.55 463.77,417.78Q408.55,473 331,473ZM816,285Q816,362.55 760.78,417.78Q705.55,473 628,473Q613,473 587,469.5Q561,466 546,461Q575,423 589.5,378.15Q604,333.3 604,285Q604,236.7 589.5,191.85Q575,147 546,109Q566,102 587,99.5Q608,97 628,97Q705.55,97 760.78,152.22Q816,207.45 816,285Z" />
|
||||
</vector>
|
||||
|
@ -1,16 +1,15 @@
|
||||
<!--
|
||||
~ Nextcloud Talk - Android Client
|
||||
~
|
||||
~ SPDX-FileCopyrightText: 2018-2025 Google LLC
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
~ SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
~ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#FFFFFFFF"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,473 799.5,465.5Q799,458 799,453Q794,482 772,501Q750,520 720,520L640,520Q607,520 583.5,496.5Q560,473 560,440L560,400L400,400L400,320Q400,287 423.5,263.5Q447,240 480,240L520,240L520,240Q520,217 532.5,199.5Q545,182 563,171Q543,166 522.5,163Q502,160 480,160Q346,160 253,253Q160,346 160,480Q160,480 160,480Q160,480 160,480L360,480Q426,480 473,527Q520,574 520,640L520,680L400,680L400,790Q420,795 439.5,797.5Q459,800 480,800Z" />
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="m7.95,0.65c-4.1,0 -7.4,3.3 -7.4,7.4s3.3,7.4 7.4,7.4 7.4,-3.3 7.4,-7.4 -3.3,-7.4 -7.4,-7.4zM8.75,1.55c1.3,0 2.4,0.8 3.5,1.3l1.8,2.5 -0.3,1.1 0.6,0.3v2.4c-0.2,0.7 -0.6,1.3 -0.9,2 -0.2,0.1 0,-0.8 -0.1,-1 0,-0.6 -0.5,-0.6 -0.9,-0.2 -0.4,0.3 -1.4,0.3 -1.5,-0.4 -0.3,-0.8 0,-1.7 0.3,-2.5l-0.6,-0.7 0.2,-1.8 -0.8,-0.9 0.2,-1 -1,-0.6c-0.2,-0.2 -0.6,-0.2 -0.7,-0.4 0.1,0 0.2,-0.1 0.2,-0.1zM6.15,1.65s0.1,0 0.1,0.1c0.4,0.2 -0.1,0.4 -0.2,0.6 -0.5,0.3 0.3,0.7 0.5,1 0.4,-0.1 0.8,-0.7 1.4,-0.5 0.7,-0.2 0.6,0.6 1.1,1 0.1,0.2 0.9,0.8 0.4,0.6 -0.5,-0.4 -1,-0.4 -1.3,0.1 -0.8,0.5 -0.3,-0.9 -0.7,-1.2 -0.6,-0.7 -0.4,0.5 -0.4,0.9 -0.4,0 -1.1,-0.3 -1.5,0.2l0.4,0.6 0.5,-0.7c0,-0.3 0.1,0.2 0.3,0.3 0.1,0.2 0.8,0.7 0.3,0.9 -0.8,0.4 -1.4,1.1 -2.1,1.7 -0.2,0.5 -0.7,0.4 -1,0 -0.7,-0.4 -0.7,0.7 -0.6,1.1l0.6,-0.4v1.1c-0.4,0.4 -0.9,-0.7 -1.3,-0.9v-1.6c0,-0.4 -0.1,-0.9 0,-1.3 0.8,-0.9 1.7,-1.9 2.2,-3h0.8c0.6,0.2 0.3,-0.7 0.5,-0.6zM4.95,9.85c0.1,0 0.2,0 0.3,0.1 0.8,0.1 1.4,0.7 2,1.1 0.5,0.5 1.6,0.3 1.7,1.2 -0.2,0.9 -1.1,1.4 -1.8,1.7 -0.2,0.1 -0.4,0.2 -0.6,0.2 -0.7,0.2 -1,-0.6 -1.2,-1.1 -0.3,-0.7 -1.1,-1.2 -1,-2.1 0,-0.4 0.2,-1 0.6,-1.1z" />
|
||||
</vector>
|
||||
|
@ -1,16 +1,15 @@
|
||||
<!--
|
||||
~ Nextcloud Talk - Android Client
|
||||
~
|
||||
~ SPDX-FileCopyrightText: 2018-2025 Google LLC
|
||||
~ SPDX-FileCopyrightText: 2018-2024 Google LLC
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#FFFFFFFF"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M240,880Q207,880 183.5,856.5Q160,833 160,800L160,400Q160,367 183.5,343.5Q207,320 240,320L280,320L280,240Q280,157 338.5,98.5Q397,40 480,40Q563,40 621.5,98.5Q680,157 680,240L680,320L720,320Q753,320 776.5,343.5Q800,367 800,400L800,800Q800,833 776.5,856.5Q753,880 720,880L240,880ZM240,800L720,800Q720,800 720,800Q720,800 720,800L720,400Q720,400 720,400Q720,400 720,400L240,400Q240,400 240,400Q240,400 240,400L240,800Q240,800 240,800Q240,800 240,800ZM480,680Q513,680 536.5,656.5Q560,633 560,600Q560,567 536.5,543.5Q513,520 480,520Q447,520 423.5,543.5Q400,567 400,600Q400,633 423.5,656.5Q447,680 480,680ZM360,320L600,320L600,240Q600,190 565,155Q530,120 480,120Q430,120 395,155Q360,190 360,240L360,320ZM240,800Q240,800 240,800Q240,800 240,800L240,400Q240,400 240,400Q240,400 240,400L240,400Q240,400 240,400Q240,400 240,400L240,800Q240,800 240,800Q240,800 240,800Z" />
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M240,880Q207,880 183.5,856.5Q160,833 160,800L160,400Q160,367 183.5,343.5Q207,320 240,320L280,320L280,240Q280,157 338.5,98.5Q397,40 480,40Q563,40 621.5,98.5Q680,157 680,240L680,320L720,320Q753,320 776.5,343.5Q800,367 800,400L800,800Q800,833 776.5,856.5Q753,880 720,880L240,880ZM480,680Q513,680 536.5,656.5Q560,633 560,600Q560,567 536.5,543.5Q513,520 480,520Q447,520 423.5,543.5Q400,567 400,600Q400,633 423.5,656.5Q447,680 480,680ZM360,320L600,320L600,240Q600,190 565,155Q530,120 480,120Q430,120 395,155Q360,190 360,240L360,320Z" />
|
||||
</vector>
|
||||
|
@ -1,16 +1,16 @@
|
||||
<!--
|
||||
~ Nextcloud Talk - Android Client
|
||||
~
|
||||
~ SPDX-FileCopyrightText: 2018-2025 Google LLC
|
||||
~ SPDX-FileCopyrightText: 2018-2024 Google LLC
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#ffffff"
|
||||
android:fillColor="#ffffff"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M480,680Q497,680 508.5,668.5Q520,657 520,640Q520,623 508.5,611.5Q497,600 480,600Q463,600 451.5,611.5Q440,623 440,640Q440,657 451.5,668.5Q463,680 480,680ZM440,520L520,520L520,280L440,280L440,520ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z" />
|
||||
android:pathData="M480,680Q497,680 508.5,668.5Q520,657 520,640Q520,623 508.5,611.5Q497,600 480,600Q463,600 451.5,611.5Q440,623 440,640Q440,657 451.5,668.5Q463,680 480,680ZM440,520L520,520L520,280L440,280L440,520ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880Z" />
|
||||
</vector>
|
||||
|
@ -4,13 +4,15 @@
|
||||
~ SPDX-FileCopyrightText: 2018-2025 Google LLC
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:tint="#969696"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#969696"
|
||||
android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM240,680L720,680L570,480L450,640L360,520L240,680ZM340,400Q365,400 382.5,382.5Q400,365 400,340Q400,315 382.5,297.5Q365,280 340,280Q315,280 297.5,297.5Q280,315 280,340Q280,365 297.5,382.5Q315,400 340,400Z" />
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp">
|
||||
|
||||
<path android:fillColor="#969696"
|
||||
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
|
||||
|
||||
</vector>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<!--
|
||||
~ Nextcloud Talk - Android Client
|
||||
~
|
||||
~ SPDX-FileCopyrightText: 2023-2025 Google LLC
|
||||
~ SPDX-FileCopyrightText: 2023-2024 Google LLC
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
@ -12,5 +12,5 @@
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M480,680Q497,680 508.5,668.5Q520,657 520,640Q520,623 508.5,611.5Q497,600 480,600Q463,600 451.5,611.5Q440,623 440,640Q440,657 451.5,668.5Q463,680 480,680ZM440,520L520,520L520,280L440,280L440,520ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z" />
|
||||
android:pathData="M480,680Q497,680 508.5,668.5Q520,657 520,640Q520,623 508.5,611.5Q497,600 480,600Q463,600 451.5,611.5Q440,623 440,640Q440,657 451.5,668.5Q463,680 480,680ZM440,520L520,520L520,280L440,280L440,520ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880Z" />
|
||||
</vector>
|
||||
|
@ -5,13 +5,14 @@
|
||||
~ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:height="24dp"
|
||||
android:tint="#969696"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#969696"
|
||||
android:pathData="M240,880Q207,880 183.5,856.5Q160,833 160,800L160,160Q160,127 183.5,103.5Q207,80 240,80L560,80L800,320L800,800Q800,833 776.5,856.5Q753,880 720,880L240,880ZM520,360L720,360L520,160L520,360Z"/>
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp">
|
||||
|
||||
<path android:fillColor="#969696"
|
||||
android:pathData="M6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6L6,2zM13,9L13,3.5L18.5,9L13,9z"/>
|
||||
|
||||
</vector>
|
||||
|
@ -4,13 +4,15 @@
|
||||
~ SPDX-FileCopyrightText: 2025 Google LLC
|
||||
~ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#969696"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#969696"
|
||||
android:pathData="M480,480Q513,480 536.5,456.5Q560,433 560,400Q560,367 536.5,343.5Q513,320 480,320Q447,320 423.5,343.5Q400,367 400,400Q400,433 423.5,456.5Q447,480 480,480ZM480,880Q319,743 239.5,625.5Q160,508 160,408Q160,258 256.5,169Q353,80 480,80Q607,80 703.5,169Q800,258 800,408Q800,508 720.5,625.5Q641,743 480,880Z" />
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp">
|
||||
|
||||
<path android:fillColor="#969696"
|
||||
android:pathData="M12,2L12,2C8.13,2 5,5.13 5,9c0,1.74 0.5,3.37 1.41,4.84c0.95,1.54 2.2,2.86 3.16,4.4c0.47,0.75 0.81,1.45 1.17,2.26C11,21.05 11.21,22 12,22h0c0.79,0 1,-0.95 1.25,-1.5c0.37,-0.81 0.7,-1.51 1.17,-2.26c0.96,-1.53 2.21,-2.85 3.16,-4.4C18.5,12.37 19,10.74 19,9C19,5.13 15.87,2 12,2zM12,11.75c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5S13.38,11.75 12,11.75z"/>
|
||||
|
||||
</vector>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<!--
|
||||
~ Nextcloud Talk - Android Client
|
||||
~
|
||||
~ SPDX-FileCopyrightText: 2025 Google LLC
|
||||
~ SPDX-FileCopyrightText: 2024 Google LLC
|
||||
~ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
|
@ -5,12 +5,13 @@
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#969696"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M480,560Q430,560 395,525Q360,490 360,440L360,200Q360,150 395,115Q430,80 480,80Q530,80 565,115Q600,150 600,200L600,440Q600,490 565,525Q530,560 480,560ZM480,320Q480,320 480,320Q480,320 480,320L480,320Q480,320 480,320Q480,320 480,320Q480,320 480,320Q480,320 480,320L480,320Q480,320 480,320Q480,320 480,320ZM440,840L440,717Q336,703 268,624Q200,545 200,440L280,440Q280,523 338.5,581.5Q397,640 480,640Q563,640 621.5,581.5Q680,523 680,440L760,440Q760,545 692,624Q624,703 520,717L520,840L440,840ZM480,480Q497,480 508.5,468.5Q520,457 520,440L520,200Q520,183 508.5,171.5Q497,160 480,160Q463,160 451.5,171.5Q440,183 440,200L440,440Q440,457 451.5,468.5Q463,480 480,480Z" />
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp">
|
||||
|
||||
<path android:fillColor="#969696"
|
||||
android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/>
|
||||
|
||||
</vector>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<!--
|
||||
~ Nextcloud Talk - Android Client
|
||||
~
|
||||
~ SPDX-FileCopyrightText: 2021-2025 Google LLC
|
||||
~ SPDX-FileCopyrightText: 2021-2024 Google LLC
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
@ -12,5 +12,5 @@
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M160,760L160,680L240,680L240,400Q240,317 290,252.5Q340,188 420,168L420,140Q420,115 437.5,97.5Q455,80 480,80Q505,80 522.5,97.5Q540,115 540,140L540,168Q620,188 670,252.5Q720,317 720,400L720,680L800,680L800,760L160,760ZM480,460L480,460L480,460L480,460Q480,460 480,460Q480,460 480,460Q480,460 480,460Q480,460 480,460ZM480,880Q447,880 423.5,856.5Q400,833 400,800L560,800Q560,833 536.5,856.5Q513,880 480,880ZM320,680L640,680L640,400Q640,334 593,287Q546,240 480,240Q414,240 367,287Q320,334 320,400L320,680Z" />
|
||||
android:pathData="M160,760L160,680L240,680L240,400Q240,317 290,252.5Q340,188 420,168L420,140Q420,115 437.5,97.5Q455,80 480,80Q505,80 522.5,97.5Q540,115 540,140L540,168Q620,188 670,252.5Q720,317 720,400L720,680L800,680L800,760L160,760ZM480,880Q447,880 423.5,856.5Q400,833 400,800L560,800Q560,833 536.5,856.5Q513,880 480,880Z" />
|
||||
</vector>
|
||||
|
@ -8,9 +8,8 @@
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#000000"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M360,560L760,560L622,380L530,500L468,420L360,560ZM320,720Q287,720 263.5,696.5Q240,673 240,640L240,160Q240,127 263.5,103.5Q287,80 320,80L800,80Q833,80 856.5,103.5Q880,127 880,160L880,640Q880,673 856.5,696.5Q833,720 800,720L320,720ZM320,640L800,640Q800,640 800,640Q800,640 800,640L800,160Q800,160 800,160Q800,160 800,160L320,160Q320,160 320,160Q320,160 320,160L320,640Q320,640 320,640Q320,640 320,640ZM160,880Q127,880 103.5,856.5Q80,833 80,800L80,240L160,240L160,800Q160,800 160,800Q160,800 160,800L720,800L720,880L160,880ZM320,160L320,160Q320,160 320,160Q320,160 320,160L320,640Q320,640 320,640Q320,640 320,640L320,640Q320,640 320,640Q320,640 320,640L320,160Q320,160 320,160Q320,160 320,160Z" />
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="@android:color/white"
|
||||
android:pathData="M22,16L22,4c0,-1.1 -0.9,-2 -2,-2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zM11,12l2.03,2.71L16,11l4,5L8,16l3,-4zM2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6L2,6z"/>
|
||||
</vector>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<!--
|
||||
~ Nextcloud Talk - Android Client
|
||||
~
|
||||
~ SPDX-FileCopyrightText: 2021-2025 Google LLC
|
||||
~ SPDX-FileCopyrightText: 2021-2024 Google LLC
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
@ -12,5 +12,5 @@
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M40,840L480,80L920,840L40,840ZM178,760L782,760L480,240L178,760ZM480,720Q497,720 508.5,708.5Q520,697 520,680Q520,663 508.5,651.5Q497,640 480,640Q463,640 451.5,651.5Q440,663 440,680Q440,697 451.5,708.5Q463,720 480,720ZM440,600L520,600L520,400L440,400L440,600ZM480,500L480,500L480,500Z" />
|
||||
android:pathData="M40,840L480,80L920,840L40,840ZM480,720Q497,720 508.5,708.5Q520,697 520,680Q520,663 508.5,651.5Q497,640 480,640Q463,640 451.5,651.5Q440,663 440,680Q440,697 451.5,708.5Q463,720 480,720ZM440,600L520,600L520,400L440,400L440,600Z" />
|
||||
</vector>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user