Notifications & various other bug fixes

This commit is contained in:
Mario Danic 2019-12-18 14:00:57 +01:00
parent ee7cd4d60b
commit 5ddd3f8422
No known key found for this signature in database
GPG Key ID: CDE0BBD2738C4CC0
20 changed files with 364 additions and 445 deletions

View File

@ -333,4 +333,5 @@ dependencies {
findbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.7'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
implementation 'com.github.Kennyc1012:BottomSheet:2.4.1'
implementation 'com.google.firebase:firebase-messaging:20.1.0'
}

View File

@ -19,6 +19,6 @@
*/
dependencies {
implementation "androidx.work:work-gcm:2.3.0-alpha02"
implementation "com.google.firebase:firebase-messaging:20.0.0"
implementation "androidx.work:work-gcm:2.3.0-beta01"
implementation "com.google.firebase:firebase-messaging:20.1.0"
}

View File

@ -20,10 +20,8 @@
package com.nextcloud.talk.services.firebase
import android.annotation.SuppressLint
import autodagger.AutoInjector
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.jobs.NotificationWorker
import com.nextcloud.talk.jobs.PushRegistrationWorker
import com.nextcloud.talk.utils.bundle.BundleKeys
@ -31,33 +29,31 @@ import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.nextcloud.talk.utils.preferences.AppPreferences
import org.koin.core.KoinComponent
import org.koin.core.inject
class MagicFirebaseMessagingService : FirebaseMessagingService(), KoinComponent {
val appPreferences: AppPreferences by inject()
@Override
fun onNewToken(token: String?) {
override fun onNewToken(token: String) {
super.onNewToken(token)
appPreferences.setPushToken(token)
val pushRegistrationWork: OneTimeWorkRequest = Builder(PushRegistrationWorker::class.java).build()
appPreferences.pushToken = token
val pushRegistrationWork: OneTimeWorkRequest = OneTimeWorkRequest.Builder(PushRegistrationWorker::class.java).build()
WorkManager.getInstance().enqueue(pushRegistrationWork)
}
@SuppressLint("LongLogTag")
@Override
fun onMessageReceived(remoteMessage: RemoteMessage?) {
if (remoteMessage == null) {
return
}
if (remoteMessage.getData() != null) {
val messageData: Data = Builder()
.putString(BundleKeys.INSTANCE.getKEY_NOTIFICATION_SUBJECT(), remoteMessage.getData().get("subject"))
.putString(BundleKeys.INSTANCE.getKEY_NOTIFICATION_SIGNATURE(), remoteMessage.getData().get("signature"))
override fun onMessageReceived(remoteMessage: RemoteMessage) {
remoteMessage.data.let {
val messageData: Data = Data.Builder()
.putString(BundleKeys.KEY_NOTIFICATION_SUBJECT, it["subject"])
.putString(BundleKeys.KEY_NOTIFICATION_SIGNATURE, it["signature"])
.build()
val pushNotificationWork: OneTimeWorkRequest = Builder(NotificationWorker::class.java)
val pushNotificationWork: OneTimeWorkRequest = OneTimeWorkRequest.Builder(NotificationWorker::class.java)
.setInputData(messageData)
.build()
WorkManager.getInstance().enqueue(pushNotificationWork)
}
}
}

View File

@ -162,8 +162,6 @@ class NextcloudTalkApplication : Application(), LifecycleObserver {
12, TimeUnit.HOURS
)
.build()
val capabilitiesUpdateWork = OneTimeWorkRequest.Builder(CapabilitiesWorker::class.java)
.build()
val signalingSettingsWork = OneTimeWorkRequest.Builder(SignalingSettingsWorker::class.java)
.build()
@ -171,8 +169,6 @@ class NextcloudTalkApplication : Application(), LifecycleObserver {
.enqueue(pushRegistrationWork)
WorkManager.getInstance()
.enqueue(accountRemovalWork)
WorkManager.getInstance()
.enqueue(capabilitiesUpdateWork)
WorkManager.getInstance()
.enqueue(signalingSettingsWork)
WorkManager.getInstance()

View File

@ -1007,22 +1007,12 @@ class CallController(args: Bundle) : BaseController() {
override fun onNext(capabilitiesOverall: CapabilitiesOverall) {
isMultiSession = capabilitiesOverall.ocs.data
.capabilities != null && capabilitiesOverall.ocs.data
.capabilities.spreedCapability != null &&
capabilitiesOverall.ocs.data
.capabilities.spreedCapability
.features != null && capabilitiesOverall.ocs.data
.capabilities.spreedCapability
.features.contains("multi-room-users")
?.features?.contains("multi-room-users") == true
needsPing = !(capabilitiesOverall.ocs.data
.capabilities != null && capabilitiesOverall.ocs.data
.capabilities.spreedCapability != null &&
capabilitiesOverall.ocs.data
.capabilities.spreedCapability
.features != null && capabilitiesOverall.ocs.data
needsPing = capabilitiesOverall.ocs.data
.capabilities.spreedCapability
.features.contains("no-ping"))
?.features?.contains("no-ping") == false
if (!hasExternalSignalingServer) {
joinRoomAndCall()

View File

@ -59,6 +59,8 @@ import com.nextcloud.talk.models.json.conversations.Conversation
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.newarch.local.models.UserNgEntity
import com.nextcloud.talk.newarch.local.models.getCredentials
import com.nextcloud.talk.newarch.utils.getCredentials
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DoNotDisturbUtils
@ -108,7 +110,7 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr
@BindView(R.id.incomingTextRelativeLayout)
var incomingTextRelativeLayout: RelativeLayout? = null
private val roomId: String
private val userBeingCalled: UserEntity?
private val userBeingCalled: UserNgEntity?
private val credentials: String?
private var currentConversation: Conversation? = null
private var mediaPlayer: MediaPlayer? = null
@ -282,7 +284,7 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr
var importantConversation = false
val arbitraryStorageEntity: ArbitraryStorageEntity? = arbitraryStorageUtils.getStorageSetting(
userBeingCalled!!.id,
userBeingCalled!!.id!!,
"important_conversation",
currentConversation!!.token
)

View File

@ -20,30 +20,151 @@
package com.nextcloud.talk.jobs
import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import android.text.TextUtils
import android.util.Base64
import android.util.Log
import androidx.work.CoroutineWorker
import androidx.work.ListenableWorker.Result
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.nextcloud.talk.R
import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.events.EventStatus
import com.nextcloud.talk.models.json.push.PushConfigurationState
import com.nextcloud.talk.models.json.push.PushRegistrationOverall
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
import com.nextcloud.talk.newarch.local.models.getCredentials
import com.nextcloud.talk.newarch.utils.hashWithAlgorithm
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.PushUtils
import com.nextcloud.talk.utils.preferences.AppPreferences
import io.reactivex.Observer
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.koin.core.KoinComponent
import org.koin.core.inject
import java.security.PublicKey
import java.util.*
class PushRegistrationWorker(
context: Context,
workerParams: WorkerParameters
) : Worker(context, workerParams), KoinComponent {
) : CoroutineWorker(context, workerParams), KoinComponent {
val usersRepository: UsersRepository by inject()
val eventBus: EventBus by inject()
val appPreferences: AppPreferences by inject()
val application: Application by inject()
val ncApi: NcApi by inject()
override fun doWork(): Result {
override suspend fun doWork(): Result {
val pushUtils = PushUtils(usersRepository)
pushUtils.generateRsa2048KeyPair()
pushUtils.pushRegistrationToServer()
pushRegistrationToServer()
return Result.success()
}
companion object {
const val TAG = "PushRegistrationWorker"
}
}
private fun pushRegistrationToServer() {
val token: String = appPreferences.pushToken
if (!TextUtils.isEmpty(token)) {
var credentials: String
val pushUtils = PushUtils(usersRepository)
val pushTokenHash = token.hashWithAlgorithm("SHA-512")
val devicePublicKey = pushUtils.readKeyFromFile(true) as PublicKey?
if (devicePublicKey != null) {
val publicKeyBytes: ByteArray? =
Base64.encode(devicePublicKey.encoded, Base64.NO_WRAP)
var publicKey = String(publicKeyBytes!!)
publicKey = publicKey.replace("(.{64})".toRegex(), "$1\n")
publicKey = "-----BEGIN PUBLIC KEY-----\n$publicKey\n-----END PUBLIC KEY-----\n"
val users = usersRepository.getUsers()
if (users.count() > 0) {
var accountPushData: PushConfigurationState?
for (userEntityObject in users) {
accountPushData = userEntityObject.pushConfiguration
if (accountPushData == null || accountPushData.pushToken != token) {
val queryMap: MutableMap<String, String> =
HashMap()
queryMap["format"] = "json"
queryMap["pushTokenHash"] = pushTokenHash
queryMap["devicePublicKey"] = publicKey
queryMap["proxyServer"] = application.getString(R.string.nc_push_server_url)
credentials = userEntityObject.getCredentials()
ncApi.registerDeviceForNotificationsWithNextcloud(
credentials,
ApiUtils.getUrlNextcloudPush(userEntityObject.baseUrl),
queryMap
)
.blockingSubscribe(object : Observer<PushRegistrationOverall> {
override fun onSubscribe(d: Disposable) {}
@SuppressLint("CheckResult")
override fun onNext(pushRegistrationOverall: PushRegistrationOverall) {
val proxyMap: MutableMap<String, String> =
HashMap()
proxyMap["pushToken"] = token
proxyMap["deviceIdentifier"] =
pushRegistrationOverall.ocs.data.deviceIdentifier
proxyMap["deviceIdentifierSignature"] = pushRegistrationOverall.ocs
.data.signature
proxyMap["userPublicKey"] = pushRegistrationOverall.ocs
.data.publicKey
ncApi.registerDeviceForNotificationsWithProxy(
ApiUtils.getUrlPushProxy(), proxyMap
).subscribe({
val pushConfigurationState = PushConfigurationState()
pushConfigurationState.pushToken = token
pushConfigurationState.deviceIdentifier = proxyMap["deviceIdentifier"]
pushConfigurationState.deviceIdentifierSignature = proxyMap["deviceIdentifierSignature"]
pushConfigurationState.userPublicKey = proxyMap["userPublicKey"]
pushConfigurationState.usesRegularPass = false
GlobalScope.launch {
val user = usersRepository.getUserWithId(userEntityObject.id!!)
user.pushConfiguration = pushConfigurationState
usersRepository.updateUser(user)
}
eventBus.post(
EventStatus(
userEntityObject.id!!,
EventStatus.EventType.PUSH_REGISTRATION,
true
)
)
}, {
eventBus.post(
EventStatus(
userEntityObject.id!!,
EventStatus.EventType.PUSH_REGISTRATION,
false))
})
}
override fun onError(e: Throwable) {
eventBus.post(
EventStatus(
userEntityObject.id!!,
EventStatus.EventType.PUSH_REGISTRATION,
false
)
)
}
override fun onComplete() {}
})
}
}
}
}
}
}
companion object {
const val TAG = "PushRegistrationWorker"
}
}

View File

@ -36,8 +36,6 @@ data class ExternalSignalingServer(
var externalSignalingServer: String? = null,
@JsonField(name = ["externalSignalingTicket"])
var externalSignalingTicket: String? = null
) : Parcelable {
override fun equals(other: Any?): Boolean {
if (this === other) return true

View File

@ -1,64 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.json.capabilities;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.parceler.Parcel;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import lombok.Data;
@Parcel
@Data
@JsonObject
public class Capabilities {
@JsonField(name = "spreed")
public SpreedCapability spreedCapability;
@JsonField(name = "notifications")
public NotificationsCapability notificationsCapability;
@JsonField(name = "theming")
public ThemingCapability themingCapability;
/*@JsonField(name = "external")
public HashMap<String, List<String>> externalCapability;*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Capabilities)) return false;
Capabilities that = (Capabilities) o;
return Objects.equals(spreedCapability, that.spreedCapability) &&
Objects.equals(notificationsCapability, that.notificationsCapability) &&
Objects.equals(themingCapability, that.themingCapability);
}
@Override
public int hashCode() {
return Objects.hash(spreedCapability, notificationsCapability, themingCapability);
}
}

View File

@ -0,0 +1,53 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.json.capabilities
import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject
import kotlinx.android.parcel.Parcelize
import lombok.Data
import org.parceler.Parcel
import java.util.*
@Data
@Parcel
@JsonObject
@Parcelize
data class Capabilities(
@JsonField(name = ["spreed"])
var spreedCapability: SpreedCapability? = null,
@JsonField(name = ["notifications"])
var notificationsCapability: NotificationsCapability? = null,
@JsonField(name = ["theming"])
var themingCapability: ThemingCapability? = null
): Parcelable {
override fun equals(o: Any?): Boolean {
if (this === o) return true
if (o !is Capabilities) return false
return (spreedCapability == o.spreedCapability &&
notificationsCapability == o.notificationsCapability &&
themingCapability == o.themingCapability)
}
override fun hashCode(): Int {
return Objects.hash(spreedCapability, notificationsCapability, themingCapability)
}
}

View File

@ -17,36 +17,32 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.json.capabilities
package com.nextcloud.talk.models.json.capabilities;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.parceler.Parcel;
import java.util.List;
import java.util.Objects;
import lombok.Data;
import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject
import kotlinx.android.parcel.Parcelize
import lombok.Data
import org.parceler.Parcel
import java.util.*
@Parcel
@Data
@JsonObject
public class NotificationsCapability {
@JsonField(name = "ocs-endpoints")
public List<String> features;
@Parcelize
data class NotificationsCapability(
@JsonField(name = ["ocs-endpoints"])
var features: List<String>? = null
): Parcelable {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof NotificationsCapability)) return false;
NotificationsCapability that = (NotificationsCapability) o;
return Objects.equals(features, that.features);
override fun equals(o: Any?): Boolean {
if (this === o) return true
if (o !is NotificationsCapability) return false
return features == o.features
}
@Override
public int hashCode() {
return Objects.hash(features);
override fun hashCode(): Int {
return Objects.hash(features)
}
}
}

View File

@ -1,57 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.json.capabilities;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.parceler.Parcel;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import lombok.Data;
@Parcel
@Data
@JsonObject
public class SpreedCapability {
@JsonField(name = "features")
public List<String> features;
@JsonField(name = "config")
public HashMap<String, HashMap<String, String>> config;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SpreedCapability)) return false;
SpreedCapability that = (SpreedCapability) o;
return Objects.equals(features, that.features) &&
Objects.equals(config, that.config);
}
@Override
public int hashCode() {
return Objects.hash(features, config);
}
}

View File

@ -0,0 +1,52 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.json.capabilities
import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject
import kotlinx.android.parcel.Parcelize
import lombok.Data
import org.parceler.Parcel
import java.util.*
@Parcel
@Data
@JsonObject
@Parcelize
data class SpreedCapability(
@JsonField(name = ["features"])
var features: List<String>? = null,
@JsonField(name = ["config"])
var config: HashMap<String, HashMap<String, String>>? = null
): Parcelable {
override fun equals(o: Any?): Boolean {
if (this === o) return true
if (o !is SpreedCapability) return false
val that = o
return features == that.features &&
config == that.config
}
override fun hashCode(): Int {
return Objects.hash(features, config)
}
}

View File

@ -1,87 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.json.capabilities;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import org.parceler.Parcel;
import java.util.Objects;
import lombok.Data;
@Parcel
@Data
@JsonObject
class ThemingCapability {
@JsonField(name = "name")
String name;
@JsonField(name = "url")
String url;
@JsonField(name = "slogan")
String slogan;
@JsonField(name = "color")
String color;
@JsonField(name = "color-text")
String colorText;
@JsonField(name = "color-element")
String colorElement;
@JsonField(name = "logo")
String logo;
@JsonField(name = "background")
String background;
@JsonField(name = "background-plain")
boolean backgroundPlain;
@JsonField(name = "background-default")
boolean backgroundDefault;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ThemingCapability)) return false;
ThemingCapability that = (ThemingCapability) o;
return backgroundPlain == that.backgroundPlain &&
backgroundDefault == that.backgroundDefault &&
Objects.equals(name, that.name) &&
Objects.equals(url, that.url) &&
Objects.equals(slogan, that.slogan) &&
Objects.equals(color, that.color) &&
Objects.equals(colorText, that.colorText) &&
Objects.equals(colorElement, that.colorElement) &&
Objects.equals(logo, that.logo) &&
Objects.equals(background, that.background);
}
@Override
public int hashCode() {
return Objects.hash(name, url, slogan, color, colorText, colorElement, logo, background, backgroundPlain, backgroundDefault);
}
}

View File

@ -0,0 +1,75 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.json.capabilities
import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject
import kotlinx.android.parcel.Parcelize
import lombok.Data
import org.parceler.Parcel
import java.util.*
@Parcel
@Data
@JsonObject
@Parcelize
data class ThemingCapability(
@JsonField(name = ["name"])
var name: String? = null,
@JsonField(name = ["url"])
var url: String? = null,
@JsonField(name = ["slogan"])
var slogan: String? = null,
@JsonField(name = ["color"])
var color: String? = null,
@JsonField(name = ["color-text"])
var colorText: String? = null,
@JsonField(name = ["color-element"])
var colorElement: String? = null,
@JsonField(name = ["logo"])
var logo: String? = null,
@JsonField(name = ["background"])
var background: String? = null,
@JsonField(name = ["background-plain"])
var backgroundPlain: Boolean = false,
@JsonField(name = ["background-default"])
var backgroundDefault: Boolean = false
): Parcelable {
override fun equals(o: Any?): Boolean {
if (this === o) return true
if (o !is ThemingCapability) return false
val that = o
return backgroundPlain == that.backgroundPlain && backgroundDefault == that.backgroundDefault &&
name == that.name &&
url == that.url &&
slogan == that.slogan &&
color == that.color &&
colorText == that.colorText &&
colorElement == that.colorElement &&
logo == that.logo &&
background == that.background
}
override fun hashCode(): Int {
return Objects.hash(name, url, slogan, color, colorText, colorElement, logo, background, backgroundPlain, backgroundDefault)
}
}

View File

@ -31,7 +31,7 @@ import org.parceler.Parcel
@Data
@JsonObject
@Parcelize
class PushConfigurationState(
data class PushConfigurationState(
@JsonField(name = ["pushToken"])
var pushToken: String? = null,
@JsonField(name = ["deviceIdentifier"])

View File

@ -216,7 +216,6 @@ class ConversationsListView : BaseView(), OnQueryTextListener,
})
conversationsLiveData.observe(this@ConversationsListView, Observer {
Log.d("MARIO", "TRIGGER")
if (it.isEmpty()) {
if (viewState.value != LOADED_EMPTY) {
viewState.value = LOADED_EMPTY

View File

@ -20,7 +20,9 @@
package com.nextcloud.talk.newarch.local.models
import android.os.Parcel
import android.os.Parcelable
import android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@ -29,8 +31,10 @@ import com.nextcloud.talk.models.json.capabilities.Capabilities
import com.nextcloud.talk.models.json.push.PushConfigurationState
import com.nextcloud.talk.newarch.local.models.other.UserStatus
import com.nextcloud.talk.utils.ApiUtils
import kotlinx.android.parcel.Parceler
import kotlinx.android.parcel.Parcelize
import kotlinx.android.parcel.RawValue
import org.parceler.Parcels
@Parcelize
@Entity(tableName = "users")
@ -44,7 +48,7 @@ data class UserNgEntity(
@ColumnInfo(
name = "push_configuration"
) var pushConfiguration: PushConfigurationState? = null,
@ColumnInfo(name = "capabilities") var capabilities: @RawValue Capabilities? = null,
@ColumnInfo(name = "capabilities") var capabilities: Capabilities? = null,
@ColumnInfo(name = "client_auth_cert") var clientCertificate: String? = null,
@ColumnInfo(
name = "external_signaling"

View File

@ -22,5 +22,11 @@ package com.nextcloud.talk.newarch.utils
import com.nextcloud.talk.models.database.UserEntity
import com.nextcloud.talk.utils.ApiUtils
import java.security.MessageDigest
fun UserEntity.getCredentials() = ApiUtils.getCredentials(username, token)
fun String.hashWithAlgorithm(algorithm: String): String {
val digest = MessageDigest.getInstance(algorithm)
val bytes = digest.digest(this.toByteArray(Charsets.UTF_8))
return bytes.fold("", { str, it -> str + "%02x".format(it) })
}

View File

@ -36,7 +36,6 @@ import com.nextcloud.talk.models.json.push.PushConfigurationState
import com.nextcloud.talk.models.json.push.PushRegistrationOverall
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
import com.nextcloud.talk.newarch.local.models.UserNgEntity
import com.nextcloud.talk.utils.database.user.UserUtils
import com.nextcloud.talk.utils.preferences.AppPreferences
import io.reactivex.Observer
import io.reactivex.disposables.Disposable
@ -53,14 +52,12 @@ import java.util.*
import kotlin.experimental.and
class PushUtils(val usersRepository: UsersRepository) : KoinComponent {
val userUtils: UserUtils by inject()
val appPreferences: AppPreferences by inject()
val eventBus: EventBus by inject()
val ncApi: NcApi by inject()
private val keysFile: File
private val publicKeyFile: File
private val privateKeyFile: File
private val proxyServer: String
fun verifySignature(
signatureBytes: ByteArray?,
subjectBytes: ByteArray?
@ -74,7 +71,7 @@ class PushUtils(val usersRepository: UsersRepository) : KoinComponent {
val userEntities: List<UserNgEntity> = usersRepository.getUsers()
try {
signature = Signature.getInstance("SHA512withRSA")
if (userEntities.size > 0) {
if (userEntities.isNotEmpty()) {
for (userEntity in userEntities) {
pushConfigurationState = userEntity.pushConfiguration
if (pushConfigurationState?.userPublicKey != null) {
@ -127,29 +124,6 @@ class PushUtils(val usersRepository: UsersRepository) : KoinComponent {
return -1
}
private fun generateSHA512Hash(pushToken: String): String {
var messageDigest: MessageDigest? = null
try {
messageDigest = MessageDigest.getInstance("SHA-512")
messageDigest.update(pushToken.toByteArray())
return bytesToHex(messageDigest.digest())
} catch (e: NoSuchAlgorithmException) {
Log.d(TAG, "SHA-512 algorithm not supported")
}
return ""
}
private fun bytesToHex(bytes: ByteArray): String {
val result = StringBuilder()
for (individualByte in bytes) {
result.append(
((individualByte and 0xff.toByte()) + 0x100).toString(16)
.substring(1)
)
}
return result.toString()
}
fun generateRsa2048KeyPair(): Int {
if (!publicKeyFile.exists() && !privateKeyFile.exists()) {
if (!keysFile.exists()) {
@ -186,139 +160,6 @@ class PushUtils(val usersRepository: UsersRepository) : KoinComponent {
return -2
}
fun pushRegistrationToServer() {
val token: String = appPreferences.pushToken
if (!TextUtils.isEmpty(token)) {
var credentials: String
val pushTokenHash = generateSHA512Hash(token).toLowerCase()
val devicePublicKey =
readKeyFromFile(true) as PublicKey?
if (devicePublicKey != null) {
val publicKeyBytes: ByteArray? =
Base64.encode(devicePublicKey.encoded, Base64.NO_WRAP)
var publicKey = String(publicKeyBytes!!)
publicKey = publicKey.replace("(.{64})".toRegex(), "$1\n")
publicKey = "-----BEGIN PUBLIC KEY-----\n$publicKey\n-----END PUBLIC KEY-----\n"
if (userUtils.anyUserExists()) {
var accountPushData: PushConfigurationState? = null
for (userEntityObject in usersRepository.getUsers()) {
val userEntity = userEntityObject
accountPushData = userEntity.pushConfiguration
if (accountPushData == null || accountPushData.pushToken != token) {
val queryMap: MutableMap<String, String> =
HashMap()
queryMap["format"] = "json"
queryMap["pushTokenHash"] = pushTokenHash
queryMap["devicePublicKey"] = publicKey
queryMap["proxyServer"] = proxyServer
credentials = ApiUtils.getCredentials(
userEntity.username, userEntity.token
)
val finalCredentials = credentials
ncApi.registerDeviceForNotificationsWithNextcloud(
credentials,
ApiUtils.getUrlNextcloudPush(userEntity.baseUrl),
queryMap
)
.subscribe(object : Observer<PushRegistrationOverall> {
override fun onSubscribe(d: Disposable) {}
override fun onNext(pushRegistrationOverall: PushRegistrationOverall) {
val proxyMap: MutableMap<String, String> =
HashMap()
proxyMap["pushToken"] = token
proxyMap["deviceIdentifier"] =
pushRegistrationOverall.ocs.data.deviceIdentifier
proxyMap["deviceIdentifierSignature"] = pushRegistrationOverall.ocs
.data.signature
proxyMap["userPublicKey"] = pushRegistrationOverall.ocs
.data.publicKey
ncApi.registerDeviceForNotificationsWithProxy(
ApiUtils.getUrlPushProxy(), proxyMap
)
.subscribeOn(Schedulers.io())
.subscribe(object : Observer<Void> {
override fun onSubscribe(d: Disposable) {}
override fun onNext(aVoid: Void) {
val pushConfigurationState =
PushConfigurationState()
pushConfigurationState.pushToken = token
pushConfigurationState.deviceIdentifier = pushRegistrationOverall
.ocs.data.deviceIdentifier
pushConfigurationState.deviceIdentifierSignature =
pushRegistrationOverall.ocs.data.signature
pushConfigurationState.userPublicKey = pushRegistrationOverall.ocs
.data.publicKey
pushConfigurationState.usesRegularPass = false
try {
userUtils.createOrUpdateUser(
null,
null, null,
userEntity.displayName,
LoganSquare.serialize(
pushConfigurationState
), null,
null, userEntity.id, null, null, null
)
.subscribe(object : Observer<UserEntity> {
override fun onSubscribe(d: Disposable) {}
override fun onNext(userEntity: UserEntity) {
eventBus.post(
EventStatus(
userEntity.id,
PUSH_REGISTRATION,
true
)
)
}
override fun onError(e: Throwable) {
eventBus.post(
EventStatus(
userEntity.id!!,
PUSH_REGISTRATION, false
)
)
}
override fun onComplete() {}
})
} catch (e: IOException) {
Log.e(TAG, "IOException while updating user")
}
}
override fun onError(e: Throwable) {
eventBus.post(
EventStatus(
userEntity.id!!,
PUSH_REGISTRATION,
false
)
)
}
override fun onComplete() {}
})
}
override fun onError(e: Throwable) {
eventBus.post(
EventStatus(
userEntity.id!!,
PUSH_REGISTRATION,
false
)
)
}
override fun onComplete() {}
})
}
}
}
}
}
}
private fun readKeyFromString(
readPublicKey: Boolean,
@ -417,8 +258,5 @@ class PushUtils(val usersRepository: UsersRepository) : KoinComponent {
Context.MODE_PRIVATE
), "push_key.priv"
)
proxyServer =
sharedApplication!!.resources
.getString(string.nc_push_server_url)
}
}