Share media from within Media Preview and share QR code image.

This commit is contained in:
Alan Evans
2020-11-04 15:51:30 -04:00
parent 5e536c3fa5
commit 2f69a9c38e
12 changed files with 418 additions and 61 deletions

View File

@@ -0,0 +1,89 @@
package org.thoughtcrime.securesms.util;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.MemoryFile;
import android.os.ParcelFileDescriptor;
import android.os.ProxyFileDescriptorCallback;
import android.os.storage.StorageManager;
import android.system.ErrnoException;
import android.system.OsConstants;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import org.thoughtcrime.securesms.logging.Log;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
@RequiresApi(api = 26)
final class MemoryFileDescriptorProxy {
private static final String TAG = Log.tag(MemoryFileDescriptorProxy.class);
public static ParcelFileDescriptor create(@NonNull Context context,
@NonNull MemoryFile file)
throws IOException
{
StorageManager storageManager = Objects.requireNonNull(context.getSystemService(StorageManager.class));
HandlerThread thread = new HandlerThread("MemoryFile");
thread.start();
Log.i(TAG, "Thread started");
Handler handler = new Handler(thread.getLooper());
ProxyCallback proxyCallback = new ProxyCallback(file, () -> {
if (thread.quitSafely()) {
Log.i(TAG, "Thread quitSafely true");
} else {
Log.w(TAG, "Thread quitSafely false");
}
});
ParcelFileDescriptor parcelFileDescriptor = storageManager.openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_ONLY,
proxyCallback,
handler);
Log.i(TAG, "Created");
return parcelFileDescriptor;
}
private static final class ProxyCallback extends ProxyFileDescriptorCallback {
private final MemoryFile memoryFile;
private final Runnable onClose;
ProxyCallback(@NonNull MemoryFile memoryFile, Runnable onClose) {
this.memoryFile = memoryFile;
this.onClose = onClose;
}
@Override
public long onGetSize() {
return memoryFile.length();
}
@Override
public int onRead(long offset, int size, byte[] data) throws ErrnoException {
try {
InputStream inputStream = memoryFile.getInputStream();
if(inputStream.skip(offset) != offset){
throw new AssertionError();
}
return inputStream.read(data, 0, size);
} catch (IOException e) {
throw new ErrnoException("onRead", OsConstants.EBADF);
}
}
@Override
public void onRelease() {
Log.i(TAG, "onRelease()");
memoryFile.close();
onClose.run();
}
}
}

View File

@@ -1,36 +1,50 @@
package org.thoughtcrime.securesms.util;
import android.annotation.SuppressLint;
import android.os.Build;
import android.os.MemoryFile;
import android.os.ParcelFileDescriptor;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MemoryFileUtil {
public final class MemoryFileUtil {
public static ParcelFileDescriptor getParcelFileDescriptor(MemoryFile file) throws IOException {
private MemoryFileUtil() {}
public static ParcelFileDescriptor getParcelFileDescriptor(@NonNull MemoryFile file)
throws IOException
{
if (Build.VERSION.SDK_INT >= 26) {
return MemoryFileDescriptorProxy.create(ApplicationDependencies.getApplication(), file);
} else {
return getParcelFileDescriptorLegacy(file);
}
}
@SuppressWarnings("JavaReflectionMemberAccess")
@SuppressLint("PrivateApi")
public static ParcelFileDescriptor getParcelFileDescriptorLegacy(@NonNull MemoryFile file)
throws IOException
{
try {
Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");
FileDescriptor fileDescriptor = (FileDescriptor) method.invoke(file);
Field field = fileDescriptor.getClass().getDeclaredField("descriptor");
Field field = fileDescriptor.getClass().getDeclaredField("descriptor");
field.setAccessible(true);
int fd = field.getInt(fileDescriptor);
return ParcelFileDescriptor.adoptFd(fd);
} catch (IllegalAccessException e) {
throw new IOException(e);
} catch (InvocationTargetException e) {
throw new IOException(e);
} catch (NoSuchMethodException e) {
throw new IOException(e);
} catch (NoSuchFieldException e) {
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | NoSuchFieldException e) {
throw new IOException(e);
}
}