fix review comments

Co-authored-by: Álvaro Brey <alvaro.brey@nextcloud.com>
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
This commit is contained in:
Andy Scherzinger 2022-06-29 18:29:13 +02:00
parent 12f793567e
commit 205292f9b0
No known key found for this signature in database
GPG Key ID: 6CADC7E3523C308B
19 changed files with 291 additions and 399 deletions

View File

@ -37,6 +37,5 @@ class MainActivityTest {
assertNotNull("Error creating user", user) assertNotNull("Error creating user", user)
sut.runOnUiThread { sut.resetConversationsList() } sut.runOnUiThread { sut.resetConversationsList() }
println("User: " + user!!.id + " / " + user.userId + " / " + user.baseUrl)
} }
} }

View File

@ -190,14 +190,10 @@ class SettingsController : NewBaseController(R.layout.controller_settings) {
} }
private fun setupPhoneBookIntegration() { private fun setupPhoneBookIntegration() {
if (CapabilitiesUtilNew.isPhoneBookIntegrationAvailable(currentUser)) { if (CapabilitiesUtilNew.isPhoneBookIntegrationAvailable(currentUser!!)) {
activity!!.runOnUiThread { binding.settingsPhoneBookIntegration.visibility = View.VISIBLE
binding.settingsPhoneBookIntegration.visibility = View.VISIBLE
}
} else { } else {
activity!!.runOnUiThread { binding.settingsPhoneBookIntegration.visibility = View.GONE
binding.settingsPhoneBookIntegration.visibility = View.GONE
}
} }
} }
@ -648,7 +644,7 @@ class SettingsController : NewBaseController(R.layout.controller_settings) {
private fun setupServerAgeWarning() { private fun setupServerAgeWarning() {
when { when {
CapabilitiesUtilNew.isServerEOL(currentUser) -> { CapabilitiesUtilNew.isServerEOL(currentUser!!) -> {
binding.serverAgeWarningText.setTextColor(ContextCompat.getColor((context)!!, R.color.nc_darkRed)) binding.serverAgeWarningText.setTextColor(ContextCompat.getColor((context)!!, R.color.nc_darkRed))
binding.serverAgeWarningText.setText(R.string.nc_settings_server_eol) binding.serverAgeWarningText.setText(R.string.nc_settings_server_eol)
binding.serverAgeWarningIcon.setColorFilter( binding.serverAgeWarningIcon.setColorFilter(
@ -656,7 +652,7 @@ class SettingsController : NewBaseController(R.layout.controller_settings) {
PorterDuff.Mode.SRC_IN PorterDuff.Mode.SRC_IN
) )
} }
CapabilitiesUtilNew.isServerAlmostEOL(currentUser) -> { CapabilitiesUtilNew.isServerAlmostEOL(currentUser!!) -> {
binding.serverAgeWarningText.setTextColor( binding.serverAgeWarningText.setTextColor(
ContextCompat.getColor((context)!!, R.color.nc_darkYellow) ContextCompat.getColor((context)!!, R.color.nc_darkYellow)
) )
@ -688,7 +684,7 @@ class SettingsController : NewBaseController(R.layout.controller_settings) {
if (CapabilitiesUtil.isReadStatusAvailable(userUtils.currentUser)) { if (CapabilitiesUtil.isReadStatusAvailable(userUtils.currentUser)) {
(binding.settingsReadPrivacy.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked = (binding.settingsReadPrivacy.findViewById<View>(R.id.mp_checkable) as Checkable).isChecked =
!CapabilitiesUtilNew.isReadStatusPrivate(currentUser) !CapabilitiesUtilNew.isReadStatusPrivate(currentUser!!)
} else { } else {
binding.settingsReadPrivacy.visibility = View.GONE binding.settingsReadPrivacy.visibility = View.GONE
} }

View File

@ -25,14 +25,12 @@ import com.nextcloud.talk.data.storage.model.ArbitraryStorageEntity
object ArbitraryStorageMapper { object ArbitraryStorageMapper {
fun toModel(entity: ArbitraryStorageEntity?): ArbitraryStorage? { fun toModel(entity: ArbitraryStorageEntity?): ArbitraryStorage? {
return if (entity == null) { return entity?.let {
null
} else {
ArbitraryStorage( ArbitraryStorage(
entity.accountIdentifier, it.accountIdentifier,
entity.key, it.key,
entity.storageObject, it.storageObject,
entity.value it.value
) )
} }
} }

View File

@ -42,7 +42,7 @@ abstract class ArbitraryStoragesDao {
): Maybe<ArbitraryStorageEntity> ): Maybe<ArbitraryStorageEntity>
@Query("DELETE FROM ArbitraryStorage WHERE accountIdentifier = :accountIdentifier") @Query("DELETE FROM ArbitraryStorage WHERE accountIdentifier = :accountIdentifier")
abstract suspend fun deleteArbitraryStorage(accountIdentifier: Long) abstract fun deleteArbitraryStorage(accountIdentifier: Long)
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun saveArbitraryStorage(arbitraryStorage: ArbitraryStorageEntity): Long abstract fun saveArbitraryStorage(arbitraryStorage: ArbitraryStorageEntity): Long

View File

@ -25,6 +25,6 @@ import io.reactivex.Maybe
interface ArbitraryStoragesRepository { interface ArbitraryStoragesRepository {
fun getStorageSetting(accountIdentifier: Long, key: String, objectString: String): Maybe<ArbitraryStorage> fun getStorageSetting(accountIdentifier: Long, key: String, objectString: String): Maybe<ArbitraryStorage>
suspend fun deleteArbitraryStorage(accountIdentifier: Long) fun deleteArbitraryStorage(accountIdentifier: Long)
fun saveArbitraryStorage(arbitraryStorage: ArbitraryStorage): Long fun saveArbitraryStorage(arbitraryStorage: ArbitraryStorage): Long
} }

View File

@ -35,7 +35,7 @@ class ArbitraryStoragesRepositoryImpl(private val arbitraryStoragesDao: Arbitrar
.map { ArbitraryStorageMapper.toModel(it) } .map { ArbitraryStorageMapper.toModel(it) }
} }
override suspend fun deleteArbitraryStorage(accountIdentifier: Long) { override fun deleteArbitraryStorage(accountIdentifier: Long) {
arbitraryStoragesDao.deleteArbitraryStorage(accountIdentifier) arbitraryStoragesDao.deleteArbitraryStorage(accountIdentifier)
} }

View File

@ -25,21 +25,13 @@ import com.nextcloud.talk.data.user.model.UserEntity
object UserMapper { object UserMapper {
fun toModel(entities: List<UserEntity?>?): List<User> { fun toModel(entities: List<UserEntity?>?): List<User> {
return if (entities == null) { return entities?.map { user: UserEntity? ->
ArrayList() toModel(user)!!
} else { } ?: emptyList()
val users = ArrayList<User>()
for (entity in entities) {
users.add(toModel(entity)!!)
}
users
}
} }
fun toModel(entity: UserEntity?): User? { fun toModel(entity: UserEntity?): User? {
return if (entity == null) { return entity?.let {
null
} else {
User( User(
entity.id, entity.id,
entity.userId, entity.userId,
@ -58,22 +50,20 @@ object UserMapper {
} }
fun toEntity(model: User): UserEntity { fun toEntity(model: User): UserEntity {
var UserEntity: UserEntity? = null val userEntity = when (val id = model.id) {
model.id?.let { null -> UserEntity(userId = model.userId, username = model.username, baseUrl = model.baseUrl)
UserEntity = UserEntity(it, model.userId, model.username, model.baseUrl) else -> UserEntity(id, model.userId, model.username, model.baseUrl)
} ?: run {
UserEntity = UserEntity(userId = model.userId, username = model.username, baseUrl = model.baseUrl)
} }
userEntity.apply {
UserEntity!!.token = model.token token = model.token
UserEntity!!.displayName = model.displayName displayName = model.displayName
UserEntity!!.pushConfigurationState = model.pushConfigurationState pushConfigurationState = model.pushConfigurationState
UserEntity!!.capabilities = model.capabilities capabilities = model.capabilities
UserEntity!!.clientCertificate = model.clientCertificate clientCertificate = model.clientCertificate
UserEntity!!.externalSignalingServer = model.externalSignalingServer externalSignalingServer = model.externalSignalingServer
UserEntity!!.current = model.current current = model.current
UserEntity!!.scheduledForDeletion = model.scheduledForDeletion scheduledForDeletion = model.scheduledForDeletion
}
return UserEntity!! return userEntity
} }
} }

View File

@ -47,9 +47,6 @@ abstract class UsersDao {
@Query("SELECT * FROM User where current = 1") @Query("SELECT * FROM User where current = 1")
abstract fun getActiveUserSynchronously(): UserEntity? abstract fun getActiveUserSynchronously(): UserEntity?
@Query("SELECT * FROM User WHERE current = 1")
abstract fun getActiveUserLiveData(): Single<UserEntity?>
@Query("DELETE FROM User WHERE id = :id") @Query("DELETE FROM User WHERE id = :id")
abstract fun deleteUserWithId(id: Long) abstract fun deleteUserWithId(id: Long)
@ -122,13 +119,9 @@ abstract class UsersDao {
@Transaction @Transaction
open suspend fun markUserForDeletion(id: Long): Boolean { open suspend fun markUserForDeletion(id: Long): Boolean {
val users = getUsers().blockingGet() getUserWithId(id).blockingGet()?.let { user ->
user.current = FALSE
for (user in users) { updateUser(user)
if (user.id == id) {
user.current = FALSE
updateUser(user)
}
} }
return setAnyUserAsActive() return setAnyUserAsActive()
@ -137,14 +130,12 @@ abstract class UsersDao {
@Transaction @Transaction
open suspend fun setAnyUserAsActive(): Boolean { open suspend fun setAnyUserAsActive(): Boolean {
val users = getUsers().blockingGet() val users = getUsers().blockingGet()
var result = FALSE
for (user in users) { val result = users.firstOrNull()?.let { user ->
user.current = TRUE user.current = TRUE
updateUser(user) updateUser(user)
result = TRUE TRUE
break } ?: FALSE
}
return result return result
} }

View File

@ -29,7 +29,6 @@ import androidx.room.PrimaryKey
import com.nextcloud.talk.models.ExternalSignalingServer import com.nextcloud.talk.models.ExternalSignalingServer
import com.nextcloud.talk.models.json.capabilities.Capabilities import com.nextcloud.talk.models.json.capabilities.Capabilities
import com.nextcloud.talk.models.json.push.PushConfigurationState import com.nextcloud.talk.models.json.push.PushConfigurationState
import com.nextcloud.talk.utils.ApiUtils
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
import java.lang.Boolean.FALSE import java.lang.Boolean.FALSE
@ -48,19 +47,4 @@ data class UserEntity(
@ColumnInfo(name = "externalSignalingServer") var externalSignalingServer: ExternalSignalingServer? = null, @ColumnInfo(name = "externalSignalingServer") var externalSignalingServer: ExternalSignalingServer? = null,
@ColumnInfo(name = "current") var current: Boolean = FALSE, @ColumnInfo(name = "current") var current: Boolean = FALSE,
@ColumnInfo(name = "scheduledForDeletion") var scheduledForDeletion: Boolean = FALSE, @ColumnInfo(name = "scheduledForDeletion") var scheduledForDeletion: Boolean = FALSE,
) : Parcelable { ) : Parcelable
fun hasSpreedFeatureCapability(capabilityName: String): Boolean {
return capabilities?.spreedCapability?.features?.contains(capabilityName) ?: false
}
fun canUserCreateGroupConversations(): Boolean {
val canCreateValue = capabilities?.spreedCapability?.config?.get("conversations")?.get("can-create")
canCreateValue?.let {
return it.toBoolean()
}
return true
}
fun getCredentials(): String = ApiUtils.getCredentials(username, token)
}

View File

@ -25,11 +25,9 @@ import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject import com.bluelinelabs.logansquare.annotation.JsonObject
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
import kotlinx.serialization.Serializable
@Parcelize @Parcelize
@JsonObject @JsonObject
@Serializable
data class ExternalSignalingServer( data class ExternalSignalingServer(
@JsonField(name = ["externalSignalingServer"]) @JsonField(name = ["externalSignalingServer"])
var externalSignalingServer: String? = null, var externalSignalingServer: String? = null,

View File

@ -25,11 +25,9 @@ import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject import com.bluelinelabs.logansquare.annotation.JsonObject
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
import kotlinx.serialization.Serializable
@Parcelize @Parcelize
@JsonObject @JsonObject
@Serializable
data class Capabilities( data class Capabilities(
@JsonField(name = ["spreed"]) @JsonField(name = ["spreed"])
var spreedCapability: SpreedCapability?, var spreedCapability: SpreedCapability?,

View File

@ -25,11 +25,9 @@ import android.os.Parcelable
import com.bluelinelabs.logansquare.annotation.JsonField import com.bluelinelabs.logansquare.annotation.JsonField
import com.bluelinelabs.logansquare.annotation.JsonObject import com.bluelinelabs.logansquare.annotation.JsonObject
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
import kotlinx.serialization.Serializable
@Parcelize @Parcelize
@JsonObject @JsonObject
@Serializable
data class PushConfigurationState( data class PushConfigurationState(
@JsonField(name = ["pushToken"]) @JsonField(name = ["pushToken"])
var pushToken: String?, var pushToken: String?,

View File

@ -157,34 +157,7 @@ public class ApiUtils {
@Deprecated @Deprecated
public static int getConversationApiVersion(UserEntity user, int[] versions) throws NoSupportedApiException { public static int getConversationApiVersion(UserEntity user, int[] versions) throws NoSupportedApiException {
boolean hasApiV4 = false; return getConversationApiVersion(LegacyUserEntityMapper.toModel(user), versions);
for (int version : versions) {
hasApiV4 |= version == APIv4;
}
if (!hasApiV4) {
Exception e = new Exception("Api call did not try conversation-v4 api");
Log.d(TAG, e.getMessage(), e);
}
for (int version : versions) {
if (CapabilitiesUtil.hasSpreedFeatureCapability(user, "conversation-v" + version)) {
return version;
}
// Fallback for old API versions
if ((version == APIv1 || version == APIv2)) {
if (CapabilitiesUtil.hasSpreedFeatureCapability(user, "conversation-v2")) {
return version;
}
if (version == APIv1 &&
CapabilitiesUtil.hasSpreedFeatureCapability(user, "mention-flag") &&
!CapabilitiesUtil.hasSpreedFeatureCapability(user, "conversation-v4")) {
return version;
}
}
}
throw new NoSupportedApiException();
} }
public static int getSignalingApiVersion(UserEntity user, int[] versions) throws NoSupportedApiException { public static int getSignalingApiVersion(UserEntity user, int[] versions) throws NoSupportedApiException {

View File

@ -93,6 +93,7 @@ import java.text.DateFormat;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -572,31 +573,7 @@ public class DisplayUtils {
@Deprecated @Deprecated
public static void loadAvatarImage(UserEntity user, SimpleDraweeView avatarImageView, boolean deleteCache) { public static void loadAvatarImage(UserEntity user, SimpleDraweeView avatarImageView, boolean deleteCache) {
String avatarId; loadAvatarImage(Objects.requireNonNull(LegacyUserEntityMapper.toModel(user)), avatarImageView, deleteCache);
if (!TextUtils.isEmpty(user.getUserId())) {
avatarId = user.getUserId();
} else {
avatarId = user.getUsername();
}
String avatarString = ApiUtils.getUrlForAvatar(user.getBaseUrl(), avatarId, true);
// clear cache
if (deleteCache) {
Uri avatarUri = Uri.parse(avatarString);
ImagePipeline imagePipeline = Fresco.getImagePipeline();
imagePipeline.evictFromMemoryCache(avatarUri);
imagePipeline.evictFromDiskCache(avatarUri);
imagePipeline.evictFromCache(avatarUri);
}
DraweeController draweeController = Fresco.newDraweeControllerBuilder()
.setOldController(avatarImageView.getController())
.setAutoPlayAnimations(true)
.setImageRequest(DisplayUtils.getImageRequestForUrl(avatarString, null))
.build();
avatarImageView.setController(draweeController);
} }
public static void loadAvatarImage(User user, SimpleDraweeView avatarImageView, boolean deleteCache) { public static void loadAvatarImage(User user, SimpleDraweeView avatarImageView, boolean deleteCache) {

View File

@ -0,0 +1,56 @@
/*
* Nextcloud Talk application
*
* @author Andy Scherzinger
* Copyright (C) 2022 Andy Scherzinger <infoi@andy-scherzinger.de>
*
* model 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.
*
* model 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 model program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.utils
import com.bluelinelabs.logansquare.LoganSquare
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.ExternalSignalingServer
import com.nextcloud.talk.models.database.UserEntity
import com.nextcloud.talk.models.json.capabilities.Capabilities
import com.nextcloud.talk.models.json.push.PushConfigurationState
object LegacyUserEntityMapper {
fun toModel(entities: List<UserEntity?>?): List<User> {
return entities?.map { user: UserEntity? ->
toModel(user)!!
} ?: emptyList()
}
@JvmStatic
fun toModel(entity: UserEntity?): User? {
return entity?.let {
User(
entity.id,
entity.userId,
entity.username,
entity.baseUrl,
entity.token,
entity.displayName,
LoganSquare.parse(entity.pushConfigurationState, PushConfigurationState::class.java),
LoganSquare.parse(entity.capabilities, Capabilities::class.java),
entity.clientCertificate,
LoganSquare.parse(entity.externalSignalingServer, ExternalSignalingServer::class.java),
entity.current,
entity.scheduledForDeletion
)
}
}
}

View File

@ -20,8 +20,10 @@
package com.nextcloud.talk.utils.database.arbitrarystorage; package com.nextcloud.talk.utils.database.arbitrarystorage;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.nextcloud.talk.models.database.ArbitraryStorage; import com.nextcloud.talk.models.database.ArbitraryStorage;
import com.nextcloud.talk.models.database.ArbitraryStorageEntity; import com.nextcloud.talk.models.database.ArbitraryStorageEntity;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
import io.requery.Persistable; import io.requery.Persistable;
@ -29,6 +31,9 @@ import io.requery.query.Result;
import io.requery.reactivex.ReactiveEntityStore; import io.requery.reactivex.ReactiveEntityStore;
import io.requery.reactivex.ReactiveScalar; import io.requery.reactivex.ReactiveScalar;
/**
* @deprecated use {@link com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager} instead.
*/
@Deprecated @Deprecated
public class ArbitraryStorageUtils { public class ArbitraryStorageUtils {
private ReactiveEntityStore<Persistable> dataStore; private ReactiveEntityStore<Persistable> dataStore;
@ -46,16 +51,16 @@ public class ArbitraryStorageUtils {
arbitraryStorageEntity.setObject(object); arbitraryStorageEntity.setObject(object);
dataStore.upsert(arbitraryStorageEntity) dataStore.upsert(arbitraryStorageEntity)
.toObservable() .toObservable()
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.subscribe(); .subscribe();
} }
public ArbitraryStorageEntity getStorageSetting(long accountIdentifier, String key, @Nullable String object) { public ArbitraryStorageEntity getStorageSetting(long accountIdentifier, String key, @Nullable String object) {
Result findStorageQueryResult = dataStore.select(ArbitraryStorage.class) Result findStorageQueryResult = dataStore.select(ArbitraryStorage.class)
.where(ArbitraryStorageEntity.ACCOUNT_IDENTIFIER.eq(accountIdentifier) .where(ArbitraryStorageEntity.ACCOUNT_IDENTIFIER.eq(accountIdentifier)
.and(ArbitraryStorageEntity.KEY.eq(key)).and(ArbitraryStorageEntity.OBJECT.eq(object))) .and(ArbitraryStorageEntity.KEY.eq(key)).and(ArbitraryStorageEntity.OBJECT.eq(object)))
.limit(1).get(); .limit(1).get();
return (ArbitraryStorageEntity) findStorageQueryResult.firstOrNull(); return (ArbitraryStorageEntity) findStorageQueryResult.firstOrNull();
} }
@ -64,6 +69,6 @@ public class ArbitraryStorageUtils {
ReactiveScalar<Integer> deleteResult = dataStore.delete(ArbitraryStorage.class).where(ArbitraryStorageEntity.ACCOUNT_IDENTIFIER.eq(accountIdentifier)).get(); ReactiveScalar<Integer> deleteResult = dataStore.delete(ArbitraryStorage.class).where(ArbitraryStorageEntity.ACCOUNT_IDENTIFIER.eq(accountIdentifier)).get();
return deleteResult.single().toObservable() return deleteResult.single().toObservable()
.subscribeOn(Schedulers.io()); .subscribeOn(Schedulers.io());
} }
} }

View File

@ -1,229 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Andy Scherzinger
* @author Mario Danic
* Copyright (C) 2021 Andy Scherzinger (info@andy-scherzinger.de)
* 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.utils.database.user;
import com.nextcloud.talk.data.user.model.User;
import com.nextcloud.talk.models.json.capabilities.Capabilities;
import java.util.HashMap;
import java.util.Map;
import androidx.annotation.Nullable;
public abstract class CapabilitiesUtilNew {
public static boolean hasNotificationsCapability(@Nullable User user, String capabilityName) {
if (user != null && user.getCapabilities() != null) {
Capabilities capabilities = user.getCapabilities();
if (capabilities.getNotificationsCapability() != null &&
capabilities.getNotificationsCapability().getFeatures() != null) {
return capabilities.getSpreedCapability().getFeatures().contains(capabilityName);
}
}
return false;
}
public static boolean hasExternalCapability(@Nullable User user, String capabilityName) {
if (user != null && user.getCapabilities() != null) {
Capabilities capabilities = user.getCapabilities();
if (capabilities.getExternalCapability() != null &&
capabilities.getExternalCapability().containsKey("v1")) {
return capabilities.getExternalCapability().get("v1").contains(capabilityName);
}
}
return false;
}
public static boolean isServerEOL(@Nullable User user) {
// Capability is available since Talk 4 => Nextcloud 14 => Autmn 2018
return !hasSpreedFeatureCapability(user, "no-ping");
}
public static boolean isServerAlmostEOL(@Nullable User user) {
// Capability is available since Talk 8 => Nextcloud 18 => January 2020
return !hasSpreedFeatureCapability(user, "chat-replies");
}
public static boolean canSetChatReadMarker(@Nullable User user) {
return hasSpreedFeatureCapability(user, "chat-read-marker");
}
public static boolean hasSpreedFeatureCapability(@Nullable User user, String capabilityName) {
if (user != null && user.getCapabilities() != null) {
Capabilities capabilities = user.getCapabilities();
if (capabilities != null && capabilities.getSpreedCapability() != null &&
capabilities.getSpreedCapability().getFeatures() != null) {
return capabilities.getSpreedCapability().getFeatures().contains(capabilityName);
}
}
return false;
}
public static Integer getMessageMaxLength(@Nullable User user) {
if (user != null && user.getCapabilities() != null) {
Capabilities capabilities = user.getCapabilities();
if (capabilities != null &&
capabilities.getSpreedCapability() != null &&
capabilities.getSpreedCapability().getConfig() != null &&
capabilities.getSpreedCapability().getConfig().containsKey("chat")) {
HashMap<String, String> chatConfigHashMap = capabilities
.getSpreedCapability()
.getConfig()
.get("chat");
if (chatConfigHashMap != null && chatConfigHashMap.containsKey("max-length")) {
int chatSize = Integer.parseInt(chatConfigHashMap.get("max-length"));
if (chatSize > 0) {
return chatSize;
} else {
return 1000;
}
}
}
}
return 1000;
}
public static boolean isPhoneBookIntegrationAvailable(@Nullable User user) {
if (user != null && user.getCapabilities() != null) {
Capabilities capabilities = user.getCapabilities();
return capabilities != null &&
capabilities.getSpreedCapability() != null &&
capabilities.getSpreedCapability().getFeatures() != null &&
capabilities.getSpreedCapability().getFeatures().contains("phonebook-search");
}
return false;
}
public static boolean isReadStatusAvailable(@Nullable User user) {
if (user != null && user.getCapabilities() != null) {
Capabilities capabilities = user.getCapabilities();
if (capabilities != null &&
capabilities.getSpreedCapability() != null &&
capabilities.getSpreedCapability().getConfig() != null &&
capabilities.getSpreedCapability().getConfig().containsKey("chat")) {
Map<String, String> map = capabilities.getSpreedCapability().getConfig().get("chat");
return map != null && map.containsKey("read-privacy");
}
}
return false;
}
public static boolean isReadStatusPrivate(@Nullable User user) {
if (user != null && user.getCapabilities() != null) {
Capabilities capabilities = user.getCapabilities();
if (capabilities != null &&
capabilities.getSpreedCapability() != null &&
capabilities.getSpreedCapability().getConfig() != null &&
capabilities.getSpreedCapability().getConfig().containsKey("chat")) {
HashMap<String, String> map = capabilities.getSpreedCapability().getConfig().get("chat");
if (map != null && map.containsKey("read-privacy")) {
return Integer.parseInt(map.get("read-privacy")) == 1;
}
}
}
return false;
}
public static boolean isUserStatusAvailable(@Nullable User user) {
if (user != null && user.getCapabilities() != null) {
Capabilities capabilities = user.getCapabilities();
if (capabilities.getUserStatusCapability() != null &&
capabilities.getUserStatusCapability().getEnabled() &&
capabilities.getUserStatusCapability().getSupportsEmoji()) {
return true;
}
}
return false;
}
public static String getAttachmentFolder(@Nullable User user) {
if (user != null && user.getCapabilities() != null) {
Capabilities capabilities = user.getCapabilities();
if (capabilities != null &&
capabilities.getSpreedCapability() != null &&
capabilities.getSpreedCapability().getConfig() != null &&
capabilities.getSpreedCapability().getConfig().containsKey("attachments")) {
HashMap<String, String> map = capabilities.getSpreedCapability().getConfig().get("attachments");
if (map != null && map.containsKey("folder")) {
return map.get("folder");
}
}
}
return "/Talk";
}
public static String getServerName(@Nullable User user) {
if (user != null && user.getCapabilities() != null) {
Capabilities capabilities = user.getCapabilities();
if (capabilities != null && capabilities.getThemingCapability() != null) {
return capabilities.getThemingCapability().getName();
}
}
return "";
}
// TODO later avatar can also be checked via user fields, for now it is in Talk capability
public static boolean isAvatarEndpointAvailable(@Nullable User user) {
if (user != null && user.getCapabilities() != null) {
Capabilities capabilities = user.getCapabilities();
return (capabilities != null &&
capabilities.getSpreedCapability() != null &&
capabilities.getSpreedCapability().getFeatures() != null &&
capabilities.getSpreedCapability().getFeatures().contains("temp-user-avatar-api"));
}
return false;
}
public static boolean canEditScopes(@Nullable User user) {
if (user != null && user.getCapabilities() != null) {
Capabilities capabilities = user.getCapabilities();
return (capabilities != null &&
capabilities.getProvisioningCapability() != null &&
capabilities.getProvisioningCapability().getAccountPropertyScopesVersion() != null &&
capabilities.getProvisioningCapability().getAccountPropertyScopesVersion() > 1);
}
return false;
}
public static boolean isAbleToCall(@Nullable User user) {
if (user != null && user.getCapabilities() != null) {
Capabilities capabilities = user.getCapabilities();
if (capabilities != null &&
capabilities.getSpreedCapability() != null &&
capabilities.getSpreedCapability().getConfig() != null &&
capabilities.getSpreedCapability().getConfig().containsKey("call") &&
capabilities.getSpreedCapability().getConfig().get("call") != null &&
capabilities.getSpreedCapability().getConfig().get("call").containsKey("enabled")) {
return Boolean.parseBoolean(
capabilities.getSpreedCapability().getConfig().get("call").get("enabled"));
} else {
// older nextcloud versions without the capability can't disable the calls
return true;
}
}
return false;
}
public static boolean isUnifiedSearchAvailable(@Nullable final User user) {
return hasSpreedFeatureCapability(user, "unified-search");
}
}

View File

@ -0,0 +1,154 @@
/*
* Nextcloud Talk application
*
* @author Andy Scherzinger
* @author Mario Danic
* Copyright (C) 2021 Andy Scherzinger (info@andy-scherzinger.de)
* 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.utils.database.user
import com.nextcloud.talk.data.user.model.User
@Suppress("TooManyFunctions")
object CapabilitiesUtilNew {
fun hasNotificationsCapability(user: User, capabilityName: String): Boolean {
return user.capabilities?.spreedCapability?.features?.contains(capabilityName) == true
}
fun hasExternalCapability(user: User, capabilityName: String?): Boolean {
if (user.capabilities?.externalCapability?.containsKey("v1") == true) {
return user.capabilities!!.externalCapability!!["v1"]?.contains(capabilityName!!) == true
}
return false
}
fun isServerEOL(user: User): Boolean {
// Capability is available since Talk 4 => Nextcloud 14 => Autmn 2018
return !hasSpreedFeatureCapability(user, "no-ping")
}
fun isServerAlmostEOL(user: User): Boolean {
// Capability is available since Talk 8 => Nextcloud 18 => January 2020
return !hasSpreedFeatureCapability(user, "chat-replies")
}
fun canSetChatReadMarker(user: User): Boolean {
return hasSpreedFeatureCapability(user, "chat-read-marker")
}
fun hasSpreedFeatureCapability(user: User, capabilityName: String): Boolean {
if (user.capabilities?.spreedCapability?.features != null) {
return user.capabilities!!.spreedCapability!!.features!!.contains(capabilityName)
}
return false
}
fun getMessageMaxLength(user: User): Int {
val capabilities = user.capabilities!!
if (user.capabilities?.spreedCapability?.config?.containsKey("chat") == true) {
val chatConfigHashMap = user.capabilities!!.spreedCapability!!.config!!["chat"]
if (chatConfigHashMap?.containsKey("max-length") == true) {
val chatSize = chatConfigHashMap["max-length"]!!.toInt()
return if (chatSize > 0) {
chatSize
} else {
DEFAULT_CHAT_SIZE
}
}
}
return DEFAULT_CHAT_SIZE
}
fun isPhoneBookIntegrationAvailable(user: User): Boolean {
return user.capabilities?.spreedCapability?.features?.contains("phonebook-search") == true
}
fun isReadStatusAvailable(user: User): Boolean {
if (user.capabilities?.spreedCapability?.config?.containsKey("chat") == true) {
val map: Map<String, String>? = user.capabilities!!.spreedCapability!!.config!!["chat"]
return map != null && map.containsKey("read-privacy")
}
return false
}
fun isReadStatusPrivate(user: User): Boolean {
if (user.capabilities?.spreedCapability?.config?.containsKey("chat") == true) {
val map = user.capabilities!!.spreedCapability!!.config!!["chat"]
if (map?.containsKey("read-privacy") == true) {
return map["read-privacy"]!!.toInt() == 1
}
}
return false
}
fun isUserStatusAvailable(user: User): Boolean {
return user.capabilities?.userStatusCapability?.enabled == true &&
user.capabilities?.userStatusCapability?.supportsEmoji == true
}
fun getAttachmentFolder(user: User): String? {
if (user.capabilities?.spreedCapability?.config?.containsKey("attachments") == true) {
val map = user.capabilities!!.spreedCapability!!.config!!["attachments"]
if (map?.containsKey("folder") == true) {
return map["folder"]
}
}
return "/Talk"
}
fun getServerName(user: User): String? {
if (user.capabilities?.themingCapability != null) {
return user.capabilities!!.themingCapability!!.name
}
return ""
}
// TODO later avatar can also be checked via user fields, for now it is in Talk capability
fun isAvatarEndpointAvailable(user: User): Boolean {
return user.capabilities?.spreedCapability?.features?.contains("temp-user-avatar-api") == true
}
fun canEditScopes(user: User): Boolean {
return user.capabilities?.provisioningCapability?.accountPropertyScopesVersion != null &&
user.capabilities!!.provisioningCapability!!.accountPropertyScopesVersion!! > 1
}
fun isAbleToCall(user: User): Boolean {
if (user.capabilities != null) {
val capabilities = user.capabilities
return if (
capabilities?.spreedCapability?.config?.containsKey("call") == true &&
capabilities.spreedCapability!!.config!!["call"] != null &&
capabilities.spreedCapability!!.config!!["call"]!!.containsKey("enabled")
) {
java.lang.Boolean.parseBoolean(capabilities.spreedCapability!!.config!!["call"]!!["enabled"])
} else {
// older nextcloud versions without the capability can't disable the calls
true
}
}
return false
}
fun isUnifiedSearchAvailable(user: User): Boolean {
return hasSpreedFeatureCapability(user, "unified-search")
}
const val DEFAULT_CHAT_SIZE = 1000
}

View File

@ -35,6 +35,9 @@ import io.requery.Persistable;
import io.requery.query.Result; import io.requery.query.Result;
import io.requery.reactivex.ReactiveEntityStore; import io.requery.reactivex.ReactiveEntityStore;
/**
* @deprecated use {@link com.nextcloud.talk.users.UserManager} instead.
*/
@Deprecated @Deprecated
public class UserUtils implements CurrentUserProvider { public class UserUtils implements CurrentUserProvider {
private ReactiveEntityStore<Persistable> dataStore; private ReactiveEntityStore<Persistable> dataStore;
@ -45,24 +48,24 @@ public class UserUtils implements CurrentUserProvider {
public boolean anyUserExists() { public boolean anyUserExists() {
return (dataStore.count(User.class).where(UserEntity.SCHEDULED_FOR_DELETION.notEqual(Boolean.TRUE)) return (dataStore.count(User.class).where(UserEntity.SCHEDULED_FOR_DELETION.notEqual(Boolean.TRUE))
.limit(1).get().value() > 0); .limit(1).get().value() > 0);
} }
public boolean hasMultipleUsers() { public boolean hasMultipleUsers() {
return (dataStore.count(User.class).where(UserEntity.SCHEDULED_FOR_DELETION.notEqual(Boolean.TRUE)) return (dataStore.count(User.class).where(UserEntity.SCHEDULED_FOR_DELETION.notEqual(Boolean.TRUE))
.get().value() > 1); .get().value() > 1);
} }
public List getUsers() { public List getUsers() {
Result findUsersQueryResult = dataStore.select(User.class).where Result findUsersQueryResult = dataStore.select(User.class).where
(UserEntity.SCHEDULED_FOR_DELETION.notEqual(Boolean.TRUE)).get(); (UserEntity.SCHEDULED_FOR_DELETION.notEqual(Boolean.TRUE)).get();
return findUsersQueryResult.toList(); return findUsersQueryResult.toList();
} }
public List getUsersScheduledForDeletion() { public List getUsersScheduledForDeletion() {
Result findUsersQueryResult = dataStore.select(User.class) Result findUsersQueryResult = dataStore.select(User.class)
.where(UserEntity.SCHEDULED_FOR_DELETION.eq(Boolean.TRUE)).get(); .where(UserEntity.SCHEDULED_FOR_DELETION.eq(Boolean.TRUE)).get();
return findUsersQueryResult.toList(); return findUsersQueryResult.toList();
} }
@ -70,8 +73,8 @@ public class UserUtils implements CurrentUserProvider {
public UserEntity getAnyUserAndSetAsActive() { public UserEntity getAnyUserAndSetAsActive() {
Result findUserQueryResult = dataStore.select(User.class) Result findUserQueryResult = dataStore.select(User.class)
.where(UserEntity.SCHEDULED_FOR_DELETION.notEqual(Boolean.TRUE)) .where(UserEntity.SCHEDULED_FOR_DELETION.notEqual(Boolean.TRUE))
.limit(1).get(); .limit(1).get();
UserEntity userEntity; UserEntity userEntity;
if ((userEntity = (UserEntity) findUserQueryResult.firstOrNull()) != null) { if ((userEntity = (UserEntity) findUserQueryResult.firstOrNull()) != null) {
@ -84,10 +87,11 @@ public class UserUtils implements CurrentUserProvider {
} }
@Override @Override
public @Nullable UserEntity getCurrentUser() { public @Nullable
UserEntity getCurrentUser() {
Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.CURRENT.eq(Boolean.TRUE) Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.CURRENT.eq(Boolean.TRUE)
.and(UserEntity.SCHEDULED_FOR_DELETION.notEqual(Boolean.TRUE))) .and(UserEntity.SCHEDULED_FOR_DELETION.notEqual(Boolean.TRUE)))
.limit(1).get(); .limit(1).get();
return (UserEntity) findUserQueryResult.firstOrNull(); return (UserEntity) findUserQueryResult.firstOrNull();
} }
@ -98,8 +102,8 @@ public class UserUtils implements CurrentUserProvider {
UserEntity user = (UserEntity) findUserQueryResult.firstOrNull(); UserEntity user = (UserEntity) findUserQueryResult.firstOrNull();
return dataStore.delete(user) return dataStore.delete(user)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()); .observeOn(AndroidSchedulers.mainThread());
} }
@ -109,20 +113,20 @@ public class UserUtils implements CurrentUserProvider {
UserEntity user = (UserEntity) findUserQueryResult.firstOrNull(); UserEntity user = (UserEntity) findUserQueryResult.firstOrNull();
return dataStore.delete(user) return dataStore.delete(user)
.subscribeOn(Schedulers.io()); .subscribeOn(Schedulers.io());
} }
public UserEntity getUserById(String id) { public UserEntity getUserById(String id) {
Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.USER_ID.eq(id)) Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.USER_ID.eq(id))
.limit(1).get(); .limit(1).get();
return (UserEntity) findUserQueryResult.firstOrNull(); return (UserEntity) findUserQueryResult.firstOrNull();
} }
public UserEntity getUserWithId(long id) { public UserEntity getUserWithId(long id) {
Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(id)) Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(id))
.limit(1).get(); .limit(1).get();
return (UserEntity) findUserQueryResult.firstOrNull(); return (UserEntity) findUserQueryResult.firstOrNull();
} }
@ -140,8 +144,8 @@ public class UserUtils implements CurrentUserProvider {
public boolean checkIfUserIsScheduledForDeletion(String username, String server) { public boolean checkIfUserIsScheduledForDeletion(String username, String server) {
Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.USERNAME.eq(username)) Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.USERNAME.eq(username))
.and(UserEntity.BASE_URL.eq(server)) .and(UserEntity.BASE_URL.eq(server))
.limit(1).get(); .limit(1).get();
UserEntity userEntity; UserEntity userEntity;
if ((userEntity = (UserEntity) findUserQueryResult.firstOrNull()) != null) { if ((userEntity = (UserEntity) findUserQueryResult.firstOrNull()) != null) {
@ -153,23 +157,23 @@ public class UserUtils implements CurrentUserProvider {
public UserEntity getUserWithInternalId(long internalId) { public UserEntity getUserWithInternalId(long internalId) {
Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(internalId) Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(internalId)
.and(UserEntity.SCHEDULED_FOR_DELETION.notEqual(Boolean.TRUE))) .and(UserEntity.SCHEDULED_FOR_DELETION.notEqual(Boolean.TRUE)))
.limit(1).get(); .limit(1).get();
return (UserEntity) findUserQueryResult.firstOrNull(); return (UserEntity) findUserQueryResult.firstOrNull();
} }
public boolean getIfUserWithUsernameAndServer(String username, String server) { public boolean getIfUserWithUsernameAndServer(String username, String server) {
Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.USERNAME.eq(username) Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.USERNAME.eq(username)
.and(UserEntity.BASE_URL.eq(server))) .and(UserEntity.BASE_URL.eq(server)))
.limit(1).get(); .limit(1).get();
return findUserQueryResult.firstOrNull() != null; return findUserQueryResult.firstOrNull() != null;
} }
public boolean scheduleUserForDeletionWithId(long id) { public boolean scheduleUserForDeletionWithId(long id) {
Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(id)) Result findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(id))
.limit(1).get(); .limit(1).get();
UserEntity userEntity; UserEntity userEntity;
if ((userEntity = (UserEntity) findUserQueryResult.firstOrNull()) != null) { if ((userEntity = (UserEntity) findUserQueryResult.firstOrNull()) != null) {
@ -194,7 +198,7 @@ public class UserUtils implements CurrentUserProvider {
Result findUserQueryResult; Result findUserQueryResult;
if (internalId == null) { if (internalId == null) {
findUserQueryResult = dataStore.select(User.class).where(UserEntity.USERNAME.eq(username). findUserQueryResult = dataStore.select(User.class).where(UserEntity.USERNAME.eq(username).
and(UserEntity.BASE_URL.eq(serverUrl))).limit(1).get(); and(UserEntity.BASE_URL.eq(serverUrl))).limit(1).get();
} else { } else {
findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(internalId)).get(); findUserQueryResult = dataStore.select(User.class).where(UserEntity.ID.eq(internalId)).get();
} }
@ -243,7 +247,7 @@ public class UserUtils implements CurrentUserProvider {
} }
if ((displayName != null && user.getDisplayName() == null) || (displayName != null && user.getDisplayName() if ((displayName != null && user.getDisplayName() == null) || (displayName != null && user.getDisplayName()
!= null && !displayName.equals(user.getDisplayName()))) { != null && !displayName.equals(user.getDisplayName()))) {
user.setDisplayName(displayName); user.setDisplayName(displayName);
} }