424 lines
15 KiB
Java
424 lines
15 KiB
Java
/*
|
|
* Nextcloud Android client application
|
|
*
|
|
* @author Tobias Kaminsky
|
|
* @author Chris Narkiewicz
|
|
*
|
|
* Copyright (C) 2018 Tobias Kaminsky
|
|
* Copyright (C) 2018 Nextcloud GmbH.
|
|
* Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package com.owncloud.android.ui.activity;
|
|
|
|
import android.accounts.Account;
|
|
import android.annotation.SuppressLint;
|
|
import android.content.ActivityNotFoundException;
|
|
import android.content.Intent;
|
|
import android.graphics.Bitmap;
|
|
import android.net.Uri;
|
|
import android.os.AsyncTask;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.text.TextUtils;
|
|
import android.view.View;
|
|
import android.webkit.JavascriptInterface;
|
|
import android.webkit.ValueCallback;
|
|
import android.webkit.WebChromeClient;
|
|
import android.webkit.WebView;
|
|
import android.widget.ImageView;
|
|
import android.widget.ProgressBar;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
|
|
import com.bumptech.glide.Glide;
|
|
import com.google.android.material.snackbar.Snackbar;
|
|
import com.nextcloud.client.account.CurrentAccountProvider;
|
|
import com.owncloud.android.R;
|
|
import com.owncloud.android.authentication.AccountUtils;
|
|
import com.owncloud.android.datamodel.OCFile;
|
|
import com.owncloud.android.datamodel.Template;
|
|
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
|
|
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
|
import com.owncloud.android.lib.common.utils.Log_OC;
|
|
import com.owncloud.android.operations.RichDocumentsCreateAssetOperation;
|
|
import com.owncloud.android.operations.RichDocumentsUrlOperation;
|
|
import com.owncloud.android.ui.fragment.OCFileListFragment;
|
|
import com.owncloud.android.utils.DisplayUtils;
|
|
import com.owncloud.android.utils.MimeTypeUtil;
|
|
import com.owncloud.android.utils.glide.CustomGlideStreamLoader;
|
|
|
|
import org.parceler.Parcels;
|
|
|
|
import java.lang.ref.WeakReference;
|
|
|
|
import javax.inject.Inject;
|
|
|
|
import androidx.annotation.RequiresApi;
|
|
import butterknife.BindView;
|
|
import butterknife.ButterKnife;
|
|
import butterknife.Unbinder;
|
|
|
|
/**
|
|
* Opens document for editing via Richdocuments app in a web view
|
|
*/
|
|
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
|
public class RichDocumentsWebView extends ExternalSiteWebView {
|
|
|
|
private static final String TAG = RichDocumentsWebView.class.getSimpleName();
|
|
private static final int REQUEST_REMOTE_FILE = 100;
|
|
|
|
public static final int REQUEST_LOCAL_FILE = 101;
|
|
|
|
public static final int MINIMUM_API = Build.VERSION_CODES.LOLLIPOP;
|
|
|
|
private Unbinder unbinder;
|
|
private OCFile file;
|
|
|
|
public ValueCallback<Uri[]> uploadMessage;
|
|
|
|
@BindView(R.id.progressBar2)
|
|
ProgressBar progressBar;
|
|
|
|
@BindView(R.id.thumbnail)
|
|
ImageView thumbnail;
|
|
|
|
@BindView(R.id.filename)
|
|
TextView fileName;
|
|
|
|
@Inject
|
|
protected CurrentAccountProvider currentAccountProvider;
|
|
|
|
@SuppressLint("AddJavascriptInterface") // suppress warning as webview is only used >= Lollipop
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
showToolbar = false;
|
|
webViewLayout = R.layout.richdocuments_webview;
|
|
super.onCreate(savedInstanceState);
|
|
|
|
unbinder = ButterKnife.bind(this);
|
|
|
|
file = getIntent().getParcelableExtra(EXTRA_FILE);
|
|
|
|
// TODO make file nullable
|
|
if (file == null) {
|
|
fileName.setText(R.string.create_file_from_template);
|
|
|
|
Template template = Parcels.unwrap(getIntent().getParcelableExtra(EXTRA_TEMPLATE));
|
|
|
|
int placeholder;
|
|
|
|
switch (template.getType()) {
|
|
case "document":
|
|
placeholder = R.drawable.file_doc;
|
|
break;
|
|
|
|
case "spreadsheet":
|
|
placeholder = R.drawable.file_xls;
|
|
break;
|
|
|
|
case "presentation":
|
|
placeholder = R.drawable.file_ppt;
|
|
break;
|
|
|
|
default:
|
|
placeholder = R.drawable.file;
|
|
break;
|
|
}
|
|
|
|
Glide.with(this).using(new CustomGlideStreamLoader(currentAccountProvider)).load(template.getThumbnailLink())
|
|
.placeholder(placeholder)
|
|
.error(placeholder)
|
|
.into(thumbnail);
|
|
} else {
|
|
setThumbnail(file, thumbnail);
|
|
fileName.setText(file.getFileName());
|
|
}
|
|
|
|
webview.addJavascriptInterface(new RichDocumentsMobileInterface(), "RichDocumentsMobileInterface");
|
|
|
|
webview.setWebChromeClient(new WebChromeClient() {
|
|
RichDocumentsWebView activity = RichDocumentsWebView.this;
|
|
|
|
@Override
|
|
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
|
|
FileChooserParams fileChooserParams) {
|
|
if (uploadMessage != null) {
|
|
uploadMessage.onReceiveValue(null);
|
|
uploadMessage = null;
|
|
}
|
|
|
|
activity.uploadMessage = filePathCallback;
|
|
|
|
Intent intent = fileChooserParams.createIntent();
|
|
intent.setType("image/*");
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
|
try {
|
|
activity.startActivityForResult(intent, REQUEST_LOCAL_FILE);
|
|
} catch (ActivityNotFoundException e) {
|
|
uploadMessage = null;
|
|
Toast.makeText(getBaseContext(), "Cannot open file chooser", Toast.LENGTH_LONG).show();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
});
|
|
|
|
// load url in background
|
|
url = getIntent().getStringExtra(EXTRA_URL);
|
|
if (TextUtils.isEmpty(url)) {
|
|
new LoadUrl(this, getAccount()).execute(file.getLocalId());
|
|
} else {
|
|
webview.loadUrl(url);
|
|
}
|
|
}
|
|
|
|
private void setThumbnail(OCFile file, ImageView thumbnailView) {
|
|
// Todo minimize: only icon by mimetype
|
|
|
|
if (file.isFolder()) {
|
|
thumbnailView.setImageDrawable(MimeTypeUtil.getFolderTypeIcon(file.isSharedWithMe() ||
|
|
file.isSharedWithSharee(), file.isSharedViaLink(), file.isEncrypted(), file.getMountType(),
|
|
this));
|
|
} else {
|
|
if ((MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file)) && file.getRemoteId() != null) {
|
|
// Thumbnail in cache?
|
|
Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
|
|
ThumbnailsCacheManager.PREFIX_THUMBNAIL + file.getRemoteId());
|
|
|
|
if (thumbnail != null && !file.isUpdateThumbnailNeeded()) {
|
|
if (MimeTypeUtil.isVideo(file)) {
|
|
Bitmap withOverlay = ThumbnailsCacheManager.addVideoOverlay(thumbnail);
|
|
thumbnailView.setImageBitmap(withOverlay);
|
|
} else {
|
|
thumbnailView.setImageBitmap(thumbnail);
|
|
}
|
|
} else {
|
|
// generate new thumbnail
|
|
if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, thumbnailView)) {
|
|
try {
|
|
final ThumbnailsCacheManager.ThumbnailGenerationTask task =
|
|
new ThumbnailsCacheManager.ThumbnailGenerationTask(thumbnailView,
|
|
getStorageManager(), getAccount());
|
|
|
|
if (thumbnail == null) {
|
|
if (MimeTypeUtil.isVideo(file)) {
|
|
thumbnail = ThumbnailsCacheManager.mDefaultVideo;
|
|
} else {
|
|
thumbnail = ThumbnailsCacheManager.mDefaultImg;
|
|
}
|
|
}
|
|
final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
|
|
new ThumbnailsCacheManager.AsyncThumbnailDrawable(getResources(), thumbnail, task);
|
|
thumbnailView.setImageDrawable(asyncDrawable);
|
|
task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file,
|
|
file.getRemoteId()));
|
|
} catch (IllegalArgumentException e) {
|
|
Log_OC.d(TAG, "ThumbnailGenerationTask : " + e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
if ("image/png".equalsIgnoreCase(file.getMimeType())) {
|
|
thumbnailView.setBackgroundColor(getResources().getColor(R.color.background_color));
|
|
}
|
|
} else {
|
|
thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimeType(), file.getFileName(),
|
|
getAccount(), this));
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onNewIntent(Intent intent) {
|
|
super.onNewIntent(intent);
|
|
}
|
|
|
|
private void openFileChooser() {
|
|
Intent action = new Intent(this, FilePickerActivity.class);
|
|
action.putExtra(OCFileListFragment.ARG_MIMETYPE, "image/");
|
|
startActivityForResult(action, REQUEST_REMOTE_FILE);
|
|
}
|
|
|
|
private void openShareDialog() {
|
|
Intent intent = new Intent(this, ShareActivity.class);
|
|
intent.putExtra(FileActivity.EXTRA_FILE, file);
|
|
intent.putExtra(FileActivity.EXTRA_ACCOUNT, getAccount());
|
|
startActivity(intent);
|
|
}
|
|
|
|
@Override
|
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
if (RESULT_OK != resultCode) {
|
|
// TODO
|
|
return;
|
|
}
|
|
|
|
switch (requestCode) {
|
|
case REQUEST_LOCAL_FILE:
|
|
handleLocalFile(data, resultCode);
|
|
break;
|
|
|
|
case REQUEST_REMOTE_FILE:
|
|
handleRemoteFile(data);
|
|
break;
|
|
|
|
default:
|
|
// unexpected, do nothing
|
|
break;
|
|
}
|
|
|
|
super.onActivityResult(requestCode, resultCode, data);
|
|
}
|
|
|
|
private void handleLocalFile(Intent data, int resultCode) {
|
|
if (uploadMessage == null) {
|
|
return;
|
|
}
|
|
|
|
uploadMessage.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data));
|
|
uploadMessage = null;
|
|
}
|
|
|
|
private void handleRemoteFile(Intent data) {
|
|
OCFile file = data.getParcelableExtra(FolderPickerActivity.EXTRA_FILES);
|
|
|
|
new Thread(() -> {
|
|
Account account = AccountUtils.getCurrentOwnCloudAccount(this);
|
|
RichDocumentsCreateAssetOperation operation = new RichDocumentsCreateAssetOperation(file.getRemotePath());
|
|
RemoteOperationResult result = operation.execute(account, this);
|
|
|
|
if (result.isSuccess()) {
|
|
String asset = (String) result.getSingleData();
|
|
|
|
runOnUiThread(() -> webview.evaluateJavascript("OCA.RichDocuments.documentsMain.postAsset('" +
|
|
file.getFileName() + "', '" + asset + "');", null));
|
|
} else {
|
|
runOnUiThread(() -> DisplayUtils.showSnackMessage(this, "Inserting image failed!"));
|
|
}
|
|
}).start();
|
|
}
|
|
|
|
@Override
|
|
protected void onSaveInstanceState(Bundle outState) {
|
|
outState.putString(EXTRA_URL, url);
|
|
super.onSaveInstanceState(outState);
|
|
}
|
|
|
|
@Override
|
|
public void onRestoreInstanceState(Bundle savedInstanceState) {
|
|
url = savedInstanceState.getString(EXTRA_URL);
|
|
super.onRestoreInstanceState(savedInstanceState);
|
|
}
|
|
|
|
@Override
|
|
protected void onDestroy() {
|
|
unbinder.unbind();
|
|
webview.destroy();
|
|
|
|
super.onDestroy();
|
|
}
|
|
|
|
private void closeView() {
|
|
webview.destroy();
|
|
finish();
|
|
}
|
|
|
|
private void hideLoading() {
|
|
thumbnail.setVisibility(View.GONE);
|
|
fileName.setVisibility(View.GONE);
|
|
progressBar.setVisibility(View.GONE);
|
|
webview.setVisibility(View.VISIBLE);
|
|
}
|
|
|
|
private class RichDocumentsMobileInterface {
|
|
@JavascriptInterface
|
|
public void close() {
|
|
runOnUiThread(RichDocumentsWebView.this::closeView);
|
|
}
|
|
|
|
@JavascriptInterface
|
|
public void insertGraphic() {
|
|
openFileChooser();
|
|
}
|
|
|
|
@JavascriptInterface
|
|
public void share() {
|
|
openShareDialog();
|
|
}
|
|
|
|
@JavascriptInterface
|
|
public void documentLoaded() {
|
|
runOnUiThread(RichDocumentsWebView.this::hideLoading);
|
|
}
|
|
}
|
|
|
|
private static class LoadUrl extends AsyncTask<String, Void, String> {
|
|
|
|
private Account account;
|
|
private WeakReference<RichDocumentsWebView> richDocumentsWebViewWeakReference;
|
|
|
|
LoadUrl(RichDocumentsWebView richDocumentsWebView, Account account) {
|
|
this.account = account;
|
|
this.richDocumentsWebViewWeakReference = new WeakReference<>(richDocumentsWebView);
|
|
}
|
|
|
|
@Override
|
|
protected String doInBackground(String... fileId) {
|
|
if (richDocumentsWebViewWeakReference.get() == null) {
|
|
return "";
|
|
}
|
|
RichDocumentsUrlOperation richDocumentsUrlOperation = new RichDocumentsUrlOperation(fileId[0]);
|
|
RemoteOperationResult result = richDocumentsUrlOperation.execute(account,
|
|
richDocumentsWebViewWeakReference.get());
|
|
|
|
if (!result.isSuccess()) {
|
|
return "";
|
|
}
|
|
|
|
return (String) result.getData().get(0);
|
|
}
|
|
|
|
@Override
|
|
protected void onPostExecute(String url) {
|
|
RichDocumentsWebView richDocumentsWebView = richDocumentsWebViewWeakReference.get();
|
|
|
|
if (richDocumentsWebView == null) {
|
|
return;
|
|
}
|
|
|
|
if (!url.isEmpty()) {
|
|
richDocumentsWebView.webview.loadUrl(url);
|
|
|
|
new Handler().postDelayed(() -> {
|
|
if (richDocumentsWebView.webview.getVisibility() != View.VISIBLE) {
|
|
DisplayUtils.createSnackbar(richDocumentsWebView.findViewById(android.R.id.content),
|
|
R.string.timeout_richDocuments, Snackbar.LENGTH_INDEFINITE)
|
|
.setActionTextColor(richDocumentsWebView.getResources().getColor(R.color.white))
|
|
.setAction(R.string.fallback_weblogin_back, v -> richDocumentsWebView.closeView()).show();
|
|
}
|
|
}, 10 * 1000);
|
|
} else {
|
|
Toast.makeText(richDocumentsWebView.getApplicationContext(),
|
|
R.string.richdocuments_failed_to_load_document, Toast.LENGTH_LONG).show();
|
|
richDocumentsWebView.finish();
|
|
}
|
|
}
|
|
}
|
|
}
|