diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 000000000..1da62f192
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,16 @@
+FROM ubuntu:focal
+
+ARG DEBIAN_FRONTEND=noninteractive
+ENV ANDROID_HOME=/usr/lib/android-sdk
+
+RUN apt-get update -y
+RUN apt-get install -y unzip wget openjdk-8-jdk vim
+
+RUN wget https://dl.google.com/android/repository/commandlinetools-linux-6858069_latest.zip -O /tmp/commandlinetools.zip
+RUN cd /tmp && unzip commandlinetools.zip
+RUN mkdir -p /usr/lib/android-sdk/cmdline-tools/
+RUN cd /tmp/ && mv cmdline-tools/ latest/ && mv latest/ /usr/lib/android-sdk/cmdline-tools/
+RUN mkdir /usr/lib/android-sdk/licenses/
+RUN chmod -R 755 /usr/lib/android-sdk/
+RUN mkdir -p $HOME/.gradle
+RUN echo "org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError" > $HOME/.gradle/gradle.properties
diff --git a/.devcontainer/README.md b/.devcontainer/README.md
new file mode 100644
index 000000000..d1c77f7f5
--- /dev/null
+++ b/.devcontainer/README.md
@@ -0,0 +1,5 @@
+# Instructions
+
+1. Start a DevContainer either on GitHub Codespaces or locally in VSCode
+2. Accept all licenses by running `yes | /usr/lib/android-sdk/cmdline-tools/latest/bin/sdkmanager --licenses`
+3. You can now build the app using `./gradlew clean build`
diff --git a/.devcontainer/devcontainer.env b/.devcontainer/devcontainer.env
new file mode 100644
index 000000000..369163cf4
--- /dev/null
+++ b/.devcontainer/devcontainer.env
@@ -0,0 +1,3 @@
+ANDROID_HOME=/usr/lib/android-sdk
+JAVA_OPTS="-Xmx8192M"
+GRADLE_OPTS="-Dorg.gradle.daemon=true"
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 000000000..eb2bcab3b
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,4 @@
+{
+ "name": "NextcloudTalkAndroid",
+ "dockerFile": "Dockerfile",
+}
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..56dbc10e5
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,2 @@
+# You can add one username per supported platform and one custom link
+custom: https://nextcloud.com/include/
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index ca927340c..da58e1a6c 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,11 +1,16 @@
version: 2
updates:
-- package-ecosystem: gradle
- directory: "/"
- schedule:
- interval: daily
- time: "03:00"
- timezone: Europe/Paris
- open-pull-requests-limit: 10
- labels:
- - 3. to review
+ - package-ecosystem: "github-actions"
+ directory: "/" # Location of package manifests
+ schedule:
+ interval: "weekly"
+ - package-ecosystem: gradle
+ directory: "/"
+ schedule:
+ interval: daily
+ time: "03:00"
+ timezone: Europe/Paris
+ open-pull-requests-limit: 10
+ labels:
+ - 3. to review
+ - dependencies
diff --git a/.github/workflows/assembleFlavors.yml b/.github/workflows/assembleFlavors.yml
new file mode 100644
index 000000000..fc3b946c6
--- /dev/null
+++ b/.github/workflows/assembleFlavors.yml
@@ -0,0 +1,30 @@
+name: "Assemble"
+
+on:
+ pull_request:
+ branches: [ master, stable-* ]
+
+jobs:
+ flavor:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ flavor: [ Generic, Gplay ]
+ steps:
+ - uses: actions/checkout@v2
+ - name: set up JDK 1.8
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+ - name: Build ${{ matrix.flavor }}
+ run: |
+ echo "org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError" >> gradle.properties
+ ./gradlew assemble${{ matrix.flavor }}
+ - name: Archive apk
+ uses: actions/upload-artifact@v2
+ if: ${{ always() }}
+ with:
+ name: Nextcloud-APK
+ path: app/build/outputs/apk/**/**/*.apk
+ retention-days: 5
diff --git a/.github/workflows/autoApproveDependabot.yml b/.github/workflows/autoApproveDependabot.yml
new file mode 100644
index 000000000..ce4498d7e
--- /dev/null
+++ b/.github/workflows/autoApproveDependabot.yml
@@ -0,0 +1,13 @@
+name: Auto approve
+on:
+ pull_request_target:
+ branches: [ master, stable-* ]
+
+jobs:
+ auto-approve:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: hmarr/auto-approve-action@v2.0.0
+ if: github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]'
+ with:
+ github-token: "${{ secrets.GITHUB_TOKEN }}"
diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
new file mode 100644
index 000000000..261effcd2
--- /dev/null
+++ b/.github/workflows/check.yml
@@ -0,0 +1,21 @@
+name: Check
+
+on:
+ pull_request:
+ branches: [ master, stable-* ]
+
+jobs:
+ check:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ task: [ detekt, ktlint ]
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up JDK 1.8
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+ - name: Check ${{ matrix.task }}
+ run: ./gradlew ${{ matrix.task }}
diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml
new file mode 100644
index 000000000..09307118b
--- /dev/null
+++ b/.github/workflows/gradle-wrapper-validation.yml
@@ -0,0 +1,13 @@
+name: "Validate Gradle Wrapper"
+
+on:
+ pull_request:
+ branches: [ master, stable-* ]
+
+jobs:
+ validation:
+ name: "Validation"
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: gradle/wrapper-validation-action@v1
diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml
new file mode 100644
index 000000000..431267bb4
--- /dev/null
+++ b/.github/workflows/qa.yml
@@ -0,0 +1,29 @@
+name: "QA"
+
+on:
+ pull_request:
+ branches: [ master, stable-* ]
+
+jobs:
+ qa:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: set up JDK 1.8
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+ - name: Build QA
+ env:
+ KS_PASS: ${{ secrets.KS_PASS }}
+ KEY_PASS: ${{ secrets.KEY_PASS }}
+ LOG_USERNAME: ${{ secrets.LOG_USERNAME }}
+ LOG_PASSWORD: ${{ secrets.LOG_PASSWORD }}
+ run: |
+ mkdir -p $HOME/.gradle
+ echo "org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError" > $HOME/.gradle/gradle.properties
+ sed -i "/qa/,/\}/ s/versionCode .*/versionCode ${{github.event.number}} /" app/build.gradle
+ sed -i "/qa/,/\}/ s/versionName .*/versionName \"${{github.event.number}}\"/" app/build.gradle
+ ./gradlew assembleQaDebug
+ $(find /usr/local/lib/android/sdk/build-tools/*/apksigner | sort | tail -n1) sign --ks-pass pass:$KS_PASS --key-pass pass:$KEY_PASS --ks-key-alias key0 --ks scripts/QA_keystore.jks app/build/outputs/apk/qa/debug/app-qa-*.apk
+ sudo scripts/uploadArtifact.sh $LOG_USERNAME $LOG_PASSWORD ${{github.event.number}} ${{github.event.number}} ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 000000000..3a6af4421
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,19 @@
+name: 'Close stale issues'
+on:
+ schedule:
+ - cron: '* */2 * * *'
+
+jobs:
+ stale:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/stale@v3
+ with:
+ days-before-stale: 28
+ days-before-close: 14
+ days-before-pr-close: -1
+ only-labels: 'bug,needs info/discussion'
+ stale-issue-message: 'This bug report did not receive an update in the last 4 weeks.
+ Please take a look again and update the issue with new details,
+ otherwise the issue will be automatically closed in 2 weeks. Thank you!'
+ exempt-all-pr-milestones: true
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 000000000..59163c1e4
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,219 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ xmlns:android
+
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+
+ ^$
+
+
+
+
+
+
+
+
+ style
+
+ ^$
+
+
+
+
+
+
+
+
+ .*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+
+ .*
+
+
+ BY_NAME
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 000000000..79ee123c2
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/ktlint.xml b/.idea/inspectionProfiles/ktlint.xml
new file mode 100644
index 000000000..7d04a74be
--- /dev/null
+++ b/.idea/inspectionProfiles/ktlint.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 000000000..64580d143
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 357dbe3c5..7f9f495e1 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -23,6 +23,11 @@ apply plugin: 'kotlin-android'
apply plugin: 'findbugs'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
+apply plugin: 'io.gitlab.arturbosch.detekt'
+
+configurations {
+ ktlint
+}
def taskRequest = getGradle().getStartParameter().getTaskRequests().toString()
if (taskRequest.contains("Gplay") || taskRequest.contains("findbugs") || taskRequest.contains("lint")) {
@@ -33,7 +38,6 @@ android {
compileSdkVersion 29
buildToolsVersion '28.0.3'
defaultConfig {
- applicationId "com.nextcloud.talk2"
minSdkVersion 21
targetSdkVersion 29
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -49,9 +53,20 @@ android {
productFlavors {
// used for f-droid
- generic
- gplay
-
+ generic {
+ applicationId 'com.nextcloud.talk2'
+ dimension "default"
+ }
+ gplay {
+ applicationId 'com.nextcloud.talk2'
+ dimension "default"
+ }
+ qa {
+ applicationId "com.nextcloud.talk2.qa"
+ dimension "default"
+ versionCode 1
+ versionName "1"
+ }
}
// Enabling multidex support.
@@ -86,6 +101,7 @@ android {
}
packagingOptions {
+ exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
exclude 'META-INF/rxjava.properties'
@@ -161,6 +177,7 @@ dependencies {
implementation ('com.gitlab.bitfireAT:dav4jvm:f2078bc846', {
exclude group: 'org.ogce', module: 'xpp3' // Android comes with its own XmlPullParser
})
+ ktlint "com.pinterest:ktlint:0.41.0"
implementation 'org.conscrypt:conscrypt-android:2.5.1'
@@ -272,6 +289,30 @@ dependencies {
findbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.6'
}
+task ktlint(type: JavaExec, group: "verification") {
+ description = "Check Kotlin code style."
+ main = "com.pinterest.ktlint.Main"
+ classpath = configurations.ktlint
+ args "--reporter=plain", "--reporter=plain,output=${buildDir}/ktlint.txt,src/**/*.kt"
+}
+
+task ktlintFormat(type: JavaExec, group: "formatting") {
+ description = "Fix Kotlin code style deviations."
+ main = "com.pinterest.ktlint.Main"
+ classpath = configurations.ktlint
+ args "-F", "src/**/*.kt"
+}
+
+detekt {
+ reports {
+ xml {
+ enabled = false
+ }
+ }
+ config = files("../detekt.yml")
+ input = files("src/")
+}
+
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions {
jvmTarget = "1.8"
diff --git a/app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.kt b/app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.kt
index 32ac819e6..f94110196 100644
--- a/app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.kt
+++ b/app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.kt
@@ -104,7 +104,6 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
@Inject
var eventBus: EventBus? = null
-
override fun onCreate() {
super.onCreate()
sharedApplication!!.componentApplication.inject(this)
@@ -148,18 +147,26 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
val pushUtils = PushUtils()
val privateKey = pushUtils.readKeyFromFile(false) as PrivateKey
try {
- signatureVerification = pushUtils.verifySignature(base64DecodedSignature,
- base64DecodedSubject)
+ signatureVerification = pushUtils.verifySignature(
+ base64DecodedSignature,
+ base64DecodedSubject
+ )
if (signatureVerification!!.signatureValid) {
val cipher = Cipher.getInstance("RSA/None/PKCS1Padding")
cipher.init(Cipher.DECRYPT_MODE, privateKey)
val decryptedSubject = cipher.doFinal(base64DecodedSubject)
- decryptedPushMessage = LoganSquare.parse(String(decryptedSubject),
- DecryptedPushMessage::class.java)
+ decryptedPushMessage = LoganSquare.parse(
+ String(decryptedSubject),
+ DecryptedPushMessage::class.java
+ )
decryptedPushMessage?.apply {
timestamp = System.currentTimeMillis()
if (delete) {
- cancelExistingNotificationWithId(applicationContext, signatureVerification!!.userEntity, notificationId)
+ cancelExistingNotificationWithId(
+ applicationContext,
+ signatureVerification!!.userEntity,
+ notificationId
+ )
} else if (deleteAll) {
cancelAllNotificationsForAccount(applicationContext, signatureVerification!!.userEntity)
} else if (type == "call") {
@@ -171,39 +178,66 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
fullScreenIntent.putExtras(bundle)
fullScreenIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
- val fullScreenPendingIntent = PendingIntent.getActivity(this@MagicFirebaseMessagingService, 0, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT)
+ val fullScreenPendingIntent = PendingIntent.getActivity(
+ this@MagicFirebaseMessagingService,
+ 0,
+ fullScreenIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT
+ )
- val audioAttributesBuilder = AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ val audioAttributesBuilder =
+ AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
audioAttributesBuilder.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST)
val ringtonePreferencesString: String? = appPreferences!!.callRingtoneUri
val soundUri = if (TextUtils.isEmpty(ringtonePreferencesString)) {
- Uri.parse("android.resource://" + applicationContext.packageName +
- "/raw/librem_by_feandesign_call")
+ Uri.parse(
+ "android.resource://" + applicationContext.packageName +
+ "/raw/librem_by_feandesign_call"
+ )
} else {
try {
- val ringtoneSettings = LoganSquare.parse(ringtonePreferencesString, RingtoneSettings::class.java)
+ val ringtoneSettings =
+ LoganSquare.parse(ringtonePreferencesString, RingtoneSettings::class.java)
ringtoneSettings.ringtoneUri
} catch (exception: IOException) {
Uri.parse("android.resource://" + applicationContext.packageName + "/raw/librem_by_feandesign_call")
}
}
- val notificationChannelId = NotificationUtils.getNotificationChannelId(applicationContext.resources
- .getString(R.string.nc_notification_channel_calls), applicationContext.resources
- .getString(R.string.nc_notification_channel_calls_description), true,
- NotificationManagerCompat.IMPORTANCE_HIGH, soundUri!!, audioAttributesBuilder.build(), null, false)
+ val notificationChannelId = NotificationUtils.getNotificationChannelId(
+ applicationContext.resources
+ .getString(R.string.nc_notification_channel_calls),
+ applicationContext.resources
+ .getString(R.string.nc_notification_channel_calls_description),
+ true,
+ NotificationManagerCompat.IMPORTANCE_HIGH,
+ soundUri!!,
+ audioAttributesBuilder.build(),
+ null,
+ false
+ )
- createNotificationChannel(applicationContext!!,
- notificationChannelId, applicationContext.resources
- .getString(R.string.nc_notification_channel_calls), applicationContext.resources
- .getString(R.string.nc_notification_channel_calls_description), true,
- NotificationManagerCompat.IMPORTANCE_HIGH, soundUri, audioAttributesBuilder.build(), null, false)
+ createNotificationChannel(
+ applicationContext!!,
+ notificationChannelId,
+ applicationContext.resources
+ .getString(R.string.nc_notification_channel_calls),
+ applicationContext.resources
+ .getString(R.string.nc_notification_channel_calls_description),
+ true,
+ NotificationManagerCompat.IMPORTANCE_HIGH,
+ soundUri,
+ audioAttributesBuilder.build(),
+ null,
+ false
+ )
val uri = Uri.parse(signatureVerification!!.userEntity.baseUrl)
val baseUrl = uri.host
- val notification = NotificationCompat.Builder(this@MagicFirebaseMessagingService, notificationChannelId)
+ val notification =
+ NotificationCompat.Builder(this@MagicFirebaseMessagingService, notificationChannelId)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_CALL)
.setSmallIcon(R.drawable.ic_call_black_24dp)
@@ -213,7 +247,7 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
.setContentTitle(EmojiCompat.get().process(decryptedPushMessage!!.subject))
.setAutoCancel(true)
.setOngoing(true)
- //.setTimeoutAfter(45000L)
+ // .setTimeoutAfter(45000L)
.setContentIntent(fullScreenPendingIntent)
.setFullScreenIntent(fullScreenPendingIntent, true)
.setSound(soundUri)
@@ -224,10 +258,12 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
startForeground(decryptedPushMessage!!.timestamp.toInt(), notification)
} else {
val messageData = Data.Builder()
- .putString(BundleKeys.KEY_NOTIFICATION_SUBJECT, subject)
- .putString(BundleKeys.KEY_NOTIFICATION_SIGNATURE, signature)
+ .putString(BundleKeys.KEY_NOTIFICATION_SUBJECT, subject)
+ .putString(BundleKeys.KEY_NOTIFICATION_SIGNATURE, signature)
+ .build()
+ val pushNotificationWork =
+ OneTimeWorkRequest.Builder(NotificationWorker::class.java).setInputData(messageData)
.build()
- val pushNotificationWork = OneTimeWorkRequest.Builder(NotificationWorker::class.java).setInputData(messageData).build()
WorkManager.getInstance().enqueue(pushNotificationWork)
}
}
@@ -244,47 +280,59 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
}
}
- private fun checkIfCallIsActive(signatureVerification: SignatureVerification, decryptedPushMessage: DecryptedPushMessage) {
- val ncApi = retrofit!!.newBuilder().client(okHttpClient!!.newBuilder().cookieJar(JavaNetCookieJar(CookieManager())).build()).build().create(NcApi::class.java)
+ private fun checkIfCallIsActive(
+ signatureVerification: SignatureVerification,
+ decryptedPushMessage: DecryptedPushMessage
+ ) {
+ val ncApi = retrofit!!.newBuilder()
+ .client(okHttpClient!!.newBuilder().cookieJar(JavaNetCookieJar(CookieManager())).build()).build()
+ .create(NcApi::class.java)
var hasParticipantsInCall = false
var inCallOnDifferentDevice = false
- ncApi.getPeersForCall(ApiUtils.getCredentials(signatureVerification.userEntity.username, signatureVerification.userEntity.token),
- ApiUtils.getUrlForCall(signatureVerification.userEntity.baseUrl,
- decryptedPushMessage.id))
- .takeWhile {
- isServiceInForeground
+ ncApi.getPeersForCall(
+ ApiUtils.getCredentials(signatureVerification.userEntity.username, signatureVerification.userEntity.token),
+ ApiUtils.getUrlForCall(
+ signatureVerification.userEntity.baseUrl,
+ decryptedPushMessage.id
+ )
+ )
+ .takeWhile {
+ isServiceInForeground
+ }
+ .subscribeOn(Schedulers.io())
+ .subscribe(object : Observer {
+ override fun onSubscribe(d: Disposable) {
}
- .subscribeOn(Schedulers.io())
- .subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {
- }
- override fun onNext(participantsOverall: ParticipantsOverall) {
- val participantList: List = participantsOverall.ocs.data
- hasParticipantsInCall = participantList.isNotEmpty()
- if (!hasParticipantsInCall) {
- for (participant in participantList) {
- if (participant.userId == signatureVerification.userEntity.userId) {
- inCallOnDifferentDevice = true
- break
- }
+ override fun onNext(participantsOverall: ParticipantsOverall) {
+ val participantList: List = participantsOverall.ocs.data
+ hasParticipantsInCall = participantList.isNotEmpty()
+ if (!hasParticipantsInCall) {
+ for (participant in participantList) {
+ if (participant.userId == signatureVerification.userEntity.userId) {
+ inCallOnDifferentDevice = true
+ break
}
}
+ }
- if (!hasParticipantsInCall || inCallOnDifferentDevice) {
- stopForeground(true)
- handler.removeCallbacksAndMessages(null)
- } else if (isServiceInForeground) {
- handler.postDelayed({
+ if (!hasParticipantsInCall || inCallOnDifferentDevice) {
+ stopForeground(true)
+ handler.removeCallbacksAndMessages(null)
+ } else if (isServiceInForeground) {
+ handler.postDelayed(
+ {
checkIfCallIsActive(signatureVerification, decryptedPushMessage)
- }, 5000)
- }
+ },
+ 5000
+ )
}
+ }
- override fun onError(e: Throwable) {}
- override fun onComplete() {
- }
- })
+ override fun onError(e: Throwable) {}
+ override fun onComplete() {
+ }
+ })
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/nextcloud/talk/activities/BaseActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/BaseActivity.kt
index f55f66caa..7a36c608a 100644
--- a/app/src/main/java/com/nextcloud/talk/activities/BaseActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/activities/BaseActivity.kt
@@ -76,8 +76,11 @@ open class BaseActivity : AppCompatActivity() {
}
}
- fun showCertificateDialog(cert: X509Certificate, magicTrustManager: MagicTrustManager,
- sslErrorHandler: SslErrorHandler?) {
+ fun showCertificateDialog(
+ cert: X509Certificate,
+ magicTrustManager: MagicTrustManager,
+ sslErrorHandler: SslErrorHandler?
+ ) {
val formatter = DateFormat.getDateInstance(DateFormat.LONG)
val validFrom = formatter.format(cert.notBefore)
val validUntil = formatter.format(cert.notAfter)
@@ -101,30 +104,30 @@ open class BaseActivity : AppCompatActivity() {
issuedFor = cert.subjectDN.name
}
- @SuppressLint("StringFormatMatches") val dialogText = String.format(resources
+ @SuppressLint("StringFormatMatches") val dialogText = String.format(
+ resources
.getString(R.string.nc_certificate_dialog_text),
- issuedBy, issuedFor, validFrom, validUntil)
+ issuedBy, issuedFor, validFrom, validUntil
+ )
LovelyStandardDialog(this)
- .setTopColorRes(R.color.nc_darkRed)
- .setNegativeButtonColorRes(R.color.nc_darkRed)
- .setPositiveButtonColorRes(R.color.colorPrimary)
- .setIcon(R.drawable.ic_security_white_24dp)
- .setTitle(R.string.nc_certificate_dialog_title)
- .setMessage(dialogText)
- .setPositiveButton(R.string.nc_yes) { v ->
- magicTrustManager.addCertInTrustStore(cert)
- sslErrorHandler?.proceed()
- }
- .setNegativeButton(R.string.nc_no) { view1 ->
- sslErrorHandler?.cancel()
- }
- .show()
-
+ .setTopColorRes(R.color.nc_darkRed)
+ .setNegativeButtonColorRes(R.color.nc_darkRed)
+ .setPositiveButtonColorRes(R.color.colorPrimary)
+ .setIcon(R.drawable.ic_security_white_24dp)
+ .setTitle(R.string.nc_certificate_dialog_title)
+ .setMessage(dialogText)
+ .setPositiveButton(R.string.nc_yes) { v ->
+ magicTrustManager.addCertInTrustStore(cert)
+ sslErrorHandler?.proceed()
+ }
+ .setNegativeButton(R.string.nc_no) { view1 ->
+ sslErrorHandler?.cancel()
+ }
+ .show()
} catch (e: CertificateParsingException) {
Log.d(TAG, "Failed to parse the certificate")
}
-
}
@Subscribe(threadMode = ThreadMode.MAIN)
diff --git a/app/src/main/java/com/nextcloud/talk/activities/FullScreenImageActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/FullScreenImageActivity.kt
index efb703f70..61ebf843b 100644
--- a/app/src/main/java/com/nextcloud/talk/activities/FullScreenImageActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/activities/FullScreenImageActivity.kt
@@ -38,7 +38,6 @@ import pl.droidsonroids.gif.GifDrawable
import pl.droidsonroids.gif.GifImageView
import java.io.File
-
class FullScreenImageActivity : AppCompatActivity() {
private lateinit var path: String
@@ -55,9 +54,11 @@ class FullScreenImageActivity : AppCompatActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item.itemId == R.id.share) {
- val shareUri = FileProvider.getUriForFile(this,
- BuildConfig.APPLICATION_ID,
- File(path))
+ val shareUri = FileProvider.getUriForFile(
+ this,
+ BuildConfig.APPLICATION_ID,
+ File(path)
+ )
val shareIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
@@ -78,19 +79,19 @@ class FullScreenImageActivity : AppCompatActivity() {
setContentView(R.layout.activity_full_screen_image)
setSupportActionBar(findViewById(R.id.imageview_toolbar))
- supportActionBar?.setDisplayShowTitleEnabled(false);
+ supportActionBar?.setDisplayShowTitleEnabled(false)
imageWrapperView = findViewById(R.id.image_wrapper_view)
photoView = findViewById(R.id.photo_view)
gifView = findViewById(R.id.gif_view)
- photoView.setOnPhotoTapListener{ view, x, y ->
+ photoView.setOnPhotoTapListener { view, x, y ->
toggleFullscreen()
}
- photoView.setOnOutsidePhotoTapListener{
+ photoView.setOnOutsidePhotoTapListener {
toggleFullscreen()
}
- gifView.setOnClickListener{
+ gifView.setOnClickListener {
toggleFullscreen()
}
@@ -115,29 +116,33 @@ class FullScreenImageActivity : AppCompatActivity() {
}
}
- private fun toggleFullscreen(){
- showFullscreen = !showFullscreen;
- if (showFullscreen){
+ private fun toggleFullscreen() {
+ showFullscreen = !showFullscreen
+ if (showFullscreen) {
hideSystemUI()
supportActionBar?.hide()
- } else{
+ } else {
showSystemUI()
supportActionBar?.show()
}
}
private fun hideSystemUI() {
- window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
+ window.decorView.systemUiVisibility = (
+ View.SYSTEM_UI_FLAG_IMMERSIVE
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_FULLSCREEN)
+ or View.SYSTEM_UI_FLAG_FULLSCREEN
+ )
}
private fun showSystemUI() {
- window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ window.decorView.systemUiVisibility = (
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
+ or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ )
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/nextcloud/talk/activities/FullScreenMediaActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/FullScreenMediaActivity.kt
index 640f581e6..d5a689624 100644
--- a/app/src/main/java/com/nextcloud/talk/activities/FullScreenMediaActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/activities/FullScreenMediaActivity.kt
@@ -22,7 +22,6 @@ package com.nextcloud.talk.activities
import android.content.Intent
import android.os.Bundle
-import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
@@ -33,7 +32,6 @@ import autodagger.AutoInjector
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer
-import com.google.android.exoplayer2.ui.PlayerControlView
import com.google.android.exoplayer2.ui.StyledPlayerView
import com.nextcloud.talk.BuildConfig
import com.nextcloud.talk.R
@@ -54,9 +52,11 @@ class FullScreenMediaActivity : AppCompatActivity(), Player.EventListener {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item.itemId == R.id.share) {
- val shareUri = FileProvider.getUriForFile(this,
- BuildConfig.APPLICATION_ID,
- File(path))
+ val shareUri = FileProvider.getUriForFile(
+ this,
+ BuildConfig.APPLICATION_ID,
+ File(path)
+ )
val shareIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
@@ -82,11 +82,11 @@ class FullScreenMediaActivity : AppCompatActivity(), Player.EventListener {
setContentView(R.layout.activity_full_screen_media)
setSupportActionBar(findViewById(R.id.mediaview_toolbar))
- supportActionBar?.setDisplayShowTitleEnabled(false);
+ supportActionBar?.setDisplayShowTitleEnabled(false)
playerView = findViewById(R.id.player_view)
- window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
playerView.showController()
if (isAudioOnly) {
@@ -121,7 +121,7 @@ class FullScreenMediaActivity : AppCompatActivity(), Player.EventListener {
private fun initializePlayer() {
player = SimpleExoPlayer.Builder(applicationContext).build()
- playerView.player = player;
+ playerView.player = player
player.playWhenReady = true
player.addListener(this)
}
@@ -131,17 +131,21 @@ class FullScreenMediaActivity : AppCompatActivity(), Player.EventListener {
}
private fun hideSystemUI() {
- window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
+ window.decorView.systemUiVisibility = (
+ View.SYSTEM_UI_FLAG_IMMERSIVE
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_FULLSCREEN)
+ or View.SYSTEM_UI_FLAG_FULLSCREEN
+ )
}
private fun showSystemUI() {
- window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ window.decorView.systemUiVisibility = (
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
+ or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ )
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/activities/FullScreenTextViewerActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/FullScreenTextViewerActivity.kt
index c550c49dc..867fb9b39 100644
--- a/app/src/main/java/com/nextcloud/talk/activities/FullScreenTextViewerActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/activities/FullScreenTextViewerActivity.kt
@@ -34,7 +34,6 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
import io.noties.markwon.Markwon
import java.io.File
-
@AutoInjector(NextcloudTalkApplication::class)
class FullScreenTextViewerActivity : AppCompatActivity() {
@@ -48,9 +47,11 @@ class FullScreenTextViewerActivity : AppCompatActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item.itemId == R.id.share) {
- val shareUri = FileProvider.getUriForFile(this,
- BuildConfig.APPLICATION_ID,
- File(path))
+ val shareUri = FileProvider.getUriForFile(
+ this,
+ BuildConfig.APPLICATION_ID,
+ File(path)
+ )
val shareIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
@@ -71,8 +72,7 @@ class FullScreenTextViewerActivity : AppCompatActivity() {
setContentView(R.layout.activity_full_screen_text)
setSupportActionBar(findViewById(R.id.textview_toolbar))
- supportActionBar?.setDisplayShowTitleEnabled(false);
-
+ supportActionBar?.setDisplayShowTitleEnabled(false)
textView = findViewById(R.id.text_view)
val fileName = intent.getStringExtra("FILE_NAME")
@@ -81,13 +81,12 @@ class FullScreenTextViewerActivity : AppCompatActivity() {
var text = readFile(path)
if (isMarkdown) {
- val markwon = Markwon.create(applicationContext);
- markwon.setMarkdown(textView, text);
+ val markwon = Markwon.create(applicationContext)
+ markwon.setMarkdown(textView, text)
} else {
textView.text = text
}
}
private fun readFile(fileName: String) = File(fileName).inputStream().readBytes().toString(Charsets.UTF_8)
-
}
diff --git a/app/src/main/java/com/nextcloud/talk/activities/MagicCallActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MagicCallActivity.kt
index 5421d24bd..3a0ee85da 100644
--- a/app/src/main/java/com/nextcloud/talk/activities/MagicCallActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/activities/MagicCallActivity.kt
@@ -48,7 +48,7 @@ class MagicCallActivity : BaseActivity() {
@BindView(R.id.controller_container)
lateinit var container: ViewGroup
-
+
@BindView(R.id.chatControllerView)
lateinit var chatContainer: ViewGroup
@@ -60,10 +60,12 @@ class MagicCallActivity : BaseActivity() {
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
requestWindowFeature(Window.FEATURE_NO_TITLE)
- window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN or
+ window.addFlags(
+ WindowManager.LayoutParams.FLAG_FULLSCREEN or
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
- WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
+ WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ )
window.decorView.systemUiVisibility = systemUiVisibility
setContentView(R.layout.activity_magic_call)
@@ -74,26 +76,32 @@ class MagicCallActivity : BaseActivity() {
if (!router!!.hasRootController()) {
if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) {
- router!!.setRoot(RouterTransaction.with(CallNotificationController(intent.extras))
+ router!!.setRoot(
+ RouterTransaction.with(CallNotificationController(intent.extras))
.pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
+ .popChangeHandler(HorizontalChangeHandler())
+ )
} else {
- router!!.setRoot(RouterTransaction.with(CallController(intent.extras))
+ router!!.setRoot(
+ RouterTransaction.with(CallController(intent.extras))
.pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
+ .popChangeHandler(HorizontalChangeHandler())
+ )
}
}
val extras = intent.extras ?: Bundle()
extras.putBoolean("showToggleChat", true)
-
+
chatController = ChatController(extras)
chatRouter = Conductor.attachRouter(this, chatContainer, savedInstanceState)
- chatRouter!!.setRoot(RouterTransaction.with(chatController)
+ chatRouter!!.setRoot(
+ RouterTransaction.with(chatController)
.pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
+ .popChangeHandler(HorizontalChangeHandler())
+ )
}
-
+
fun showChat() {
chatContainer.visibility = View.VISIBLE
container.visibility = View.GONE
diff --git a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt
index 430eca8a8..897ebbb15 100644
--- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt
@@ -79,23 +79,31 @@ class MainActivity : BaseActivity(), ActionBarProvider {
@BindView(R.id.appBar)
lateinit var appBar: AppBarLayout
+
@BindView(R.id.toolbar)
lateinit var toolbar: MaterialToolbar
+
@BindView(R.id.home_toolbar)
lateinit var searchCardView: MaterialCardView
+
@BindView(R.id.search_text)
lateinit var searchInputText: MaterialTextView
+
@BindView(R.id.switch_account_button)
lateinit var settingsButton: MaterialButton
+
@BindView(R.id.controller_container)
lateinit var container: ViewGroup
@Inject
lateinit var userUtils: UserUtils
+
@Inject
lateinit var dataStore: ReactiveEntityStore
+
@Inject
lateinit var sqlCipherDatabaseSource: SqlCipherDatabaseSource
+
@Inject
lateinit var ncApi: NcApi
@@ -124,39 +132,53 @@ class MainActivity : BaseActivity(), ActionBarProvider {
if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) {
if (!router!!.hasRootController()) {
- router!!.setRoot(RouterTransaction.with(ConversationsListController())
+ router!!.setRoot(
+ RouterTransaction.with(ConversationsListController())
.pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
+ .popChangeHandler(HorizontalChangeHandler())
+ )
}
onNewIntent(intent)
} else if (!router!!.hasRootController()) {
if (hasDb) {
if (userUtils.anyUserExists()) {
- router!!.setRoot(RouterTransaction.with(ConversationsListController())
+ router!!.setRoot(
+ RouterTransaction.with(ConversationsListController())
.pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
+ .popChangeHandler(HorizontalChangeHandler())
+ )
} else {
if (!TextUtils.isEmpty(resources.getString(R.string.weblogin_url))) {
- router!!.pushController(RouterTransaction.with(
- WebViewLoginController(resources.getString(R.string.weblogin_url), false))
+ router!!.pushController(
+ RouterTransaction.with(
+ WebViewLoginController(resources.getString(R.string.weblogin_url), false)
+ )
.pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
+ .popChangeHandler(HorizontalChangeHandler())
+ )
} else {
- router!!.setRoot(RouterTransaction.with(ServerSelectionController())
+ router!!.setRoot(
+ RouterTransaction.with(ServerSelectionController())
.pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
+ .popChangeHandler(HorizontalChangeHandler())
+ )
}
}
} else {
if (!TextUtils.isEmpty(resources.getString(R.string.weblogin_url))) {
- router!!.pushController(RouterTransaction.with(
- WebViewLoginController(resources.getString(R.string.weblogin_url), false))
+ router!!.pushController(
+ RouterTransaction.with(
+ WebViewLoginController(resources.getString(R.string.weblogin_url), false)
+ )
.pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
+ .popChangeHandler(HorizontalChangeHandler())
+ )
} else {
- router!!.setRoot(RouterTransaction.with(ServerSelectionController())
+ router!!.setRoot(
+ RouterTransaction.with(ServerSelectionController())
.pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
+ .popChangeHandler(HorizontalChangeHandler())
+ )
}
}
}
@@ -167,7 +189,7 @@ class MainActivity : BaseActivity(), ActionBarProvider {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
checkIfWeAreSecure()
}
-
+
handleActionFromContact(intent)
}
@@ -182,7 +204,7 @@ class MainActivity : BaseActivity(), ActionBarProvider {
// userId @ server
userId = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA1))
}
-
+
cursor.close()
}
@@ -193,65 +215,82 @@ class MainActivity : BaseActivity(), ActionBarProvider {
if (userUtils.currentUser?.baseUrl?.endsWith(baseUrl) == true) {
startConversation(user)
} else {
- Snackbar.make(container, R.string.nc_phone_book_integration_account_not_found, Snackbar
- .LENGTH_LONG).show()
+ Snackbar.make(
+ container, R.string.nc_phone_book_integration_account_not_found,
+ Snackbar.LENGTH_LONG
+ ).show()
}
}
}
}
}
-
+
private fun startConversation(userId: String) {
val roomType = "1"
val currentUser = userUtils.currentUser ?: return
val credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token)
- val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(currentUser.baseUrl, roomType,
- userId, null)
- ncApi.createRoom(credentials,
- retrofitBucket.url, retrofitBucket.queryMap)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {}
- override fun onNext(roomOverall: RoomOverall) {
- val conversationIntent = Intent(context, MagicCallActivity::class.java)
- val bundle = Bundle()
- bundle.putParcelable(KEY_USER_ENTITY, currentUser)
- bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs.data.token)
- bundle.putString(KEY_ROOM_ID, roomOverall.ocs.data.roomId)
- if (currentUser.hasSpreedFeatureCapability("chat-v2")) {
- ncApi.getRoom(credentials,
- ApiUtils.getRoom(currentUser.baseUrl,
- roomOverall.ocs.data.token))
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {}
- override fun onNext(roomOverall: RoomOverall) {
- bundle.putParcelable(KEY_ACTIVE_CONVERSATION,
- Parcels.wrap(roomOverall.ocs.data))
- remapChatController(router!!, currentUser.id,
- roomOverall.ocs.data.token, bundle, true)
- }
+ val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
+ currentUser.baseUrl, roomType,
+ userId, null
+ )
+ ncApi.createRoom(
+ credentials,
+ retrofitBucket.url, retrofitBucket.queryMap
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(object : Observer {
+ override fun onSubscribe(d: Disposable) {}
+ override fun onNext(roomOverall: RoomOverall) {
+ val conversationIntent = Intent(context, MagicCallActivity::class.java)
+ val bundle = Bundle()
+ bundle.putParcelable(KEY_USER_ENTITY, currentUser)
+ bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs.data.token)
+ bundle.putString(KEY_ROOM_ID, roomOverall.ocs.data.roomId)
+ if (currentUser.hasSpreedFeatureCapability("chat-v2")) {
+ ncApi.getRoom(
+ credentials,
+ ApiUtils.getRoom(
+ currentUser.baseUrl,
+ roomOverall.ocs.data.token
+ )
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(object : Observer {
+ override fun onSubscribe(d: Disposable) {}
+ override fun onNext(roomOverall: RoomOverall) {
+ bundle.putParcelable(
+ KEY_ACTIVE_CONVERSATION,
+ Parcels.wrap(roomOverall.ocs.data)
+ )
+ remapChatController(
+ router!!, currentUser.id,
+ roomOverall.ocs.data.token, bundle, true
+ )
+ }
- override fun onError(e: Throwable) {}
- override fun onComplete() {}
- })
- } else {
- conversationIntent.putExtras(bundle)
- startActivity(conversationIntent)
- Handler().postDelayed({
+ override fun onError(e: Throwable) {}
+ override fun onComplete() {}
+ })
+ } else {
+ conversationIntent.putExtras(bundle)
+ startActivity(conversationIntent)
+ Handler().postDelayed(
+ {
if (!isDestroyed) {
router!!.popCurrentController()
}
- }, 100)
- }
+ },
+ 100
+ )
}
+ }
- override fun onError(e: Throwable) {}
- override fun onComplete() {}
- })
+ override fun onError(e: Throwable) {}
+ override fun onComplete() {}
+ })
}
@RequiresApi(api = Build.VERSION_CODES.M)
@@ -260,16 +299,17 @@ class MainActivity : BaseActivity(), ActionBarProvider {
if (keyguardManager.isKeyguardSecure && appPreferences.isScreenLocked) {
if (!SecurityUtils.checkIfWeAreAuthenticated(appPreferences.screenLockTimeout)) {
if (router != null && router!!.getControllerWithTag(LockedController.TAG) == null) {
- router!!.pushController(RouterTransaction.with(LockedController())
+ router!!.pushController(
+ RouterTransaction.with(LockedController())
.pushChangeHandler(VerticalChangeHandler())
.popChangeHandler(VerticalChangeHandler())
- .tag(LockedController.TAG))
+ .tag(LockedController.TAG)
+ )
}
}
}
}
-
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
@@ -277,12 +317,16 @@ class MainActivity : BaseActivity(), ActionBarProvider {
if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) {
if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) {
- router!!.pushController(RouterTransaction.with(CallNotificationController(intent.extras))
+ router!!.pushController(
+ RouterTransaction.with(CallNotificationController(intent.extras))
.pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
+ .popChangeHandler(HorizontalChangeHandler())
+ )
} else {
- ConductorRemapping.remapChatController(router!!, intent.getLongExtra(BundleKeys.KEY_INTERNAL_USER_ID, -1),
- intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN), intent.extras!!, false)
+ ConductorRemapping.remapChatController(
+ router!!, intent.getLongExtra(BundleKeys.KEY_INTERNAL_USER_ID, -1),
+ intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN), intent.extras!!, false
+ )
}
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.kt
index aa8392f89..1de435c3a 100644
--- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.kt
+++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicIncomingTextMessageViewHolder.kt
@@ -39,7 +39,6 @@ import autodagger.AutoInjector
import butterknife.BindView
import butterknife.ButterKnife
import coil.load
-import coil.transform.CircleCropTransformation
import com.amulyakhare.textdrawable.TextDrawable
import com.facebook.drawee.view.SimpleDraweeView
import com.nextcloud.talk.R
@@ -51,7 +50,6 @@ import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.TextMatchers
import com.nextcloud.talk.utils.preferences.AppPreferences
import com.stfalcon.chatkit.messages.MessageHolders
-import com.stfalcon.chatkit.utils.DateFormatter
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
@@ -104,8 +102,8 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
init {
ButterKnife.bind(
- this,
- itemView
+ this,
+ itemView
)
}
@@ -131,13 +129,13 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
messageUserAvatarView?.setImageDrawable(DisplayUtils.getRoundedDrawable(layerDrawable))
} else if (message.actorType == "bots") {
val drawable = TextDrawable.builder()
- .beginConfig()
- .bold()
- .endConfig()
- .buildRound(
- ">",
- context!!.resources.getColor(R.color.black)
- )
+ .beginConfig()
+ .bold()
+ .endConfig()
+ .buildRound(
+ ">",
+ context!!.resources.getColor(R.color.black)
+ )
messageUserAvatarView!!.visibility = View.VISIBLE
messageUserAvatarView?.setImageDrawable(drawable)
}
@@ -165,9 +163,9 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
}
val bubbleDrawable = DisplayUtils.getMessageSelector(
- bgBubbleColor,
- resources.getColor(R.color.transparent),
- bgBubbleColor, bubbleResource
+ bgBubbleColor,
+ resources.getColor(R.color.transparent),
+ bgBubbleColor, bubbleResource
)
ViewCompat.setBackground(bubble, bubbleDrawable)
@@ -187,23 +185,23 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
if (individualHashMap["type"] == "user" || individualHashMap["type"] == "guest" || individualHashMap["type"] == "call") {
if (individualHashMap["id"] == message.activeUser!!.userId) {
messageString = DisplayUtils.searchAndReplaceWithMentionSpan(
- messageText!!.context,
- messageString,
- individualHashMap["id"]!!,
- individualHashMap["name"]!!,
- individualHashMap["type"]!!,
- message.activeUser!!,
- R.xml.chip_you
+ messageText!!.context,
+ messageString,
+ individualHashMap["id"]!!,
+ individualHashMap["name"]!!,
+ individualHashMap["type"]!!,
+ message.activeUser!!,
+ R.xml.chip_you
)
} else {
messageString = DisplayUtils.searchAndReplaceWithMentionSpan(
- messageText!!.context,
- messageString,
- individualHashMap["id"]!!,
- individualHashMap["name"]!!,
- individualHashMap["type"]!!,
- message.activeUser!!,
- R.xml.chip_others
+ messageText!!.context,
+ messageString,
+ individualHashMap["id"]!!,
+ individualHashMap["name"]!!,
+ individualHashMap["type"]!!,
+ message.activeUser!!,
+ R.xml.chip_others
)
}
} else if (individualHashMap["type"] == "file") {
@@ -231,18 +229,21 @@ class MagicIncomingTextMessageViewHolder(incomingView: View) : MessageHolders
parentChatMessage.imageUrl?.let {
quotedMessagePreview?.visibility = View.VISIBLE
quotedMessagePreview?.load(it) {
- addHeader("Authorization", ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token))
+ addHeader(
+ "Authorization",
+ ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token)
+ )
}
} ?: run {
quotedMessagePreview?.visibility = View.GONE
}
quotedUserName?.text = parentChatMessage.actorDisplayName
- ?: context!!.getText(R.string.nc_nick_guest)
+ ?: context!!.getText(R.string.nc_nick_guest)
quotedMessage?.text = parentChatMessage.text
quotedUserName?.setTextColor(context!!.resources.getColor(R.color.textColorMaxContrast))
- if(parentChatMessage.actorId?.equals(message.activeUser.userId) == true) {
+ if (parentChatMessage.actorId?.equals(message.activeUser.userId) == true) {
quoteColoredView?.setBackgroundResource(R.color.colorPrimary)
} else {
quoteColoredView?.setBackgroundResource(R.color.textColorMaxContrast)
diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.kt
index ab03b7e69..84a22d33b 100644
--- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.kt
+++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicOutcomingTextMessageViewHolder.kt
@@ -36,7 +36,6 @@ import autodagger.AutoInjector
import butterknife.BindView
import butterknife.ButterKnife
import coil.load
-import coil.transform.CircleCropTransformation
import com.google.android.flexbox.FlexboxLayout
import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication
@@ -48,8 +47,7 @@ import com.nextcloud.talk.utils.DisplayUtils.getMessageSelector
import com.nextcloud.talk.utils.DisplayUtils.searchAndReplaceWithMentionSpan
import com.nextcloud.talk.utils.TextMatchers
import com.stfalcon.chatkit.messages.MessageHolders.OutcomingTextMessageViewHolder
-import com.stfalcon.chatkit.utils.DateFormatter
-import java.util.*
+import java.util.HashMap
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
@@ -57,6 +55,7 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
@JvmField
@BindView(R.id.messageText)
var messageText: EmojiTextView? = null
+
@JvmField
@BindView(R.id.messageTime)
var messageTimeView: TextView? = null
@@ -104,20 +103,26 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
for (key in messageParameters.keys) {
val individualHashMap: HashMap? = message.messageParameters[key]
if (individualHashMap != null) {
- if (individualHashMap["type"] == "user" || (individualHashMap["type"]
- == "guest") || individualHashMap["type"] == "call") {
- messageString = searchAndReplaceWithMentionSpan(messageText!!.context,
- messageString,
- individualHashMap["id"]!!,
- individualHashMap["name"]!!,
- individualHashMap["type"]!!,
- message.activeUser,
- R.xml.chip_others)
+ if (individualHashMap["type"] == "user" || (
+ individualHashMap["type"] == "guest"
+ ) || individualHashMap["type"] == "call"
+ ) {
+ messageString = searchAndReplaceWithMentionSpan(
+ messageText!!.context,
+ messageString,
+ individualHashMap["id"]!!,
+ individualHashMap["name"]!!,
+ individualHashMap["type"]!!,
+ message.activeUser,
+ R.xml.chip_others
+ )
} else if (individualHashMap["type"] == "file") {
- realView.setOnClickListener(View.OnClickListener { v: View? ->
- val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(individualHashMap["link"]))
- context!!.startActivity(browserIntent)
- })
+ realView.setOnClickListener(
+ View.OnClickListener { v: View? ->
+ val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(individualHashMap["link"]))
+ context!!.startActivity(browserIntent)
+ }
+ )
}
}
}
@@ -135,17 +140,19 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
}
if (message.isGrouped) {
val bubbleDrawable = getMessageSelector(
- bgBubbleColor,
- resources.getColor(R.color.transparent),
- bgBubbleColor,
- R.drawable.shape_grouped_outcoming_message)
+ bgBubbleColor,
+ resources.getColor(R.color.transparent),
+ bgBubbleColor,
+ R.drawable.shape_grouped_outcoming_message
+ )
ViewCompat.setBackground(bubble, bubbleDrawable)
} else {
val bubbleDrawable = getMessageSelector(
- bgBubbleColor,
- resources.getColor(R.color.transparent),
- bgBubbleColor,
- R.drawable.shape_outcoming_message)
+ bgBubbleColor,
+ resources.getColor(R.color.transparent),
+ bgBubbleColor,
+ R.drawable.shape_outcoming_message
+ )
ViewCompat.setBackground(bubble, bubbleDrawable)
}
messageText!!.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
@@ -160,13 +167,16 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
parentChatMessage.imageUrl?.let {
quotedMessagePreview?.visibility = View.VISIBLE
quotedMessagePreview?.load(it) {
- addHeader("Authorization", ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token))
+ addHeader(
+ "Authorization",
+ ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token)
+ )
}
} ?: run {
quotedMessagePreview?.visibility = View.GONE
}
quotedUserName?.text = parentChatMessage.actorDisplayName
- ?: context!!.getText(R.string.nc_nick_guest)
+ ?: context!!.getText(R.string.nc_nick_guest)
quotedMessage?.text = parentChatMessage.text
quotedMessage?.setTextColor(context!!.resources.getColor(R.color.nc_outcoming_text_default))
quotedUserName?.setTextColor(context!!.resources.getColor(R.color.nc_grey))
diff --git a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt
index ce8e1f912..3ecad94cf 100644
--- a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt
+++ b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt
@@ -90,6 +90,7 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
@Inject
lateinit var appPreferences: AppPreferences
+
@Inject
lateinit var okHttpClient: OkHttpClient
//endregion
@@ -105,8 +106,10 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true)
}
- PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(this)
- .createInitializationOptions())
+ PeerConnectionFactory.initialize(
+ PeerConnectionFactory.InitializationOptions.builder(this)
+ .createInitializationOptions()
+ )
} catch (e: UnsatisfiedLinkError) {
Log.w(TAG, e)
}
@@ -120,8 +123,8 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
val securityKeyManager = SecurityKeyManager.getInstance()
val securityKeyConfig = SecurityKeyManagerConfig.Builder()
- .setEnableDebugLogging(BuildConfig.DEBUG)
- .build()
+ .setEnableDebugLogging(BuildConfig.DEBUG)
+ .build()
securityKeyManager.init(this, securityKeyConfig)
initializeWebRtc()
@@ -136,13 +139,15 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
super.onCreate()
val imagePipelineConfig = ImagePipelineConfig.newBuilder(this)
- .setNetworkFetcher(OkHttpNetworkFetcherWithCache(okHttpClient))
- .setMainDiskCacheConfig(DiskCacheConfig.newBuilder(this)
- .setMaxCacheSize(0)
- .setMaxCacheSizeOnLowDiskSpace(0)
- .setMaxCacheSizeOnVeryLowDiskSpace(0)
- .build())
- .build()
+ .setNetworkFetcher(OkHttpNetworkFetcherWithCache(okHttpClient))
+ .setMainDiskCacheConfig(
+ DiskCacheConfig.newBuilder(this)
+ .setMaxCacheSize(0)
+ .setMaxCacheSizeOnLowDiskSpace(0)
+ .setMaxCacheSizeOnVeryLowDiskSpace(0)
+ .build()
+ )
+ .build()
Fresco.initialize(this, imagePipelineConfig)
Security.insertProviderAt(Conscrypt.newProvider(), 1)
@@ -152,8 +157,10 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
val pushRegistrationWork = OneTimeWorkRequest.Builder(PushRegistrationWorker::class.java).build()
val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build()
- val periodicCapabilitiesUpdateWork = PeriodicWorkRequest.Builder(CapabilitiesWorker::class.java,
- 12, TimeUnit.HOURS).build()
+ val periodicCapabilitiesUpdateWork = PeriodicWorkRequest.Builder(
+ CapabilitiesWorker::class.java,
+ 12, TimeUnit.HOURS
+ ).build()
val capabilitiesUpdateWork = OneTimeWorkRequest.Builder(CapabilitiesWorker::class.java).build()
val signalingSettingsWork = OneTimeWorkRequest.Builder(SignalingSettingsWorker::class.java).build()
@@ -161,7 +168,11 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
WorkManager.getInstance().enqueue(accountRemovalWork)
WorkManager.getInstance().enqueue(capabilitiesUpdateWork)
WorkManager.getInstance().enqueue(signalingSettingsWork)
- WorkManager.getInstance().enqueueUniquePeriodicWork("DailyCapabilitiesUpdateWork", ExistingPeriodicWorkPolicy.REPLACE, periodicCapabilitiesUpdateWork)
+ WorkManager.getInstance().enqueueUniquePeriodicWork(
+ "DailyCapabilitiesUpdateWork",
+ ExistingPeriodicWorkPolicy.REPLACE,
+ periodicCapabilitiesUpdateWork
+ )
val config = BundledEmojiCompatConfig(this)
config.setReplaceAll(true)
@@ -176,17 +187,16 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
}
//endregion
-
//region Protected methods
protected fun buildComponent() {
componentApplication = DaggerNextcloudTalkApplicationComponent.builder()
- .busModule(BusModule())
- .contextModule(ContextModule(applicationContext))
- .databaseModule(DatabaseModule())
- .restModule(RestModule(applicationContext))
- .userModule(UserModule())
- .arbitraryStorageModule(ArbitraryStorageModule())
- .build()
+ .busModule(BusModule())
+ .contextModule(ContextModule(applicationContext))
+ .databaseModule(DatabaseModule())
+ .restModule(RestModule(applicationContext))
+ .userModule(UserModule())
+ .arbitraryStorageModule(ArbitraryStorageModule())
+ .build()
}
override fun attachBaseContext(base: Context) {
@@ -196,19 +206,20 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver {
private fun buildDefaultImageLoader(): ImageLoader {
return ImageLoader.Builder(applicationContext)
- .availableMemoryPercentage(0.5) // Use 50% of the application's available memory.
- .crossfade(true) // Show a short crossfade when loading images from network or disk into an ImageView.
- .componentRegistry {
- if (SDK_INT >= P) {
- add(ImageDecoderDecoder(applicationContext))
- } else {
- add(GifDecoder())
- }
- add(SvgDecoder(applicationContext))
+ .availableMemoryPercentage(0.5) // Use 50% of the application's available memory.
+ .crossfade(true) // Show a short crossfade when loading images from network or disk into an ImageView.
+ .componentRegistry {
+ if (SDK_INT >= P) {
+ add(ImageDecoderDecoder(applicationContext))
+ } else {
+ add(GifDecoder())
}
- .okHttpClient(okHttpClient)
- .build()
+ add(SvgDecoder(applicationContext))
+ }
+ .okHttpClient(okHttpClient)
+ .build()
}
+
companion object {
private val TAG = NextcloudTalkApplication::class.java.simpleName
//region Singleton
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java b/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java
index 7fbb99d02..64b8e77ba 100644
--- a/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java
+++ b/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java
@@ -44,10 +44,6 @@ import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.constraintlayout.widget.ConstraintLayout;
-
import com.bluelinelabs.conductor.RouterTransaction;
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
import com.bluelinelabs.logansquare.LoganSquare;
@@ -71,7 +67,6 @@ import com.nextcloud.talk.models.RingtoneSettings;
import com.nextcloud.talk.models.database.UserEntity;
import com.nextcloud.talk.models.json.conversations.Conversation;
import com.nextcloud.talk.models.json.conversations.RoomOverall;
-import com.nextcloud.talk.models.json.conversations.RoomsOverall;
import com.nextcloud.talk.models.json.participants.Participant;
import com.nextcloud.talk.models.json.participants.ParticipantsOverall;
import com.nextcloud.talk.utils.ApiUtils;
@@ -94,6 +89,9 @@ import java.util.List;
import javax.inject.Inject;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.constraintlayout.widget.ConstraintLayout;
import autodagger.AutoInjector;
import butterknife.BindView;
import butterknife.OnClick;
@@ -206,13 +204,13 @@ public class CallNotificationController extends BaseController {
originalBundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), currentConversation.getDisplayName());
getRouter().replaceTopController(RouterTransaction.with(new CallController(originalBundle))
- .popChangeHandler(new HorizontalChangeHandler())
- .pushChangeHandler(new HorizontalChangeHandler()));
+ .popChangeHandler(new HorizontalChangeHandler())
+ .pushChangeHandler(new HorizontalChangeHandler()));
}
private void checkIfAnyParticipantsRemainInRoom() {
ncApi.getPeersForCall(credentials, ApiUtils.getUrlForCall(userBeingCalled.getBaseUrl(),
- currentConversation.getToken()))
+ currentConversation.getToken()))
.subscribeOn(Schedulers.io())
.takeWhile(observable -> !leavingScreen)
.subscribe(new Observer() {
@@ -261,7 +259,7 @@ public class CallNotificationController extends BaseController {
private void handleFromNotification() {
boolean isConversationApiV3 = userBeingCalled.hasSpreedFeatureCapability("conversation-v3");
- if(isConversationApiV3) {
+ if (isConversationApiV3) {
ncApi.getRoom(credentials, ApiUtils.getRoomV3(userBeingCalled.getBaseUrl(), roomId))
.subscribeOn(Schedulers.io())
.retry(3)
@@ -279,12 +277,12 @@ public class CallNotificationController extends BaseController {
boolean hasCallFlags = userBeingCalled.hasSpreedFeatureCapability("conversation-call-flags");
if (hasCallFlags) {
- if (isInCallWithVideo(currentConversation.callFlag)){
+ if (isInCallWithVideo(currentConversation.callFlag)) {
incomingCallVoiceOrVideoTextView.setText(String.format(getResources().getString(R.string.nc_call_video),
- getResources().getString(R.string.nc_app_name)));
+ getResources().getString(R.string.nc_app_name)));
} else {
incomingCallVoiceOrVideoTextView.setText(String.format(getResources().getString(R.string.nc_call_voice),
- getResources().getString(R.string.nc_app_name)));
+ getResources().getString(R.string.nc_app_name)));
}
}
}
@@ -412,7 +410,7 @@ public class CallNotificationController extends BaseController {
ImageRequest imageRequest =
DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(userBeingCalled.getBaseUrl(),
- currentConversation.getName(), R.dimen.avatar_size_very_big), null);
+ currentConversation.getName(), R.dimen.avatar_size_very_big), null);
ImagePipeline imagePipeline = Fresco.getImagePipeline();
DataSource> dataSource = imagePipeline.fetchDecodedImage(imageRequest, null);
@@ -422,11 +420,11 @@ public class CallNotificationController extends BaseController {
protected void onNewResultImpl(@Nullable Bitmap bitmap) {
if (avatarImageView != null) {
avatarImageView.getHierarchy().setImage(new BitmapDrawable(bitmap), 100,
- true);
+ true);
if (getResources() != null) {
incomingTextRelativeLayout.setBackground(getResources().getDrawable(R.drawable
- .incoming_gradient));
+ .incoming_gradient));
}
if ((AvatarStatusCodeHolder.getInstance().getStatusCode() == 200 || AvatarStatusCodeHolder.getInstance().getStatusCode() == 0) &&
@@ -512,7 +510,7 @@ public class CallNotificationController extends BaseController {
if (TextUtils.isEmpty(callRingtonePreferenceString)) {
// play default sound
ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() +
- "/raw/librem_by_feandesign_call");
+ "/raw/librem_by_feandesign_call");
} else {
try {
RingtoneSettings ringtoneSettings = LoganSquare.parse(callRingtonePreferenceString, RingtoneSettings.class);
@@ -520,7 +518,7 @@ public class CallNotificationController extends BaseController {
} catch (IOException e) {
Log.e(TAG, "Failed to parse ringtone settings");
ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() +
- "/raw/librem_by_feandesign_call");
+ "/raw/librem_by_feandesign_call");
}
}
@@ -531,7 +529,7 @@ public class CallNotificationController extends BaseController {
mediaPlayer.setLooping(true);
AudioAttributes audioAttributes = new AudioAttributes.Builder().setContentType(AudioAttributes
- .CONTENT_TYPE_SONIFICATION).setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build();
+ .CONTENT_TYPE_SONIFICATION).setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build();
mediaPlayer.setAudioAttributes(audioAttributes);
mediaPlayer.setOnPreparedListener(mp -> mediaPlayer.start());
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt
index 15427beec..7facc123a 100644
--- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt
+++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt
@@ -40,8 +40,22 @@ import android.text.TextUtils
import android.text.TextWatcher
import android.util.Log
import android.util.TypedValue
-import android.view.*
-import android.widget.*
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import android.widget.AbsListView
+import android.widget.ImageButton
+import android.widget.ImageView
+import android.widget.PopupMenu
+import android.widget.ProgressBar
+import android.widget.RelativeLayout
+import android.widget.Space
+import android.widget.TextView
+import android.widget.Toast
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
import androidx.emoji.text.EmojiCompat
import androidx.emoji.widget.EmojiEditText
@@ -55,7 +69,6 @@ import autodagger.AutoInjector
import butterknife.BindView
import butterknife.OnClick
import coil.load
-import coil.transform.CircleCropTransformation
import com.bluelinelabs.conductor.RouterTransaction
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler
@@ -69,7 +82,12 @@ import com.facebook.imagepipeline.image.CloseableImage
import com.google.android.flexbox.FlexboxLayout
import com.nextcloud.talk.R
import com.nextcloud.talk.activities.MagicCallActivity
-import com.nextcloud.talk.adapters.messages.*
+import com.nextcloud.talk.adapters.messages.MagicIncomingTextMessageViewHolder
+import com.nextcloud.talk.adapters.messages.MagicOutcomingTextMessageViewHolder
+import com.nextcloud.talk.adapters.messages.MagicPreviewMessageViewHolder
+import com.nextcloud.talk.adapters.messages.MagicSystemMessageViewHolder
+import com.nextcloud.talk.adapters.messages.MagicUnreadNoticeMessageViewHolder
+import com.nextcloud.talk.adapters.messages.TalkMessagesListAdapter
import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.callbacks.MentionAutocompleteCallback
@@ -91,7 +109,14 @@ import com.nextcloud.talk.models.json.generic.GenericOverall
import com.nextcloud.talk.models.json.mention.Mention
import com.nextcloud.talk.presenters.MentionAutocompletePresenter
import com.nextcloud.talk.ui.dialog.AttachmentDialog
-import com.nextcloud.talk.utils.*
+import com.nextcloud.talk.utils.ApiUtils
+import com.nextcloud.talk.utils.ConductorRemapping
+import com.nextcloud.talk.utils.DateUtils
+import com.nextcloud.talk.utils.DisplayUtils
+import com.nextcloud.talk.utils.KeyboardUtils
+import com.nextcloud.talk.utils.MagicCharPolicy
+import com.nextcloud.talk.utils.NotificationUtils
+import com.nextcloud.talk.utils.UriUtils
import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.database.user.UserUtils
import com.nextcloud.talk.utils.preferences.AppPreferences
@@ -121,14 +146,20 @@ import org.parceler.Parcels
import retrofit2.HttpException
import retrofit2.Response
import java.net.HttpURLConnection
-import java.util.*
+import java.util.ArrayList
+import java.util.Date
+import java.util.HashMap
+import java.util.Objects
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
-class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
-.OnLoadMoreListener, MessagesListAdapter.Formatter, MessagesListAdapter
-.OnMessageViewLongClickListener, MessageHolders.ContentChecker {
+class ChatController(args: Bundle) :
+ BaseController(args),
+ MessagesListAdapter.OnLoadMoreListener,
+ MessagesListAdapter.Formatter,
+ MessagesListAdapter.OnMessageViewLongClickListener,
+ MessageHolders.ContentChecker {
@Inject
@JvmField
@@ -236,7 +267,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
this.roomToken = args.getString(BundleKeys.KEY_ROOM_TOKEN, "")
if (args.containsKey(BundleKeys.KEY_ACTIVE_CONVERSATION)) {
- this.currentConversation = Parcels.unwrap(args.getParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION))
+ this.currentConversation =
+ Parcels.unwrap(args.getParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION))
}
this.roomPassword = args.getString(BundleKeys.KEY_CONVERSATION_PASSWORD, "")
@@ -260,74 +292,71 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
checkingLobbyStatus = true
}
-
if (conversationUser != null) {
- ncApi?.getRoom(credentials, ApiUtils.getRoom(conversationUser.baseUrl, roomToken))?.subscribeOn(Schedulers.io())
- ?.observeOn(AndroidSchedulers.mainThread())
- ?.subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {
- disposableList.add(d)
+ ncApi?.getRoom(credentials, ApiUtils.getRoom(conversationUser.baseUrl, roomToken))
+ ?.subscribeOn(Schedulers.io())
+ ?.observeOn(AndroidSchedulers.mainThread())
+ ?.subscribe(object : Observer {
+ override fun onSubscribe(d: Disposable) {
+ disposableList.add(d)
+ }
+
+ override fun onNext(roomOverall: RoomOverall) {
+ currentConversation = roomOverall.ocs.data
+ loadAvatarForStatusBar()
+
+ setTitle()
+ setupMentionAutocomplete()
+ checkReadOnlyState()
+ checkLobbyState()
+
+ if (!inConversation) {
+ joinRoomWithPassword()
}
+ }
- override fun onNext(roomOverall: RoomOverall) {
- currentConversation = roomOverall.ocs.data
- loadAvatarForStatusBar()
+ override fun onError(e: Throwable) {
+ }
- setTitle()
- setupMentionAutocomplete()
- checkReadOnlyState()
- checkLobbyState()
-
- if (!inConversation) {
- joinRoomWithPassword()
+ override fun onComplete() {
+ if (shouldRepeat) {
+ if (lobbyTimerHandler == null) {
+ lobbyTimerHandler = Handler()
}
+ lobbyTimerHandler?.postDelayed({ getRoomInfo() }, 5000)
}
-
- override fun onError(e: Throwable) {
-
- }
-
- override fun onComplete() {
- if (shouldRepeat) {
- if (lobbyTimerHandler == null) {
- lobbyTimerHandler = Handler()
- }
-
- lobbyTimerHandler?.postDelayed({ getRoomInfo() }, 5000)
- }
- }
- })
+ }
+ })
}
}
private fun handleFromNotification() {
ncApi?.getRooms(credentials, ApiUtils.getUrlForGetRooms(conversationUser?.baseUrl))
- ?.subscribeOn(Schedulers.io())?.observeOn(AndroidSchedulers.mainThread())?.subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {
- disposableList.add(d)
- }
+ ?.subscribeOn(Schedulers.io())?.observeOn(AndroidSchedulers.mainThread())
+ ?.subscribe(object : Observer {
+ override fun onSubscribe(d: Disposable) {
+ disposableList.add(d)
+ }
- override fun onNext(roomsOverall: RoomsOverall) {
- for (conversation in roomsOverall.ocs.data) {
- if (roomId == conversation.roomId) {
- roomToken = conversation.token
- currentConversation = conversation
- setTitle()
- getRoomInfo()
- break
- }
+ override fun onNext(roomsOverall: RoomsOverall) {
+ for (conversation in roomsOverall.ocs.data) {
+ if (roomId == conversation.roomId) {
+ roomToken = conversation.token
+ currentConversation = conversation
+ setTitle()
+ getRoomInfo()
+ break
}
}
+ }
- override fun onError(e: Throwable) {
+ override fun onError(e: Throwable) {
+ }
- }
-
- override fun onComplete() {
-
- }
- })
+ override fun onComplete() {
+ }
+ })
}
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
@@ -336,33 +365,44 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
private fun loadAvatarForStatusBar() {
if (inOneToOneCall() && activity != null && conversationVoiceCallMenuItem != null) {
- val avatarSize = DisplayUtils.convertDpToPixel(conversationVoiceCallMenuItem?.icon!!
- .intrinsicWidth.toFloat(), activity).toInt()
+ val avatarSize = DisplayUtils.convertDpToPixel(
+ conversationVoiceCallMenuItem?.icon!!
+ .intrinsicWidth.toFloat(),
+ activity
+ ).toInt()
- val imageRequest = DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithNameAndPixels(conversationUser?.baseUrl,
- currentConversation?.name, avatarSize / 2), conversationUser!!)
+ val imageRequest = DisplayUtils.getImageRequestForUrl(
+ ApiUtils.getUrlForAvatarWithNameAndPixels(
+ conversationUser?.baseUrl,
+ currentConversation?.name, avatarSize / 2
+ ),
+ conversationUser!!
+ )
val imagePipeline = Fresco.getImagePipeline()
val dataSource = imagePipeline.fetchDecodedImage(imageRequest, null)
- dataSource.subscribe(object : BaseBitmapDataSubscriber() {
- override fun onNewResultImpl(bitmap: Bitmap?) {
- if (actionBar != null && bitmap != null && resources != null) {
- val roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(resources!!, bitmap)
- roundedBitmapDrawable.isCircular = true
- roundedBitmapDrawable.setAntiAlias(true)
- actionBar?.setIcon(roundedBitmapDrawable)
+ dataSource.subscribe(
+ object : BaseBitmapDataSubscriber() {
+ override fun onNewResultImpl(bitmap: Bitmap?) {
+ if (actionBar != null && bitmap != null && resources != null) {
+ val roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(resources!!, bitmap)
+ roundedBitmapDrawable.isCircular = true
+ roundedBitmapDrawable.setAntiAlias(true)
+ actionBar?.setIcon(roundedBitmapDrawable)
+ }
}
- }
- override fun onFailureImpl(dataSource: DataSource>) {}
- }, UiThreadImmediateExecutorService.getInstance())
+ override fun onFailureImpl(dataSource: DataSource>) {}
+ },
+ UiThreadImmediateExecutorService.getInstance()
+ )
}
}
private fun inOneToOneCall() = currentConversation != null && currentConversation?.type != null &&
- currentConversation?.type == Conversation.ConversationType
- .ROOM_TYPE_ONE_TO_ONE_CALL
+ currentConversation?.type == Conversation.ConversationType
+ .ROOM_TYPE_ONE_TO_ONE_CALL
override fun onViewBound(view: View) {
actionBar?.show()
@@ -374,29 +414,49 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
adapterWasNull = true
val messageHolders = MessageHolders()
- messageHolders.setIncomingTextConfig(MagicIncomingTextMessageViewHolder::class.java, R.layout.item_custom_incoming_text_message)
- messageHolders.setOutcomingTextConfig(MagicOutcomingTextMessageViewHolder::class.java, R.layout.item_custom_outcoming_text_message)
+ messageHolders.setIncomingTextConfig(
+ MagicIncomingTextMessageViewHolder::class.java,
+ R.layout.item_custom_incoming_text_message
+ )
+ messageHolders.setOutcomingTextConfig(
+ MagicOutcomingTextMessageViewHolder::class.java,
+ R.layout.item_custom_outcoming_text_message
+ )
- messageHolders.setIncomingImageConfig(MagicPreviewMessageViewHolder::class.java, R.layout.item_custom_incoming_preview_message)
- messageHolders.setOutcomingImageConfig(MagicPreviewMessageViewHolder::class.java, R.layout.item_custom_outcoming_preview_message)
+ messageHolders.setIncomingImageConfig(
+ MagicPreviewMessageViewHolder::class.java,
+ R.layout.item_custom_incoming_preview_message
+ )
+ messageHolders.setOutcomingImageConfig(
+ MagicPreviewMessageViewHolder::class.java,
+ R.layout.item_custom_outcoming_preview_message
+ )
- messageHolders.registerContentType(CONTENT_TYPE_SYSTEM_MESSAGE, MagicSystemMessageViewHolder::class.java,
- R.layout.item_system_message, MagicSystemMessageViewHolder::class.java, R.layout.item_system_message,
- this)
+ messageHolders.registerContentType(
+ CONTENT_TYPE_SYSTEM_MESSAGE, MagicSystemMessageViewHolder::class.java,
+ R.layout.item_system_message, MagicSystemMessageViewHolder::class.java, R.layout.item_system_message,
+ this
+ )
- messageHolders.registerContentType(CONTENT_TYPE_UNREAD_NOTICE_MESSAGE,
- MagicUnreadNoticeMessageViewHolder::class.java, R.layout.item_date_header,
- MagicUnreadNoticeMessageViewHolder::class.java, R.layout.item_date_header, this)
+ messageHolders.registerContentType(
+ CONTENT_TYPE_UNREAD_NOTICE_MESSAGE,
+ MagicUnreadNoticeMessageViewHolder::class.java, R.layout.item_date_header,
+ MagicUnreadNoticeMessageViewHolder::class.java, R.layout.item_date_header, this
+ )
- adapter = TalkMessagesListAdapter(conversationUser?.userId, messageHolders, ImageLoader { imageView, url, payload ->
- val draweeController = Fresco.newDraweeControllerBuilder()
+ adapter = TalkMessagesListAdapter(
+ conversationUser?.userId,
+ messageHolders,
+ ImageLoader { imageView, url, payload ->
+ val draweeController = Fresco.newDraweeControllerBuilder()
.setImageRequest(DisplayUtils.getImageRequestForUrl(url, conversationUser))
.setControllerListener(DisplayUtils.getImageControllerListener(imageView))
.setOldController(imageView.controller)
.setAutoPlayAnimations(true)
.build()
- imageView.controller = draweeController
- })
+ imageView.controller = draweeController
+ }
+ )
} else {
messagesListView?.visibility = View.VISIBLE
}
@@ -449,7 +509,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
}
})
-
val filters = arrayOfNulls(1)
val lengthFilter = conversationUser?.messageMaxLength ?: 1000
@@ -458,27 +517,34 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
messageInput?.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
-
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (s.length >= lengthFilter) {
- messageInput?.error = String.format(Objects.requireNonNull
- (resources).getString(R.string.nc_limit_hit), Integer.toString(lengthFilter))
+ messageInput?.error = String.format(
+ Objects.requireNonNull
+ (resources).getString(R.string.nc_limit_hit),
+ Integer.toString(lengthFilter)
+ )
} else {
messageInput?.error = null
}
val editable = messageInput?.editableText
if (editable != null && messageInput != null) {
- val mentionSpans = editable.getSpans(0, messageInput!!.length(),
- Spans.MentionChipSpan::class.java)
+ val mentionSpans = editable.getSpans(
+ 0, messageInput!!.length(),
+ Spans.MentionChipSpan::class.java
+ )
var mentionSpan: Spans.MentionChipSpan
for (i in mentionSpans.indices) {
mentionSpan = mentionSpans[i]
if (start >= editable.getSpanStart(mentionSpan) && start < editable.getSpanEnd(mentionSpan)) {
- if (editable.subSequence(editable.getSpanStart(mentionSpan),
- editable.getSpanEnd(mentionSpan)).toString().trim { it <= ' ' } != mentionSpan.label) {
+ if (editable.subSequence(
+ editable.getSpanStart(mentionSpan),
+ editable.getSpanEnd(mentionSpan)
+ ).toString().trim { it <= ' ' } != mentionSpan.label
+ ) {
editable.removeSpan(mentionSpan)
}
}
@@ -487,18 +553,19 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
}
override fun afterTextChanged(s: Editable) {
-
}
})
messageInputView?.setAttachmentsListener {
- activity?.let { AttachmentDialog(it, this).show() };
+ activity?.let { AttachmentDialog(it, this).show() }
}
messageInputView?.button?.setOnClickListener { v -> submitMessage() }
- messageInputView?.button?.contentDescription = resources?.getString(R.string
- .nc_description_send_message_button)
+ messageInputView?.button?.contentDescription = resources?.getString(
+ R.string
+ .nc_description_send_message_button
+ )
if (currentConversation != null && currentConversation?.roomId != null) {
loadAvatarForStatusBar()
@@ -516,7 +583,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
super.onViewBound(view)
}
-
private fun checkReadOnlyState() {
if (currentConversation != null) {
if (currentConversation?.shouldShowLobby(conversationUser) ?: false || currentConversation?.conversationReadOnlyState != null && currentConversation?.conversationReadOnlyState == Conversation.ConversationReadOnlyState.CONVERSATION_READ_ONLY) {
@@ -524,7 +590,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
conversationVoiceCallMenuItem?.icon?.alpha = 99
conversationVideoMenuItem?.icon?.alpha = 99
messageInputView?.visibility = View.GONE
-
} else {
if (conversationVoiceCallMenuItem != null) {
conversationVoiceCallMenuItem?.icon?.alpha = 255
@@ -535,7 +600,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
}
if (currentConversation != null && currentConversation!!.shouldShowLobby
- (conversationUser)) {
+ (conversationUser)
+ ) {
messageInputView?.visibility = View.GONE
} else {
messageInputView?.visibility = View.VISIBLE
@@ -558,9 +624,15 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
loadingProgressBar?.visibility = View.GONE
if (currentConversation?.lobbyTimer != null && currentConversation?.lobbyTimer !=
- 0L) {
- conversationLobbyText?.text = String.format(resources!!.getString(R.string.nc_lobby_waiting_with_date), DateUtils.getLocalDateStringFromTimestampForLobby(currentConversation?.lobbyTimer
- ?: 0))
+ 0L
+ ) {
+ conversationLobbyText?.text = String.format(
+ resources!!.getString(R.string.nc_lobby_waiting_with_date),
+ DateUtils.getLocalDateStringFromTimestampForLobby(
+ currentConversation?.lobbyTimer
+ ?: 0
+ )
+ )
} else {
conversationLobbyText?.setText(R.string.nc_lobby_waiting)
}
@@ -607,7 +679,7 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
filenamesWithLinebreaks += filename + "\n"
}
- val confirmationQuestion = when(files.size) {
+ val confirmationQuestion = when (files.size) {
1 -> context?.resources?.getString(R.string.nc_upload_confirm_send_single)?.let {
String.format(it, title)
}
@@ -617,21 +689,25 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
}
LovelyStandardDialog(activity)
- .setPositiveButtonColorRes(R.color.nc_darkGreen)
- .setTitle(confirmationQuestion)
- .setMessage(filenamesWithLinebreaks)
- .setPositiveButton(R.string.nc_yes) { v ->
- uploadFiles(files)
- Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_in_progess), Toast
- .LENGTH_LONG).show();
- }
- .setNegativeButton(R.string.nc_no) {}
- .show()
+ .setPositiveButtonColorRes(R.color.nc_darkGreen)
+ .setTitle(confirmationQuestion)
+ .setMessage(filenamesWithLinebreaks)
+ .setPositiveButton(R.string.nc_yes) { v ->
+ uploadFiles(files)
+ Toast.makeText(
+ context, context?.resources?.getString(R.string.nc_upload_in_progess),
+ Toast.LENGTH_LONG
+ ).show()
+ }
+ .setNegativeButton(R.string.nc_no) {}
+ .show()
} catch (e: IllegalStateException) {
- Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG).show()
+ Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG)
+ .show()
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
} catch (e: IllegalArgumentException) {
- Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG).show()
+ Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG)
+ .show()
Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e)
}
}
@@ -642,13 +718,13 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
try {
require(files.isNotEmpty())
val data: Data = Data.Builder()
- .putStringArray(UploadAndShareFilesWorker.DEVICE_SOURCEFILES, files.toTypedArray())
- .putString(UploadAndShareFilesWorker.NC_TARGETPATH, conversationUser?.getAttachmentFolder())
- .putString(UploadAndShareFilesWorker.ROOM_TOKEN, roomToken)
- .build()
+ .putStringArray(UploadAndShareFilesWorker.DEVICE_SOURCEFILES, files.toTypedArray())
+ .putString(UploadAndShareFilesWorker.NC_TARGETPATH, conversationUser?.getAttachmentFolder())
+ .putString(UploadAndShareFilesWorker.ROOM_TOKEN, roomToken)
+ .build()
val uploadWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(UploadAndShareFilesWorker::class.java)
- .setInputData(data)
- .build()
+ .setInputData(data)
+ .build()
WorkManager.getInstance().enqueue(uploadWorker)
} catch (e: IllegalArgumentException) {
Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG).show()
@@ -662,8 +738,15 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
addCategory(Intent.CATEGORY_OPENABLE)
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
}
- startActivityForResult(Intent.createChooser(action, context?.resources?.getString(
- R.string.nc_upload_choose_local_files)), REQUEST_CODE_CHOOSE_FILE);
+ startActivityForResult(
+ Intent.createChooser(
+ action,
+ context?.resources?.getString(
+ R.string.nc_upload_choose_local_files
+ )
+ ),
+ REQUEST_CODE_CHOOSE_FILE
+ )
}
fun showBrowserScreen(browserType: BrowserController.BrowserType) {
@@ -671,19 +754,23 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
bundle.putParcelable(BundleKeys.KEY_BROWSER_TYPE, Parcels.wrap(browserType))
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap(conversationUser))
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
- router.pushController(RouterTransaction.with(BrowserForSharingController(bundle))
+ router.pushController(
+ RouterTransaction.with(BrowserForSharingController(bundle))
.pushChangeHandler(VerticalChangeHandler())
- .popChangeHandler(VerticalChangeHandler()))
+ .popChangeHandler(VerticalChangeHandler())
+ )
}
private fun showConversationInfoScreen() {
val bundle = Bundle()
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser)
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
- bundle.putBoolean(BundleKeys.KEY_ROOM_ONE_TO_ONE, inOneToOneCall());
- router.pushController(RouterTransaction.with(ConversationInfoController(bundle))
+ bundle.putBoolean(BundleKeys.KEY_ROOM_ONE_TO_ONE, inOneToOneCall())
+ router.pushController(
+ RouterTransaction.with(ConversationInfoController(bundle))
.pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()))
+ .popChangeHandler(HorizontalChangeHandler())
+ )
}
private fun setupMentionAutocomplete() {
@@ -691,17 +778,19 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
resources?.let {
val backgroundDrawable = ColorDrawable(it.getColor(R.color.bg_default))
val presenter = MentionAutocompletePresenter(applicationContext, roomToken)
- val callback = MentionAutocompleteCallback(activity,
- conversationUser, messageInput)
+ val callback = MentionAutocompleteCallback(
+ activity,
+ conversationUser, messageInput
+ )
if (mentionAutocomplete == null && messageInput != null) {
mentionAutocomplete = Autocomplete.on(messageInput)
- .with(elevation)
- .with(backgroundDrawable)
- .with(MagicCharPolicy('@'))
- .with(presenter)
- .with(callback)
- .build()
+ .with(elevation)
+ .with(backgroundDrawable)
+ .with(MagicCharPolicy('@'))
+ .with(presenter)
+ .with(callback)
+ .build()
}
}
}
@@ -730,8 +819,10 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
smileyButton?.setColorFilter(resources!!.getColor(R.color.colorPrimary), PorterDuff.Mode.SRC_IN)
}
}.setOnEmojiPopupDismissListener {
- smileyButton?.setColorFilter(resources!!.getColor(R.color.emoji_icons),
- PorterDuff.Mode.SRC_IN)
+ smileyButton?.setColorFilter(
+ resources!!.getColor(R.color.emoji_icons),
+ PorterDuff.Mode.SRC_IN
+ )
}.setOnEmojiClickListener { emoji, imageView -> messageInput?.editableText?.append(" ") }.build(it)
}
@@ -753,11 +844,15 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
private fun cancelNotificationsForCurrentConversation() {
if (conversationUser != null) {
if (!conversationUser.hasSpreedFeatureCapability("no-ping") && !TextUtils.isEmpty(roomId)) {
- NotificationUtils.cancelExistingNotificationsForRoom(applicationContext,
- conversationUser, roomId)
+ NotificationUtils.cancelExistingNotificationsForRoom(
+ applicationContext,
+ conversationUser, roomId
+ )
} else if (!TextUtils.isEmpty(roomToken)) {
- NotificationUtils.cancelExistingNotificationsForRoom(applicationContext,
- conversationUser, roomToken!!)
+ NotificationUtils.cancelExistingNotificationsForRoom(
+ applicationContext,
+ conversationUser, roomToken!!
+ )
}
}
}
@@ -775,8 +870,12 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
activity?.findViewById(R.id.toolbar)?.setOnClickListener(null)
}
- if (conversationUser != null && conversationUser?.hasSpreedFeatureCapability("no-ping")
- && activity != null && !activity?.isChangingConfigurations!! && !isLeavingForConversation) {
+ if (conversationUser != null &&
+ conversationUser.hasSpreedFeatureCapability("no-ping") &&
+ activity != null &&
+ !activity?.isChangingConfigurations!! &&
+ !isLeavingForConversation
+ ) {
wasDetached = true
leaveRoom()
}
@@ -819,26 +918,30 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
private fun startPing() {
if (conversationUser != null && !conversationUser.hasSpreedFeatureCapability("no-ping")) {
- ncApi?.pingCall(credentials, ApiUtils.getUrlForCallPing(conversationUser.baseUrl,
- roomToken))
- ?.subscribeOn(Schedulers.io())
- ?.observeOn(AndroidSchedulers.mainThread())
- ?.repeatWhen { observable -> observable.delay(5000, TimeUnit.MILLISECONDS) }
- ?.takeWhile { observable -> inConversation }
- ?.retry(3) { observable -> inConversation }
- ?.subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {
- disposableList.add(d)
- }
+ ncApi?.pingCall(
+ credentials,
+ ApiUtils.getUrlForCallPing(
+ conversationUser.baseUrl,
+ roomToken
+ )
+ )
+ ?.subscribeOn(Schedulers.io())
+ ?.observeOn(AndroidSchedulers.mainThread())
+ ?.repeatWhen { observable -> observable.delay(5000, TimeUnit.MILLISECONDS) }
+ ?.takeWhile { observable -> inConversation }
+ ?.retry(3) { observable -> inConversation }
+ ?.subscribe(object : Observer {
+ override fun onSubscribe(d: Disposable) {
+ disposableList.add(d)
+ }
- override fun onNext(genericOverall: GenericOverall) {
+ override fun onNext(genericOverall: GenericOverall) {
+ }
- }
+ override fun onError(e: Throwable) {}
- override fun onError(e: Throwable) {}
-
- override fun onComplete() {}
- })
+ override fun onComplete() {}
+ })
}
}
@@ -850,57 +953,63 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
private fun joinRoomWithPassword() {
if (currentConversation == null || TextUtils.isEmpty(currentConversation?.sessionId) ||
- currentConversation?.sessionId == "0") {
- ncApi?.joinRoom(credentials,
- ApiUtils.getUrlForSettingMyselfAsActiveParticipant(conversationUser?.baseUrl, roomToken), roomPassword)
- ?.subscribeOn(Schedulers.io())
- ?.observeOn(AndroidSchedulers.mainThread())
- ?.retry(3)
- ?.subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {
- disposableList.add(d)
+ currentConversation?.sessionId == "0"
+ ) {
+ ncApi?.joinRoom(
+ credentials,
+ ApiUtils.getUrlForSettingMyselfAsActiveParticipant(conversationUser?.baseUrl, roomToken), roomPassword
+ )
+ ?.subscribeOn(Schedulers.io())
+ ?.observeOn(AndroidSchedulers.mainThread())
+ ?.retry(3)
+ ?.subscribe(object : Observer {
+ override fun onSubscribe(d: Disposable) {
+ disposableList.add(d)
+ }
+
+ override fun onNext(roomOverall: RoomOverall) {
+ inConversation = true
+ currentConversation?.sessionId = roomOverall.ocs.data.sessionId
+
+ ApplicationWideCurrentRoomHolder.getInstance().session =
+ currentConversation?.sessionId
+ startPing()
+
+ setupWebsocket()
+ checkLobbyState()
+
+ if (isFirstMessagesProcessing) {
+ pullChatMessages(0)
+ } else {
+ pullChatMessages(1, 0)
}
- override fun onNext(roomOverall: RoomOverall) {
- inConversation = true
- currentConversation?.sessionId = roomOverall.ocs.data.sessionId
-
- ApplicationWideCurrentRoomHolder.getInstance().session =
- currentConversation?.sessionId
- startPing()
-
- setupWebsocket()
- checkLobbyState()
-
- if (isFirstMessagesProcessing) {
- pullChatMessages(0)
- } else {
- pullChatMessages(1, 0)
- }
-
- if (magicWebSocketInstance != null) {
- magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(roomToken, currentConversation?.sessionId)
- }
- if (startCallFromNotification != null && startCallFromNotification ?: false) {
- startCallFromNotification = false
- startACall(voiceOnly)
- }
+ if (magicWebSocketInstance != null) {
+ magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(
+ roomToken,
+ currentConversation?.sessionId
+ )
}
-
- override fun onError(e: Throwable) {
-
+ if (startCallFromNotification != null && startCallFromNotification ?: false) {
+ startCallFromNotification = false
+ startACall(voiceOnly)
}
+ }
- override fun onComplete() {
+ override fun onError(e: Throwable) {
+ }
- }
- })
+ override fun onComplete() {
+ }
+ })
} else {
inConversation = true
ApplicationWideCurrentRoomHolder.getInstance().session = currentConversation?.sessionId
if (magicWebSocketInstance != null) {
- magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(roomToken,
- currentConversation?.sessionId)
+ magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(
+ roomToken,
+ currentConversation?.sessionId
+ )
}
startPing()
if (isFirstMessagesProcessing) {
@@ -912,39 +1021,45 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
}
private fun leaveRoom() {
- ncApi?.leaveRoom(credentials,
- ApiUtils.getUrlForSettingMyselfAsActiveParticipant(conversationUser?.baseUrl,
- roomToken))
- ?.subscribeOn(Schedulers.io())
- ?.observeOn(AndroidSchedulers.mainThread())
- ?.subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {
- disposableList.add(d)
+ ncApi?.leaveRoom(
+ credentials,
+ ApiUtils.getUrlForSettingMyselfAsActiveParticipant(
+ conversationUser?.baseUrl,
+ roomToken
+ )
+ )
+ ?.subscribeOn(Schedulers.io())
+ ?.observeOn(AndroidSchedulers.mainThread())
+ ?.subscribe(object : Observer {
+ override fun onSubscribe(d: Disposable) {
+ disposableList.add(d)
+ }
+
+ override fun onNext(genericOverall: GenericOverall) {
+ checkingLobbyStatus = false
+
+ if (lobbyTimerHandler != null) {
+ lobbyTimerHandler?.removeCallbacksAndMessages(null)
}
- override fun onNext(genericOverall: GenericOverall) {
- checkingLobbyStatus = false
-
- if (lobbyTimerHandler != null) {
- lobbyTimerHandler?.removeCallbacksAndMessages(null)
- }
-
- if (magicWebSocketInstance != null && currentConversation != null) {
- magicWebSocketInstance?.joinRoomWithRoomTokenAndSession("",
- currentConversation?.sessionId)
- }
-
- if (!isDestroyed && !isBeingDestroyed && !wasDetached) {
- router.popCurrentController()
- }
+ if (magicWebSocketInstance != null && currentConversation != null) {
+ magicWebSocketInstance?.joinRoomWithRoomTokenAndSession(
+ "",
+ currentConversation?.sessionId
+ )
}
- override fun onError(e: Throwable) {}
-
- override fun onComplete() {
- dispose()
+ if (!isDestroyed && !isBeingDestroyed && !wasDetached) {
+ router.popCurrentController()
}
- })
+ }
+
+ override fun onError(e: Throwable) {}
+
+ override fun onComplete() {
+ dispose()
+ }
+ })
}
private fun setSenderId() {
@@ -957,14 +1072,15 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} catch (e: IllegalAccessException) {
Log.w(TAG, "Failed to access and set field")
}
-
}
private fun submitMessage() {
if (messageInput != null) {
val editable = messageInput!!.editableText
- val mentionSpans = editable.getSpans(0, editable.length,
- Spans.MentionChipSpan::class.java)
+ val mentionSpans = editable.getSpans(
+ 0, editable.length,
+ Spans.MentionChipSpan::class.java
+ )
var mentionSpan: Spans.MentionChipSpan
for (i in mentionSpans.indices) {
mentionSpan = mentionSpans[i]
@@ -977,7 +1093,10 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
messageInput?.setText("")
val replyMessageId: Int? = view?.findViewById(R.id.quotedChatMessageView)?.tag as Int?
- sendMessage(editable, if (view?.findViewById(R.id.quotedChatMessageView)?.visibility == View.VISIBLE) replyMessageId else null)
+ sendMessage(
+ editable,
+ if (view?.findViewById(R.id.quotedChatMessageView)?.visibility == View.VISIBLE) replyMessageId else null
+ )
cancelReply()
}
}
@@ -986,55 +1105,54 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
if (conversationUser != null) {
ncApi!!.sendChatMessage(
- credentials,
- ApiUtils.getUrlForChat(conversationUser.baseUrl, roomToken),
- message,
- conversationUser.displayName,
- replyTo
+ credentials,
+ ApiUtils.getUrlForChat(conversationUser.baseUrl, roomToken),
+ message,
+ conversationUser.displayName,
+ replyTo
)
- ?.subscribeOn(Schedulers.io())
- ?.observeOn(AndroidSchedulers.mainThread())
- ?.subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {
+ ?.subscribeOn(Schedulers.io())
+ ?.observeOn(AndroidSchedulers.mainThread())
+ ?.subscribe(object : Observer {
+ override fun onSubscribe(d: Disposable) {
+ }
+ override fun onNext(genericOverall: GenericOverall) {
+ myFirstMessage = message
+
+ if (popupBubble?.isShown ?: false) {
+ popupBubble?.hide()
}
- override fun onNext(genericOverall: GenericOverall) {
- myFirstMessage = message
+ messagesListView?.smoothScrollToPosition(0)
+ }
- if (popupBubble?.isShown ?: false) {
- popupBubble?.hide()
- }
+ override fun onError(e: Throwable) {
+ if (e is HttpException) {
+ val code = e.code()
+ if (Integer.toString(code).startsWith("2")) {
+ myFirstMessage = message
- messagesListView?.smoothScrollToPosition(0)
- }
-
- override fun onError(e: Throwable) {
- if (e is HttpException) {
- val code = e.code()
- if (Integer.toString(code).startsWith("2")) {
- myFirstMessage = message
-
- if (popupBubble?.isShown ?: false) {
- popupBubble?.hide()
- }
-
- messagesListView?.smoothScrollToPosition(0)
+ if (popupBubble?.isShown ?: false) {
+ popupBubble?.hide()
}
+
+ messagesListView?.smoothScrollToPosition(0)
}
}
+ }
- override fun onComplete() {
-
- }
- })
+ override fun onComplete() {
+ }
+ })
}
}
private fun setupWebsocket() {
if (conversationUser != null) {
if (WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id) != null) {
- magicWebSocketInstance = WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id)
+ magicWebSocketInstance =
+ WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id)
} else {
magicWebSocketInstance = null
}
@@ -1047,7 +1165,7 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
}
if (currentConversation != null && currentConversation!!.shouldShowLobby(conversationUser)) {
- //return
+ // return
}
val fieldMap = HashMap()
@@ -1090,60 +1208,61 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
if (!wasDetached) {
if (lookIntoFuture > 0) {
val finalTimeout = timeout
- ncApi?.pullChatMessages(credentials,
- ApiUtils.getUrlForChat(conversationUser?.baseUrl, roomToken), fieldMap)
- ?.subscribeOn(Schedulers.io())
- ?.observeOn(AndroidSchedulers.mainThread())
- ?.takeWhile { observable -> inConversation && !wasDetached }
- ?.subscribe(object : Observer> {
- override fun onSubscribe(d: Disposable) {
- disposableList.add(d)
+ ncApi?.pullChatMessages(
+ credentials,
+ ApiUtils.getUrlForChat(conversationUser?.baseUrl, roomToken), fieldMap
+ )
+ ?.subscribeOn(Schedulers.io())
+ ?.observeOn(AndroidSchedulers.mainThread())
+ ?.takeWhile { observable -> inConversation && !wasDetached }
+ ?.subscribe(object : Observer> {
+ override fun onSubscribe(d: Disposable) {
+ disposableList.add(d)
+ }
+
+ override fun onNext(response: Response<*>) {
+ if (response.code() == 304) {
+ pullChatMessages(1, setReadMarker, xChatLastCommonRead)
+ } else if (response.code() == 412) {
+ futurePreconditionFailed = true
+ } else {
+ processMessages(response, true, finalTimeout)
}
+ }
- override fun onNext(response: Response<*>) {
- if (response.code() == 304) {
- pullChatMessages(1, setReadMarker, xChatLastCommonRead)
- } else if (response.code() == 412) {
- futurePreconditionFailed = true
- } else {
- processMessages(response, true, finalTimeout)
- }
- }
-
- override fun onError(e: Throwable) {
- }
-
- override fun onComplete() {
-
- }
- })
+ override fun onError(e: Throwable) {
+ }
+ override fun onComplete() {
+ }
+ })
} else {
- ncApi?.pullChatMessages(credentials,
- ApiUtils.getUrlForChat(conversationUser?.baseUrl, roomToken), fieldMap)
- ?.subscribeOn(Schedulers.io())
- ?.observeOn(AndroidSchedulers.mainThread())
- ?.takeWhile { observable -> inConversation && !wasDetached }
- ?.subscribe(object : Observer> {
- override fun onSubscribe(d: Disposable) {
- disposableList.add(d)
- }
+ ncApi?.pullChatMessages(
+ credentials,
+ ApiUtils.getUrlForChat(conversationUser?.baseUrl, roomToken), fieldMap
+ )
+ ?.subscribeOn(Schedulers.io())
+ ?.observeOn(AndroidSchedulers.mainThread())
+ ?.takeWhile { observable -> inConversation && !wasDetached }
+ ?.subscribe(object : Observer> {
+ override fun onSubscribe(d: Disposable) {
+ disposableList.add(d)
+ }
- override fun onNext(response: Response<*>) {
- if (response.code() == 412) {
- pastPreconditionFailed = true
- } else {
- processMessages(response, false, 0)
- }
+ override fun onNext(response: Response<*>) {
+ if (response.code() == 412) {
+ pastPreconditionFailed = true
+ } else {
+ processMessages(response, false, 0)
}
+ }
- override fun onError(e: Throwable) {
- }
+ override fun onError(e: Throwable) {
+ }
- override fun onComplete() {
-
- }
- })
+ override fun onComplete() {
+ }
+ })
}
}
}
@@ -1180,7 +1299,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
loadingProgressBar?.visibility = View.GONE
messagesListView?.visibility = View.VISIBLE
-
}
var countGroupedMessages = 0
@@ -1189,11 +1307,14 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
for (i in chatMessageList.indices) {
if (chatMessageList.size > i + 1) {
if (TextUtils.isEmpty(chatMessageList[i].systemMessage) &&
- TextUtils.isEmpty(chatMessageList[i + 1].systemMessage) &&
- chatMessageList[i + 1].actorId == chatMessageList[i].actorId &&
- countGroupedMessages < 4 && DateFormatter.isSameDay(chatMessageList[i].createdAt,
- chatMessageList[i + 1].createdAt)) {
- chatMessageList[i].isGrouped = true;
+ TextUtils.isEmpty(chatMessageList[i + 1].systemMessage) &&
+ chatMessageList[i + 1].actorId == chatMessageList[i].actorId &&
+ countGroupedMessages < 4 && DateFormatter.isSameDay(
+ chatMessageList[i].createdAt,
+ chatMessageList[i + 1].createdAt
+ )
+ ) {
+ chatMessageList[i].isGrouped = true
countGroupedMessages++
} else {
countGroupedMessages = 0
@@ -1201,7 +1322,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
}
val chatMessage = chatMessageList[i]
- chatMessage.isOneToOneConversation = currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
+ chatMessage.isOneToOneConversation =
+ currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
chatMessage.isLinkPreviewAllowed = isLinkPreviewAllowed
chatMessage.activeUser = conversationUser
}
@@ -1209,7 +1331,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
if (adapter != null) {
adapter?.addToEnd(chatMessageList, false)
}
-
} else {
var chatMessage: ChatMessage
@@ -1225,7 +1346,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
adapter?.addToStart(unreadChatMessage, false)
}
- val isThereANewNotice = shouldAddNewMessagesNotice || adapter?.getMessagePositionByIdInReverse("-1") != -1
+ val isThereANewNotice =
+ shouldAddNewMessagesNotice || adapter?.getMessagePositionByIdInReverse("-1") != -1
for (i in chatMessageList.indices) {
chatMessage = chatMessageList[i]
@@ -1241,7 +1363,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
}
}
- val shouldScroll = !isThereANewNotice && !shouldAddNewMessagesNotice && layoutManager?.findFirstVisibleItemPosition() == 0 || adapter != null && adapter?.itemCount == 0
+ val shouldScroll =
+ !isThereANewNotice && !shouldAddNewMessagesNotice && layoutManager?.findFirstVisibleItemPosition() == 0 || adapter != null && adapter?.itemCount == 0
if (!shouldAddNewMessagesNotice && !shouldScroll && popupBubble != null) {
if (!popupBubble!!.isShown) {
@@ -1255,18 +1378,24 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
}
if (adapter != null) {
- chatMessage.isGrouped = (adapter!!.isPreviousSameAuthor(chatMessage
- .actorId, -1) && adapter!!.getSameAuthorLastMessagesCount(chatMessage.actorId) % 5 > 0)
- chatMessage.isOneToOneConversation = (currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL)
+ chatMessage.isGrouped = (
+ adapter!!.isPreviousSameAuthor(
+ chatMessage.actorId,
+ -1
+ ) && adapter!!.getSameAuthorLastMessagesCount(chatMessage.actorId) % 5 > 0
+ )
+ chatMessage.isOneToOneConversation =
+ (currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL)
adapter?.addToStart(chatMessage, shouldScroll)
}
-
}
if (shouldAddNewMessagesNotice && adapter != null && messagesListView != null) {
- layoutManager?.scrollToPositionWithOffset(adapter!!.getMessagePositionByIdInReverse("-1"), messagesListView!!.height / 2)
+ layoutManager?.scrollToPositionWithOffset(
+ adapter!!.getMessagePositionByIdInReverse("-1"),
+ messagesListView!!.height / 2
+ )
}
-
}
// update read status of all messages
@@ -1310,7 +1439,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
}
}
-
override fun format(date: Date): String {
return if (DateFormatter.isToday(date)) {
resources!!.getString(R.string.nc_date_header_today)
@@ -1344,7 +1472,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
}
}
-
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> {
@@ -1394,10 +1521,9 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
private fun isInfoMessageAboutDeletion(currentMessage: MutableMap.MutableEntry): Boolean {
return currentMessage.value.parentMessage != null && currentMessage.value.systemMessageType == ChatMessage
- .SystemMessageType.PARENT_MESSAGE_DELETED
+ .SystemMessageType.PARENT_MESSAGE_DELETED
}
-
private fun startACall(isVoiceOnlyCall: Boolean) {
isLeavingForConversation = true
val callIntent = getIntentForCall(isVoiceOnlyCall)
@@ -1427,7 +1553,7 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
} else {
null
}
- } ?:run {
+ } ?: run {
return null
}
}
@@ -1440,13 +1566,17 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
}
override fun onMessageViewLongClick(view: View?, message: IMessage?) {
- PopupMenu(this.context, view, if (message?.user?.id == conversationUser?.userId) Gravity.END else Gravity.START).apply {
+ PopupMenu(
+ this.context,
+ view,
+ if (message?.user?.id == conversationUser?.userId) Gravity.END else Gravity.START
+ ).apply {
setOnMenuItemClickListener { item ->
when (item?.itemId) {
R.id.action_copy_message -> {
val clipboardManager =
- activity?.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
+ activity?.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
val clipData = ClipData.newPlainText(resources?.getString(R.string.nc_app_name), message?.text)
clipboardManager.setPrimaryClip(clipData)
true
@@ -1456,28 +1586,40 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
chatMessage?.let {
messageInputView?.findViewById(R.id.attachmentButton)?.visibility = View.GONE
messageInputView?.findViewById(R.id.attachmentButtonSpace)?.visibility = View.GONE
- messageInputView?.findViewById(R.id.cancelReplyButton)?.visibility = View.VISIBLE
+ messageInputView?.findViewById(R.id.cancelReplyButton)?.visibility =
+ View.VISIBLE
messageInputView?.findViewById(R.id.quotedMessage)?.maxLines = 2
- messageInputView?.findViewById(R.id.quotedMessage)?.ellipsize = TextUtils.TruncateAt.END
+ messageInputView?.findViewById(R.id.quotedMessage)?.ellipsize =
+ TextUtils.TruncateAt.END
messageInputView?.findViewById(R.id.quotedMessage)?.text = it.text
- messageInputView?.findViewById(R.id.quotedMessageAuthor)?.text = it.actorDisplayName
- ?: context!!.getText(R.string.nc_nick_guest)
+ messageInputView?.findViewById(R.id.quotedMessageAuthor)?.text =
+ it.actorDisplayName ?: context!!.getText(R.string.nc_nick_guest)
conversationUser?.let { currentUser ->
chatMessage.imageUrl?.let { previewImageUrl ->
- messageInputView?.findViewById(R.id.quotedMessageImage)?.visibility = View.VISIBLE
+ messageInputView?.findViewById(R.id.quotedMessageImage)?.visibility =
+ View.VISIBLE
- val px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 96f, resources?.displayMetrics)
- messageInputView?.findViewById(R.id.quotedMessageImage)?.maxHeight = px.toInt()
- val layoutParams = messageInputView?.findViewById(R.id.quotedMessageImage)?.layoutParams as FlexboxLayout.LayoutParams
+ val px = TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ 96f,
+ resources?.displayMetrics
+ )
+ messageInputView?.findViewById(R.id.quotedMessageImage)?.maxHeight =
+ px.toInt()
+ val layoutParams =
+ messageInputView?.findViewById(R.id.quotedMessageImage)?.layoutParams as FlexboxLayout.LayoutParams
layoutParams.flexGrow = 0f
- messageInputView?.findViewById(R.id.quotedMessageImage)?.layoutParams = layoutParams
- messageInputView?.findViewById(R.id.quotedMessageImage)?.load(previewImageUrl) {
- addHeader("Authorization", credentials!!)
- }
+ messageInputView?.findViewById(R.id.quotedMessageImage)?.layoutParams =
+ layoutParams
+ messageInputView?.findViewById(R.id.quotedMessageImage)
+ ?.load(previewImageUrl) {
+ addHeader("Authorization", credentials!!)
+ }
} ?: run {
- messageInputView?.findViewById(R.id.quotedMessageImage)?.visibility = View.GONE
+ messageInputView?.findViewById(R.id.quotedMessageImage)?.visibility =
+ View.GONE
}
}
@@ -1488,30 +1630,36 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
}
R.id.action_delete_message -> {
ncApi?.deleteChatMessage(
- credentials,
- ApiUtils.getUrlForMessageDeletion(conversationUser?.baseUrl, roomToken, message?.id)
+ credentials,
+ ApiUtils.getUrlForMessageDeletion(conversationUser?.baseUrl, roomToken, message?.id)
)?.subscribeOn(Schedulers.io())
- ?.observeOn(AndroidSchedulers.mainThread())
- ?.subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {
- }
+ ?.observeOn(AndroidSchedulers.mainThread())
+ ?.subscribe(object : Observer {
+ override fun onSubscribe(d: Disposable) {
+ }
- override fun onNext(t: ChatOverallSingleMessage) {
- if (t.ocs.meta.statusCode == HttpURLConnection.HTTP_ACCEPTED) {
- Toast.makeText(context, R.string.nc_delete_message_leaked_to_matterbridge,
- Toast.LENGTH_LONG).show()
- }
+ override fun onNext(t: ChatOverallSingleMessage) {
+ if (t.ocs.meta.statusCode == HttpURLConnection.HTTP_ACCEPTED) {
+ Toast.makeText(
+ context, R.string.nc_delete_message_leaked_to_matterbridge,
+ Toast.LENGTH_LONG
+ ).show()
}
+ }
- override fun onError(e: Throwable) {
- Log.e(TAG, "Something went wrong when trying to delete message with id " +
- message?.id, e)
- Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
- }
+ override fun onError(e: Throwable) {
+ Log.e(
+ TAG,
+ "Something went wrong when trying to delete message with id " +
+ message?.id,
+ e
+ )
+ Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+ }
- override fun onComplete() {
- }
- })
+ override fun onComplete() {
+ }
+ })
true
}
else -> false
@@ -1531,7 +1679,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
val messageTemp = message as ChatMessage
messageTemp.isDeleted = true
- messageTemp.isOneToOneConversation = currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
+ messageTemp.isOneToOneConversation =
+ currentConversation?.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
messageTemp.isLinkPreviewAllowed = isLinkPreviewAllowed
messageTemp.activeUser = conversationUser
@@ -1561,7 +1710,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
return true
}
-
override fun hasContentFor(message: IMessage, type: Byte): Boolean {
when (type) {
CONTENT_TYPE_SYSTEM_MESSAGE -> return !TextUtils.isEmpty(message.systemMessage)
@@ -1589,55 +1737,63 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
@Subscribe(threadMode = ThreadMode.BACKGROUND)
fun onMessageEvent(userMentionClickEvent: UserMentionClickEvent) {
- if (currentConversation?.type != Conversation.ConversationType
- .ROOM_TYPE_ONE_TO_ONE_CALL || currentConversation?.name !=
- userMentionClickEvent.userId) {
- val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(conversationUser?.baseUrl, "1",
- userMentionClickEvent.userId, null)
+ if (currentConversation?.type != Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL ||
+ currentConversation?.name != userMentionClickEvent.userId
+ ) {
+ val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
+ conversationUser?.baseUrl, "1",
+ userMentionClickEvent.userId, null
+ )
- ncApi?.createRoom(credentials,
- retrofitBucket.url, retrofitBucket.queryMap)
- ?.subscribeOn(Schedulers.io())
- ?.observeOn(AndroidSchedulers.mainThread())
- ?.subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {
+ ncApi?.createRoom(
+ credentials,
+ retrofitBucket.url, retrofitBucket.queryMap
+ )
+ ?.subscribeOn(Schedulers.io())
+ ?.observeOn(AndroidSchedulers.mainThread())
+ ?.subscribe(object : Observer {
+ override fun onSubscribe(d: Disposable) {
+ }
- }
+ override fun onNext(roomOverall: RoomOverall) {
+ val conversationIntent = Intent(activity, MagicCallActivity::class.java)
+ val bundle = Bundle()
+ bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser)
+ bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.ocs.data.token)
+ bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.ocs.data.roomId)
- override fun onNext(roomOverall: RoomOverall) {
- val conversationIntent = Intent(activity, MagicCallActivity::class.java)
- val bundle = Bundle()
- bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser)
- bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.ocs.data.token)
- bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.ocs.data.roomId)
-
- if (conversationUser != null) {
- if (conversationUser.hasSpreedFeatureCapability("chat-v2")) {
- bundle.putParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION,
- Parcels.wrap(roomOverall.ocs.data))
- conversationIntent.putExtras(bundle)
-
- ConductorRemapping.remapChatController(router, conversationUser.id,
- roomOverall.ocs.data.token, bundle, false)
- }
-
- } else {
+ if (conversationUser != null) {
+ if (conversationUser.hasSpreedFeatureCapability("chat-v2")) {
+ bundle.putParcelable(
+ BundleKeys.KEY_ACTIVE_CONVERSATION,
+ Parcels.wrap(roomOverall.ocs.data)
+ )
conversationIntent.putExtras(bundle)
- startActivity(conversationIntent)
- Handler().postDelayed({
+
+ ConductorRemapping.remapChatController(
+ router, conversationUser.id,
+ roomOverall.ocs.data.token, bundle, false
+ )
+ }
+ } else {
+ conversationIntent.putExtras(bundle)
+ startActivity(conversationIntent)
+ Handler().postDelayed(
+ {
if (!isDestroyed && !isBeingDestroyed) {
router.popCurrentController()
}
- }, 100)
- }
+ },
+ 100
+ )
}
+ }
- override fun onError(e: Throwable) {
+ override fun onError(e: Throwable) {
+ }
- }
-
- override fun onComplete() {}
- })
+ override fun onComplete() {}
+ })
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt
index 84d03acd2..ac2d2f262 100644
--- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt
+++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt
@@ -85,50 +85,69 @@ import io.reactivex.schedulers.Schedulers
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
-import java.util.*
+import java.util.Calendar
+import java.util.Collections
+import java.util.Comparator
+import java.util.Locale
import javax.inject.Inject
-import kotlin.collections.ArrayList
-
@AutoInjector(NextcloudTalkApplication::class)
class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleAdapter.OnItemClickListener {
@BindView(R.id.notification_settings)
lateinit var notificationsPreferenceScreen: MaterialPreferenceScreen
+
@BindView(R.id.progressBar)
lateinit var progressBar: ProgressBar
+
@BindView(R.id.conversation_info_message_notifications)
lateinit var messageNotificationLevel: MaterialChoicePreference
+
@BindView(R.id.webinar_settings)
lateinit var conversationInfoWebinar: MaterialPreferenceScreen
+
@BindView(R.id.conversation_info_lobby)
lateinit var conversationInfoLobby: MaterialSwitchPreference
+
@BindView(R.id.conversation_info_name)
lateinit var nameCategoryView: MaterialPreferenceCategory
+
@BindView(R.id.start_time_preferences)
lateinit var startTimeView: MaterialStandardPreference
+
@BindView(R.id.avatar_image)
lateinit var conversationAvatarImageView: SimpleDraweeView
+
@BindView(R.id.display_name_text)
lateinit var conversationDisplayName: EmojiTextView
+
@BindView(R.id.participants_list_category)
lateinit var participantsListCategory: MaterialPreferenceCategory
+
@BindView(R.id.addParticipantsAction)
- lateinit var addParticipantsAction: MaterialStandardPreference;
+ lateinit var addParticipantsAction: MaterialStandardPreference
+
@BindView(R.id.recycler_view)
lateinit var recyclerView: RecyclerView
+
@BindView(R.id.deleteConversationAction)
lateinit var deleteConversationAction: MaterialStandardPreference
+
@BindView(R.id.leaveConversationAction)
lateinit var leaveConversationAction: MaterialStandardPreference
+
@BindView(R.id.ownOptions)
lateinit var ownOptionsCategory: MaterialPreferenceCategory
+
@BindView(R.id.muteCalls)
lateinit var muteCalls: MaterialSwitchPreference
+
@set:Inject
lateinit var ncApi: NcApi
+
@set:Inject
lateinit var context: Context
+
@set:Inject
lateinit var eventBus: EventBus
@@ -164,7 +183,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
NextcloudTalkApplication.sharedApplication?.componentApplication?.inject(this)
conversationUser = args.getParcelable(BundleKeys.KEY_USER_ENTITY)
conversationToken = args.getString(BundleKeys.KEY_ROOM_TOKEN)
- hasAvatarSpacing = args.getBoolean(BundleKeys.KEY_ROOM_ONE_TO_ONE, false);
+ hasAvatarSpacing = args.getBoolean(BundleKeys.KEY_ROOM_ONE_TO_ONE, false)
credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token)
}
@@ -207,14 +226,19 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
}
private fun setupWebinaryView() {
- if (conversationUser!!.hasSpreedFeatureCapability("webinary-lobby") && (conversation!!.type
- == Conversation.ConversationType.ROOM_GROUP_CALL || conversation!!.type ==
- Conversation.ConversationType.ROOM_PUBLIC_CALL) && conversation!!.canModerate(conversationUser)) {
+ if (conversationUser!!.hasSpreedFeatureCapability("webinary-lobby") &&
+ (
+ conversation!!.type == Conversation.ConversationType.ROOM_GROUP_CALL ||
+ conversation!!.type == Conversation.ConversationType.ROOM_PUBLIC_CALL
+ ) &&
+ conversation!!.canModerate(conversationUser)
+ ) {
conversationInfoWebinar.visibility = View.VISIBLE
- val isLobbyOpenToModeratorsOnly = conversation!!.lobbyState == Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY
+ val isLobbyOpenToModeratorsOnly =
+ conversation!!.lobbyState == Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY
(conversationInfoLobby.findViewById(R.id.mp_checkable) as SwitchCompat)
- .isChecked = isLobbyOpenToModeratorsOnly
+ .isChecked = isLobbyOpenToModeratorsOnly
reconfigureLobbyTimerView()
@@ -225,12 +249,17 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
currentTimeCalendar.timeInMillis = conversation!!.lobbyTimer * 1000
}
- dateTimePicker(minDateTime = Calendar.getInstance(), requireFutureDateTime =
- true, currentDateTime = currentTimeCalendar, dateTimeCallback = { _,
- dateTime ->
- reconfigureLobbyTimerView(dateTime)
- submitLobbyChanges()
- })
+ dateTimePicker(
+ minDateTime = Calendar.getInstance(),
+ requireFutureDateTime =
+ true,
+ currentDateTime = currentTimeCalendar,
+ dateTimeCallback = { _,
+ dateTime ->
+ reconfigureLobbyTimerView(dateTime)
+ submitLobbyChanges()
+ }
+ )
}
}
@@ -253,7 +282,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
}
conversation!!.lobbyState = if (isChecked) Conversation.LobbyState
- .LOBBY_STATE_MODERATORS_ONLY else Conversation.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS
+ .LOBBY_STATE_MODERATORS_ONLY else Conversation.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS
if (conversation!!.lobbyTimer != null && conversation!!.lobbyTimer != java.lang.Long.MIN_VALUE && conversation!!.lobbyTimer != 0L) {
startTimeView.setSummary(DateUtils.getLocalDateStringFromTimestampForLobby(conversation!!.lobbyTimer))
@@ -269,27 +298,34 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
}
fun submitLobbyChanges() {
- val state = if ((conversationInfoLobby.findViewById(R.id
- .mp_checkable) as SwitchCompat).isChecked) 1 else 0
- ncApi.setLobbyForConversation(ApiUtils.getCredentials(conversationUser!!.username,
- conversationUser.token), ApiUtils.getUrlForLobbyForConversation
- (conversationUser.baseUrl, conversation!!.token), state, conversation!!.lobbyTimer)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(object : Observer {
- override fun onComplete() {
- }
+ val state = if (
+ (
+ conversationInfoLobby.findViewById(
+ R.id.mp_checkable
+ ) as SwitchCompat
+ ).isChecked
+ ) 1 else 0
+ ncApi.setLobbyForConversation(
+ ApiUtils.getCredentials(conversationUser!!.username, conversationUser.token),
+ ApiUtils.getUrlForLobbyForConversation(conversationUser.baseUrl, conversation!!.token),
+ state,
+ conversation!!.lobbyTimer
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(object : Observer {
+ override fun onComplete() {
+ }
- override fun onSubscribe(d: Disposable) {
- }
+ override fun onSubscribe(d: Disposable) {
+ }
- override fun onNext(t: GenericOverall) {
- }
+ override fun onNext(t: GenericOverall) {
+ }
- override fun onError(e: Throwable) {
- }
-
- })
+ override fun onError(e: Throwable) {
+ }
+ })
}
private fun showLovelyDialog(dialogId: Int, savedInstanceState: Bundle) {
@@ -313,17 +349,21 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
private fun showDeleteConversationDialog(savedInstanceState: Bundle?) {
if (activity != null) {
LovelyStandardDialog(activity, LovelyStandardDialog.ButtonLayout.HORIZONTAL)
- .setTopColorRes(R.color.nc_darkRed)
- .setIcon(DisplayUtils.getTintedDrawable(context!!.resources,
- R.drawable.ic_delete_black_24dp, R.color.bg_default))
- .setPositiveButtonColor(context!!.resources.getColor(R.color.nc_darkRed))
- .setTitle(R.string.nc_delete_call)
- .setMessage(conversation!!.deleteWarningMessage)
- .setPositiveButton(R.string.nc_delete) { deleteConversation() }
- .setNegativeButton(R.string.nc_cancel, null)
- .setInstanceStateHandler(ID_DELETE_CONVERSATION_DIALOG, saveStateHandler!!)
- .setSavedInstanceState(savedInstanceState)
- .show()
+ .setTopColorRes(R.color.nc_darkRed)
+ .setIcon(
+ DisplayUtils.getTintedDrawable(
+ context!!.resources,
+ R.drawable.ic_delete_black_24dp, R.color.bg_default
+ )
+ )
+ .setPositiveButtonColor(context!!.resources.getColor(R.color.nc_darkRed))
+ .setTitle(R.string.nc_delete_call)
+ .setMessage(conversation!!.deleteWarningMessage)
+ .setPositiveButton(R.string.nc_delete) { deleteConversation() }
+ .setNegativeButton(R.string.nc_cancel, null)
+ .setInstanceStateHandler(ID_DELETE_CONVERSATION_DIALOG, saveStateHandler!!)
+ .setSavedInstanceState(savedInstanceState)
+ .show()
}
}
@@ -335,8 +375,8 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
override fun onRestoreViewState(view: View, savedViewState: Bundle) {
super.onRestoreViewState(view, savedViewState)
if (LovelySaveStateHandler.wasDialogOnScreen(savedViewState)) {
- //Dialog won't be restarted automatically, so we need to call this method.
- //Each dialog knows how to restore its state
+ // Dialog won't be restarted automatically, so we need to call this method.
+ // Each dialog knows how to restore its state
showLovelyDialog(LovelySaveStateHandler.getSavedDialogId(savedViewState), savedViewState)
}
}
@@ -397,27 +437,28 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
}
private fun getListOfParticipants() {
- ncApi.getPeersForCall(credentials, ApiUtils.getUrlForParticipants(conversationUser!!.baseUrl, conversationToken))
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {
- participantsDisposable = d
- }
+ ncApi.getPeersForCall(
+ credentials,
+ ApiUtils.getUrlForParticipants(conversationUser!!.baseUrl, conversationToken)
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(object : Observer {
+ override fun onSubscribe(d: Disposable) {
+ participantsDisposable = d
+ }
- override fun onNext(participantsOverall: ParticipantsOverall) {
- handleParticipants(participantsOverall.ocs.data)
- }
+ override fun onNext(participantsOverall: ParticipantsOverall) {
+ handleParticipants(participantsOverall.ocs.data)
+ }
- override fun onError(e: Throwable) {
-
- }
-
- override fun onComplete() {
- participantsDisposable!!.dispose()
- }
- })
+ override fun onError(e: Throwable) {
+ }
+ override fun onComplete() {
+ participantsDisposable!!.dispose()
+ }
+ })
}
@OnClick(R.id.addParticipantsAction)
@@ -430,21 +471,33 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
existingParticipantsId.add(userItem.model.userId)
}
- bundle.putBoolean(BundleKeys.KEY_ADD_PARTICIPANTS, true);
+ bundle.putBoolean(BundleKeys.KEY_ADD_PARTICIPANTS, true)
bundle.putStringArrayList(BundleKeys.KEY_EXISTING_PARTICIPANTS, existingParticipantsId)
bundle.putString(BundleKeys.KEY_TOKEN, conversation!!.token)
- getRouter().pushController((RouterTransaction.with(ContactsController(bundle))
- .pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler())))
+ getRouter().pushController(
+ (
+ RouterTransaction.with(
+ ContactsController(bundle)
+ )
+ .pushChangeHandler(
+ HorizontalChangeHandler()
+ )
+ .popChangeHandler(
+ HorizontalChangeHandler()
+ )
+ )
+ )
}
@OnClick(R.id.leaveConversationAction)
internal fun leaveConversation() {
workerData?.let {
- WorkManager.getInstance().enqueue(OneTimeWorkRequest.Builder
- (LeaveConversationWorker::class
- .java).setInputData(it).build()
+ WorkManager.getInstance().enqueue(
+ OneTimeWorkRequest.Builder(
+ LeaveConversationWorker::class
+ .java
+ ).setInputData(it).build()
)
popTwoLastControllers()
}
@@ -452,8 +505,11 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
private fun deleteConversation() {
workerData?.let {
- WorkManager.getInstance().enqueue(OneTimeWorkRequest.Builder
- (DeleteConversationWorker::class.java).setInputData(it).build())
+ WorkManager.getInstance().enqueue(
+ OneTimeWorkRequest.Builder(
+ DeleteConversationWorker::class.java
+ ).setInputData(it).build()
+ )
popTwoLastControllers()
}
}
@@ -471,69 +527,67 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
private fun fetchRoomInfo() {
ncApi.getRoom(credentials, ApiUtils.getRoom(conversationUser!!.baseUrl, conversationToken))
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {
- roomDisposable = d
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(object : Observer {
+ override fun onSubscribe(d: Disposable) {
+ roomDisposable = d
+ }
+
+ override fun onNext(roomOverall: RoomOverall) {
+ conversation = roomOverall.ocs.data
+
+ val conversationCopy = conversation
+
+ if (conversationCopy!!.canModerate(conversationUser)) {
+ addParticipantsAction.visibility = View.VISIBLE
+ } else {
+ addParticipantsAction.visibility = View.GONE
}
- override fun onNext(roomOverall: RoomOverall) {
- conversation = roomOverall.ocs.data
+ if (isAttached && (!isBeingDestroyed || !isDestroyed)) {
+ ownOptionsCategory.visibility = View.VISIBLE
- val conversationCopy = conversation
+ setupWebinaryView()
- if (conversationCopy!!.canModerate(conversationUser)) {
- addParticipantsAction.visibility = View.VISIBLE
+ if (!conversation!!.canLeave(conversationUser)) {
+ leaveConversationAction.visibility = View.GONE
} else {
- addParticipantsAction.visibility = View.GONE
+ leaveConversationAction.visibility = View.VISIBLE
}
- if (isAttached && (!isBeingDestroyed || !isDestroyed)) {
- ownOptionsCategory.visibility = View.VISIBLE
-
- setupWebinaryView()
-
- if (!conversation!!.canLeave(conversationUser)) {
- leaveConversationAction.visibility = View.GONE
- } else {
- leaveConversationAction.visibility = View.VISIBLE
- }
-
- if (!conversation!!.canModerate(conversationUser)) {
- deleteConversationAction.visibility = View.GONE
- } else {
- deleteConversationAction.visibility = View.VISIBLE
- }
-
- if (Conversation.ConversationType.ROOM_SYSTEM == conversation!!.type) {
- muteCalls.visibility = View.GONE
- }
-
- getListOfParticipants()
-
- progressBar.visibility = View.GONE
-
- nameCategoryView.visibility = View.VISIBLE
-
- conversationDisplayName.text = conversation!!.displayName
-
-
- loadConversationAvatar()
- adjustNotificationLevelUI()
-
- notificationsPreferenceScreen.visibility = View.VISIBLE
+ if (!conversation!!.canModerate(conversationUser)) {
+ deleteConversationAction.visibility = View.GONE
+ } else {
+ deleteConversationAction.visibility = View.VISIBLE
}
- }
- override fun onError(e: Throwable) {
+ if (Conversation.ConversationType.ROOM_SYSTEM == conversation!!.type) {
+ muteCalls.visibility = View.GONE
+ }
- }
+ getListOfParticipants()
- override fun onComplete() {
- roomDisposable!!.dispose()
+ progressBar.visibility = View.GONE
+
+ nameCategoryView.visibility = View.VISIBLE
+
+ conversationDisplayName.text = conversation!!.displayName
+
+ loadConversationAvatar()
+ adjustNotificationLevelUI()
+
+ notificationsPreferenceScreen.visibility = View.VISIBLE
}
- })
+ }
+
+ override fun onError(e: Throwable) {
+ }
+
+ override fun onComplete() {
+ roomDisposable!!.dispose()
+ }
+ })
}
private fun adjustNotificationLevelUI() {
@@ -543,12 +597,13 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
messageNotificationLevel.alpha = 1.0f
if (conversation!!.notificationLevel != Conversation.NotificationLevel.DEFAULT) {
- val stringValue: String = when (EnumNotificationLevelConverter().convertToInt(conversation!!.notificationLevel)) {
- 1 -> "always"
- 2 -> "mention"
- 3 -> "never"
- else -> "mention"
- }
+ val stringValue: String =
+ when (EnumNotificationLevelConverter().convertToInt(conversation!!.notificationLevel)) {
+ 1 -> "always"
+ 2 -> "mention"
+ 3 -> "never"
+ else -> "mention"
+ }
messageNotificationLevel.value = stringValue
} else {
@@ -577,22 +632,38 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
private fun loadConversationAvatar() {
when (conversation!!.type) {
- Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty
- (conversation!!.name)) {
+ Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (
+ !TextUtils.isEmpty(conversation!!.name)
+ ) {
val draweeController = Fresco.newDraweeControllerBuilder()
- .setOldController(conversationAvatarImageView.controller)
- .setAutoPlayAnimations(true)
- .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(conversationUser!!.baseUrl,
- conversation!!.name, R.dimen.avatar_size_big), conversationUser))
- .build()
+ .setOldController(conversationAvatarImageView.controller)
+ .setAutoPlayAnimations(true)
+ .setImageRequest(
+ DisplayUtils.getImageRequestForUrl(
+ ApiUtils.getUrlForAvatarWithName(
+ conversationUser!!.baseUrl,
+ conversation!!.name, R.dimen.avatar_size_big
+ ),
+ conversationUser
+ )
+ )
+ .build()
conversationAvatarImageView.controller = draweeController
}
- Conversation.ConversationType.ROOM_GROUP_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage(DisplayUtils
- .getRoundedBitmapDrawableFromVectorDrawableResource(resources,
- R.drawable.ic_people_group_white_24px))
- Conversation.ConversationType.ROOM_PUBLIC_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage(DisplayUtils
- .getRoundedBitmapDrawableFromVectorDrawableResource(resources,
- R.drawable.ic_link_white_24px))
+ Conversation.ConversationType.ROOM_GROUP_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage(
+ DisplayUtils
+ .getRoundedBitmapDrawableFromVectorDrawableResource(
+ resources,
+ R.drawable.ic_people_group_white_24px
+ )
+ )
+ Conversation.ConversationType.ROOM_PUBLIC_CALL -> conversationAvatarImageView.hierarchy.setPlaceholderImage(
+ DisplayUtils
+ .getRoundedBitmapDrawableFromVectorDrawableResource(
+ resources,
+ R.drawable.ic_link_white_24px
+ )
+ )
Conversation.ConversationType.ROOM_SYSTEM -> {
val layers = arrayOfNulls(2)
layers[0] = context.getDrawable(R.drawable.ic_launcher_background)
@@ -610,13 +681,14 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
val userItem = adapter?.getItem(position) as UserItem
val participant = userItem.model
-
if (participant.userId != conversationUser!!.userId) {
var items = mutableListOf(
- BasicListItemWithImage(R.drawable.ic_pencil_grey600_24dp, context.getString(R.string.nc_promote)),
- BasicListItemWithImage(R.drawable.ic_pencil_grey600_24dp, context.getString(R.string.nc_demote)),
- BasicListItemWithImage(R.drawable.ic_delete_grey600_24dp,
- context.getString(R.string.nc_remove_participant))
+ BasicListItemWithImage(R.drawable.ic_pencil_grey600_24dp, context.getString(R.string.nc_promote)),
+ BasicListItemWithImage(R.drawable.ic_pencil_grey600_24dp, context.getString(R.string.nc_demote)),
+ BasicListItemWithImage(
+ R.drawable.ic_delete_grey600_24dp,
+ context.getString(R.string.nc_remove_participant)
+ )
)
if (!conversation!!.canModerate(conversationUser)) {
@@ -629,7 +701,6 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
}
}
-
if (items.isNotEmpty()) {
MaterialDialog(activity!!, BottomSheet(WRAP_CONTENT)).show {
cornerRadius(res = R.dimen.corner_radius)
@@ -639,38 +710,62 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
if (index == 0) {
if (participant.type == Participant.ParticipantType.MODERATOR) {
- ncApi.demoteModeratorToUser(credentials, ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token), participant.userId)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe {
- getListOfParticipants()
- }
+ ncApi.demoteModeratorToUser(
+ credentials,
+ ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token),
+ participant.userId
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe {
+ getListOfParticipants()
+ }
} else if (participant.type == Participant.ParticipantType.USER) {
- ncApi.promoteUserToModerator(credentials, ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token), participant.userId)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe {
- getListOfParticipants()
- }
+ ncApi.promoteUserToModerator(
+ credentials,
+ ApiUtils.getUrlForModerators(conversationUser.baseUrl, conversation!!.token),
+ participant.userId
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe {
+ getListOfParticipants()
+ }
}
} else if (index == 1) {
if (participant.type == Participant.ParticipantType.GUEST ||
- participant.type == Participant.ParticipantType.USER_FOLLOWING_LINK) {
- ncApi.removeParticipantFromConversation(credentials, ApiUtils.getUrlForRemovingParticipantFromConversation(conversationUser.baseUrl, conversation!!.token, true), participant.sessionId)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe {
- getListOfParticipants()
- }
-
+ participant.type == Participant.ParticipantType.USER_FOLLOWING_LINK
+ ) {
+ ncApi.removeParticipantFromConversation(
+ credentials,
+ ApiUtils.getUrlForRemovingParticipantFromConversation(
+ conversationUser.baseUrl,
+ conversation!!.token,
+ true
+ ),
+ participant.sessionId
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe {
+ getListOfParticipants()
+ }
} else {
- ncApi.removeParticipantFromConversation(credentials, ApiUtils.getUrlForRemovingParticipantFromConversation(conversationUser.baseUrl, conversation!!.token, false), participant.userId)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe {
- getListOfParticipants()
- // get participants again
- }
+ ncApi.removeParticipantFromConversation(
+ credentials,
+ ApiUtils.getUrlForRemovingParticipantFromConversation(
+ conversationUser.baseUrl,
+ conversation!!.token,
+ false
+ ),
+ participant.userId
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe {
+ getListOfParticipants()
+ // get participants again
+ }
}
}
}
@@ -678,7 +773,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
}
}
- return true;
+ return true
}
companion object {
@@ -709,7 +804,7 @@ class ConversationInfoController(args: Bundle) : BaseController(args), FlexibleA
}
return left.model.displayName.toLowerCase(Locale.ROOT).compareTo(
- right.model.displayName.toLowerCase(Locale.ROOT)
+ right.model.displayName.toLowerCase(Locale.ROOT)
)
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/base/ButterKnifeController.kt b/app/src/main/java/com/nextcloud/talk/controllers/base/ButterKnifeController.kt
index e6191f49f..8f813a4b4 100644
--- a/app/src/main/java/com/nextcloud/talk/controllers/base/ButterKnifeController.kt
+++ b/app/src/main/java/com/nextcloud/talk/controllers/base/ButterKnifeController.kt
@@ -53,5 +53,4 @@ abstract class ButterKnifeController : Controller {
unbinder!!.unbind()
unbinder = null
}
-
}
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/BasicListItemWithImage.kt b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/BasicListItemWithImage.kt
index 8d75b08ba..80399135b 100644
--- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/BasicListItemWithImage.kt
+++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/BasicListItemWithImage.kt
@@ -29,10 +29,11 @@ interface ListItemWithImage {
}
data class BasicListItemWithImage(
- @DrawableRes val iconRes: Int,
- override val title: String) : ListItemWithImage {
+ @DrawableRes val iconRes: Int,
+ override val title: String
+) : ListItemWithImage {
override fun populateIcon(imageView: ImageView) {
imageView.setImageResource(iconRes)
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/ListIconDialogAdapter.kt b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/ListIconDialogAdapter.kt
index 43c66fd1a..fb67eed04 100644
--- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/ListIconDialogAdapter.kt
+++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/ListIconDialogAdapter.kt
@@ -23,7 +23,6 @@ package com.nextcloud.talk.controllers.bottomsheet.items
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
-import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.WhichButton
@@ -34,14 +33,14 @@ import com.afollestad.materialdialogs.internal.rtl.RtlTextView
import com.afollestad.materialdialogs.list.getItemSelector
import com.afollestad.materialdialogs.utils.MDUtil.inflate
import com.afollestad.materialdialogs.utils.MDUtil.maybeSetTextColor
-import com.google.android.material.textview.MaterialTextView
import com.nextcloud.talk.R
private const val KEY_ACTIVATED_INDEX = "activated_index"
internal class ListItemViewHolder(
- itemView: View,
- private val adapter: ListIconDialogAdapter<*>) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
+ itemView: View,
+ private val adapter: ListIconDialogAdapter<*>
+) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
init {
itemView.setOnClickListener(this)
}
@@ -53,11 +52,12 @@ internal class ListItemViewHolder(
}
internal class ListIconDialogAdapter(
- private var dialog: MaterialDialog,
- private var items: List,
- disabledItems: IntArray?,
- private var waitForPositiveButton: Boolean,
- private var selection: ListItemListener) : RecyclerView.Adapter(), DialogAdapter> {
+ private var dialog: MaterialDialog,
+ private var items: List,
+ disabledItems: IntArray?,
+ private var waitForPositiveButton: Boolean,
+ private var selection: ListItemListener
+) : RecyclerView.Adapter(), DialogAdapter> {
private var disabledIndices: IntArray = disabledItems ?: IntArray(0)
@@ -81,12 +81,13 @@ internal class ListIconDialogAdapter(
}
override fun onCreateViewHolder(
- parent: ViewGroup,
- viewType: Int): ListItemViewHolder {
+ parent: ViewGroup,
+ viewType: Int
+ ): ListItemViewHolder {
val listItemView: View = parent.inflate(dialog.windowContext, R.layout.menu_item_sheet)
val viewHolder = ListItemViewHolder(
- itemView = listItemView,
- adapter = this
+ itemView = listItemView,
+ adapter = this
)
viewHolder.titleView.maybeSetTextColor(dialog.windowContext, R.attr.md_color_content)
return viewHolder
@@ -95,8 +96,9 @@ internal class ListIconDialogAdapter(
override fun getItemCount() = items.size
override fun onBindViewHolder(
- holder: ListItemViewHolder,
- position: Int) {
+ holder: ListItemViewHolder,
+ position: Int
+ ) {
holder.itemView.isEnabled = !disabledIndices.contains(position)
val currentItem = items[position]
@@ -121,8 +123,9 @@ internal class ListIconDialogAdapter(
}
override fun replaceItems(
- items: List,
- listener: ListItemListener) {
+ items: List,
+ listener: ListItemListener
+ ) {
this.items = items
if (listener != null) {
this.selection = listener
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/MagicBottomSheets.kt b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/MagicBottomSheets.kt
index ab2be552a..6d70f3f8c 100644
--- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/MagicBottomSheets.kt
+++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/items/MagicBottomSheets.kt
@@ -28,37 +28,40 @@ import com.afollestad.materialdialogs.list.customListAdapter
import com.afollestad.materialdialogs.list.getListAdapter
typealias ListItemListener =
- ((dialog: MaterialDialog, index: Int, item: IT) -> Unit)?
+ ((dialog: MaterialDialog, index: Int, item: IT) -> Unit)?
-@CheckResult fun MaterialDialog.listItemsWithImage(
- items: List,
- disabledIndices: IntArray? = null,
- waitForPositiveButton: Boolean = true,
- selection: ListItemListener = null): MaterialDialog {
+@CheckResult
+fun MaterialDialog.listItemsWithImage(
+ items: List,
+ disabledIndices: IntArray? = null,
+ waitForPositiveButton: Boolean = true,
+ selection: ListItemListener = null
+): MaterialDialog {
if (getListAdapter() != null) {
return updateListItemsWithImage(
- items = items,
- disabledIndices = disabledIndices
+ items = items,
+ disabledIndices = disabledIndices
)
}
val layoutManager = LinearLayoutManager(windowContext)
return customListAdapter(
- adapter = ListIconDialogAdapter(
- dialog = this,
- items = items,
- disabledItems = disabledIndices,
- waitForPositiveButton = waitForPositiveButton,
- selection = selection
- ),
- layoutManager = layoutManager
+ adapter = ListIconDialogAdapter(
+ dialog = this,
+ items = items,
+ disabledItems = disabledIndices,
+ waitForPositiveButton = waitForPositiveButton,
+ selection = selection
+ ),
+ layoutManager = layoutManager
)
}
fun MaterialDialog.updateListItemsWithImage(
- items: List,
- disabledIndices: IntArray? = null): MaterialDialog {
+ items: List,
+ disabledIndices: IntArray? = null
+): MaterialDialog {
val adapter = getListAdapter()
check(adapter != null) {
"updateGridItems(...) can't be used before you've created a bottom sheet grid dialog."
diff --git a/app/src/main/java/com/nextcloud/talk/events/CallNotificationClick.kt b/app/src/main/java/com/nextcloud/talk/events/CallNotificationClick.kt
index c883ee04a..ad8c52fde 100644
--- a/app/src/main/java/com/nextcloud/talk/events/CallNotificationClick.kt
+++ b/app/src/main/java/com/nextcloud/talk/events/CallNotificationClick.kt
@@ -20,6 +20,4 @@
package com.nextcloud.talk.events
-class CallNotificationClick {
-
-}
\ No newline at end of file
+class CallNotificationClick
diff --git a/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt
index 38be955fa..f7703623c 100644
--- a/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt
+++ b/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt
@@ -33,7 +33,11 @@ import android.provider.ContactsContract
import android.util.Log
import androidx.core.content.ContextCompat
import androidx.core.os.ConfigurationCompat
-import androidx.work.*
+import androidx.work.Data
+import androidx.work.OneTimeWorkRequest
+import androidx.work.WorkManager
+import androidx.work.Worker
+import androidx.work.WorkerParameters
import autodagger.AutoInjector
import com.bluelinelabs.conductor.Controller
import com.google.gson.Gson
@@ -54,10 +58,9 @@ import okhttp3.MediaType
import okhttp3.RequestBody
import javax.inject.Inject
-
@AutoInjector(NextcloudTalkApplication::class)
class ContactAddressBookWorker(val context: Context, workerParameters: WorkerParameters) :
- Worker(context, workerParameters) {
+ Worker(context, workerParameters) {
@Inject
lateinit var ncApi: NcApi
@@ -85,7 +88,7 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
}
val deleteAll = inputData.getBoolean(DELETE_ALL, false)
- if(deleteAll){
+ if (deleteAll) {
deleteAllLinkedAccounts()
return Result.success()
}
@@ -99,7 +102,7 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
}
}
- if(AccountManager.get(context).getAccountsByType(accountType).isEmpty()){
+ if (AccountManager.get(context).getAccountsByType(accountType).isEmpty()) {
AccountManager.get(context).addAccountExplicitly(Account(accountName, accountType), "", null)
} else {
Log.d(TAG, "Account already exists")
@@ -107,7 +110,7 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
val deviceContactsWithNumbers = collectContactsWithPhoneNumbersFromDevice()
- if(deviceContactsWithNumbers.isNotEmpty()){
+ if (deviceContactsWithNumbers.isNotEmpty()) {
val currentLocale = ConfigurationCompat.getLocales(context.resources.configuration)[0].country
val map = mutableMapOf()
@@ -117,28 +120,29 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
val json = Gson().toJson(map)
ncApi.searchContactsByPhoneNumber(
- ApiUtils.getCredentials(currentUser.username, currentUser.token),
- ApiUtils.getUrlForSearchByNumber(currentUser.baseUrl),
- RequestBody.create(MediaType.parse("application/json"), json))
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(object : Observer {
- override fun onComplete() {
- }
+ ApiUtils.getCredentials(currentUser.username, currentUser.token),
+ ApiUtils.getUrlForSearchByNumber(currentUser.baseUrl),
+ RequestBody.create(MediaType.parse("application/json"), json)
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(object : Observer {
+ override fun onComplete() {
+ }
- override fun onSubscribe(d: Disposable) {
- }
+ override fun onSubscribe(d: Disposable) {
+ }
- override fun onNext(foundContacts: ContactsByNumberOverall) {
- val contactsWithAssociatedPhoneNumbers = foundContacts.ocs.map
- deleteLinkedAccounts(contactsWithAssociatedPhoneNumbers)
- createLinkedAccounts(contactsWithAssociatedPhoneNumbers)
- }
+ override fun onNext(foundContacts: ContactsByNumberOverall) {
+ val contactsWithAssociatedPhoneNumbers = foundContacts.ocs.map
+ deleteLinkedAccounts(contactsWithAssociatedPhoneNumbers)
+ createLinkedAccounts(contactsWithAssociatedPhoneNumbers)
+ }
- override fun onError(e: Throwable) {
- Log.e(javaClass.simpleName, "Failed to searchContactsByPhoneNumber", e)
- }
- })
+ override fun onError(e: Throwable) {
+ Log.e(javaClass.simpleName, "Failed to searchContactsByPhoneNumber", e)
+ }
+ })
}
// store timestamp
@@ -151,11 +155,11 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
val deviceContactsWithNumbers: MutableMap> = mutableMapOf()
val contactCursor = context.contentResolver.query(
- ContactsContract.Contacts.CONTENT_URI,
- null,
- null,
- null,
- null
+ ContactsContract.Contacts.CONTENT_URI,
+ null,
+ null,
+ null,
+ null
)
if (contactCursor != null) {
@@ -163,7 +167,8 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
contactCursor.moveToFirst()
for (i in 0 until contactCursor.count) {
val id = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts._ID))
- val lookup = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY))
+ val lookup =
+ contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY))
deviceContactsWithNumbers[lookup] = getPhoneNumbersFromDeviceContact(id)
contactCursor.moveToNext()
}
@@ -178,39 +183,50 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
Log.d(TAG, "deleteLinkedAccount")
fun deleteLinkedAccount(id: String) {
val rawContactUri = ContactsContract.RawContacts.CONTENT_URI
- .buildUpon()
- .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
- .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
- .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
- .build()
- val count = context.contentResolver.delete(rawContactUri, ContactsContract.RawContacts.CONTACT_ID + " " +
- "LIKE \"" + id + "\"", null)
- Log.d(TAG, "deleted $count linked accounts for id $id")
- }
-
- val rawContactUri = ContactsContract.Data.CONTENT_URI
.buildUpon()
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
- .appendQueryParameter(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat")
.build()
+ val count = context.contentResolver.delete(
+ rawContactUri,
+ ContactsContract.RawContacts.CONTACT_ID + " " + "LIKE \"" + id + "\"",
+ null
+ )
+ Log.d(TAG, "deleted $count linked accounts for id $id")
+ }
+
+ val rawContactUri = ContactsContract.Data.CONTENT_URI
+ .buildUpon()
+ .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
+ .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
+ .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
+ .appendQueryParameter(
+ ContactsContract.Data.MIMETYPE,
+ "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat"
+ )
+ .build()
val rawContactsCursor = context.contentResolver.query(
- rawContactUri,
- null,
- null,
- null,
- null
+ rawContactUri,
+ null,
+ null,
+ null,
+ null
)
if (rawContactsCursor != null) {
if (rawContactsCursor.count > 0) {
while (rawContactsCursor.moveToNext()) {
- val lookupKey = rawContactsCursor.getString(rawContactsCursor.getColumnIndex(ContactsContract.Data.LOOKUP_KEY))
- val contactId = rawContactsCursor.getString(rawContactsCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID))
+ val lookupKey =
+ rawContactsCursor.getString(rawContactsCursor.getColumnIndex(ContactsContract.Data.LOOKUP_KEY))
+ val contactId =
+ rawContactsCursor.getString(rawContactsCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID))
- if (contactsWithAssociatedPhoneNumbers == null || !contactsWithAssociatedPhoneNumbers.containsKey(lookupKey)) {
+ if (contactsWithAssociatedPhoneNumbers == null || !contactsWithAssociatedPhoneNumbers.containsKey(
+ lookupKey
+ )
+ ) {
deleteLinkedAccount(contactId)
}
}
@@ -222,25 +238,29 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
}
private fun createLinkedAccounts(contactsWithAssociatedPhoneNumbers: Map?) {
- fun hasLinkedAccount(id: String) : Boolean {
+ fun hasLinkedAccount(id: String): Boolean {
var hasLinkedAccount = false
- val where = ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ?"
+ val where =
+ ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ?"
val params = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id)
val rawContactUri = ContactsContract.Data.CONTENT_URI
- .buildUpon()
- .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
- .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
- .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
- .appendQueryParameter(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat")
- .build()
+ .buildUpon()
+ .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
+ .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
+ .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
+ .appendQueryParameter(
+ ContactsContract.Data.MIMETYPE,
+ "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat"
+ )
+ .build()
val rawContactsCursor = context.contentResolver.query(
- rawContactUri,
- null,
- where,
- params,
- null
+ rawContactUri,
+ null,
+ where,
+ params,
+ null
)
if (rawContactsCursor != null) {
@@ -259,18 +279,19 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
val lookupUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey)
val lookupContactUri = ContactsContract.Contacts.lookupContact(context.contentResolver, lookupUri)
val contactCursor = context.contentResolver.query(
- lookupContactUri,
- null,
- null,
- null,
- null)
+ lookupContactUri,
+ null,
+ null,
+ null,
+ null
+ )
if (contactCursor != null) {
if (contactCursor.count > 0) {
contactCursor.moveToFirst()
val id = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.Contacts._ID))
- if(hasLinkedAccount(id)){
+ if (hasLinkedAccount(id)) {
return
}
@@ -285,34 +306,60 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
val rawContactsUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().build()
val dataUri = ContactsContract.Data.CONTENT_URI.buildUpon().build()
- ops.add(ContentProviderOperation
+ ops.add(
+ ContentProviderOperation
.newInsert(rawContactsUri)
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
- .withValue(ContactsContract.RawContacts.AGGREGATION_MODE,
- ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT)
+ .withValue(
+ ContactsContract.RawContacts.AGGREGATION_MODE,
+ ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT
+ )
.withValue(ContactsContract.RawContacts.SYNC2, cloudId)
- .build())
- ops.add(ContentProviderOperation
+ .build()
+ )
+ ops.add(
+ ContentProviderOperation
.newInsert(dataUri)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
+ .withValue(
+ ContactsContract.Data.MIMETYPE,
+ ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
+ )
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, numbers[0])
- .build())
- ops.add(ContentProviderOperation
+ .build()
+ )
+ ops.add(
+ ContentProviderOperation
.newInsert(dataUri)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
+ .withValue(
+ ContactsContract.Data.MIMETYPE,
+ ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE
+ )
.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName)
- .build())
- ops.add(ContentProviderOperation
+ .build()
+ )
+ ops.add(
+ ContentProviderOperation
.newInsert(dataUri)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
- .withValue(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat")
+ .withValue(
+ ContactsContract.Data.MIMETYPE,
+ "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat"
+ )
.withValue(ContactsContract.Data.DATA1, cloudId)
- .withValue(ContactsContract.Data.DATA2, String.format(context.resources.getString(R
- .string.nc_phone_book_integration_chat_via), accountName))
- .build())
+ .withValue(
+ ContactsContract.Data.DATA2,
+ String.format(
+ context.resources.getString(
+ R.string.nc_phone_book_integration_chat_via
+ ),
+ accountName
+ )
+ )
+ .build()
+ )
try {
context.contentResolver.applyBatch(ContactsContract.AUTHORITY, ops)
@@ -322,8 +369,11 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
Log.e(javaClass.simpleName, "", e)
}
- Log.d(TAG, "added new entry for contact $displayName (cloudId: $cloudId | lookupKey: $lookupKey" +
- " | id: $id)")
+ Log.d(
+ TAG,
+ "added new entry for contact $displayName (cloudId: $cloudId | lookupKey: $lookupKey" +
+ " | id: $id)"
+ )
}
contactCursor.close()
}
@@ -341,18 +391,21 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
}
private fun getDisplayNameFromDeviceContact(id: String?): String? {
- var displayName:String? = null
- val whereName = ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ?"
+ var displayName: String? = null
+ val whereName =
+ ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ?"
val whereNameParams = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id)
val nameCursor = context.contentResolver.query(
- ContactsContract.Data.CONTENT_URI,
- null,
- whereName,
- whereNameParams,
- ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME)
+ ContactsContract.Data.CONTENT_URI,
+ null,
+ whereName,
+ whereNameParams,
+ ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME
+ )
if (nameCursor != null) {
while (nameCursor.moveToNext()) {
- displayName = nameCursor.getString(nameCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME))
+ displayName =
+ nameCursor.getString(nameCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME))
}
nameCursor.close()
}
@@ -362,11 +415,12 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
private fun getPhoneNumbersFromDeviceContact(id: String?): MutableList {
val numbers = mutableListOf()
val phonesNumbersCursor = context.contentResolver.query(
- ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
- null,
- ContactsContract.Data.CONTACT_ID + " = " + id,
- null,
- null)
+ ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
+ null,
+ ContactsContract.Data.CONTACT_ID + " = " + id,
+ null,
+ null
+ )
if (phonesNumbersCursor != null) {
while (phonesNumbersCursor.moveToNext()) {
@@ -374,7 +428,7 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
}
phonesNumbersCursor.close()
}
- if(numbers.size > 0){
+ if (numbers.size > 0) {
Log.d(TAG, "Found ${numbers.size} phone numbers for contact with id $id")
}
return numbers
@@ -382,11 +436,11 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
fun deleteAllLinkedAccounts() {
val rawContactUri = ContactsContract.RawContacts.CONTENT_URI
- .buildUpon()
- .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
- .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
- .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
- .build()
+ .buildUpon()
+ .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
+ .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName)
+ .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType)
+ .build()
context.contentResolver.delete(rawContactUri, null, null)
Log.d(TAG, "deleted all linked accounts")
}
@@ -398,42 +452,63 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
const val DELETE_ALL = "DELETE_ALL"
fun run(context: Context) {
- if (ContextCompat.checkSelfPermission(context,
- Manifest.permission.WRITE_CONTACTS) == PackageManager.PERMISSION_GRANTED &&
- ContextCompat.checkSelfPermission(context,
- Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
+ if (ContextCompat.checkSelfPermission(
+ context,
+ Manifest.permission.WRITE_CONTACTS
+ ) == PackageManager.PERMISSION_GRANTED &&
+ ContextCompat.checkSelfPermission(
+ context,
+ Manifest.permission.READ_CONTACTS
+ ) == PackageManager.PERMISSION_GRANTED
+ ) {
WorkManager
- .getInstance()
- .enqueue(OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
- .setInputData(Data.Builder().putBoolean(KEY_FORCE, false).build())
- .build())
+ .getInstance()
+ .enqueue(
+ OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
+ .setInputData(Data.Builder().putBoolean(KEY_FORCE, false).build())
+ .build()
+ )
}
}
fun checkPermission(controller: Controller, context: Context): Boolean {
- if (ContextCompat.checkSelfPermission(context,
- Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED ||
- ContextCompat.checkSelfPermission(context,
- Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
- controller.requestPermissions(arrayOf(Manifest.permission.WRITE_CONTACTS,
- Manifest.permission.READ_CONTACTS), REQUEST_PERMISSION)
+ if (ContextCompat.checkSelfPermission(
+ context,
+ Manifest.permission.WRITE_CONTACTS
+ ) != PackageManager.PERMISSION_GRANTED ||
+ ContextCompat.checkSelfPermission(
+ context,
+ Manifest.permission.READ_CONTACTS
+ ) != PackageManager.PERMISSION_GRANTED
+ ) {
+ controller.requestPermissions(
+ arrayOf(
+ Manifest.permission.WRITE_CONTACTS,
+ Manifest.permission.READ_CONTACTS
+ ),
+ REQUEST_PERMISSION
+ )
return false
} else {
WorkManager
- .getInstance()
- .enqueue(OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
- .setInputData(Data.Builder().putBoolean(KEY_FORCE, true).build())
- .build())
+ .getInstance()
+ .enqueue(
+ OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
+ .setInputData(Data.Builder().putBoolean(KEY_FORCE, true).build())
+ .build()
+ )
return true
}
}
fun deleteAll() {
- WorkManager
- .getInstance()
- .enqueue(OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
- .setInputData(Data.Builder().putBoolean(DELETE_ALL, true).build())
- .build())
+ WorkManager
+ .getInstance()
+ .enqueue(
+ OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java)
+ .setInputData(Data.Builder().putBoolean(DELETE_ALL, true).build())
+ .build()
+ )
}
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/jobs/DownloadFileToCacheWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/DownloadFileToCacheWorker.kt
index a01046c6e..08584e431 100644
--- a/app/src/main/java/com/nextcloud/talk/jobs/DownloadFileToCacheWorker.kt
+++ b/app/src/main/java/com/nextcloud/talk/jobs/DownloadFileToCacheWorker.kt
@@ -33,13 +33,16 @@ import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.database.user.UserUtils
import com.nextcloud.talk.utils.preferences.AppPreferences
import okhttp3.ResponseBody
-import java.io.*
+import java.io.BufferedInputStream
+import java.io.File
+import java.io.FileOutputStream
+import java.io.InputStream
+import java.io.OutputStream
import javax.inject.Inject
-
@AutoInjector(NextcloudTalkApplication::class)
class DownloadFileToCacheWorker(val context: Context, workerParameters: WorkerParameters) :
- Worker(context, workerParameters) {
+ Worker(context, workerParameters) {
private var totalFileSize: Int = -1
@@ -86,8 +89,9 @@ class DownloadFileToCacheWorker(val context: Context, workerParameters: WorkerPa
private fun downloadFile(currentUser: UserEntity, url: String, fileName: String): Result {
val downloadCall = ncApi.downloadFile(
- ApiUtils.getCredentials(currentUser.username, currentUser.token),
- url)
+ ApiUtils.getCredentials(currentUser.username, currentUser.token),
+ url
+ )
return executeDownload(downloadCall.execute().body(), fileName)
}
@@ -152,6 +156,5 @@ class DownloadFileToCacheWorker(val context: Context, workerParameters: WorkerPa
const val KEY_FILE_SIZE = "KEY_FILE_SIZE"
const val PROGRESS = "PROGRESS"
const val SUCCESS = "SUCCESS"
-
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt
index 33a97451a..4eb4a9157 100644
--- a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt
+++ b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt
@@ -23,7 +23,11 @@ package com.nextcloud.talk.jobs
import android.content.Context
import android.net.Uri
import android.util.Log
-import androidx.work.*
+import androidx.work.Data
+import androidx.work.OneTimeWorkRequest
+import androidx.work.WorkManager
+import androidx.work.Worker
+import androidx.work.WorkerParameters
import autodagger.AutoInjector
import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication
@@ -46,13 +50,12 @@ import retrofit2.Response
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
-import java.util.*
+import java.util.ArrayList
import javax.inject.Inject
-
@AutoInjector(NextcloudTalkApplication::class)
class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerParameters) :
- Worker(context, workerParameters) {
+ Worker(context, workerParameters) {
@Inject
lateinit var ncApi: NcApi
@@ -107,31 +110,37 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
return requestBody
}
- private fun uploadFile(currentUser: UserEntity, ncTargetpath: String?, filename: String, roomToken: String?,
- requestBody: RequestBody?, sourcefileUri: Uri) {
+ private fun uploadFile(
+ currentUser: UserEntity,
+ ncTargetpath: String?,
+ filename: String,
+ roomToken: String?,
+ requestBody: RequestBody?,
+ sourcefileUri: Uri
+ ) {
ncApi.uploadFile(
- ApiUtils.getCredentials(currentUser.username, currentUser.token),
- ApiUtils.getUrlForFileUpload(currentUser.baseUrl, currentUser.userId, ncTargetpath, filename),
- requestBody
+ ApiUtils.getCredentials(currentUser.username, currentUser.token),
+ ApiUtils.getUrlForFileUpload(currentUser.baseUrl, currentUser.userId, ncTargetpath, filename),
+ requestBody
)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(object : Observer> {
- override fun onSubscribe(d: Disposable) {
- }
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(object : Observer> {
+ override fun onSubscribe(d: Disposable) {
+ }
- override fun onNext(t: Response) {
- }
+ override fun onNext(t: Response) {
+ }
- override fun onError(e: Throwable) {
- Log.e(TAG, "failed to upload file $filename")
- }
+ override fun onError(e: Throwable) {
+ Log.e(TAG, "failed to upload file $filename")
+ }
- override fun onComplete() {
- shareFile(roomToken, currentUser, ncTargetpath, filename)
- copyFileToCache(sourcefileUri, filename)
- }
- })
+ override fun onComplete() {
+ shareFile(roomToken, currentUser, ncTargetpath, filename)
+ copyFileToCache(sourcefileUri, filename)
+ }
+ })
}
private fun copyFileToCache(sourceFileUri: Uri, filename: String) {
@@ -151,13 +160,13 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
paths.add("$ncTargetpath/$filename")
val data = Data.Builder()
- .putLong(KEY_INTERNAL_USER_ID, currentUser.id)
- .putString(KEY_ROOM_TOKEN, roomToken)
- .putStringArray(KEY_FILE_PATHS, paths.toTypedArray())
- .build()
+ .putLong(KEY_INTERNAL_USER_ID, currentUser.id)
+ .putString(KEY_ROOM_TOKEN, roomToken)
+ .putStringArray(KEY_FILE_PATHS, paths.toTypedArray())
+ .build()
val shareWorker = OneTimeWorkRequest.Builder(ShareOperationWorker::class.java)
- .setInputData(data)
- .build()
+ .setInputData(data)
+ .build()
WorkManager.getInstance().enqueue(shareWorker)
}
@@ -167,4 +176,4 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
const val NC_TARGETPATH = "NC_TARGETPATH"
const val ROOM_TOKEN = "ROOM_TOKEN"
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.kt b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.kt
index 24ee28e11..9e060409f 100644
--- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.kt
+++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.kt
@@ -25,7 +25,7 @@ package com.nextcloud.talk.models.json.chat
class ChatUtils {
companion object {
fun getParsedMessage(message: String?, messageParameters: HashMap>?):
- String? {
+ String? {
var resultMessage = message
if (messageParameters != null && messageParameters.size > 0) {
for (key in messageParameters.keys) {
@@ -46,4 +46,3 @@ class ChatUtils {
}
}
}
-
diff --git a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt
index cc791068c..8bb830c8d 100644
--- a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt
+++ b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt
@@ -22,11 +22,28 @@ package com.nextcloud.talk.models.json.converters
import com.bluelinelabs.logansquare.typeconverters.StringBasedTypeConverter
import com.nextcloud.talk.models.json.chat.ChatMessage
-
-import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.*
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.CALL_ENDED
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.CALL_JOINED
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.CALL_LEFT
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.CALL_STARTED
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.CONVERSATION_CREATED
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.CONVERSATION_RENAMED
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.DUMMY
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.FILE_SHARED
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.GUESTS_ALLOWED
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.GUESTS_DISALLOWED
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.LOBBY_NONE
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.LOBBY_NON_MODERATORS
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.LOBBY_OPEN_TO_EVERYONE
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MODERATOR_DEMOTED
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.MODERATOR_PROMOTED
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.PARENT_MESSAGE_DELETED
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.PASSWORD_REMOVED
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.PASSWORD_SET
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.USER_ADDED
+import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.USER_REMOVED
/*
-
conversation_created - {actor} created the conversation
conversation_renamed - {actor} renamed the conversation from "foo" to "bar"
call_joined - {actor} joined the call
@@ -40,7 +57,6 @@ import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.*
user_removed - {actor} removed {user} from the conversation
moderator_promoted - {actor} promoted {user} to moderator
moderator_demoted - {actor} demoted {user} from moderator
-
*/
class EnumSystemMessageTypeConverter : StringBasedTypeConverter() {
override fun getFromString(string: String): ChatMessage.SystemMessageType {
diff --git a/app/src/main/java/com/nextcloud/talk/receivers/PackageReplacedReceiver.kt b/app/src/main/java/com/nextcloud/talk/receivers/PackageReplacedReceiver.kt
index e7bfd5c25..714eab4f5 100644
--- a/app/src/main/java/com/nextcloud/talk/receivers/PackageReplacedReceiver.kt
+++ b/app/src/main/java/com/nextcloud/talk/receivers/PackageReplacedReceiver.kt
@@ -20,12 +20,10 @@
package com.nextcloud.talk.receivers
-import android.app.NotificationChannelGroup
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
-import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
@@ -34,7 +32,6 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.utils.NotificationUtils
import com.nextcloud.talk.utils.database.user.UserUtils
import com.nextcloud.talk.utils.preferences.AppPreferences
-
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
@@ -50,16 +47,20 @@ class PackageReplacedReceiver : BroadcastReceiver() {
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
if (intent != null && intent.action != null &&
- intent.action == "android.intent.action.MY_PACKAGE_REPLACED") {
+ intent.action == "android.intent.action.MY_PACKAGE_REPLACED"
+ ) {
try {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
if (packageInfo.versionCode > 43 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- val notificationManager = context.getSystemService(Context
- .NOTIFICATION_SERVICE) as NotificationManager
+ val notificationManager = context.getSystemService(
+ Context
+ .NOTIFICATION_SERVICE
+ ) as NotificationManager
if (!appPreferences.isNotificationChannelUpgradedToV2) {
- for (notificationChannelGroup in notificationManager
- .notificationChannelGroups) {
+ for (
+ notificationChannelGroup in notificationManager.notificationChannelGroups
+ ) {
notificationManager.deleteNotificationChannelGroup(notificationChannelGroup.id)
}
@@ -80,7 +81,6 @@ class PackageReplacedReceiver : BroadcastReceiver() {
} catch (e: PackageManager.NameNotFoundException) {
Log.e(TAG, "Failed to fetch package info")
}
-
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt
index 569fffbc2..921f2f2ab 100644
--- a/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt
+++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt
@@ -32,7 +32,6 @@ import com.nextcloud.talk.R
import com.nextcloud.talk.components.filebrowser.controllers.BrowserController
import com.nextcloud.talk.controllers.ChatController
-
class AttachmentDialog(val activity: Activity, var chatController: ChatController) : BottomSheetDialog(activity) {
@BindView(R.id.txt_attach_file_from_local)
@@ -54,7 +53,7 @@ class AttachmentDialog(val activity: Activity, var chatController: ChatControlle
var serverName = chatController.conversationUser?.serverName
attachFromCloud?.text = chatController.resources?.let {
- if(serverName.isNullOrEmpty()){
+ if (serverName.isNullOrEmpty()) {
serverName = it.getString(R.string.nc_server_product_name)
}
String.format(it.getString(R.string.nc_upload_from_cloud), serverName)
diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ScopeDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/ScopeDialog.kt
index 207c3d478..d573aa255 100644
--- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ScopeDialog.kt
+++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ScopeDialog.kt
@@ -30,12 +30,13 @@ import com.nextcloud.talk.R
import com.nextcloud.talk.controllers.ProfileController
import com.nextcloud.talk.models.json.userprofile.Scope
-
-class ScopeDialog(con: Context,
- private val userInfoAdapter: ProfileController.UserInfoAdapter,
- private val field: ProfileController.Field,
- private val position: Int) :
- BottomSheetDialog(con) {
+class ScopeDialog(
+ con: Context,
+ private val userInfoAdapter: ProfileController.UserInfoAdapter,
+ private val field: ProfileController.Field,
+ private val position: Int
+) :
+ BottomSheetDialog(con) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val view = layoutInflater.inflate(R.layout.dialog_scope, null)
diff --git a/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.kt
index 83e12c9df..9fc105c81 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.kt
@@ -32,7 +32,8 @@ import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.models.ImportAccount
import com.nextcloud.talk.models.database.UserEntity
-import java.util.*
+import java.util.ArrayList
+import java.util.Arrays
object AccountUtils {
@@ -60,14 +61,15 @@ object AccountUtils {
break
}
} else {
- if (internalUserEntity.username == importAccount.username && (internalUserEntity
- .baseUrl == "http://" + importAccount.baseUrl ||
- internalUserEntity.baseUrl == "https://" + importAccount
- .baseUrl)) {
+ if (internalUserEntity.username == importAccount.username &&
+ (
+ internalUserEntity.baseUrl == "http://" + importAccount.baseUrl ||
+ internalUserEntity.baseUrl == "https://" + importAccount.baseUrl
+ )
+ ) {
accountFound = true
break
}
-
}
} else {
accountFound = true
@@ -88,8 +90,12 @@ object AccountUtils {
val packageManager = context.packageManager
var appName = ""
try {
- appName = packageManager.getApplicationLabel(packageManager.getApplicationInfo(packageName,
- PackageManager.GET_META_DATA)) as String
+ appName = packageManager.getApplicationLabel(
+ packageManager.getApplicationInfo(
+ packageName,
+ PackageManager.GET_META_DATA
+ )
+ ) as String
} catch (e: PackageManager.NameNotFoundException) {
Log.e(TAG, "Failed to get app name based on package")
}
@@ -103,7 +109,10 @@ object AccountUtils {
val packageInfo = pm.getPackageInfo(context.getString(R.string.nc_import_accounts_from), 0)
if (packageInfo.versionCode >= 30060151) {
val ownSignatures = pm.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES).signatures
- val filesAppSignatures = pm.getPackageInfo(context.getString(R.string.nc_import_accounts_from), PackageManager.GET_SIGNATURES).signatures
+ val filesAppSignatures = pm.getPackageInfo(
+ context.getString(R.string.nc_import_accounts_from),
+ PackageManager.GET_SIGNATURES
+ ).signatures
if (Arrays.equals(ownSignatures, filesAppSignatures)) {
val accMgr = AccountManager.get(context)
@@ -118,7 +127,7 @@ object AccountUtils {
}
}
} catch (appNotFoundException: PackageManager.NameNotFoundException) {
-
+ // ignore
}
return false
@@ -146,4 +155,3 @@ object AccountUtils {
return ImportAccount(username, password, urlString)
}
}
-
diff --git a/app/src/main/java/com/nextcloud/talk/utils/ConductorRemapping.kt b/app/src/main/java/com/nextcloud/talk/utils/ConductorRemapping.kt
index e2845a0b7..0f64722e2 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/ConductorRemapping.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/ConductorRemapping.kt
@@ -27,7 +27,13 @@ import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
import com.nextcloud.talk.controllers.ChatController
object ConductorRemapping {
- fun remapChatController(router: Router, internalUserId: Long, roomTokenOrId: String, bundle: Bundle, replaceTop: Boolean) {
+ fun remapChatController(
+ router: Router,
+ internalUserId: Long,
+ roomTokenOrId: String,
+ bundle: Bundle,
+ replaceTop: Boolean
+ ) {
val tag = "$internalUserId@$roomTokenOrId"
if (router.getControllerWithTag(tag) != null) {
val backstack = router.backstack
@@ -44,13 +50,17 @@ object ConductorRemapping {
router.setBackstack(backstack, HorizontalChangeHandler())
} else {
if (!replaceTop) {
- router.pushController(RouterTransaction.with(ChatController(bundle))
+ router.pushController(
+ RouterTransaction.with(ChatController(bundle))
.pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()).tag(tag))
+ .popChangeHandler(HorizontalChangeHandler()).tag(tag)
+ )
} else {
- router.replaceTopController(RouterTransaction.with(ChatController(bundle))
+ router.replaceTopController(
+ RouterTransaction.with(ChatController(bundle))
.pushChangeHandler(HorizontalChangeHandler())
- .popChangeHandler(HorizontalChangeHandler()).tag(tag))
+ .popChangeHandler(HorizontalChangeHandler()).tag(tag)
+ )
}
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/utils/DateUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/DateUtils.kt
index 20fceb297..afa477884 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/DateUtils.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/DateUtils.kt
@@ -21,8 +21,9 @@
package com.nextcloud.talk.utils
import java.text.DateFormat
-import java.util.*
-
+import java.util.Calendar
+import java.util.Date
+import java.util.Locale
object DateUtils {
fun getLocalDateTimeStringFromTimestamp(timestamp: Long): String {
@@ -30,14 +31,16 @@ object DateUtils {
val tz = cal.timeZone
/* date formatter in local timezone */
- val format = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.SHORT, Locale
- .getDefault())
+ val format = DateFormat.getDateTimeInstance(
+ DateFormat.DEFAULT, DateFormat.SHORT,
+ Locale.getDefault()
+ )
format.timeZone = tz
return format.format(Date(timestamp))
}
fun getLocalDateStringFromTimestampForLobby(timestamp: Long): String {
- return getLocalDateTimeStringFromTimestamp(timestamp * 1000);
+ return getLocalDateTimeStringFromTimestamp(timestamp * 1000)
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/utils/DrawableUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/DrawableUtils.kt
index 14afd4298..2deafa3e5 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/DrawableUtils.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/DrawableUtils.kt
@@ -21,12 +21,10 @@
package com.nextcloud.talk.utils
import com.nextcloud.talk.R
-
import java.util.HashMap
object DrawableUtils {
-
fun getDrawableResourceIdForMimeType(mimetype: String): Int {
var localMimetype = mimetype
val drawableMap = HashMap()
@@ -55,15 +53,20 @@ object DrawableUtils {
drawableMap["application/vnd.google-earth.kmz"] = R.drawable.ic_mimetype_location
drawableMap["application/vnd.ms-excel"] = R.drawable.ic_mimetype_x_office_spreadsheet
drawableMap["application/vnd.ms-excel.addin.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_spreadsheet
- drawableMap["application/vnd.ms-excel.sheet.binary.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_spreadsheet
+ drawableMap["application/vnd.ms-excel.sheet.binary.macroEnabled.12"] =
+ R.drawable.ic_mimetype_x_office_spreadsheet
drawableMap["application/vnd.ms-excel.sheet.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_spreadsheet
drawableMap["application/vnd.ms-excel.template.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_spreadsheet
drawableMap["application/vnd.ms-fontobject"] = R.drawable.ic_mimetype_image
drawableMap["application/vnd.ms-powerpoint"] = R.drawable.ic_mimetype_x_office_presentation
- drawableMap["application/vnd.ms-powerpoint.addin.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_presentation
- drawableMap["application/vnd.ms-powerpoint.presentation.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_presentation
- drawableMap["application/vnd.ms-powerpoint.slideshow.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_presentation
- drawableMap["application/vnd.ms-powerpoint.template.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_presentation
+ drawableMap["application/vnd.ms-powerpoint.addin.macroEnabled.12"] =
+ R.drawable.ic_mimetype_x_office_presentation
+ drawableMap["application/vnd.ms-powerpoint.presentation.macroEnabled.12"] =
+ R.drawable.ic_mimetype_x_office_presentation
+ drawableMap["application/vnd.ms-powerpoint.slideshow.macroEnabled.12"] =
+ R.drawable.ic_mimetype_x_office_presentation
+ drawableMap["application/vnd.ms-powerpoint.template.macroEnabled.12"] =
+ R.drawable.ic_mimetype_x_office_presentation
drawableMap["application/vnd.ms-visio.drawing.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.ms-visio.drawing"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.ms-visio.stencil.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_document
@@ -72,20 +75,29 @@ object DrawableUtils {
drawableMap["application/vnd.ms-visio.template"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.ms-word.template.macroEnabled.12"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.oasis.opendocument.presentation"] = R.drawable.ic_mimetype_x_office_presentation
- drawableMap["application/vnd.oasis.opendocument.presentation-template"] = R.drawable.ic_mimetype_x_office_presentation
+ drawableMap["application/vnd.oasis.opendocument.presentation-template"] =
+ R.drawable.ic_mimetype_x_office_presentation
drawableMap["application/vnd.oasis.opendocument.spreadsheet"] = R.drawable.ic_mimetype_x_office_spreadsheet
- drawableMap["application/vnd.oasis.opendocument.spreadsheet-template"] = R.drawable.ic_mimetype_x_office_spreadsheet
+ drawableMap["application/vnd.oasis.opendocument.spreadsheet-template"] =
+ R.drawable.ic_mimetype_x_office_spreadsheet
drawableMap["application/vnd.oasis.opendocument.text"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.oasis.opendocument.text-master"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.oasis.opendocument.text-template"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.oasis.opendocument.text-web"] = R.drawable.ic_mimetype_x_office_document
- drawableMap["application/vnd.openxmlformats-officedocument.presentationml.presentation"] = R.drawable.ic_mimetype_x_office_presentation
- drawableMap["application/vnd.openxmlformats-officedocument.presentationml.slideshow"] = R.drawable.ic_mimetype_x_office_presentation
- drawableMap["application/vnd.openxmlformats-officedocument.presentationml.template"] = R.drawable.ic_mimetype_x_office_presentation
- drawableMap["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"] = R.drawable.ic_mimetype_x_office_spreadsheet
- drawableMap["application/vnd.openxmlformats-officedocument.spreadsheetml.template"] = R.drawable.ic_mimetype_x_office_spreadsheet
- drawableMap["application/vnd.openxmlformats-officedocument.wordprocessingml.document"] = R.drawable.ic_mimetype_x_office_document
- drawableMap["application/vnd.openxmlformats-officedocument.wordprocessingml.template"] = R.drawable.ic_mimetype_x_office_document
+ drawableMap["application/vnd.openxmlformats-officedocument.presentationml.presentation"] =
+ R.drawable.ic_mimetype_x_office_presentation
+ drawableMap["application/vnd.openxmlformats-officedocument.presentationml.slideshow"] =
+ R.drawable.ic_mimetype_x_office_presentation
+ drawableMap["application/vnd.openxmlformats-officedocument.presentationml.template"] =
+ R.drawable.ic_mimetype_x_office_presentation
+ drawableMap["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"] =
+ R.drawable.ic_mimetype_x_office_spreadsheet
+ drawableMap["application/vnd.openxmlformats-officedocument.spreadsheetml.template"] =
+ R.drawable.ic_mimetype_x_office_spreadsheet
+ drawableMap["application/vnd.openxmlformats-officedocument.wordprocessingml.document"] =
+ R.drawable.ic_mimetype_x_office_document
+ drawableMap["application/vnd.openxmlformats-officedocument.wordprocessingml.template"] =
+ R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.visio"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/vnd.wordperfect"] = R.drawable.ic_mimetype_x_office_document
drawableMap["application/x-7z-compressed"] = R.drawable.ic_mimetype_package_x_generic
diff --git a/app/src/main/java/com/nextcloud/talk/utils/LoggingUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/LoggingUtils.kt
index 774a906b1..d3ca89a83 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/LoggingUtils.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/LoggingUtils.kt
@@ -29,7 +29,7 @@ import com.nextcloud.talk.BuildConfig
import java.io.FileNotFoundException
import java.io.IOException
import java.text.SimpleDateFormat
-import java.util.*
+import java.util.Date
object LoggingUtils {
fun writeLogEntryToFile(context: Context, logEntry: String) {
@@ -38,8 +38,10 @@ object LoggingUtils {
val logEntryWithDateTime = dateFormat.format(date) + ": " + logEntry + "\n"
try {
- val outputStream = context.openFileOutput("nc_log.txt",
- Context.MODE_PRIVATE or Context.MODE_APPEND)
+ val outputStream = context.openFileOutput(
+ "nc_log.txt",
+ Context.MODE_PRIVATE or Context.MODE_APPEND
+ )
outputStream.write(logEntryWithDateTime.toByteArray())
outputStream.flush()
outputStream.close()
@@ -48,13 +50,12 @@ object LoggingUtils {
} catch (e: IOException) {
e.printStackTrace()
}
-
}
fun sendMailWithAttachment(context: Context) {
val logFile = context.getFileStreamPath("nc_log.txt")
val emailIntent = Intent(Intent.ACTION_SEND)
- val mailto = "mario@nextcloud.com"
+ val mailto = "android@nextcloud.com"
emailIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf(mailto))
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Talk logs")
emailIntent.type = "text/plain"
diff --git a/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt
index f91f6636a..a4896c6e0 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt
@@ -33,7 +33,7 @@ import android.service.notification.StatusBarNotification
import com.nextcloud.talk.R
import com.nextcloud.talk.models.database.UserEntity
import com.nextcloud.talk.utils.bundle.BundleKeys
-import java.util.*
+import java.util.Objects
object NotificationUtils {
val NOTIFICATION_CHANNEL_CALLS = "NOTIFICATION_CHANNEL_CALLS"
@@ -47,25 +47,50 @@ object NotificationUtils {
return longArrayOf(0L, 400L, 800L, 600L, 800L, 800L, 800L, 1000L)
}
- fun getNotificationChannelId(channelName: String,
- channelDescription: String, enableLights: Boolean,
- importance: Int, sound: Uri, audioAttributes: AudioAttributes, vibrationPattern: LongArray?, bypassDnd: Boolean): String {
- return Objects.hash(channelName, channelDescription, enableLights, importance, sound, audioAttributes, vibrationPattern, bypassDnd).toString()
+ fun getNotificationChannelId(
+ channelName: String,
+ channelDescription: String,
+ enableLights: Boolean,
+ importance: Int,
+ sound: Uri,
+ audioAttributes: AudioAttributes,
+ vibrationPattern: LongArray?,
+ bypassDnd: Boolean
+ ): String {
+ return Objects.hash(
+ channelName,
+ channelDescription,
+ enableLights,
+ importance,
+ sound,
+ audioAttributes,
+ vibrationPattern,
+ bypassDnd
+ ).toString()
}
@TargetApi(Build.VERSION_CODES.O)
- fun createNotificationChannel(context: Context,
- channelId: String, channelName: String,
- channelDescription: String, enableLights: Boolean,
- importance: Int, sound: Uri, audioAttributes: AudioAttributes,
- vibrationPattern: LongArray?, bypassDnd: Boolean = false) {
+ fun createNotificationChannel(
+ context: Context,
+ channelId: String,
+ channelName: String,
+ channelDescription: String,
+ enableLights: Boolean,
+ importance: Int,
+ sound: Uri,
+ audioAttributes: AudioAttributes,
+ vibrationPattern: LongArray?,
+ bypassDnd: Boolean = false
+ ) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && notificationManager.getNotificationChannel(channelId) == null) {
- val channel = NotificationChannel(channelId, channelName,
- importance)
+ val channel = NotificationChannel(
+ channelId, channelName,
+ importance
+ )
channel.description = channelDescription
channel.enableLights(enableLights)
@@ -84,8 +109,11 @@ object NotificationUtils {
}
@TargetApi(Build.VERSION_CODES.O)
- fun createNotificationChannelGroup(context: Context,
- groupId: String, groupName: CharSequence) {
+ fun createNotificationChannelGroup(
+ context: Context,
+ groupId: String,
+ groupName: CharSequence
+ ) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@@ -113,12 +141,12 @@ object NotificationUtils {
}
}
}
-
}
fun cancelExistingNotificationWithId(context: Context?, conversationUser: UserEntity, notificationId: Long) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L &&
- context != null) {
+ context != null
+ ) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@@ -128,7 +156,10 @@ object NotificationUtils {
notification = statusBarNotification.notification
if (notification != null && !notification.extras.isEmpty) {
- if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) && notificationId == notification.extras.getLong(BundleKeys.KEY_NOTIFICATION_ID)) {
+ if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) && notificationId == notification.extras.getLong(
+ BundleKeys.KEY_NOTIFICATION_ID
+ )
+ ) {
notificationManager.cancel(statusBarNotification.id)
}
}
@@ -136,11 +167,14 @@ object NotificationUtils {
}
}
- fun findNotificationForRoom(context: Context?,
- conversationUser: UserEntity,
- roomTokenOrId: String): StatusBarNotification? {
+ fun findNotificationForRoom(
+ context: Context?,
+ conversationUser: UserEntity,
+ roomTokenOrId: String
+ ): StatusBarNotification? {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L &&
- context != null) {
+ context != null
+ ) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@@ -150,7 +184,10 @@ object NotificationUtils {
notification = statusBarNotification.notification
if (notification != null && !notification.extras.isEmpty) {
- if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) && roomTokenOrId == statusBarNotification.notification.extras.getString(BundleKeys.KEY_ROOM_TOKEN)) {
+ if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) && roomTokenOrId == statusBarNotification.notification.extras.getString(
+ BundleKeys.KEY_ROOM_TOKEN
+ )
+ ) {
return statusBarNotification
}
}
@@ -160,10 +197,14 @@ object NotificationUtils {
return null
}
- fun cancelExistingNotificationsForRoom(context: Context?, conversationUser: UserEntity,
- roomTokenOrId: String) {
+ fun cancelExistingNotificationsForRoom(
+ context: Context?,
+ conversationUser: UserEntity,
+ roomTokenOrId: String
+ ) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && conversationUser.id != -1L &&
- context != null) {
+ context != null
+ ) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@@ -173,7 +214,11 @@ object NotificationUtils {
notification = statusBarNotification.notification
if (notification != null && !notification.extras.isEmpty) {
- if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) && roomTokenOrId == statusBarNotification.notification.extras.getString(BundleKeys.KEY_ROOM_TOKEN)) {
+ if (conversationUser.id == notification.extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) &&
+ roomTokenOrId == statusBarNotification.notification.extras.getString(
+ BundleKeys.KEY_ROOM_TOKEN
+ )
+ ) {
notificationManager.cancel(statusBarNotification.id)
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/utils/UriUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/UriUtils.kt
index a4db61cf3..42dc9eb24 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/UriUtils.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/UriUtils.kt
@@ -25,7 +25,6 @@ import android.database.Cursor
import android.net.Uri
import android.provider.OpenableColumns
import android.util.Log
-import com.nextcloud.talk.jobs.UploadAndShareFilesWorker
object UriUtils {
@@ -51,5 +50,4 @@ object UriUtils {
}
return filename
}
-
}
diff --git a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt
index 9de10f25c..49c400ae7 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt
@@ -21,8 +21,8 @@
package com.nextcloud.talk.utils.bundle
object BundleKeys {
- val KEY_SELECTED_USERS = "KEY_SELECTED_USERS";
- val KEY_SELECTED_GROUPS = "KEY_SELECTED_GROUPS";
+ val KEY_SELECTED_USERS = "KEY_SELECTED_USERS"
+ val KEY_SELECTED_GROUPS = "KEY_SELECTED_GROUPS"
val KEY_USERNAME = "KEY_USERNAME"
val KEY_TOKEN = "KEY_TOKEN"
val KEY_BASE_URL = "KEY_BASE_URL"
diff --git a/app/src/main/java/com/nextcloud/talk/utils/ssl/SSLSocketFactoryCompat.kt b/app/src/main/java/com/nextcloud/talk/utils/ssl/SSLSocketFactoryCompat.kt
index 699e558e7..83ed2a0e2 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/ssl/SSLSocketFactoryCompat.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/ssl/SSLSocketFactoryCompat.kt
@@ -13,11 +13,17 @@ import java.io.IOException
import java.net.InetAddress
import java.net.Socket
import java.security.GeneralSecurityException
-import java.util.*
-import javax.net.ssl.*
+import java.util.LinkedList
+import javax.net.ssl.KeyManager
+import javax.net.ssl.SSLContext
+import javax.net.ssl.SSLSocket
+import javax.net.ssl.SSLSocketFactory
+import javax.net.ssl.X509TrustManager
-class SSLSocketFactoryCompat(keyManager: KeyManager?,
- trustManager: X509TrustManager) : SSLSocketFactory() {
+class SSLSocketFactoryCompat(
+ keyManager: KeyManager?,
+ trustManager: X509TrustManager
+) : SSLSocketFactory() {
private var delegate: SSLSocketFactory
@@ -50,24 +56,24 @@ class SSLSocketFactoryCompat(keyManager: KeyManager?,
/* set up reasonable cipher suites */
val knownCiphers = arrayOf(
- // TLS 1.2
- "TLS_RSA_WITH_AES_256_GCM_SHA384",
- "TLS_RSA_WITH_AES_128_GCM_SHA256",
- "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
- "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
- "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
- "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
- "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
- // maximum interoperability
- "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
- "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_RSA_WITH_AES_128_CBC_SHA",
- // additionally
- "TLS_RSA_WITH_AES_256_CBC_SHA",
- "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
- "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
- "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"
+ // TLS 1.2
+ "TLS_RSA_WITH_AES_256_GCM_SHA384",
+ "TLS_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
+ "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
+ "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
+ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+ // maximum interoperability
+ "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
+ "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_RSA_WITH_AES_128_CBC_SHA",
+ // additionally
+ "TLS_RSA_WITH_AES_256_CBC_SHA",
+ "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"
)
val availableCiphers = socket.supportedCipherSuites
@@ -89,31 +95,31 @@ class SSLSocketFactoryCompat(keyManager: KeyManager?,
} catch (e: IOException) {
// Exception is to be ignored
} finally {
- socket?.close() // doesn't implement Closeable on all supported Android versions
+ socket?.close() // doesn't implement Closeable on all supported Android versions
}
}
}
}
-
init {
try {
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(
- if (keyManager != null) arrayOf(keyManager) else null,
- arrayOf(trustManager),
- null)
+ if (keyManager != null) arrayOf(keyManager) else null,
+ arrayOf(trustManager),
+ null
+ )
delegate = sslContext.socketFactory
} catch (e: GeneralSecurityException) {
- throw IllegalStateException() // system has no TLS
+ throw IllegalStateException() // system has no TLS
}
}
override fun getDefaultCipherSuites(): Array? = cipherSuites
- ?: delegate.defaultCipherSuites
+ ?: delegate.defaultCipherSuites
override fun getSupportedCipherSuites(): Array? = cipherSuites
- ?: delegate.supportedCipherSuites
+ ?: delegate.supportedCipherSuites
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
val ssl = delegate.createSocket(s, host, port, autoClose)
@@ -150,10 +156,8 @@ class SSLSocketFactoryCompat(keyManager: KeyManager?,
return ssl
}
-
private fun upgradeTLS(ssl: SSLSocket) {
protocols?.let { ssl.enabledProtocols = it }
cipherSuites?.let { ssl.enabledCipherSuites = it }
}
-
}
diff --git a/app/src/qa/ic_launcher-web.png b/app/src/qa/ic_launcher-web.png
new file mode 100644
index 000000000..9bde487a3
Binary files /dev/null and b/app/src/qa/ic_launcher-web.png differ
diff --git a/app/src/qa/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.java b/app/src/qa/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.java
new file mode 100644
index 000000000..66496bd31
--- /dev/null
+++ b/app/src/qa/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.java
@@ -0,0 +1,36 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017-2018 Mario Danic
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.nextcloud.talk.utils;
+
+
+import com.nextcloud.talk.interfaces.ClosedInterface;
+
+public class ClosedInterfaceImpl implements ClosedInterface {
+ @Override
+ public void providerInstallerInstallIfNeededAsync() {
+ // does absolutely nothing :)
+ }
+
+ @Override
+ public boolean isGooglePlayServicesAvailable() {
+ return false;
+ }
+}
diff --git a/app/src/qa/res/drawable-v24/ic_launcher_background.xml b/app/src/qa/res/drawable-v24/ic_launcher_background.xml
new file mode 100644
index 000000000..3f3d4b826
--- /dev/null
+++ b/app/src/qa/res/drawable-v24/ic_launcher_background.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/qa/res/drawable/ic_launcher_background.xml b/app/src/qa/res/drawable/ic_launcher_background.xml
new file mode 100644
index 000000000..a931ebc5f
--- /dev/null
+++ b/app/src/qa/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/qa/res/drawable/ic_launcher_foreground.xml b/app/src/qa/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 000000000..2b7fc86b2
--- /dev/null
+++ b/app/src/qa/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/app/src/qa/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/qa/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..bbd3e0212
--- /dev/null
+++ b/app/src/qa/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/qa/res/mipmap-hdpi/ic_launcher.png b/app/src/qa/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..99024db8e
Binary files /dev/null and b/app/src/qa/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/qa/res/mipmap-mdpi/ic_launcher.png b/app/src/qa/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..2b6d24b1c
Binary files /dev/null and b/app/src/qa/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/qa/res/mipmap-xhdpi/ic_launcher.png b/app/src/qa/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..a0528c11b
Binary files /dev/null and b/app/src/qa/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/qa/res/mipmap-xxhdpi/ic_launcher.png b/app/src/qa/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..a2a2f2731
Binary files /dev/null and b/app/src/qa/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/qa/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/qa/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..677ffdd25
Binary files /dev/null and b/app/src/qa/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/qa/res/values/setup.xml b/app/src/qa/res/values/setup.xml
new file mode 100644
index 000000000..0650249ab
--- /dev/null
+++ b/app/src/qa/res/values/setup.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ Nextcloud Talk QA
+ Nextcloud
+
diff --git a/build.gradle b/build.gradle
index f5d7cb644..fbe358efa 100644
--- a/build.gradle
+++ b/build.gradle
@@ -34,6 +34,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}"
+ classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.13.1"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/detekt.yml b/detekt.yml
new file mode 100644
index 000000000..7b254e8ec
--- /dev/null
+++ b/detekt.yml
@@ -0,0 +1,472 @@
+build:
+ maxIssues: 346
+ weights:
+ # complexity: 2
+ # LongParameterList: 1
+ # style: 1
+ # comments: 1
+
+processors:
+ active: true
+ exclude:
+ # - 'FunctionCountProcessor'
+ # - 'PropertyCountProcessor'
+ # - 'ClassCountProcessor'
+ # - 'PackageCountProcessor'
+ # - 'KtFileCountProcessor'
+
+console-reports:
+ active: true
+ exclude:
+ # - 'ProjectStatisticsReport'
+ # - 'ComplexityReport'
+ # - 'NotificationReport'
+ # - 'FindingsReport'
+ # - 'BuildFailureReport'
+
+comments:
+ active: true
+ CommentOverPrivateFunction:
+ active: false
+ CommentOverPrivateProperty:
+ active: false
+ EndOfSentenceFormat:
+ active: false
+ endOfSentenceFormat: ([.?!][ \t\n\r\f<])|([.?!]$)
+ UndocumentedPublicClass:
+ active: false
+ searchInNestedClass: true
+ searchInInnerClass: true
+ searchInInnerObject: true
+ searchInInnerInterface: true
+ UndocumentedPublicFunction:
+ active: false
+
+complexity:
+ active: true
+ ComplexCondition:
+ active: true
+ threshold: 4
+ ComplexInterface:
+ active: false
+ threshold: 10
+ includeStaticDeclarations: false
+ ComplexMethod:
+ active: true
+ threshold: 10
+ ignoreSingleWhenExpression: false
+ ignoreSimpleWhenEntries: false
+ excludes: ['**/androidTest/**']
+ LabeledExpression:
+ active: false
+ ignoredLabels: ""
+ LargeClass:
+ active: true
+ threshold: 600
+ LongMethod:
+ active: true
+ threshold: 60
+ excludes: ['**/androidTest/**']
+ LongParameterList:
+ active: true
+ threshold: 6
+ ignoreDefaultParameters: false
+ MethodOverloading:
+ active: false
+ threshold: 6
+ NestedBlockDepth:
+ active: true
+ threshold: 4
+ StringLiteralDuplication:
+ active: false
+ threshold: 3
+ ignoreAnnotation: true
+ excludeStringsWithLessThan5Characters: true
+ ignoreStringsRegex: '$^'
+ TooManyFunctions:
+ active: true
+ thresholdInFiles: 15
+ thresholdInClasses: 15
+ thresholdInInterfaces: 15
+ thresholdInObjects: 15
+ thresholdInEnums: 11
+ ignoreDeprecated: true
+ ignorePrivate: false
+ ignoreOverridden: true
+
+empty-blocks:
+ active: true
+ EmptyCatchBlock:
+ active: true
+ allowedExceptionNameRegex: "^(_|(ignore|expected).*)"
+ EmptyClassBlock:
+ active: true
+ EmptyDefaultConstructor:
+ active: true
+ EmptyDoWhileBlock:
+ active: true
+ EmptyElseBlock:
+ active: true
+ EmptyFinallyBlock:
+ active: true
+ EmptyForBlock:
+ active: true
+ EmptyFunctionBlock:
+ active: true
+ ignoreOverridden: false
+ EmptyIfBlock:
+ active: true
+ EmptyInitBlock:
+ active: true
+ EmptyKtFile:
+ active: true
+ EmptySecondaryConstructor:
+ active: true
+ EmptyWhenBlock:
+ active: true
+ EmptyWhileBlock:
+ active: true
+
+exceptions:
+ active: true
+ ExceptionRaisedInUnexpectedLocation:
+ active: false
+ methodNames: 'toString,hashCode,equals,finalize'
+ InstanceOfCheckForException:
+ active: false
+ NotImplementedDeclaration:
+ active: false
+ PrintStackTrace:
+ active: false
+ RethrowCaughtException:
+ active: false
+ ReturnFromFinally:
+ active: false
+ SwallowedException:
+ active: false
+ ignoredExceptionTypes: 'InterruptedException,NumberFormatException,ParseException,MalformedURLException'
+ ThrowingExceptionFromFinally:
+ active: false
+ ThrowingExceptionInMain:
+ active: false
+ ThrowingExceptionsWithoutMessageOrCause:
+ active: false
+ exceptions: 'IllegalArgumentException,IllegalStateException,IOException'
+ ThrowingNewInstanceOfSameException:
+ active: false
+ TooGenericExceptionCaught:
+ active: true
+ exceptionNames:
+ - ArrayIndexOutOfBoundsException
+ - Error
+ - Exception
+ - IllegalMonitorStateException
+ - NullPointerException
+ - IndexOutOfBoundsException
+ - RuntimeException
+ - Throwable
+ allowedExceptionNameRegex: "^(_|(ignore|expected).*)"
+ TooGenericExceptionThrown:
+ active: true
+ exceptionNames:
+ - Error
+ - Exception
+ - Throwable
+ - RuntimeException
+
+formatting:
+ active: true
+ android: false
+ ChainWrapping:
+ active: true
+ CommentSpacing:
+ active: true
+ Filename:
+ active: true
+ FinalNewline:
+ active: true
+ ImportOrdering:
+ active: false
+ Indentation:
+ active: true
+ indentSize: 4
+ continuationIndentSize: 4
+ MaximumLineLength:
+ active: true
+ maxLineLength: 120
+ ModifierOrdering:
+ active: true
+ NoBlankLineBeforeRbrace:
+ active: true
+ NoConsecutiveBlankLines:
+ active: true
+ NoEmptyClassBody:
+ active: true
+ NoLineBreakAfterElse:
+ active: true
+ NoLineBreakBeforeAssignment:
+ active: true
+ NoMultipleSpaces:
+ active: true
+ NoSemicolons:
+ active: true
+ NoTrailingSpaces:
+ active: true
+ NoUnitReturn:
+ active: true
+ NoUnusedImports:
+ active: true
+ NoWildcardImports:
+ active: true
+ PackageName:
+ active: true
+ ParameterListWrapping:
+ active: true
+ indentSize: 4
+ SpacingAroundColon:
+ active: true
+ SpacingAroundComma:
+ active: true
+ SpacingAroundCurly:
+ active: true
+ SpacingAroundKeyword:
+ active: true
+ SpacingAroundOperators:
+ active: true
+ SpacingAroundParens:
+ active: true
+ SpacingAroundRangeOperator:
+ active: true
+ StringTemplate:
+ active: true
+
+naming:
+ active: true
+ ClassNaming:
+ active: true
+ classPattern: '[A-Z$][a-zA-Z0-9$]*'
+ ConstructorParameterNaming:
+ active: true
+ parameterPattern: '[a-z][A-Za-z0-9]*'
+ privateParameterPattern: '[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+ EnumNaming:
+ active: true
+ enumEntryPattern: '^[A-Z][_a-zA-Z0-9]*'
+ ForbiddenClassName:
+ active: false
+ forbiddenName: ''
+ FunctionMaxLength:
+ active: false
+ maximumFunctionNameLength: 30
+ FunctionMinLength:
+ active: false
+ minimumFunctionNameLength: 3
+ FunctionNaming:
+ active: true
+ functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+ excludes: "**/*Test.kt"
+ FunctionParameterNaming:
+ active: true
+ parameterPattern: '[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+ MatchingDeclarationName:
+ active: true
+ MemberNameEqualsClassName:
+ active: false
+ ignoreOverridden: true
+ ObjectPropertyNaming:
+ active: true
+ constantPattern: '[A-Za-z][_A-Za-z0-9]*'
+ propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
+ privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
+ PackageNaming:
+ active: true
+ packagePattern: '^[a-z]+(\.[a-z][A-Za-z0-9]*)*$'
+ TopLevelPropertyNaming:
+ active: true
+ constantPattern: '[A-Z][_A-Z0-9]*'
+ propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
+ privatePropertyPattern: '(_)?[A-Za-z][A-Za-z0-9]*'
+ VariableMaxLength:
+ active: false
+ maximumVariableNameLength: 64
+ VariableMinLength:
+ active: false
+ minimumVariableNameLength: 1
+ VariableNaming:
+ active: true
+ variablePattern: '[a-z][A-Za-z0-9]*'
+ privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+
+performance:
+ active: true
+ ArrayPrimitive:
+ active: false
+ ForEachOnRange:
+ active: true
+ SpreadOperator:
+ active: true
+ UnnecessaryTemporaryInstantiation:
+ active: true
+
+potential-bugs:
+ active: true
+ DuplicateCaseInWhenExpression:
+ active: true
+ EqualsAlwaysReturnsTrueOrFalse:
+ active: false
+ EqualsWithHashCodeExist:
+ active: true
+ ExplicitGarbageCollectionCall:
+ active: true
+ InvalidRange:
+ active: false
+ IteratorHasNextCallsNextMethod:
+ active: false
+ IteratorNotThrowingNoSuchElementException:
+ active: false
+ LateinitUsage:
+ active: false
+ excludeAnnotatedProperties: ""
+ ignoreOnClassesPattern: ""
+ UnconditionalJumpStatementInLoop:
+ active: false
+ UnreachableCode:
+ active: true
+ UnsafeCallOnNullableType:
+ active: false
+ UnsafeCast:
+ active: false
+ UselessPostfixExpression:
+ active: false
+ WrongEqualsTypeParameter:
+ active: false
+
+style:
+ active: true
+ CollapsibleIfStatements:
+ active: false
+ DataClassContainsFunctions:
+ active: false
+ conversionFunctionPrefix: 'to'
+ EqualsNullCall:
+ active: false
+ EqualsOnSignatureLine:
+ active: false
+ ExplicitItLambdaParameter:
+ active: false
+ ExpressionBodySyntax:
+ active: false
+ includeLineWrapping: false
+ ForbiddenComment:
+ active: true
+ values: 'TODO:,FIXME:,STOPSHIP:'
+ ForbiddenImport:
+ active: false
+ imports: ''
+ ForbiddenVoid:
+ active: false
+ FunctionOnlyReturningConstant:
+ active: false
+ ignoreOverridableFunction: true
+ excludedFunctions: 'describeContents'
+ LoopWithTooManyJumpStatements:
+ active: false
+ maxJumpCount: 1
+ MagicNumber:
+ active: true
+ ignoreNumbers: '-1,0,1,2'
+ ignoreHashCodeFunction: true
+ ignorePropertyDeclaration: false
+ ignoreConstantDeclaration: true
+ ignoreCompanionObjectPropertyDeclaration: true
+ ignoreAnnotation: false
+ ignoreNamedArgument: true
+ ignoreEnums: false
+ excludes: "**/*Test.kt"
+ MandatoryBracesIfStatements:
+ active: false
+ MaxLineLength:
+ active: true
+ maxLineLength: 120
+ excludePackageStatements: true
+ excludeImportStatements: true
+ excludeCommentStatements: false
+ MayBeConst:
+ active: false
+ ModifierOrder:
+ active: true
+ NestedClassesVisibility:
+ active: false
+ NewLineAtEndOfFile:
+ active: true
+ NoTabs:
+ active: false
+ OptionalAbstractKeyword:
+ active: true
+ OptionalUnit:
+ active: false
+ OptionalWhenBraces:
+ active: false
+ PreferToOverPairSyntax:
+ active: false
+ ProtectedMemberInFinalClass:
+ active: false
+ RedundantVisibilityModifierRule:
+ active: false
+ ReturnCount:
+ active: true
+ max: 2
+ excludedFunctions: "equals"
+ excludeLabeled: false
+ excludeReturnFromLambda: true
+ SafeCast:
+ active: true
+ SerialVersionUIDInSerializableClass:
+ active: false
+ SpacingBetweenPackageAndImports:
+ active: false
+ ThrowsCount:
+ active: true
+ max: 2
+ TrailingWhitespace:
+ active: false
+ UnderscoresInNumericLiterals:
+ active: false
+ acceptableDecimalLength: 5
+ UnnecessaryAbstractClass:
+ active: false
+ excludeAnnotatedClasses: "dagger.Module"
+ UnnecessaryApply:
+ active: false
+ UnnecessaryInheritance:
+ active: false
+ UnnecessaryLet:
+ active: false
+ UnnecessaryParentheses:
+ active: false
+ UntilInsteadOfRangeTo:
+ active: false
+ UnusedImports:
+ active: false
+ UnusedPrivateClass:
+ active: false
+ UnusedPrivateMember:
+ active: false
+ allowedNames: "(_|ignored|expected|serialVersionUID)"
+ UseDataClass:
+ active: false
+ excludeAnnotatedClasses: ""
+ UtilityClassWithPublicConstructor:
+ active: false
+ VarCouldBeVal:
+ active: false
+ WildcardImport:
+ active: true
+ excludeImports: 'java.util.*,kotlinx.android.synthetic.*'
diff --git a/drawable_resources/icon-background.svg b/drawable_resources/icon-background.svg
index b2618fccd..fc99ed29e 100644
--- a/drawable_resources/icon-background.svg
+++ b/drawable_resources/icon-background.svg
@@ -1,309 +1,4 @@
-
-
-
+
diff --git a/drawable_resources/icon-foreground_qa.svg b/drawable_resources/icon-foreground_qa.svg
new file mode 100644
index 000000000..42f765cfa
--- /dev/null
+++ b/drawable_resources/icon-foreground_qa.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/scripts/QA_keystore.jks b/scripts/QA_keystore.jks
new file mode 100644
index 000000000..2b8fb9bc2
Binary files /dev/null and b/scripts/QA_keystore.jks differ
diff --git a/scripts/uploadArtifact.sh b/scripts/uploadArtifact.sh
new file mode 100755
index 000000000..383abf6fe
--- /dev/null
+++ b/scripts/uploadArtifact.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+
+#1: LOG_USERNAME
+#2: LOG_PASSWORD
+#3: DRONE_BUILD_NUMBER
+#4: DRONE_PULL_REQUEST
+#5: GITHUB_TOKEN
+
+DAV_URL=https://nextcloud.kaminsky.me/remote.php/webdav/android-artifacts/
+PUBLIC_URL=https://www.kaminsky.me/nc-dev/android-artifacts
+USER=$1
+PASS=$2
+BUILD=$3
+PR=$4
+GITHUB_TOKEN=$5
+
+if ! test -e app/build/outputs/apk/qa/debug/app-qa-*.apk ; then
+ exit 1
+fi
+echo "Uploaded artifact to $DAV_URL/$BUILD-talk.apk"
+
+# delete all old comments, starting with "APK file:"
+oldComments=$(curl 2>/dev/null --header "authorization: Bearer $GITHUB_TOKEN" -X GET https://api.github.com/repos/nextcloud/talk-android/issues/$PR/comments | jq '.[] | (.id |tostring) + "|" + (.user.login | test("github-actions") | tostring) + "|" + (.body | test("APK file:.*") | tostring)' | grep "true|true" | tr -d "\"" | cut -f1 -d"|")
+
+echo $oldComments | while read comment ; do
+ curl 2>/dev/null --header "authorization: Bearer $GITHUB_TOKEN" -X DELETE https://api.github.com/repos/nextcloud/talk-android/issues/comments/$comment
+done
+
+apt-get -y install qrencode
+
+qrencode -o $PR.png "$PUBLIC_URL/$BUILD-talk.apk"
+
+curl -u $USER:$PASS -X PUT $DAV_URL/$BUILD-talk.apk --upload-file app/build/outputs/apk/qa/debug/app-qa-*.apk
+curl -u $USER:$PASS -X PUT $DAV_URL/$BUILD-talk.png --upload-file $PR.png
+curl --header "authorization: Bearer $GITHUB_TOKEN" -X POST https://api.github.com/repos/nextcloud/talk-android/issues/$PR/comments -d "{ \"body\" : \"APK file: $PUBLIC_URL/$BUILD-talk.apk
![qrcode]($PUBLIC_URL/$BUILD-talk.png)
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud Talk app. \" }"