From cdaab781e1648ce1f930472dab10ce6c2ce13fc0 Mon Sep 17 00:00:00 2001 From: daniele-verducci Date: Tue, 29 Sep 2026 14:56:16 +0200 Subject: [PATCH 1/8] Better error management during CreateFileFromTemplate Signed-off-by: daniele-verducci # Conflicts: # app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt --- app/src/main/res/values/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fa3557334b9e..288deecfabe5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1389,7 +1389,10 @@ Type in the search bar above to find files, contacts, calendar events, and more across your account. Found no images or videos + Error creating file Error creating file from template + Error reading file + Error saving local file No app available for sending the selected files All files access Media read-only From bcbbad4ae256e120c212b20edb69144fa48ced2c Mon Sep 17 00:00:00 2001 From: daniele-verducci Date: Tue, 29 Sep 2026 16:34:14 +0200 Subject: [PATCH 2/8] Check filename length, similarly to what happens server-side Signed-off-by: daniele-verducci --- .../nextcloud/utils/fileNameValidator/FileNameValidator.kt | 6 ++++++ app/src/main/res/values/strings.xml | 1 + 2 files changed, 7 insertions(+) diff --git a/app/src/main/java/com/nextcloud/utils/fileNameValidator/FileNameValidator.kt b/app/src/main/java/com/nextcloud/utils/fileNameValidator/FileNameValidator.kt index 7b96e925ae9a..297d24bcdb44 100644 --- a/app/src/main/java/com/nextcloud/utils/fileNameValidator/FileNameValidator.kt +++ b/app/src/main/java/com/nextcloud/utils/fileNameValidator/FileNameValidator.kt @@ -22,6 +22,8 @@ import com.owncloud.android.datamodel.OCFile import com.owncloud.android.lib.resources.status.OCCapability object FileNameValidator { + // As defined, server-side, in FilenameValidator.php:validateFilename() + private const val MAX_FILENAME_SIZE = 250 /** * Checks the validity of a file name. @@ -45,6 +47,10 @@ object FileNameValidator { return context.getString(R.string.filename_empty) } + if (filename.length > MAX_FILENAME_SIZE) { + return context.getString(R.string.filename_too_long) + } + if (isFileNameAlreadyExist(filename, existingFileNames)) { return context.getString(R.string.file_already_exists) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 288deecfabe5..c18bcf8427e0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -486,6 +486,7 @@ Forbidden characters: / \\ < > : " | ? * Filename contains at least one invalid character Filename cannot be empty + Filename cannot be longer than 250 characters Folder name cannot be empty Label cannot be empty Wait a moment… From 7aacbed792e5ff57859b00b3fec1a8fba67ddfb5 Mon Sep 17 00:00:00 2001 From: daniele-verducci Date: Tue, 29 Sep 2026 17:02:15 +0200 Subject: [PATCH 3/8] Implemented tests Signed-off-by: daniele-verducci --- .../nextcloud/utils/FileNameValidatorTests.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/src/androidTest/java/com/nextcloud/utils/FileNameValidatorTests.kt b/app/src/androidTest/java/com/nextcloud/utils/FileNameValidatorTests.kt index 13f2e4585281..b973e482042e 100644 --- a/app/src/androidTest/java/com/nextcloud/utils/FileNameValidatorTests.kt +++ b/app/src/androidTest/java/com/nextcloud/utils/FileNameValidatorTests.kt @@ -23,6 +23,9 @@ import org.junit.Test @Suppress("TooManyFunctions") class FileNameValidatorTests : AbstractOnServerIT() { + companion object { + private const val MAX_FILENAME_SIZE = 250 + } private var capability: OCCapability = fileDataStorageManager.getCapability(account.name) @@ -109,6 +112,20 @@ class FileNameValidatorTests : AbstractOnServerIT() { assertEquals(targetContext.getString(R.string.filename_empty), result) } + @Test + fun testLongFileName() { + val filename = "a".repeat(MAX_FILENAME_SIZE) + val result = FileNameValidator.checkFileName(filename, capability, targetContext) + assertNull(result) + } + + @Test + fun testTooLongFileName() { + val filename = "a".repeat(MAX_FILENAME_SIZE + 1) + val result = FileNameValidator.checkFileName(filename, capability, targetContext) + assertEquals(targetContext.getString(R.string.filename_too_long), result) + } + @Test fun testFileAlreadyExists() { val existingFiles = setOf("existingFile") From c11023634cde2e8643c807855ff47ee02ee0c0eb Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 1 Oct 2026 14:44:11 +0200 Subject: [PATCH 4/8] simplify error handling Signed-off-by: alperozturk96 --- .../ui/dialog/ChooseTemplateDialogFragment.kt | 90 ++++++++++++------- 1 file changed, 59 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt index 3c87d97dc6b6..e1f047198215 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt @@ -13,6 +13,7 @@ package com.owncloud.android.ui.dialog import android.annotation.SuppressLint import android.app.Dialog +import android.content.ContentResolver import android.content.Intent import android.os.AsyncTask import android.os.Bundle @@ -195,7 +196,15 @@ class ChooseTemplateDialogFragment : } private fun createFromTemplate(template: Template, path: String) { - CreateFileFromTemplateTask(this, clientFactory, currentAccount.user, template, path, creator).execute() + CreateFileFromTemplateTask( + this, + clientFactory, + currentAccount.user, + template, + path, + creator, + requireContext().contentResolver + ).execute() } @SuppressLint("NotifyDataSetChanged") @@ -285,21 +294,22 @@ class ChooseTemplateDialogFragment : @Suppress("LongParameterList", "DEPRECATION") private class CreateFileFromTemplateTask( chooseTemplateDialogFragment: ChooseTemplateDialogFragment, - private val clientFactory: ClientFactory?, + private val clientFactory: ClientFactory, private val user: User, private val template: Template, private val path: String, - private val creator: Creator? - ) : AsyncTask() { + private val creator: Creator?, + private val contentResolver: ContentResolver + ) : AsyncTask() { private val chooseTemplateDialogFragmentWeakReference: WeakReference = WeakReference(chooseTemplateDialogFragment) private var file: OCFile? = null @Deprecated("Deprecated in Java") @Suppress("ReturnCount") // legacy code - override fun doInBackground(vararg params: Void): String { + override fun doInBackground(vararg params: Void): CreateFileFromTemplateResult { return try { - val client = clientFactory?.create(user) ?: return "" + val client = clientFactory.create(user) val nextcloudClient = clientFactory.createNextcloudClient(user) val result = DirectEditingCreateFileRemoteOperation( path, @@ -308,54 +318,72 @@ class ChooseTemplateDialogFragment : template.id ).execute(nextcloudClient) if (!result.isSuccess) { - return "" + return CreateFileFromTemplateResult.FailedCreateFile } val newFileResult = ReadFileRemoteOperation(path).execute(client) if (!newFileResult.isSuccess) { - return "" + return CreateFileFromTemplateResult.FailedReadFile } - val fragment = chooseTemplateDialogFragmentWeakReference.get() ?: return "" - val context = fragment.context - ?: // fragment has been detached - return "" - val storageManager = FileDataStorageManager( - user, - context.contentResolver - ) + val storageManager = FileDataStorageManager(user, contentResolver) val temp = FileStorageUtils.fillOCFile(newFileResult.data[0] as RemoteFile) storageManager.saveFile(temp) file = storageManager.getFileByPath(path) result.resultData + CreateFileFromTemplateResult.Success(result.resultData) } catch (e: CreationException) { Log_OC.e(TAG, "Error creating file from template!", e) - "" + CreateFileFromTemplateResult.FailedCreateFromTemplate } } - @Deprecated("Deprecated in Java") - override fun onPostExecute(url: String) { + override fun onPostExecute(result: CreateFileFromTemplateResult) { val fragment = chooseTemplateDialogFragmentWeakReference.get() if (fragment == null || !fragment.isAdded) { Log_OC.e(TAG, "Error creating file from template!") return } - if (url.isEmpty()) { - SnackbarUtil.show(fragment.binding.list, R.string.error_creating_file_from_template) - return - } + when (result) { + is CreateFileFromTemplateResult.Success -> { + val editorWebView = Intent(MainApp.getAppContext(), TextEditorWebView::class.java).apply { + putExtra(ExternalSiteWebView.EXTRA_TITLE, "Text") + putExtra(ExternalSiteWebView.EXTRA_URL, result.url) + putExtra(FileActivity.EXTRA_FILE, file) + putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false) + } + + fragment.run { + startActivity(editorWebView) + dismiss() + } + } - val editorWebView = Intent(MainApp.getAppContext(), TextEditorWebView::class.java).apply { - putExtra(ExternalSiteWebView.EXTRA_TITLE, "Text") - putExtra(ExternalSiteWebView.EXTRA_URL, url) - putExtra(FileActivity.EXTRA_FILE, file) - putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false) + else -> { + result.showError(fragment.binding.list) + } } + } + } - fragment.run { - startActivity(editorWebView) - dismiss() + sealed class CreateFileFromTemplateResult { + data class Success(val url: String) : CreateFileFromTemplateResult() + + data object FailedCreateFile : CreateFileFromTemplateResult() + data object FailedCreateFromTemplate : CreateFileFromTemplateResult() + data object FailedReadFile : CreateFileFromTemplateResult() + data object FailedSaveLocalFile : CreateFileFromTemplateResult(); + + fun showError(view: View) { + if (this is Success) return + + val messageId = when(this) { + FailedCreateFile -> R.string.error_creating_file + FailedCreateFromTemplate -> R.string.error_creating_file_from_template + FailedReadFile -> R.string.error_reading_file + FailedSaveLocalFile -> R.string.error_saving_local_file } + + SnackbarUtil.show(view, messageId) } } From d041cd25298440877947b8316948773d52185f2f Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 1 Oct 2026 14:50:18 +0200 Subject: [PATCH 5/8] get rid of the async task Signed-off-by: alperozturk96 --- .../ui/dialog/ChooseTemplateDialogFragment.kt | 145 ++++++++---------- 1 file changed, 61 insertions(+), 84 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt index e1f047198215..bc7565f629e5 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt @@ -13,7 +13,6 @@ package com.owncloud.android.ui.dialog import android.annotation.SuppressLint import android.app.Dialog -import android.content.ContentResolver import android.content.Intent import android.os.AsyncTask import android.os.Bundle @@ -22,6 +21,7 @@ import android.text.TextWatcher import android.view.View import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.button.MaterialButton import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -54,6 +54,9 @@ import com.owncloud.android.ui.adapter.TemplateAdapter import com.owncloud.android.utils.FileStorageUtils import com.owncloud.android.utils.KeyboardUtils import com.owncloud.android.utils.theme.ViewThemeUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.lang.ref.WeakReference import javax.inject.Inject @@ -196,15 +199,63 @@ class ChooseTemplateDialogFragment : } private fun createFromTemplate(template: Template, path: String) { - CreateFileFromTemplateTask( - this, - clientFactory, - currentAccount.user, - template, - path, - creator, - requireContext().contentResolver - ).execute() + lifecycleScope.launch { + var file: OCFile? = null + val result = withContext(Dispatchers.IO) { + return@withContext try { + val user = currentAccount.user + val client = clientFactory.create(user) + val nextcloudClient = clientFactory.createNextcloudClient(user) + val result = DirectEditingCreateFileRemoteOperation( + path, + creator?.editor, + creator?.id, + template.id + ).execute(nextcloudClient) + if (!result.isSuccess) { + return@withContext CreateFileFromTemplateResult.FailedCreateFile + } + val newFileResult = ReadFileRemoteOperation(path).execute(client) + if (!newFileResult.isSuccess) { + return@withContext CreateFileFromTemplateResult.FailedReadFile + } + val storageManager = FileDataStorageManager(user, context?.contentResolver) + val temp = FileStorageUtils.fillOCFile(newFileResult.data[0] as RemoteFile) + storageManager.saveFile(temp) + file = storageManager.getFileByPath(path) + result.resultData + CreateFileFromTemplateResult.Success(result.resultData) + } catch (e: CreationException) { + Log_OC.e(TAG, "Error creating file from template!", e) + CreateFileFromTemplateResult.FailedCreateFromTemplate + } + } + + withContext(Dispatchers.Main) { + if (!isAdded) { + Log_OC.e(TAG, "Error creating file from template!") + return@withContext + } + + when (result) { + is CreateFileFromTemplateResult.Success -> { + val editorWebView = Intent(MainApp.getAppContext(), TextEditorWebView::class.java).apply { + putExtra(ExternalSiteWebView.EXTRA_TITLE, "Text") + putExtra(ExternalSiteWebView.EXTRA_URL, result.url) + putExtra(FileActivity.EXTRA_FILE, file) + putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false) + } + + startActivity(editorWebView) + dismiss() + } + + else -> { + result.showError(binding.list) + } + } + } + } } @SuppressLint("NotifyDataSetChanged") @@ -291,80 +342,6 @@ class ChooseTemplateDialogFragment : binding.filenameContainer.error = state.errorMessage } - @Suppress("LongParameterList", "DEPRECATION") - private class CreateFileFromTemplateTask( - chooseTemplateDialogFragment: ChooseTemplateDialogFragment, - private val clientFactory: ClientFactory, - private val user: User, - private val template: Template, - private val path: String, - private val creator: Creator?, - private val contentResolver: ContentResolver - ) : AsyncTask() { - private val chooseTemplateDialogFragmentWeakReference: WeakReference = - WeakReference(chooseTemplateDialogFragment) - private var file: OCFile? = null - - @Deprecated("Deprecated in Java") - @Suppress("ReturnCount") // legacy code - override fun doInBackground(vararg params: Void): CreateFileFromTemplateResult { - return try { - val client = clientFactory.create(user) - val nextcloudClient = clientFactory.createNextcloudClient(user) - val result = DirectEditingCreateFileRemoteOperation( - path, - creator?.editor, - creator?.id, - template.id - ).execute(nextcloudClient) - if (!result.isSuccess) { - return CreateFileFromTemplateResult.FailedCreateFile - } - val newFileResult = ReadFileRemoteOperation(path).execute(client) - if (!newFileResult.isSuccess) { - return CreateFileFromTemplateResult.FailedReadFile - } - val storageManager = FileDataStorageManager(user, contentResolver) - val temp = FileStorageUtils.fillOCFile(newFileResult.data[0] as RemoteFile) - storageManager.saveFile(temp) - file = storageManager.getFileByPath(path) - result.resultData - CreateFileFromTemplateResult.Success(result.resultData) - } catch (e: CreationException) { - Log_OC.e(TAG, "Error creating file from template!", e) - CreateFileFromTemplateResult.FailedCreateFromTemplate - } - } - - override fun onPostExecute(result: CreateFileFromTemplateResult) { - val fragment = chooseTemplateDialogFragmentWeakReference.get() - if (fragment == null || !fragment.isAdded) { - Log_OC.e(TAG, "Error creating file from template!") - return - } - - when (result) { - is CreateFileFromTemplateResult.Success -> { - val editorWebView = Intent(MainApp.getAppContext(), TextEditorWebView::class.java).apply { - putExtra(ExternalSiteWebView.EXTRA_TITLE, "Text") - putExtra(ExternalSiteWebView.EXTRA_URL, result.url) - putExtra(FileActivity.EXTRA_FILE, file) - putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false) - } - - fragment.run { - startActivity(editorWebView) - dismiss() - } - } - - else -> { - result.showError(fragment.binding.list) - } - } - } - } - sealed class CreateFileFromTemplateResult { data class Success(val url: String) : CreateFileFromTemplateResult() From 8299dd7f7b822d607b6e833c58b5e8b0c433c178 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 1 Oct 2026 14:54:10 +0200 Subject: [PATCH 6/8] get rid of the async task Signed-off-by: alperozturk96 --- .../ui/dialog/ChooseTemplateDialogFragment.kt | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt index bc7565f629e5..e6ce5dda791b 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt @@ -200,11 +200,9 @@ class ChooseTemplateDialogFragment : private fun createFromTemplate(template: Template, path: String) { lifecycleScope.launch { - var file: OCFile? = null val result = withContext(Dispatchers.IO) { return@withContext try { val user = currentAccount.user - val client = clientFactory.create(user) val nextcloudClient = clientFactory.createNextcloudClient(user) val result = DirectEditingCreateFileRemoteOperation( path, @@ -212,19 +210,20 @@ class ChooseTemplateDialogFragment : creator?.id, template.id ).execute(nextcloudClient) + if (!result.isSuccess) { return@withContext CreateFileFromTemplateResult.FailedCreateFile } + + val client = clientFactory.create(user) val newFileResult = ReadFileRemoteOperation(path).execute(client) if (!newFileResult.isSuccess) { return@withContext CreateFileFromTemplateResult.FailedReadFile } - val storageManager = FileDataStorageManager(user, context?.contentResolver) + val temp = FileStorageUtils.fillOCFile(newFileResult.data[0] as RemoteFile) - storageManager.saveFile(temp) - file = storageManager.getFileByPath(path) - result.resultData - CreateFileFromTemplateResult.Success(result.resultData) + fileDataStorageManager.saveFile(temp) + CreateFileFromTemplateResult.Success(result.resultData, fileDataStorageManager.getFileByPath(path)) } catch (e: CreationException) { Log_OC.e(TAG, "Error creating file from template!", e) CreateFileFromTemplateResult.FailedCreateFromTemplate @@ -242,7 +241,7 @@ class ChooseTemplateDialogFragment : val editorWebView = Intent(MainApp.getAppContext(), TextEditorWebView::class.java).apply { putExtra(ExternalSiteWebView.EXTRA_TITLE, "Text") putExtra(ExternalSiteWebView.EXTRA_URL, result.url) - putExtra(FileActivity.EXTRA_FILE, file) + putExtra(FileActivity.EXTRA_FILE, result.file) putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false) } @@ -343,17 +342,17 @@ class ChooseTemplateDialogFragment : } sealed class CreateFileFromTemplateResult { - data class Success(val url: String) : CreateFileFromTemplateResult() + data class Success(val url: String, val file: OCFile) : CreateFileFromTemplateResult() data object FailedCreateFile : CreateFileFromTemplateResult() data object FailedCreateFromTemplate : CreateFileFromTemplateResult() data object FailedReadFile : CreateFileFromTemplateResult() - data object FailedSaveLocalFile : CreateFileFromTemplateResult(); + data object FailedSaveLocalFile : CreateFileFromTemplateResult() fun showError(view: View) { if (this is Success) return - val messageId = when(this) { + val messageId = when (this) { FailedCreateFile -> R.string.error_creating_file FailedCreateFromTemplate -> R.string.error_creating_file_from_template FailedReadFile -> R.string.error_reading_file From 26e3e68561d624eca718be6824dab950d7fabf16 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 1 Oct 2026 14:57:36 +0200 Subject: [PATCH 7/8] simplify comment Signed-off-by: alperozturk96 --- .../com/nextcloud/utils/fileNameValidator/FileNameValidator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/utils/fileNameValidator/FileNameValidator.kt b/app/src/main/java/com/nextcloud/utils/fileNameValidator/FileNameValidator.kt index 297d24bcdb44..36444224c6b2 100644 --- a/app/src/main/java/com/nextcloud/utils/fileNameValidator/FileNameValidator.kt +++ b/app/src/main/java/com/nextcloud/utils/fileNameValidator/FileNameValidator.kt @@ -22,7 +22,7 @@ import com.owncloud.android.datamodel.OCFile import com.owncloud.android.lib.resources.status.OCCapability object FileNameValidator { - // As defined, server-side, in FilenameValidator.php:validateFilename() + // As defined, server-side, in FilenameValidator::validateFilename private const val MAX_FILENAME_SIZE = 250 /** From 48eca42bdb33c7562219cd83e562a48e6e63f6c6 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 1 Oct 2026 15:17:23 +0200 Subject: [PATCH 8/8] simplify createFileOnServer Signed-off-by: alperozturk96 --- .../ui/dialog/ChooseTemplateDialogFragment.kt | 105 +++++++++--------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt index e6ce5dda791b..0feae1dc60e5 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt @@ -19,6 +19,7 @@ import android.os.Bundle import android.text.Editable import android.text.TextWatcher import android.view.View +import androidx.annotation.WorkerThread import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment import androidx.lifecycle.lifecycleScope @@ -32,10 +33,10 @@ import com.nextcloud.client.account.User import com.nextcloud.client.di.Injectable import com.nextcloud.client.network.ClientFactory import com.nextcloud.client.network.ClientFactory.CreationException +import com.nextcloud.utils.ResultParser.data import com.nextcloud.utils.SnackbarUtil import com.nextcloud.utils.extensions.getParcelableArgument import com.nextcloud.utils.fileNameValidator.FileNameValidator -import com.owncloud.android.MainApp import com.owncloud.android.R import com.owncloud.android.databinding.ChooseTemplateBinding import com.owncloud.android.datamodel.FileDataStorageManager @@ -200,63 +201,63 @@ class ChooseTemplateDialogFragment : private fun createFromTemplate(template: Template, path: String) { lifecycleScope.launch { - val result = withContext(Dispatchers.IO) { - return@withContext try { - val user = currentAccount.user - val nextcloudClient = clientFactory.createNextcloudClient(user) - val result = DirectEditingCreateFileRemoteOperation( - path, - creator?.editor, - creator?.id, - template.id - ).execute(nextcloudClient) - - if (!result.isSuccess) { - return@withContext CreateFileFromTemplateResult.FailedCreateFile - } - - val client = clientFactory.create(user) - val newFileResult = ReadFileRemoteOperation(path).execute(client) - if (!newFileResult.isSuccess) { - return@withContext CreateFileFromTemplateResult.FailedReadFile - } - - val temp = FileStorageUtils.fillOCFile(newFileResult.data[0] as RemoteFile) - fileDataStorageManager.saveFile(temp) - CreateFileFromTemplateResult.Success(result.resultData, fileDataStorageManager.getFileByPath(path)) - } catch (e: CreationException) { - Log_OC.e(TAG, "Error creating file from template!", e) - CreateFileFromTemplateResult.FailedCreateFromTemplate - } - } + val result = withContext(Dispatchers.IO) { createFileOnServer(template, path) } - withContext(Dispatchers.Main) { - if (!isAdded) { - Log_OC.e(TAG, "Error creating file from template!") - return@withContext - } + val binding = _binding + if (!isAdded || binding == null) { + Log_OC.w(TAG, "Dialog no longer attached, ignoring create from template result") + return@launch + } - when (result) { - is CreateFileFromTemplateResult.Success -> { - val editorWebView = Intent(MainApp.getAppContext(), TextEditorWebView::class.java).apply { - putExtra(ExternalSiteWebView.EXTRA_TITLE, "Text") - putExtra(ExternalSiteWebView.EXTRA_URL, result.url) - putExtra(FileActivity.EXTRA_FILE, result.file) - putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false) - } - - startActivity(editorWebView) - dismiss() - } - - else -> { - result.showError(binding.list) - } - } + when (result) { + is CreateFileFromTemplateResult.Success -> openEditor(result) + else -> result.showError(binding.list) } } } + @WorkerThread + private fun createFileOnServer(template: Template, path: String): CreateFileFromTemplateResult { + return try { + val user = currentAccount.user + + val createResult = DirectEditingCreateFileRemoteOperation(path, creator?.editor, creator?.id, template.id) + .execute(clientFactory.createNextcloudClient(user)) + val editorUrl = createResult.resultData?.takeIf { createResult.isSuccess } + ?: return CreateFileFromTemplateResult.FailedCreateFile + + readAndStoreCreatedFile(user, path, editorUrl) + } catch (e: CreationException) { + Log_OC.e(TAG, "Error creating file from template!", e) + CreateFileFromTemplateResult.FailedCreateFromTemplate + } + } + + @WorkerThread + private fun readAndStoreCreatedFile(user: User, path: String, editorUrl: String): CreateFileFromTemplateResult { + val remoteFile = ReadFileRemoteOperation(path) + .execute(clientFactory.create(user)) + .data(RemoteFile::class.java) + ?: return CreateFileFromTemplateResult.FailedReadFile + + fileDataStorageManager.saveFile(FileStorageUtils.fillOCFile(remoteFile)) + return fileDataStorageManager.getFileByPath(path) + ?.let { CreateFileFromTemplateResult.Success(editorUrl, it) } + ?: CreateFileFromTemplateResult.FailedSaveLocalFile + } + + private fun openEditor(result: CreateFileFromTemplateResult.Success) { + val editorWebView = Intent(requireContext(), TextEditorWebView::class.java).apply { + putExtra(ExternalSiteWebView.EXTRA_TITLE, "Text") + putExtra(ExternalSiteWebView.EXTRA_URL, result.url) + putExtra(FileActivity.EXTRA_FILE, result.file) + putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false) + } + + startActivity(editorWebView) + dismiss() + } + @SuppressLint("NotifyDataSetChanged") fun setTemplateList(templateList: TemplateList?) { adapter?.setTemplateList(templateList)