diff --git a/app/src/main/java/app/grapheneos/pdfviewer/KtUtils.kt b/app/src/main/java/app/grapheneos/pdfviewer/KtUtils.kt new file mode 100644 index 0000000..3f67aa6 --- /dev/null +++ b/app/src/main/java/app/grapheneos/pdfviewer/KtUtils.kt @@ -0,0 +1,25 @@ +package app.grapheneos.pdfviewer + +import android.content.Context +import android.net.Uri +import java.io.FileNotFoundException +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream + +@Throws(FileNotFoundException::class, IOException::class) +fun saveAs(context: Context, existingUri: Uri, saveAs: Uri) { + + context.asInputStream(existingUri)?.use { inputStream -> + context.asOutputStream(saveAs)?.use { outputStream -> + outputStream.write(inputStream.readBytes()) + } + } + +} + +@Throws(FileNotFoundException::class) +private fun Context.asInputStream(uri: Uri): InputStream? = contentResolver.openInputStream(uri) + +@Throws(FileNotFoundException::class) +private fun Context.asOutputStream(uri: Uri): OutputStream? = contentResolver.openOutputStream(uri) diff --git a/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java b/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java index 00f6547..32abbc0 100644 --- a/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java +++ b/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java @@ -132,6 +132,19 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader } }); + private final ActivityResultLauncher saveAsLauncher = registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), result -> { + if (result == null) return; + if (result.getResultCode() != RESULT_OK) return; + Intent resultData = result.getData(); + if (resultData != null) { + Uri path = resultData.getData(); + if (path != null) { + saveDocumentAs(path); + } + } + }); + private class Channel { @JavascriptInterface public int getPage() { @@ -514,10 +527,10 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader @Override public boolean onPrepareOptionsMenu(Menu menu) { - final int[] ids = { R.id.action_zoom_in, R.id.action_zoom_out, R.id.action_jump_to_page, + final int[] ids = {R.id.action_zoom_in, R.id.action_zoom_out, R.id.action_jump_to_page, R.id.action_next, R.id.action_previous, R.id.action_first, R.id.action_last, R.id.action_rotate_clockwise, R.id.action_rotate_counterclockwise, - R.id.action_view_document_properties, R.id.action_share }; + R.id.action_view_document_properties, R.id.action_share, R.id.action_save_as}; if (mDocumentState < STATE_LOADED) { for (final int id : ids) { final MenuItem item = menu.findItem(id); @@ -541,6 +554,7 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader enableDisableMenuItem(menu.findItem(R.id.action_share), mUri != null); enableDisableMenuItem(menu.findItem(R.id.action_next), mPage < mNumPages); enableDisableMenuItem(menu.findItem(R.id.action_previous), mPage > 1); + enableDisableMenuItem(menu.findItem(R.id.action_save_as), mUri != null); return true; } @@ -587,8 +601,42 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader } else if (itemId == R.id.action_share) { shareDocument(); return true; + } else if (itemId == R.id.action_save_as) { + saveDocument(); } return super.onOptionsItemSelected(item); } + + private void saveDocument() { + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("application/pdf"); + intent.putExtra(Intent.EXTRA_TITLE, getCurrentDocumentName()); + saveAsLauncher.launch(intent); + } + + private String getCurrentDocumentName() { + if (mDocumentProperties == null || mDocumentProperties.isEmpty()) return ""; + String fileName = ""; + String title = ""; + for (CharSequence property : mDocumentProperties) { + if (property.toString().startsWith("File name:")) { + fileName = property.toString().replace("File name:", ""); + } + if (property.toString().startsWith("Title:-")) { + title = property.toString().replace("Title:-", ""); + } + } + return fileName.length() > 2 ? fileName : title; + } + + private void saveDocumentAs(Uri uri) { + try { + KtUtilsKt.saveAs(this, mUri, uri); + } catch (IOException e) { + e.printStackTrace(); + snackbar.setText(R.string.error_while_saving).show(); + } + } } diff --git a/app/src/main/res/drawable/ic_save_24dp.xml b/app/src/main/res/drawable/ic_save_24dp.xml new file mode 100644 index 0000000..1ce5843 --- /dev/null +++ b/app/src/main/res/drawable/ic_save_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/menu/pdf_viewer.xml b/app/src/main/res/menu/pdf_viewer.xml index acdd788..6649c21 100644 --- a/app/src/main/res/menu/pdf_viewer.xml +++ b/app/src/main/res/menu/pdf_viewer.xml @@ -73,6 +73,12 @@ android:title="@string/action_rotate_counterclockwise" app:showAsAction="ifRoom" /> + + Cannot open file with invalid MIME type Cannot open legacy file paths from insecure apps Received I/O error trying to open content + Error encountered while saving WebView out-of-date Your current WebView version is %d. The WebView should be at least version %d for the PDF Viewer to work. Share + Save as