Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import com.owncloud.android.ui.asynctasks.RichDocumentsLoadUrlTask
import com.owncloud.android.ui.fragment.OCFileListFragment
import com.owncloud.android.utils.DisplayUtils
import com.owncloud.android.utils.FileStorageUtils
import com.owncloud.android.utils.RichDocumentDownloadAsParser
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import org.json.JSONException
import org.json.JSONObject
Expand Down Expand Up @@ -163,22 +164,16 @@ class RichDocumentsEditorWebView : EditorWebView() {

@JavascriptInterface
fun downloadAs(json: String?) {
try {
json ?: return
val downloadJson = JSONObject(json)
val url = downloadJson.getString(URL).toUri()
when (downloadJson.getString(TYPE)) {
PRINT -> printFile(url)
val result = RichDocumentDownloadAsParser.parse(json) ?: return
val url = result.url.toUri()
when (result.format) {
PRINT -> printFile(url)

SLIDESHOW -> showSlideShow(url)
SLIDESHOW -> showSlideShow(url)

else -> {
val downloadFileName = downloadJson.optString(FILENAME, fileName)
downloadFile(url, downloadFileName)
}
else -> {
downloadFile(url, result.fileName)
}
} catch (e: JSONException) {
Log_OC.e(this, "Failed to parse download json message: $e")
}
}

Expand Down Expand Up @@ -218,12 +213,9 @@ class RichDocumentsEditorWebView : EditorWebView() {
}

companion object {
private const val URL = "URL"
private const val HYPERLINK = "Url"
private const val TYPE = "Type"
private const val PRINT = "print"
private const val SLIDESHOW = "slideshow"
private const val NEW_NAME = "NewName"
private const val FILENAME = "filename"
}
}
10 changes: 10 additions & 0 deletions app/src/main/java/com/owncloud/android/ui/model/DownloadAs.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.owncloud.android.ui.model

data class DownloadAs(val format: String, val fileName: String, val url: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.owncloud.android.utils

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
import kotlinx.serialization.json.jsonPrimitive

object RichDocumentDownloadAsParser {

private const val TAG = "RichDocumentDownloadAsParser"
private const val URL_UPPERCASE = "URL"
private const val URL_LOWERCASE = "url"

private const val FORMAT = "format"
private const val NAME = "name"
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 url = obj[URL_LOWERCASE]?.jsonPrimitive?.contentOrNull
?: obj[URL_UPPERCASE]?.jsonPrimitive?.contentOrNull
tryParseV2(obj, url) ?: tryParseV1(obj, url)
} catch (e: Exception) {
Log_OC.e(TAG, "parse failed: $e")
null
}
}

private fun tryParseV2(obj: JsonObject, url: String?): DownloadAs? {
val format = obj[FORMAT]?.jsonPrimitive?.contentOrNull
val name = obj[NAME]?.jsonPrimitive?.contentOrNull
if (format == null || url == null) return null
return DownloadAs(format = format, fileName = name ?: "", url = url)
}

private fun tryParseV1(obj: JsonObject, url: String?): DownloadAs? {
val type = obj[TYPE]?.jsonPrimitive?.contentOrNull
val filename = obj[FILENAME]?.jsonPrimitive?.contentOrNull
if (type == null || url == null) return null
return DownloadAs(format = type, fileName = filename ?: "", url = url)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.owncloud.android.ui.model

import com.owncloud.android.utils.RichDocumentDownloadAsParser
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Test

class RichDocumentDownloadAsParserTests {

@Test
fun `parse returns null for null-like blank input`() {
assertNull(RichDocumentDownloadAsParser.parse(""))
assertNull(RichDocumentDownloadAsParser.parse(" "))
}

@Test
fun `parse returns null for malformed json`() {
assertNull(RichDocumentDownloadAsParser.parse("not json at all"))
assertNull(RichDocumentDownloadAsParser.parse("{invalid}"))
}

@Test
fun `parse returns null when required v2 fields are missing`() {
assertNull(RichDocumentDownloadAsParser.parse("""{"format":"pdf"}"""))
assertNull(RichDocumentDownloadAsParser.parse("""{"format":"pdf","name":"file.pdf"}"""))
}

@Test
fun `parse returns null when required v1 fields are missing`() {
assertNull(RichDocumentDownloadAsParser.parse("""{"Type":"print"}"""))
assertNull(RichDocumentDownloadAsParser.parse("""{"Type":"print","filename":"file.pdf"}"""))
}

@Test
fun `parse v2 with lowercase url succeeds`() {
val json = """{"format":"pdf","name":"document.pdf","url":"https://example.com/file.pdf"}"""
val result = RichDocumentDownloadAsParser.parse(json)
assertNotNull(result)
assertEquals("pdf", result!!.format)
assertEquals("document.pdf", result.fileName)
assertEquals("https://example.com/file.pdf", result.url)
}

@Test
fun `parse v2 with uppercase URL succeeds`() {
val json = """{"format":"pdf","name":"document.pdf","URL":"https://example.com/file.pdf"}"""
val result = RichDocumentDownloadAsParser.parse(json)
assertNotNull(result)
assertEquals("pdf", result!!.format)
assertEquals("document.pdf", result.fileName)
assertEquals("https://example.com/file.pdf", result.url)
}

@Test
fun `parse v2 with format print succeeds`() {
val json = """{"format":"print","name":"document.pdf","url":"https://example.com/file.pdf"}"""
val result = RichDocumentDownloadAsParser.parse(json)
assertNotNull(result)
assertEquals("print", result!!.format)
}

@Test
fun `parse v2 with format slideshow succeeds`() {
val json = """{"format":"slideshow","name":"slides.pdf","url":"https://example.com/slides.pdf"}"""
val result = RichDocumentDownloadAsParser.parse(json)
assertNotNull(result)
assertEquals("slideshow", result!!.format)
}

@Test
fun `parse v1 with uppercase URL succeeds`() {
val json = """{"Type":"print","URL":"https://example.com/file.pdf","filename":"file.pdf"}"""
val result = RichDocumentDownloadAsParser.parse(json)
assertNotNull(result)
assertEquals("print", result!!.format)
assertEquals("file.pdf", result.fileName)
assertEquals("https://example.com/file.pdf", result.url)
}

@Test
fun `parse v1 with lowercase url succeeds`() {
val json = """{"Type":"print","url":"https://example.com/file.pdf","filename":"file.pdf"}"""
val result = RichDocumentDownloadAsParser.parse(json)
assertNotNull(result)
assertEquals("print", result!!.format)
assertEquals("file.pdf", result.fileName)
assertEquals("https://example.com/file.pdf", result.url)
}

@Test
fun `parse v1 with slideshow type succeeds`() {
val json = """{"Type":"slideshow","URL":"https://example.com/slides.pdf","filename":"slides.pdf"}"""
val result = RichDocumentDownloadAsParser.parse(json)
assertNotNull(result)
assertEquals("slideshow", result!!.format)
assertEquals("slides.pdf", result.fileName)
}
}
Loading