Skip to content
This repository has been archived by the owner on Feb 4, 2022. It is now read-only.

Commit

Permalink
🚧 Migrate share plugin to new Flutter plugin API (and a new class)
Browse files Browse the repository at this point in the history
Also doing some minor refactoring of the share code
  • Loading branch information
Komposten committed May 23, 2020
1 parent a0611f5 commit d43c373
Show file tree
Hide file tree
Showing 2 changed files with 293 additions and 244 deletions.
263 changes: 19 additions & 244 deletions android/app/src/main/java/com/example/openbook/MainActivity.java
Original file line number Diff line number Diff line change
@@ -1,260 +1,35 @@
package social.openbook.app;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.webkit.MimeTypeMap;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import com.example.openbook.ImageConverter;
import com.example.openbook.plugins.ImageConverterPlugin;
import com.example.openbook.util.InputStreamSupplier;

import com.example.openbook.plugins.SharePlugin;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.plugins.PluginRegistry;
import io.flutter.plugin.common.EventChannel;

public class MainActivity extends FlutterActivity {
private SharePlugin sharePlugin;

public static final String SHARE_STREAM = "openbook.social/receive_share";

private EventChannel.EventSink eventSink = null;
private List<Intent> intentBacklog = new ArrayList<>();
private boolean streamCanceled = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Register the ImageConverterPlugin manually. It won't register automatically since it isn't added as a plugin via
// our pubspec.yaml.
// Note: getFlutterEngine() should not be null here since it is created in super.onCreate().
PluginRegistry pluginRegistry = getFlutterEngine().getPlugins();
pluginRegistry.add(new ImageConverterPlugin());

sendIntent(getIntent());
}

@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
sendIntent(intent);
}

private void sendIntent(Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SEND)) {
if (eventSink == null) {
if (!streamCanceled && !intentBacklog.contains(intent)) {
intentBacklog.add(intent);
}
return;
}

Map<String, String> args = new HashMap<>();

try {
if (intent.getType().startsWith("image/")) {
Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
if (!getExtensionFromUri(uri).equalsIgnoreCase("gif")) {
args.put("image", copyImageToTempFile(uri).toString());
} else {
args.put("video", copyVideoToTempFile(uri).toString());
}
} else if (intent.getType().startsWith("video/")) {
Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
args.put("video", copyVideoToTempFile(uri).toString());
} else if (intent.getType().startsWith("text/")) {
args.put("text", intent.getStringExtra(Intent.EXTRA_TEXT));
} else {
Log.w(getClass().getSimpleName(), "unknown intent type \"" + intent.getType() + "\" received, ignoring");
return;
}
} catch (KeyedException e) {
String msg;
if (e.getCause() != null) {
msg = String.format("an exception occurred while receiving share of type %s" +
"%n %s%n caused by %s", intent.getType(), e.toString(), e.getCause().toString());
} else {
msg = String.format("an exception occurred while receiving share of type %s" +
"%n %s", intent.getType(), e.toString());
}
String errorTextKey = getLocalizationKey(e);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

args.put("error", errorTextKey);
Log.w(getClass().getSimpleName(), msg);
}
// Register the ImageConverterPlugin manually. It won't register automatically since it isn't added as a plugin via
// our pubspec.yaml.
// Note: getFlutterEngine() should not be null here since it is created in super.onCreate().
PluginRegistry pluginRegistry = getFlutterEngine().getPlugins();
pluginRegistry.add(new ImageConverterPlugin());
sharePlugin = new SharePlugin();
pluginRegistry.add(sharePlugin);

Log.i(getClass().getSimpleName(), "sending intent to flutter");
eventSink.success(args);
// Pass the intent that created this activity to the share plugin,
// just in case it is an ACTION_SEND intent with share data.
sharePlugin.handleIntent(getIntent());
}
}

private Uri copyImageToTempFile(Uri imageUri) throws KeyedException {
byte[] data = convertImage(imageUri);
File imageFile = createTemporaryFile(".jpeg");
copyResourceToFile(() -> new ByteArrayInputStream(data), imageFile);

return Uri.fromFile(imageFile);
}

private byte[] convertImage(Uri imageUri) throws KeyedException {
try {
InputStream imageStream;

if (imageUri.getScheme().equals("content")) {
imageStream = this.getContentResolver().openInputStream(imageUri);
} else if (imageUri.getScheme().equals("file")) {
imageStream = new FileInputStream(imageUri.getPath());
} else {
throw new KeyedException(KeyedException.Key.UriSchemeNotSupported, imageUri.getScheme(), null);
}

return ImageConverter.convertImageData(imageStream, ImageConverter.TargetFormat.JPEG);
} catch (FileNotFoundException e) {
throw new KeyedException(KeyedException.Key.ReadFileMissing, e);
}
}

private Uri copyVideoToTempFile(Uri videoUri) throws KeyedException {
Uri result = null;

if (videoUri.getScheme().equals("content") || videoUri.getScheme().equals("file")) {
String extension = getExtensionFromUri(videoUri);
File tempFile = createTemporaryFile("." + extension);
copyResourceToFile(() -> getContentResolver().openInputStream(videoUri), tempFile);
result = Uri.fromFile(tempFile);
} else {
throw new KeyedException(KeyedException.Key.UriSchemeNotSupported, videoUri.getScheme(), null);
}

return result;
}

private String getExtensionFromUri(Uri uri) throws KeyedException {
if (uri.getScheme().equals("content")) {
String mime = this.getContentResolver().getType(uri);
return MimeTypeMap.getSingleton().getExtensionFromMimeType(mime);
} else if (uri.getScheme().equals("file")) {
return MimeTypeMap.getFileExtensionFromUrl(uri.toString());
} else {
throw new KeyedException(KeyedException.Key.UriSchemeNotSupported, uri.getScheme(), null);
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
sharePlugin.handleIntent(intent);
}
}

private File createTemporaryFile(String extension) throws KeyedException {
String name = UUID.randomUUID().toString();

try {
File directory = new File(getCacheDir(), "mediaCache");
if (!directory.exists()) {
directory.mkdirs();
}

return File.createTempFile(name, extension, directory);
} catch (IOException e) {
throw new KeyedException(KeyedException.Key.TempCreationFailed, e);
} catch (SecurityException e) {
throw new KeyedException(KeyedException.Key.TempCreationDenied, e);
}
}

private void copyResourceToFile(InputStreamSupplier inputSupplier, File target) throws KeyedException {
try (InputStream input = inputSupplier.get()) {
try (OutputStream output = new FileOutputStream(target)) {
byte[] data = new byte[1024];
int length;
while ((length = input.read(data)) > 0) {
output.write(data, 0, length);
}
}
catch (FileNotFoundException e) {
throw new KeyedException(KeyedException.Key.WriteTempMissing, e);
} catch (IOException e) {
throw new KeyedException(KeyedException.Key.WriteTempFailed, e);
} catch (SecurityException e) {
throw new KeyedException(KeyedException.Key.WriteTempDenied, e);
}
}
catch (FileNotFoundException e) {
throw new KeyedException(KeyedException.Key.ReadFileMissing, e);
} catch (IOException e) {
//Exception when closing the streams. Ignore.
}
}

private String getLocalizationKey(KeyedException e) {
String errorTextKey = "";

switch (e.getKey()) {
case TempCreationFailed:
case WriteTempFailed:
case WriteTempMissing:
errorTextKey = "error__receive_share_temp_write_failed";
break;
case WriteTempDenied:
case TempCreationDenied:
errorTextKey = "error__receive_share_temp_write_denied";
break;
case UriSchemeNotSupported:
errorTextKey = "error__receive_share_invalid_uri_scheme";
break;
case ReadFileMissing:
errorTextKey = "error__receive_share_file_not_found";
break;
}

return errorTextKey;
}
}

class KeyedException extends Exception {
public enum Key {
TempCreationFailed("Failed to create temporary file."),
TempCreationDenied(TempCreationFailed.message),
WriteTempDenied("Failed to write to temporary file."),
WriteTempFailed(WriteTempDenied.message),
WriteTempMissing(WriteTempDenied.message),
ReadFileMissing("Failed to read the shared file."),
UriSchemeNotSupported("Unsupported URI scheme: ");

private String message;

private Key(String msg) {
message = msg;
}
}
private final Key key;

public KeyedException(Key key, Throwable cause) {
super("", cause);
this.key = key;
}

public KeyedException(Key key, String message, Throwable cause) {
super(message, cause);
this.key = key;
}

public Key getKey() {
return key;
}

@Override
public String getMessage() {
return key.message + super.getMessage();
}
}
Loading

0 comments on commit d43c373

Please sign in to comment.