diff options
author | Tor Andersson <tor.andersson@artifex.com> | 2022-01-20 15:45:01 +0100 |
---|---|---|
committer | Tor Andersson <tor.andersson@artifex.com> | 2022-01-20 15:45:01 +0100 |
commit | bde4c7bf4ab17f5dcb3421fdcd951abb44f5442c (patch) | |
tree | 0ed2b5d3656c89194074b5f1038a0f4d3832660c | |
parent | c83b19432e4d49900ea4e844a4e22c388a691fed (diff) |
Various file handling cleanups.
Add a class to wrap an InputStream into something seekable for
MuPDF rather than pre-loading the entire file into a buffer.
If the input stream is not seekable, we may end up pre-loading
the entire file anyway, but by using a BufferedInputStream.
Use the ContentProvider to get the mime-type, file name, and
file size used as a hint to the input stream wrapper.
-rw-r--r-- | lib/src/main/java/com/artifex/mupdf/mini/DocumentActivity.java | 52 | ||||
-rw-r--r-- | lib/src/main/java/com/artifex/mupdf/mini/SeekableInputStreamWrapper.java | 62 |
2 files changed, 89 insertions, 25 deletions
diff --git a/lib/src/main/java/com/artifex/mupdf/mini/DocumentActivity.java b/lib/src/main/java/com/artifex/mupdf/mini/DocumentActivity.java index f12e205..6875028 100644 --- a/lib/src/main/java/com/artifex/mupdf/mini/DocumentActivity.java +++ b/lib/src/main/java/com/artifex/mupdf/mini/DocumentActivity.java @@ -11,9 +11,11 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.database.Cursor; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; +import android.provider.OpenableColumns; import android.text.Editable; import android.text.TextWatcher; import android.text.method.PasswordTransformationMethod; @@ -35,13 +37,9 @@ import android.widget.Toast; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; +import java.io.IOException; import java.util.ArrayList; import java.util.Stack; -import java.io.InputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; public class DocumentActivity extends Activity { @@ -56,7 +54,7 @@ public class DocumentActivity extends Activity protected String key; protected String mimetype; - protected byte[] buffer; + protected SeekableInputStream file; protected boolean hasLoaded; protected boolean isReflowable; @@ -119,28 +117,32 @@ public class DocumentActivity extends Activity Uri uri = getIntent().getData(); mimetype = getIntent().getType(); - if (mimetype == null || mimetype.equals("application/octet-stream")) - mimetype = uri.getLastPathSegment(); - key = uri.toString(); - title = uri.getLastPathSegment(); - Log.i(APP, "URI " + uri.toString()); - Log.i(APP, "MAGIC " + mimetype); - Log.i(APP, "TITLE " + title); + Cursor cursor = getContentResolver().query(uri, null, null, null); + cursor.moveToFirst(); + title = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); + long size = cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE)); + if (size == 0) + size = -1; + + Log.i(APP, "OPEN URI " + uri.toString()); + Log.i(APP, " NAME " + title); + Log.i(APP, " SIZE " + size); + + Log.i(APP, " MAGIC (Intent) " + mimetype); + if (mimetype == null || mimetype.equals("application/octet-stream")) { + mimetype = getContentResolver().getType(uri); + Log.i(APP, " MAGIC (Resolver) " + mimetype); + } + if (mimetype == null || mimetype.equals("application/octet-stream")) { + mimetype = title; + Log.i(APP, " MAGIC (Filename) " + mimetype); + } try { - InputStream stm = getContentResolver().openInputStream(uri); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] buf = new byte[16384]; - int n; - while ((n = stm.read(buf)) != -1) - out.write(buf, 0, n); - out.flush(); - buffer = out.toByteArray(); - key = toHex(MessageDigest.getInstance("MD5").digest(buffer)); - Log.i(APP, "BUFFER " + buffer.length + " " + key); - } catch (IOException | NoSuchAlgorithmException x) { + file = new SeekableInputStreamWrapper(getContentResolver().openInputStream(uri), size); + } catch (IOException x) { Log.e(APP, x.toString()); Toast.makeText(this, x.getMessage(), Toast.LENGTH_SHORT).show(); } @@ -328,7 +330,7 @@ public class DocumentActivity extends Activity boolean needsPassword; public void work() { Log.i(APP, "open document"); - doc = Document.openDocument(buffer, mimetype); + doc = Document.openDocument(file, mimetype); needsPassword = doc.needsPassword(); } public void run() { diff --git a/lib/src/main/java/com/artifex/mupdf/mini/SeekableInputStreamWrapper.java b/lib/src/main/java/com/artifex/mupdf/mini/SeekableInputStreamWrapper.java new file mode 100644 index 0000000..d19949a --- /dev/null +++ b/lib/src/main/java/com/artifex/mupdf/mini/SeekableInputStreamWrapper.java @@ -0,0 +1,62 @@ +package com.artifex.mupdf.mini; + +import java.io.IOException; +import java.io.InputStream; +import java.io.BufferedInputStream; + +import com.artifex.mupdf.fitz.SeekableInputStream; + +public class SeekableInputStreamWrapper implements SeekableInputStream +{ + // Max file size supported if the size is unknown + protected final int MAX_SIZE = (128<<20); + + protected InputStream is; + protected long p, length; + + public SeekableInputStreamWrapper(InputStream raw, long n) throws IOException { + int size = n < 0 ? MAX_SIZE : (int)n + 1; + if (!raw.markSupported()) { + is = new BufferedInputStream(raw, size); + } else { + is = raw; + } + is.mark(size); + length = n; + p = 0; + } + + public long seek(long offset, int whence) throws IOException { + switch (whence) { + case SEEK_SET: + p = offset; + break; + case SEEK_CUR: + p = p + offset; + break; + case SEEK_END: + if (length < 0) { + is.reset(); + length = is.skip(MAX_SIZE); + if (length < 0) + throw new IOException("could not find length of stream"); + } + p = length + offset; + break; + } + is.reset(); + is.skip(p); + return p; + } + + public long position() throws IOException { + return p; + } + + public int read(byte[] buf) throws IOException { + int n = is.read(buf); + if (n > 0) + p += n; + return n; + } +} |