Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 19 additions & 17 deletions app/src/gplay/java/com/owncloud/android/utils/PushUtils.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-FileCopyrightText: 2019 Tobias Kaminsky <tobias@kaminsky.me>
* SPDX-FileCopyrightText: 2019 Chris Narkiewicz <hello@ezaquarii.com>
* SPDX-FileCopyrightText: 2017-2018 Mario Danic <mario@lovelyhq.com>
Expand All @@ -15,7 +16,6 @@
import android.text.TextUtils;
import android.util.Base64;

import com.google.gson.Gson;
import com.nextcloud.client.account.UserAccountManager;
import com.nextcloud.client.preferences.AppPreferences;
import com.nextcloud.client.preferences.AppPreferencesImpl;
Expand All @@ -24,8 +24,8 @@
import com.owncloud.android.R;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl;
import com.owncloud.android.datamodel.PushConfigurationState;
import com.owncloud.android.datamodel.SignatureVerification;
import com.owncloud.android.datamodel.pushconfig.PushConfigurationState;
import com.owncloud.android.lib.common.OwnCloudAccount;
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
Expand Down Expand Up @@ -144,10 +144,8 @@ private static void deleteRegistrationForAccount(Account account) {
if (remoteOperationResult.getHttpCode() == HttpStatus.SC_ACCEPTED) {
String arbitraryValue;
if (!TextUtils.isEmpty(arbitraryValue = arbitraryDataProvider.getValue(account.name, KEY_PUSH))) {
Gson gson = new Gson();
PushConfigurationState pushArbitraryData = gson.fromJson(arbitraryValue,
PushConfigurationState.class);
RemoteOperationResult unregisterResult = new UnregisterAccountDeviceForProxyOperation(
PushConfigurationState pushArbitraryData = PushConfigurationState.fromJson(arbitraryValue);
final var unregisterResult = new UnregisterAccountDeviceForProxyOperation(
context.getResources().getString(R.string.push_server_url),
pushArbitraryData.getDeviceIdentifier(),
pushArbitraryData.getDeviceIdentifierSignature(),
Expand Down Expand Up @@ -187,18 +185,16 @@ public static void pushRegistrationToServer(final UserAccountManager accountMana
Context context = MainApp.getAppContext();
String providerValue;
PushConfigurationState accountPushData;
Gson gson = new Gson();
for (Account account : accountManager.getAccounts()) {
providerValue = arbitraryDataProvider.getValue(account.name, KEY_PUSH);
if (!TextUtils.isEmpty(providerValue)) {
accountPushData = gson.fromJson(providerValue,
PushConfigurationState.class);
accountPushData = PushConfigurationState.fromJson(providerValue);
} else {
accountPushData = null;
}

if (accountPushData != null && !accountPushData.getPushToken().equals(token) &&
!accountPushData.isShouldBeDeleted() ||
!accountPushData.getShouldBeDeleted() ||
TextUtils.isEmpty(providerValue)) {
try {
OwnCloudAccount ocAccount = new OwnCloudAccount(account, context);
Expand All @@ -223,11 +219,18 @@ public static void pushRegistrationToServer(final UserAccountManager accountMana
.run();

if (resultProxy.isSuccess()) {
String deviceIdentifier = pushResponse.getDeviceIdentifier();
String signature = pushResponse.getSignature();
String userPublicKey = pushResponse.getPublicKey();
if (deviceIdentifier == null || signature == null || userPublicKey == null) {
Log_OC.e(TAG, "Push registration response is incomplete, not storing it");
continue;
}

PushConfigurationState pushArbitraryData = new PushConfigurationState(token,
pushResponse.getDeviceIdentifier(), pushResponse.getSignature(),
pushResponse.getPublicKey(), false);
deviceIdentifier, signature, userPublicKey, false);
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, KEY_PUSH,
gson.toJson(pushArbitraryData));
PushConfigurationState.toJson(pushArbitraryData));
}
} else if (remoteOperationResult.getCode() ==
RemoteOperationResult.ResultCode.ACCOUNT_USES_STANDARD_PASSWORD) {
Expand All @@ -243,7 +246,7 @@ public static void pushRegistrationToServer(final UserAccountManager accountMana
} catch (OperationCanceledException e) {
Log_OC.d(TAG, "Failed via OperationCanceledException");
}
} else if (accountPushData != null && accountPushData.isShouldBeDeleted()) {
} else if (accountPushData != null && accountPushData.getShouldBeDeleted()) {
deleteRegistrationForAccount(account);
}
}
Expand Down Expand Up @@ -401,16 +404,15 @@ public static SignatureVerification verifySignature(

ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(context);
String arbitraryValue;
Gson gson = new Gson();
PushConfigurationState pushArbitraryData;

try {
signature = Signature.getInstance("SHA512withRSA");
if (accounts.length > 0) {
for (Account account : accounts) {
if (!TextUtils.isEmpty(arbitraryValue = arbitraryDataProvider.getValue(account.name, KEY_PUSH))) {
pushArbitraryData = gson.fromJson(arbitraryValue, PushConfigurationState.class);
if (!pushArbitraryData.isShouldBeDeleted()) {
pushArbitraryData = PushConfigurationState.fromJson(arbitraryValue);
if (!pushArbitraryData.getShouldBeDeleted()) {
publicKey = (PublicKey) readKeyFromString(true, pushArbitraryData.getUserPublicKey());
signature.initVerify(publicKey);
signature.update(subjectBytes);
Expand Down
32 changes: 16 additions & 16 deletions app/src/main/java/com/nextcloud/client/jobs/AccountRemovalWork.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* @author Tobias Kaminsky
* @author Chris Narkiewicz
*
* Copyright (C) 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* Copyright (C) 2017 Tobias Kaminsky
* Copyright (C) 2017 Nextcloud GmbH.
* Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
Expand All @@ -16,7 +17,6 @@ import android.content.Context
import android.text.TextUtils
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.google.gson.Gson
import com.nextcloud.client.account.User
import com.nextcloud.client.account.UserAccountManager
import com.nextcloud.client.core.Clock
Expand All @@ -28,9 +28,9 @@ import com.owncloud.android.datamodel.ArbitraryDataProvider
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl
import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.FilesystemDataProvider
import com.owncloud.android.datamodel.PushConfigurationState
import com.owncloud.android.datamodel.SyncedFolderProvider
import com.owncloud.android.datamodel.UploadsStorageManager
import com.owncloud.android.datamodel.pushconfig.PushConfigurationState
import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory
import com.owncloud.android.lib.common.utils.Log_OC
Expand Down Expand Up @@ -152,22 +152,22 @@ class AccountRemovalWork(
user: User,
arbitraryDataProvider: ArbitraryDataProvider
) {
val arbitraryDataPushString = arbitraryDataProvider.getValue(user, PushUtils.KEY_PUSH)
val pushConfigurationJson = arbitraryDataProvider.getValue(user, PushUtils.KEY_PUSH)
val pushServerUrl = context.resources.getString(R.string.push_server_url)
if (!TextUtils.isEmpty(arbitraryDataPushString) && !TextUtils.isEmpty(pushServerUrl)) {
val gson = Gson()
val pushArbitraryData = gson.fromJson(
arbitraryDataPushString,
PushConfigurationState::class.java
)
pushArbitraryData.isShouldBeDeleted = true
arbitraryDataProvider.storeOrUpdateKeyValue(
user.accountName,
PushUtils.KEY_PUSH,
gson.toJson(pushArbitraryData)
)
PushUtils.pushRegistrationToServer(userAccountManager, pushArbitraryData.getPushToken())
if (pushConfigurationJson.isEmpty() || pushServerUrl.isEmpty()) {
return
}

val newPushConfigurationState = PushConfigurationState.fromJson(pushConfigurationJson).apply {
shouldBeDeleted = true
}
val updatedPushConfigurationJson = PushConfigurationState.toJson(newPushConfigurationState)
arbitraryDataProvider.storeOrUpdateKeyValue(
user.accountName,
PushUtils.KEY_PUSH,
updatedPushConfigurationJson
)
PushUtils.pushRegistrationToServer(userAccountManager, newPushConfigurationState.pushToken)
}

private fun removeSyncedFolders(context: Context, user: User, clock: Clock) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ import com.nextcloud.client.player.model.state.PlayerState
import com.nextcloud.client.player.model.state.RepeatMode
import com.nextcloud.client.player.model.state.VideoSize
import com.nextcloud.utils.extensions.resolveMimeType
import com.nextcloud.utils.serialization.AppJson
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.lib.resources.shares.OCShare
import com.owncloud.android.utils.MimeTypeUtil
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.serialization.json.Json
import java.io.File
import java.util.Collections

Expand All @@ -55,8 +55,6 @@ object PlayerUtil {
private const val SECOND_IN_MILLISECONDS = 1000L
private const val PLAYBACK_FILE_CACHE_SIZE = 512

private val playbackJson = Json { ignoreUnknownKeys = true }

private val playbackFileCache = LruCache<String, PlaybackFile>(PLAYBACK_FILE_CACHE_SIZE)

fun PlaybackFile.toMediaItem(): MediaItem = MediaItem
Expand Down Expand Up @@ -214,14 +212,14 @@ object PlayerUtil {
?.let { pathSegments.firstOrNull()?.toLongOrNull() }

fun Bundle.putPlaybackFile(key: String, playbackFile: PlaybackFile) {
putString(key, playbackJson.encodeToString(playbackFile))
putString(key, AppJson.instance.encodeToString(playbackFile))
}

fun Bundle?.getPlaybackFile(key: String): PlaybackFile? {
val encoded = this?.getString(key) ?: return null

return playbackFileCache[encoded]
?: runCatching { playbackJson.decodeFromString<PlaybackFile>(encoded) }
?: runCatching { AppJson.instance.decodeFromString<PlaybackFile>(encoded) }
.getOrNull()
?.also { playbackFileCache.put(encoded, it) }
}
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/java/com/nextcloud/utils/serialization/AppJson.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.nextcloud.utils.serialization

import kotlinx.serialization.json.Json

object AppJson {
val instance: Json = Json { ignoreUnknownKeys = true }
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.owncloud.android.datamodel.pushconfig

import com.nextcloud.utils.serialization.AppJson
import kotlinx.serialization.Serializable

@Serializable
data class PushConfigurationState(
val pushToken: String,
val deviceIdentifier: String,
val deviceIdentifierSignature: String,
val userPublicKey: String,
var shouldBeDeleted: Boolean
) {
companion object {
@JvmStatic
fun fromJson(value: String): PushConfigurationState = AppJson.instance.decodeFromString(value)

@JvmStatic
fun toJson(value: PushConfigurationState): String = AppJson.instance.encodeToString(value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

package com.owncloud.android.utils

import com.nextcloud.utils.serialization.AppJson
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.ui.model.DownloadAs
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.jsonObject
Expand All @@ -26,14 +26,12 @@ object RichDocumentDownloadAsParser {
private const val TYPE = "Type"
private const val FILENAME = "filename"

private val json = Json { ignoreUnknownKeys = true }

@Suppress("TooGenericExceptionCaught")
fun parse(jsonString: String?): DownloadAs? {
if (jsonString.isNullOrBlank()) return null

return try {
val obj = json.parseToJsonElement(jsonString).jsonObject
val obj = AppJson.instance.parseToJsonElement(jsonString).jsonObject
val url = obj[URL_LOWERCASE]?.jsonPrimitive?.contentOrNull
?: obj[URL_UPPERCASE]?.jsonPrimitive?.contentOrNull
tryParseV2(obj, url) ?: tryParseV1(obj, url)
Expand Down
Loading