diff --git a/app/src/main/java/com/owncloud/android/ui/activity/WhiteboardWebView.kt b/app/src/main/java/com/owncloud/android/ui/activity/WhiteboardWebView.kt new file mode 100644 index 000000000000..7693a89a8158 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/WhiteboardWebView.kt @@ -0,0 +1,227 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2019 Chris Narkiewicz + * SPDX-FileCopyrightText: 2018 Tobias Kaminsky + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH + * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + */ +package com.owncloud.android.ui.activity + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.text.TextUtils +import android.view.KeyEvent +import android.webkit.JavascriptInterface +import androidx.activity.result.ActivityResult +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.net.toUri +import com.nextcloud.client.account.CurrentAccountProvider +import com.nextcloud.client.network.ClientFactory +import com.nextcloud.utils.extensions.getParcelableArgument +import com.owncloud.android.R +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.operations.RichDocumentsCreateAssetOperation +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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings +import org.json.JSONException +import org.json.JSONObject +import java.io.File +import javax.inject.Inject + +/** + * Opens document for editing via Richdocuments app in a web view + */ +class WhiteboardWebView : EditorWebView() { + @JvmField + @Inject + var currentAccountProvider: CurrentAccountProvider? = null + + @JvmField + @Inject + var clientFactory: ClientFactory? = null + + private var activityResult: ActivityResultLauncher? = null + + @SuppressFBWarnings("ANDROID_WEB_VIEW_JAVASCRIPT_INTERFACE") + override fun postOnCreate() { + super.postOnCreate() + + webView.addJavascriptInterface(RichDocumentsMobileInterface(), "RichDocumentsMobileInterface") + + loadUrl(intent.getStringExtra(EXTRA_URL)) + + registerActivityResult() + } + + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + } + + private fun openFileChooser() { + val action = Intent(this, FilePickerActivity::class.java) + action.putExtra(OCFileListFragment.ARG_MIMETYPE, "image/") + activityResult?.launch(action) + } + + private fun registerActivityResult() { + activityResult = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult -> + if (RESULT_OK == result.resultCode) { + result.data?.let { + handleRemoteFile(it) + } + } + } + } + + private fun handleRemoteFile(data: Intent) { + val file = FolderPickerActivity.EXTRA_FILES?.let { data.getParcelableArgument(it, OCFile::class.java) } + + Thread { + val user = currentAccountProvider?.user + val operation = RichDocumentsCreateAssetOperation(file?.remotePath) + val result = operation.execute(user, this) + if (result.isSuccess) { + val asset = result.singleData as String + runOnUiThread { + webView.evaluateJavascript( + "OCA.RichDocuments.documentsMain.postAsset('" + + file?.fileName + "', '" + asset + "');", + null + ) + } + } else { + runOnUiThread { DisplayUtils.showSnackMessage(this, "Inserting image failed!") } + } + }.start() + } + + override fun onSaveInstanceState(outState: Bundle) { + outState.putString(EXTRA_URL, url) + super.onSaveInstanceState(outState) + } + + override fun onRestoreInstanceState(savedInstanceState: Bundle) { + url = savedInstanceState.getString(EXTRA_URL) + super.onRestoreInstanceState(savedInstanceState) + } + + override fun onResume() { + super.onResume() + webView.evaluateJavascript( + "if (typeof OCA.RichDocuments.documentsMain.postGrabFocus !== 'undefined') " + + "{ OCA.RichDocuments.documentsMain.postGrabFocus(); }", + null + ) + } + + private fun printFile(url: Uri) { + val account = accountManager.currentOwnCloudAccount + if (account == null) { + DisplayUtils.showSnackMessage(webView, getString(R.string.failed_to_print)) + return + } + val targetFile = File(FileStorageUtils.getTemporalPath(account.name) + "/print.pdf") + // PrintAsyncTask(targetFile, url.toString(), WeakReference(this)).execute() + } + + public override fun loadUrl(url: String?) { + if (TextUtils.isEmpty(url)) { + RichDocumentsLoadUrlTask(this, user.get(), file).execute() + } else { + super.loadUrl(url) + } + } + + private fun showSlideShow(url: Uri) { + val intent = Intent(this, ExternalSiteWebView::class.java) + intent.putExtra(EXTRA_URL, url.toString()) + intent.putExtra(EXTRA_SHOW_SIDEBAR, false) + intent.putExtra(EXTRA_SHOW_TOOLBAR, false) + startActivity(intent) + } + + private inner class RichDocumentsMobileInterface : MobileInterface() { + @JavascriptInterface + fun insertGraphic() { + openFileChooser() + } + + @JavascriptInterface + fun documentLoaded() { + runOnUiThread { hideLoading() } + } + + @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) + + SLIDESHOW -> showSlideShow(url) + + else -> { + val downloadFileName = downloadJson.optString(FILENAME, fileName) + downloadFile(url, downloadFileName) + } + } + } catch (e: JSONException) { + Log_OC.e(this, "Failed to parse download json message: $e") + } + } + + @JavascriptInterface + fun fileRename(renameString: String?) { + // when shared file is renamed in another instance, we will get notified about it + // need to change filename for sharing + try { + renameString ?: return + val renameJson = JSONObject(renameString) + val newName = renameJson.getString(NEW_NAME) + file?.fileName = newName + } catch (e: JSONException) { + Log_OC.e(this, "Failed to parse rename json message: $e") + } + } + + @JavascriptInterface + fun paste() { + // Javascript cannot do this by itself, so help out. + webView.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PASTE)) + webView.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_PASTE)) + } + + @JavascriptInterface + fun hyperlink(hyperlink: String?) { + try { + hyperlink ?: return + val url = JSONObject(hyperlink).getString(HYPERLINK) + val intent = Intent(Intent.ACTION_VIEW) + intent.data = url.toUri() + startActivity(intent) + } catch (e: JSONException) { + Log_OC.e(this, "Failed to parse download json message: $e") + } + } + } + + 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" + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java index b0f6c533a4ea..4a5662523081 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -1352,6 +1352,8 @@ public boolean onFileActionChosen(@IdRes final int itemId, Set checkedFi mContainerActivity.getFileOperationsHelper().openFileWithTextEditor(singleFile, getContext()); } else if (EditImageActivity.Companion.canBePreviewed(singleFile)) { ((FileDisplayActivity) mContainerActivity).startImageEditor(singleFile); + } else if (singleFile.getFileName().endsWith(".whiteboard")) { + mContainerActivity.getFileOperationsHelper().openFileAsWhiteboard(singleFile, getContext()); } else { mContainerActivity.getFileOperationsHelper().openFileAsRichDocument(singleFile, getContext()); } diff --git a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index 1062dfc933f6..8378beeab35f 100755 --- a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -62,6 +62,7 @@ import com.owncloud.android.ui.activity.RichDocumentsEditorWebView; import com.owncloud.android.ui.activity.ShareActivity; import com.owncloud.android.ui.activity.TextEditorWebView; +import com.owncloud.android.ui.activity.WhiteboardWebView; import com.owncloud.android.ui.dialog.SendFilesDialog; import com.owncloud.android.ui.dialog.SendShareDialog; import com.owncloud.android.ui.events.EncryptionEvent; @@ -355,6 +356,14 @@ public void openFile(OCFile file) { }); }).start(); } + + public void openFileAsWhiteboard(OCFile file, Context context) { + Intent collaboraWebViewIntent = new Intent(context, WhiteboardWebView.class); + collaboraWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, "Whiteboard"); + collaboraWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_FILE, file); + collaboraWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false); + context.startActivity(collaboraWebViewIntent); + } public void openFileAsRichDocument(OCFile file, Context context) { Intent collaboraWebViewIntent = new Intent(context, RichDocumentsEditorWebView.class);