Make UniFile actually backed by ParcelFileDescriptor, implement IO stream as extensions, Resolve #896

This commit is contained in:
tarsin committed 2023-02-27 18:13:00 +08:00
1 parent 24ccf60870
commit 3973f5ef70
12 files changed
+82 -276

No files matched your search

@@ -17,6 +17,7 @@ package com.hippo.ehviewer.gallery
import android.content.Context
import android.net.Uri
import android.os.ParcelFileDescriptor
import com.hippo.UriArchiveAccessor
import com.hippo.ehviewer.Settings
import com.hippo.image.Image
@@ -35,7 +36,6 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.sync.withPermit
import java.io.FileOutputStream
import java.io.IOException
class ArchivePageLoader(context: Context, uri: Uri, passwdFlow: Flow<String>) : PageLoader2(),
@@ -137,16 +137,16 @@ class ArchivePageLoader(context: Context, uri: Uri, passwdFlow: Flow<String>) :
}
override fun save(index: Int, file: UniFile): Boolean {
val stream: FileOutputStream
val fd: ParcelFileDescriptor
try {
stream = file.openOutputStream() as FileOutputStream
fd = file.openFileDescriptor("w")
} catch (e: IOException) {
e.printStackTrace()
return false
}
archiveAccessor.extractToFd(index, stream.fd)
archiveAccessor.extractToFd(index, fd.fileDescriptor)
try {
stream.close()
fd.close()
} catch (e: IOException) {
e.printStackTrace()
return false
@@ -37,6 +37,8 @@ import com.hippo.ehviewer.gallery.SUPPORT_IMAGE_EXTENSIONS
import com.hippo.image.Image.CloseableSource
import com.hippo.unifile.RawFile
import com.hippo.unifile.UniFile
import com.hippo.unifile.openInputStream
import com.hippo.unifile.openOutputStream
import com.hippo.yorozuya.FileUtils
import com.hippo.yorozuya.MathUtils
import kotlinx.coroutines.currentCoroutineContext
@@ -117,9 +119,9 @@ class SpiderDen(private val mGalleryInfo: GalleryInfo) {
sCache.read(key) {
val extension = fixExtension("." + metadata.toFile().readText())
val file = dir.createFile(generateImageFilename(index, extension)) ?: return false
(file.openOutputStream() as FileOutputStream).use { outputStream ->
file.openFileDescriptor("w").use { outFd ->
ParcelFileDescriptor.open(data.toFile(), MODE_READ_WRITE).use {
it sendTo outputStream.fd
it sendTo outFd
}
}
}
@@ -199,7 +201,7 @@ class SpiderDen(private val mGalleryInfo: GalleryInfo) {
findDownloadFileForIndex(index, extension)?.runSuspendCatching {
openOutputStream().use { outputStream ->
(outputStream as FileOutputStream).channel.use { return doSave(it) == length }
outputStream.channel.use { return doSave(it) == length }
}
}?.onFailure {
it.printStackTrace()
@@ -254,7 +256,7 @@ class SpiderDen(private val mGalleryInfo: GalleryInfo) {
downloadDir?.let { uniFile ->
runCatching {
findImageFile(uniFile, index)?.openInputStream()?.use {
copy(it as FileInputStream)
copy(it)
}
}.onFailure {
it.printStackTrace()
@@ -4,6 +4,8 @@ import coil.disk.DiskCache
import com.hippo.compat.readLegacySpiderInfo
import com.hippo.ehviewer.EhApplication
import com.hippo.unifile.UniFile
import com.hippo.unifile.openInputStream
import com.hippo.unifile.openOutputStream
import kotlinx.serialization.Serializable
import kotlinx.serialization.cbor.Cbor
import kotlinx.serialization.decodeFromByteArray
@@ -18,11 +18,11 @@ package com.hippo.ehviewer.ui
import android.app.Activity
import android.content.Intent
import android.os.Build
import android.os.ParcelFileDescriptor
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import com.hippo.app.ListCheckBoxDialogBuilder
import com.hippo.ehviewer.EhApplication.Companion.application
import com.hippo.ehviewer.download.DownloadManager as downloadManager
import com.hippo.ehviewer.EhApplication.Companion.favouriteStatusRouter
import com.hippo.ehviewer.EhDB
import com.hippo.ehviewer.R
@@ -36,7 +36,7 @@ import com.hippo.unifile.UniFile
import com.hippo.yorozuya.IOUtils
import com.hippo.yorozuya.collect.LongList
import java.io.IOException
import java.io.InputStream
import com.hippo.ehviewer.download.DownloadManager as downloadManager
object CommonOperations {
private fun doAddToFavorites(
@@ -227,9 +227,9 @@ object CommonOperations {
return
}
val noMedia = file.createFile(".nomedia") ?: return
var `is`: InputStream? = null
var `is`: ParcelFileDescriptor? = null
try {
`is` = noMedia.openInputStream()
`is` = noMedia.openFileDescriptor("r")
} catch (e: IOException) {
// Ignore
} finally {
@@ -21,14 +21,13 @@ import android.content.Context;
import android.database.Cursor;
import android.graphics.ImageDecoder;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import androidx.annotation.NonNull;
import com.hippo.image.Image;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
final class Contracts {
private Contracts() {
@@ -80,45 +79,12 @@ final class Contracts {
}
@NonNull
static OutputStream openOutputStream(Context context, Uri uri) throws IOException {
try {
OutputStream os = context.getContentResolver().openOutputStream(uri);
if (os == null) {
throw new IOException("Can't open OutputStream");
}
return os;
} catch (Throwable e) {
Utils.throwIfFatal(e);
throw new IOException("Can't open OutputStream", e);
}
}
@NonNull
static OutputStream openOutputStream(Context context, Uri uri, boolean append) throws IOException {
try {
OutputStream os = context.getContentResolver().openOutputStream(uri, append ? "wa" : "w");
if (os == null) {
throw new IOException("Can't open OutputStream");
}
return os;
} catch (Throwable e) {
Utils.throwIfFatal(e);
throw new IOException("Can't open OutputStream", e);
}
}
@NonNull
static InputStream openInputStream(Context context, Uri uri) throws IOException {
try {
InputStream is = context.getContentResolver().openInputStream(uri);
if (is == null) {
throw new IOException("Can't open InputStream");
}
return is;
} catch (Throwable e) {
Utils.throwIfFatal(e);
throw new IOException("Can't open InputStream", e);
static ParcelFileDescriptor openFileDescriptor(Context context, Uri uri, String mode) throws IOException {
ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, mode);
if (pfd == null) {
throw new IOException("Can't open ParcelFileDescriptor");
}
return pfd;
}
@NonNull
@@ -18,14 +18,13 @@ package com.hippo.unifile;
import android.content.Context;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import androidx.annotation.NonNull;
import com.hippo.image.Image;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
class MediaFile extends UniFile {
@@ -76,14 +75,7 @@ class MediaFile extends UniFile {
@Override
public boolean isFile() {
InputStream is;
try {
is = openInputStream();
} catch (IOException e) {
return false;
}
Utils.closeQuietly(is);
return true;
return DocumentsContractApi19.isFile(mContext, mUri);
}
@Override
@@ -103,13 +95,12 @@ class MediaFile extends UniFile {
@Override
public boolean canWrite() {
OutputStream os;
try {
os = openOutputStream(true);
var fd = openFileDescriptor("w");
fd.close();
} catch (IOException e) {
return false;
}
Utils.closeQuietly(os);
return true;
}
@@ -120,18 +111,7 @@ class MediaFile extends UniFile {
@Override
public boolean ensureFile() {
if (isFile()) {
return true;
} else {
OutputStream os;
try {
os = openOutputStream();
} catch (IOException e) {
return false;
}
Utils.closeQuietly(os);
return true;
}
return isFile();
}
@Override
@@ -169,27 +149,15 @@ class MediaFile extends UniFile {
return false;
}
@NonNull
@Override
public OutputStream openOutputStream() throws IOException {
return UriOutputStream.create(mContext, mUri, "w");
}
@NonNull
@Override
public OutputStream openOutputStream(boolean append) throws IOException {
return UriOutputStream.create(mContext, mUri, append ? "wa" : "w");
}
@NonNull
@Override
public InputStream openInputStream() throws IOException {
return Contracts.openInputStream(mContext, mUri);
}
@NonNull
@Override
public Image.CloseableSource getImageSource() {
return Contracts.getImageSource(mContext, mUri);
}
@NonNull
@Override
public ParcelFileDescriptor openFileDescriptor(@NonNull String mode) throws IOException {
return Contracts.openFileDescriptor(mContext, mUri, mode);
}
}
@@ -18,6 +18,7 @@ package com.hippo.unifile;
import android.graphics.ImageDecoder;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.webkit.MimeTypeMap;
@@ -26,10 +27,8 @@ import androidx.annotation.NonNull;
import com.hippo.image.Image;
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.ArrayList;
@@ -227,7 +226,7 @@ public class RawFile extends UniFile {
results.add(new RawFile(this, file));
}
}
return results.toArray(new UniFile[results.size()]);
return results.toArray(new UniFile[0]);
}
@Override
@@ -247,24 +246,6 @@ public class RawFile extends UniFile {
}
}
@Override
@NonNull
public OutputStream openOutputStream() throws IOException {
return new FileOutputStream(mFile);
}
@Override
@NonNull
public OutputStream openOutputStream(boolean append) throws IOException {
return new FileOutputStream(mFile, append);
}
@Override
@NonNull
public InputStream openInputStream() throws IOException {
return new FileInputStream(mFile);
}
@NonNull
@Override
public Image.CloseableSource getImageSource() {
@@ -282,4 +263,15 @@ public class RawFile extends UniFile {
}
};
}
@NonNull
@Override
public ParcelFileDescriptor openFileDescriptor(@NonNull String mode) throws IOException {
var md = ParcelFileDescriptor.parseMode(mode);
var pfd = ParcelFileDescriptor.open(mFile, md);
if (pfd == null) {
throw new IOException("Can't open ParcelFileDescriptor");
}
return pfd;
}
}
@@ -18,14 +18,13 @@ package com.hippo.unifile;
import android.content.Context;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import androidx.annotation.NonNull;
import com.hippo.image.Image;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
class SingleDocumentFile extends UniFile {
@@ -101,18 +100,7 @@ class SingleDocumentFile extends UniFile {
@Override
public boolean ensureFile() {
if (isFile()) {
return true;
} else {
OutputStream os;
try {
os = openOutputStream();
} catch (IOException e) {
return false;
}
Utils.closeQuietly(os);
return true;
}
return isFile();
}
@Override
@@ -150,27 +138,15 @@ class SingleDocumentFile extends UniFile {
return false;
}
@NonNull
@Override
public OutputStream openOutputStream() throws IOException {
return Contracts.openOutputStream(mContext, mUri);
}
@NonNull
@Override
public OutputStream openOutputStream(boolean append) throws IOException {
return Contracts.openOutputStream(mContext, mUri, append);
}
@NonNull
@Override
public InputStream openInputStream() throws IOException {
return Contracts.openInputStream(mContext, mUri);
}
@NonNull
@Override
public Image.CloseableSource getImageSource() {
return Contracts.getImageSource(mContext, mUri);
}
@NonNull
@Override
public ParcelFileDescriptor openFileDescriptor(@NonNull String mode) throws IOException {
return Contracts.openFileDescriptor(mContext, mUri, mode);
}
}
@@ -18,6 +18,7 @@ package com.hippo.unifile;
import android.content.Context;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.MimeTypeMap;
@@ -27,8 +28,6 @@ import androidx.annotation.NonNull;
import com.hippo.image.Image;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
class TreeDocumentFile extends UniFile {
@@ -221,14 +220,13 @@ class TreeDocumentFile extends UniFile {
final Uri[] result = DocumentsContractApi21.listFiles(mContext, mUri);
final ArrayList<UniFile> results = new ArrayList<>();
for (int i = 0, n = result.length; i < n; i++) {
Uri uri = result[i];
for (Uri uri : result) {
String name = getFilenameForUri(uri);
if (filter.accept(this, name)) {
results.add(new TreeDocumentFile(this, mContext, uri, name));
}
}
return results.toArray(new UniFile[results.size()]);
return results.toArray(new UniFile[0]);
}
@Override
@@ -249,27 +247,15 @@ class TreeDocumentFile extends UniFile {
}
}
@NonNull
@Override
public OutputStream openOutputStream() throws IOException {
return Contracts.openOutputStream(mContext, mUri);
}
@NonNull
@Override
public OutputStream openOutputStream(boolean append) throws IOException {
return Contracts.openOutputStream(mContext, mUri, append);
}
@NonNull
@Override
public InputStream openInputStream() throws IOException {
return Contracts.openInputStream(mContext, mUri);
}
@NonNull
@Override
public Image.CloseableSource getImageSource() {
return Contracts.getImageSource(mContext, mUri);
}
@NonNull
@Override
public ParcelFileDescriptor openFileDescriptor(@NonNull String mode) throws IOException {
return Contracts.openFileDescriptor(mContext, mUri, mode);
}
}
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -29,8 +30,6 @@ import com.hippo.image.Image;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
@@ -414,35 +413,9 @@ public abstract class UniFile {
*/
public abstract boolean renameTo(String displayName);
/**
* Open a stream on to the content associated with the file, clean it if it exists
*
* @return the {@link OutputStream}
* @throws IOException
*/
@NonNull
public abstract OutputStream openOutputStream() throws IOException;
/**
* Open a stream on to the content associated with the file
*
* @param append {@code true} for do not clean it if it exists
* @return the {@link OutputStream}
* @throws IOException
*/
@NonNull
public abstract OutputStream openOutputStream(boolean append) throws IOException;
/**
* Open a stream on to the content associated with the file
*
* @return the {@link InputStream}
* @throws IOException
*/
@NonNull
public abstract InputStream openInputStream() throws IOException;
@NonNull
public abstract Image.CloseableSource getImageSource();
@NonNull
public abstract ParcelFileDescriptor openFileDescriptor(@NonNull String mode) throws IOException;
}
@@ -0,0 +1,14 @@
package com.hippo.unifile
import android.os.ParcelFileDescriptor.AutoCloseInputStream
import android.os.ParcelFileDescriptor.AutoCloseOutputStream
import java.io.FileInputStream
import java.io.FileOutputStream
fun UniFile.openInputStream(): FileInputStream {
return AutoCloseInputStream(openFileDescriptor("r"))
}
fun UniFile.openOutputStream(): FileOutputStream {
return AutoCloseOutputStream(openFileDescriptor("w"))
}
@@ -1,73 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.unifile;
/*
* Created by Hippo on 8/15/2016.
*/
import android.content.Context;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import androidx.annotation.NonNull;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
// The OutputStream from Context.getContentResolver().openOutputStream()
// and FileProvider uri may throw Exception when write. The Exception looks like:
// java.io.IOException: write failed: EBADF (Bad file descriptor)
// But UriOutputStream can avoid it on my Nexus 5 cm13.
// TODO need more test
class UriOutputStream extends FileOutputStream {
private final ParcelFileDescriptor mPfd;
private UriOutputStream(ParcelFileDescriptor pfd, FileDescriptor fd) {
super(fd);
mPfd = pfd;
}
@NonNull
static OutputStream create(Context context, Uri uri, String mode) throws IOException {
ParcelFileDescriptor pfd;
try {
pfd = context.getContentResolver().openFileDescriptor(uri, mode);
} catch (Throwable e) {
Utils.throwIfFatal(e);
throw new IOException("Can't get ParcelFileDescriptor", e);
}
if (pfd == null) {
throw new IOException("Can't get ParcelFileDescriptor");
}
FileDescriptor fd = pfd.getFileDescriptor();
if (fd == null) {
throw new IOException("Can't get FileDescriptor");
}
return new UriOutputStream(pfd, fd);
}
@Override
public void close() throws IOException {
mPfd.close();
super.close();
}
}