Implement Storage Access Framework
* re-work finished mission database * re-work DownloadMission and bump it Serializable version * keep the classic Java IO API * SAF Tree API support on Android Lollipop or higher * add wrapper for SAF stream opening * implement Closeable in SharpStream to replace the dispose() method * do required changes for this API: ** remove any file creation logic from DownloadInitializer ** make PostProcessing Serializable and reduce the number of iterations ** update all strings.xml files ** storage helpers: StoredDirectoryHelper & StoredFileHelper ** best effort to handle any kind of SAF errors/exceptions
This commit is contained in:
parent
9e34fee58c
commit
f6b32823ba
62 changed files with 2439 additions and 1180 deletions
140
app/src/main/java/us/shandian/giga/io/FileStreamSAF.java
Normal file
140
app/src/main/java/us/shandian/giga/io/FileStreamSAF.java
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package us.shandian.giga.io;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import org.schabi.newpipe.streams.io.SharpStream;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
public class FileStreamSAF extends SharpStream {
|
||||
|
||||
private final FileInputStream in;
|
||||
private final FileOutputStream out;
|
||||
private final FileChannel channel;
|
||||
private final ParcelFileDescriptor file;
|
||||
|
||||
private boolean disposed;
|
||||
|
||||
public FileStreamSAF(@NonNull ContentResolver contentResolver, Uri fileUri) throws IOException {
|
||||
// Notes:
|
||||
// the file must exists first
|
||||
// ¡read-write mode must allow seek!
|
||||
// It is not guaranteed to work with files in the cloud (virtual files), tested in local storage devices
|
||||
|
||||
file = contentResolver.openFileDescriptor(fileUri, "rw");
|
||||
|
||||
if (file == null) {
|
||||
throw new IOException("Cannot get the ParcelFileDescriptor for " + fileUri.toString());
|
||||
}
|
||||
|
||||
in = new FileInputStream(file.getFileDescriptor());
|
||||
out = new FileOutputStream(file.getFileDescriptor());
|
||||
channel = out.getChannel();// or use in.getChannel()
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return in.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] buffer) throws IOException {
|
||||
return in.read(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] buffer, int offset, int count) throws IOException {
|
||||
return in.read(buffer, offset, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long amount) throws IOException {
|
||||
return in.skip(amount);// ¿or use channel.position(channel.position() + amount)?
|
||||
}
|
||||
|
||||
@Override
|
||||
public long available() {
|
||||
try {
|
||||
return in.available();
|
||||
} catch (IOException e) {
|
||||
return 0;// ¡but not -1!
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rewind() throws IOException {
|
||||
seek(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
disposed = true;
|
||||
|
||||
file.close();
|
||||
in.close();
|
||||
out.close();
|
||||
channel.close();
|
||||
} catch (IOException e) {
|
||||
Log.e("FileStreamSAF", "close() error", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return disposed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRewind() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRead() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canWrite() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean canSetLength() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean canSeek() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte value) throws IOException {
|
||||
out.write(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] buffer) throws IOException {
|
||||
out.write(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] buffer, int offset, int count) throws IOException {
|
||||
out.write(buffer, offset, count);
|
||||
}
|
||||
|
||||
public void setLength(long length) throws IOException {
|
||||
channel.truncate(length);
|
||||
}
|
||||
|
||||
public void seek(long offset) throws IOException {
|
||||
channel.position(offset);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue