mirror of
https://github.com/nextcloud/android.git
synced 2024-11-27 09:39:25 +03:00
Add printing system to RichDocuments (#4365)
Add printing system to RichDocuments
This commit is contained in:
commit
575be806e6
5 changed files with 324 additions and 28 deletions
|
@ -1 +1 @@
|
|||
411
|
||||
426
|
||||
|
|
|
@ -49,16 +49,20 @@ import android.widget.Toast;
|
|||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.nextcloud.client.account.CurrentAccountProvider;
|
||||
import com.nextcloud.client.network.ClientFactory;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.datamodel.Template;
|
||||
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
|
||||
import com.owncloud.android.lib.common.OwnCloudAccount;
|
||||
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.ui.asynctasks.LoadUrlTask;
|
||||
import com.owncloud.android.ui.asynctasks.PrintAsyncTask;
|
||||
import com.owncloud.android.ui.fragment.OCFileListFragment;
|
||||
import com.owncloud.android.utils.DisplayUtils;
|
||||
import com.owncloud.android.utils.FileStorageUtils;
|
||||
import com.owncloud.android.utils.MimeTypeUtil;
|
||||
import com.owncloud.android.utils.glide.CustomGlideStreamLoader;
|
||||
|
||||
|
@ -66,6 +70,9 @@ import org.json.JSONException;
|
|||
import org.json.JSONObject;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
@ -81,12 +88,14 @@ import lombok.Setter;
|
|||
@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;
|
||||
public static final int REQUEST_LOCAL_FILE = 101;
|
||||
private static final int REQUEST_REMOTE_FILE = 100;
|
||||
private static final String TAG = RichDocumentsWebView.class.getSimpleName();
|
||||
private static final String URL = "URL";
|
||||
private static final String TYPE = "Type";
|
||||
private static final String PRINT = "print";
|
||||
private static final String NEW_NAME = "NewName";
|
||||
|
||||
private Unbinder unbinder;
|
||||
private OCFile file;
|
||||
|
@ -106,6 +115,9 @@ public class RichDocumentsWebView extends ExternalSiteWebView {
|
|||
@Inject
|
||||
protected CurrentAccountProvider currentAccountProvider;
|
||||
|
||||
@Inject
|
||||
protected ClientFactory clientFactory;
|
||||
|
||||
@SuppressLint("AddJavascriptInterface") // suppress warning as webview is only used >= Lollipop
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -196,8 +208,8 @@ public class RichDocumentsWebView extends ExternalSiteWebView {
|
|||
|
||||
if (file.isFolder()) {
|
||||
thumbnailView.setImageDrawable(MimeTypeUtil.getFolderTypeIcon(file.isSharedWithMe() ||
|
||||
file.isSharedWithSharee(), file.isSharedViaLink(), file.isEncrypted(), file.getMountType(),
|
||||
this));
|
||||
file.isSharedWithSharee(), file.isSharedViaLink(), file.isEncrypted(), file.getMountType(),
|
||||
this));
|
||||
} else {
|
||||
if ((MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file)) && file.getRemoteId() != null) {
|
||||
// Thumbnail in cache?
|
||||
|
@ -217,7 +229,7 @@ public class RichDocumentsWebView extends ExternalSiteWebView {
|
|||
try {
|
||||
final ThumbnailsCacheManager.ThumbnailGenerationTask task =
|
||||
new ThumbnailsCacheManager.ThumbnailGenerationTask(thumbnailView,
|
||||
getStorageManager(), getAccount());
|
||||
getStorageManager(), getAccount());
|
||||
|
||||
if (thumbnail == null) {
|
||||
if (MimeTypeUtil.isVideo(file)) {
|
||||
|
@ -230,7 +242,7 @@ public class RichDocumentsWebView extends ExternalSiteWebView {
|
|||
new ThumbnailsCacheManager.AsyncThumbnailDrawable(getResources(), thumbnail, task);
|
||||
thumbnailView.setImageDrawable(asyncDrawable);
|
||||
task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file,
|
||||
file.getRemoteId()));
|
||||
file.getRemoteId()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log_OC.d(TAG, "ThumbnailGenerationTask : " + e.getMessage());
|
||||
}
|
||||
|
@ -242,7 +254,7 @@ public class RichDocumentsWebView extends ExternalSiteWebView {
|
|||
}
|
||||
} else {
|
||||
thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimeType(), file.getFileName(),
|
||||
getAccount(), this));
|
||||
getAccount(), this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -310,7 +322,7 @@ public class RichDocumentsWebView extends ExternalSiteWebView {
|
|||
String asset = (String) result.getSingleData();
|
||||
|
||||
runOnUiThread(() -> webview.evaluateJavascript("OCA.RichDocuments.documentsMain.postAsset('" +
|
||||
file.getFileName() + "', '" + asset + "');", null));
|
||||
file.getFileName() + "', '" + asset + "');", null));
|
||||
} else {
|
||||
runOnUiThread(() -> DisplayUtils.showSnackMessage(this, "Inserting image failed!"));
|
||||
}
|
||||
|
@ -361,6 +373,34 @@ public class RichDocumentsWebView extends ExternalSiteWebView {
|
|||
"{ OCA.RichDocuments.documentsMain.postGrabFocus(); }", null);
|
||||
}
|
||||
|
||||
private void printFile(Uri url) {
|
||||
OwnCloudAccount account = accountManager.getCurrentOwnCloudAccount();
|
||||
|
||||
if (account == null) {
|
||||
DisplayUtils.showSnackMessage(webview, getString(R.string.failed_to_print));
|
||||
return;
|
||||
}
|
||||
|
||||
File targetFile = new File(FileStorageUtils.getTemporalPath(account.getName()) + "/print.pdf");
|
||||
|
||||
new PrintAsyncTask(targetFile, url.toString(), new WeakReference<>(this)).execute();
|
||||
}
|
||||
|
||||
private void downloadFile(Uri url) {
|
||||
DownloadManager downloadmanager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
|
||||
if (downloadmanager == null) {
|
||||
DisplayUtils.showSnackMessage(webview, getString(R.string.failed_to_download));
|
||||
return;
|
||||
}
|
||||
|
||||
DownloadManager.Request request = new DownloadManager.Request(url);
|
||||
request.allowScanningByMediaScanner();
|
||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||
|
||||
downloadmanager.enqueue(request);
|
||||
}
|
||||
|
||||
private class RichDocumentsMobileInterface {
|
||||
@JavascriptInterface
|
||||
public void close() {
|
||||
|
@ -384,27 +424,22 @@ public class RichDocumentsWebView extends ExternalSiteWebView {
|
|||
|
||||
@JavascriptInterface
|
||||
public void downloadAs(String json) {
|
||||
Uri downloadUrl;
|
||||
try {
|
||||
JSONObject downloadJson = new JSONObject(json);
|
||||
downloadUrl = Uri.parse(downloadJson.getString("URL"));
|
||||
|
||||
Uri url = Uri.parse(downloadJson.getString(URL));
|
||||
|
||||
if (downloadJson.getString(TYPE).equalsIgnoreCase(PRINT)) {
|
||||
printFile(url);
|
||||
} else {
|
||||
downloadFile(url);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log_OC.e(this, "Failed to parse rename json message: " + e);
|
||||
Log_OC.e(this, "Failed to parse download json message: " + e);
|
||||
return;
|
||||
}
|
||||
|
||||
DownloadManager downloadmanager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
|
||||
if (downloadmanager == null) {
|
||||
DisplayUtils.showSnackMessage(webview, getString(R.string.failed_to_download));
|
||||
return;
|
||||
}
|
||||
|
||||
DownloadManager.Request request = new DownloadManager.Request(downloadUrl);
|
||||
request.allowScanningByMediaScanner();
|
||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||
|
||||
downloadmanager.enqueue(request);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
|
@ -413,7 +448,7 @@ public class RichDocumentsWebView extends ExternalSiteWebView {
|
|||
// need to change filename for sharing
|
||||
try {
|
||||
JSONObject renameJson = new JSONObject(renameString);
|
||||
String newName = renameJson.getString("NewName");
|
||||
String newName = renameJson.getString(NEW_NAME);
|
||||
file.setFileName(newName);
|
||||
} catch (JSONException e) {
|
||||
Log_OC.e(this, "Failed to parse rename json message: " + e);
|
||||
|
@ -422,7 +457,7 @@ public class RichDocumentsWebView extends ExternalSiteWebView {
|
|||
|
||||
@JavascriptInterface
|
||||
public void paste() {
|
||||
// Javascript cannot do this by itself, so help out.
|
||||
// Javascript cannot do this by itself, so help out.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
webview.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PASTE));
|
||||
webview.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_PASTE));
|
||||
|
|
110
src/main/java/com/owncloud/android/ui/adapter/PrintAdapter.java
Normal file
110
src/main/java/com/owncloud/android/ui/adapter/PrintAdapter.java
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2019 Tobias Kaminsky
|
||||
* Copyright (C) 2019 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.ui.adapter;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.print.PageRange;
|
||||
import android.print.PrintAttributes;
|
||||
import android.print.PrintDocumentAdapter;
|
||||
import android.print.PrintDocumentInfo;
|
||||
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public class PrintAdapter extends PrintDocumentAdapter {
|
||||
private static final String TAG = PrintAdapter.class.getSimpleName();
|
||||
private static final String PDF_NAME = "finalPrint.pdf";
|
||||
|
||||
private String filePath;
|
||||
|
||||
public PrintAdapter(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayout(PrintAttributes oldAttributes,
|
||||
PrintAttributes newAttributes,
|
||||
CancellationSignal cancellationSignal,
|
||||
LayoutResultCallback callback,
|
||||
Bundle extras) {
|
||||
if (cancellationSignal.isCanceled()) {
|
||||
callback.onLayoutCancelled();
|
||||
} else {
|
||||
PrintDocumentInfo.Builder builder = new PrintDocumentInfo.Builder(PDF_NAME);
|
||||
builder.setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
|
||||
.setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN)
|
||||
.build();
|
||||
callback.onLayoutFinished(builder.build(), !newAttributes.equals(oldAttributes));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onWrite(PageRange[] pages,
|
||||
ParcelFileDescriptor destination,
|
||||
CancellationSignal cancellationSignal,
|
||||
WriteResultCallback callback) {
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
try {
|
||||
in = new FileInputStream(new File(filePath));
|
||||
out = new FileOutputStream(destination.getFileDescriptor());
|
||||
|
||||
byte[] buf = new byte[16384];
|
||||
int size;
|
||||
|
||||
while ((size = in.read(buf)) >= 0 && !cancellationSignal.isCanceled()) {
|
||||
out.write(buf, 0, size);
|
||||
}
|
||||
|
||||
if (cancellationSignal.isCanceled()) {
|
||||
callback.onWriteCancelled();
|
||||
} else {
|
||||
callback.onWriteFinished(new PageRange[]{PageRange.ALL_PAGES});
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
Log_OC.e(TAG, "Error using temp file", e);
|
||||
} finally {
|
||||
try {
|
||||
Objects.requireNonNull(in).close();
|
||||
Objects.requireNonNull(out).close();
|
||||
|
||||
} catch (IOException | NullPointerException e) {
|
||||
Log_OC.e(TAG, "Error closing streams", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Tobias Kaminsky
|
||||
* Copyright (C) 2019 Tobias Kaminsky
|
||||
* Copyright (C) 2019 Nextcloud GmbH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.ui.asynctasks;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.print.PrintAttributes;
|
||||
import android.print.PrintDocumentAdapter;
|
||||
import android.print.PrintManager;
|
||||
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.ui.activity.RichDocumentsWebView;
|
||||
import com.owncloud.android.ui.adapter.PrintAdapter;
|
||||
import com.owncloud.android.utils.DisplayUtils;
|
||||
|
||||
import org.apache.commons.httpclient.Header;
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
import org.apache.commons.httpclient.methods.GetMethod;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import static android.content.Context.PRINT_SERVICE;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public class PrintAsyncTask extends AsyncTask<Void, Void, Boolean> {
|
||||
private static final String TAG = PrintAsyncTask.class.getSimpleName();
|
||||
private static final String JOB_NAME = "Document";
|
||||
|
||||
private File file;
|
||||
private String url;
|
||||
private WeakReference<RichDocumentsWebView> richDocumentsWebViewWeakReference;
|
||||
|
||||
public PrintAsyncTask(File file, String url, WeakReference<RichDocumentsWebView> richDocumentsWebViewWeakReference) {
|
||||
this.file = file;
|
||||
this.url = url;
|
||||
this.richDocumentsWebViewWeakReference = richDocumentsWebViewWeakReference;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
richDocumentsWebViewWeakReference.get().runOnUiThread(
|
||||
() -> richDocumentsWebViewWeakReference.get().showLoadingDialog(
|
||||
richDocumentsWebViewWeakReference.get().getString(R.string.common_loading)));
|
||||
|
||||
super.onPreExecute();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... voids) {
|
||||
HttpClient client = new HttpClient();
|
||||
GetMethod getMethod = new GetMethod(url);
|
||||
|
||||
FileOutputStream fos;
|
||||
try {
|
||||
int status = client.executeMethod(getMethod);
|
||||
if (status == HttpStatus.SC_OK) {
|
||||
if (file.exists()) {
|
||||
if (!file.delete()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
file.getParentFile().mkdirs();
|
||||
|
||||
if (!file.getParentFile().exists()) {
|
||||
Log_OC.d(TAG, file.getParentFile().getAbsolutePath() + " does not exist");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file.createNewFile()) {
|
||||
Log_OC.d(TAG, file.getAbsolutePath() + " could not be created");
|
||||
return false;
|
||||
}
|
||||
|
||||
BufferedInputStream bis = new BufferedInputStream(getMethod.getResponseBodyAsStream());
|
||||
fos = new FileOutputStream(file);
|
||||
long transferred = 0;
|
||||
|
||||
Header contentLength = getMethod.getResponseHeader("Content-Length");
|
||||
long totalToTransfer = contentLength != null && contentLength.getValue().length() > 0 ?
|
||||
Long.parseLong(contentLength.getValue()) : 0;
|
||||
|
||||
byte[] bytes = new byte[4096];
|
||||
int readResult;
|
||||
while ((readResult = bis.read(bytes)) != -1) {
|
||||
fos.write(bytes, 0, readResult);
|
||||
transferred += readResult;
|
||||
}
|
||||
// Check if the file is completed
|
||||
if (transferred != totalToTransfer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getMethod.getResponseBodyAsStream() != null) {
|
||||
getMethod.getResponseBodyAsStream().close();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log_OC.e(TAG, "Error reading file", e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
RichDocumentsWebView richDocumentsWebView = richDocumentsWebViewWeakReference.get();
|
||||
richDocumentsWebView.dismissLoadingDialog();
|
||||
|
||||
PrintManager printManager = (PrintManager) richDocumentsWebView.getSystemService(PRINT_SERVICE);
|
||||
|
||||
if (!result || printManager == null) {
|
||||
DisplayUtils.showSnackMessage(richDocumentsWebView,
|
||||
richDocumentsWebView.getString(R.string.failed_to_print));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
PrintDocumentAdapter printAdapter = new PrintAdapter(file.getAbsolutePath());
|
||||
|
||||
printManager.print(JOB_NAME, printAdapter, new PrintAttributes.Builder().build());
|
||||
}
|
||||
}
|
|
@ -884,6 +884,7 @@
|
|||
<string name="copy_internal_link">Copy internal link</string>
|
||||
<string name="copy_internal_link_subline">Only works for users with access to this folder</string>
|
||||
<string name="failed_to_download">Failed to pass file to download manager</string>
|
||||
<string name="failed_to_print">Failed to print file</string>
|
||||
<string name="autoupload_disable_power_save_check">Disable power save check</string>
|
||||
<string name="power_save_check_dialog_message">Disabling power save check might result in uploading files when in low battery state!</string>
|
||||
<string name="etm_title">Engineering Test Mode</string>
|
||||
|
|
Loading…
Reference in a new issue