Skip to content

ente-io/media_extension

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

media_extension

Plugin to help a Flutter application behave like a native gallery, by ente.

Features

  1. Allows to identify and handle invocation from a different app, along with details of the intent-action with which it was triggered (PICK / EDIT / VIEW). You can also use the setResult method to pass the URI of the selected media file to the requesting app, by creating a temporary copy within the content-provider path.
  2. Ability to open an image with an third-party app (gallery, editor, wallpaper manager, etc.)

Implementation

  • Native code invokes Flutter code by invoking the Flutter method from onAttachedEngineMethod, which returns the mode asynchronously. We use a Completer in the Dart code to get the status and set the mode of the app.

  • While behaving as a Gallery, the URI of the chosen image from the applications document storage directory and another method call is made to native code with the URI as the parameter.

  • We create a temporary file in our cache-directory with the received URI, and grants other applications permission to read from that directory. Once the content://uri (Content Provider) is created the result is sent to the requested activity via the intent.

DataTypes Descriptions

Types Fields
IntentAction enumeration of main, pick, edit, view
MediaExtentionAction

IntentAction action [type of intent-action which invoked the app]

String? uri [uri of the image sent by other app with the intent]

Method Descriptions

Methods Parameters Return
getIntentAction MediaExtentionAction
setResult

String uri [path of the file selected]

void
setAs

String uri [path of the file selected]

String mimeType [mimeType of the file selected]
void
openWith

String uri [path of the file selected]

String mimeType [mimeType of the file selected]
void
edit

String uri [path of the file selected]

String mimeType [mimeType of the file selected]
void

Getting started

To use this plugin:

  • Run the following command in terminal

    • flutter pub get media_extension
      
      Or
  • Add the following in pubspec.yaml file

    • dependencies:
          flutter:
              sdk: flutter
          media_extension:   
  • Add the following in android/src/main/res/xml/provider_paths.xml

     <?xml version="1.0" encoding="utf-8"?>
     <paths>
          <external-path
              name="external_files"
              path="." />
    
          <cache-path
              name="embedded"
              path="." />
      </paths>
  • Add the following in android/src/main/AndroidManifest.xml

      <application>
      
            <activity
              android:name=".MainActivity">
    
              <intent-filter>
                  <action android:name="android.intent.action.VIEW" />
                  <category android:name="android.intent.category.DEFAULT" />
                  <data android:mimeType="image/*" />
              </intent-filter>
    
              <intent-filter>
                  <action android:name="android.intent.action.PICK" />
                  <category android:name="android.intent.category.DEFAULT" />
                  <data android:mimeType="image/*" />
              </intent-filter>
    
              <intent-filter>
                  <action android:name="android.intent.action.GET_CONTENT" />
                  <category android:name="android.intent.category.DEFAULT" />
                  <category android:name="android.intent.category.OPENABLE" />
                  <data android:mimeType="image/*" />
              </intent-filter>
          </activity>
    
      </application>

Example

class MyAppState extends State<MyApp> {
  final imgUrl = "https://cdn-chat.sstatic.net/chat/img/404_funny_hats.jpg";
  IntentAction _intentAction = IntentAction.main;
  final _mediaExtensionPlugin = MediaExtension();
  final _downloadHelper = DownloadHelper(Dio());

  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    IntentAction intentAction = IntentAction.main;
    try {
      final actionResult = await _mediaExtensionPlugin.getIntentAction();
      intentAction = actionResult.action!;
    } on PlatformException {
      intentAction = IntentAction.unknown;
    }
    if (!mounted) return;

    setState(() {
      _intentAction = intentAction;
    });
  }

  Future<String> _getLocalFile(String filename) async {
    var tempDir = await getTemporaryDirectory();
    String fullPath = "${tempDir.path}/image.jpg";
    await _downloadHelper.downloadToFile(imgUrl, fullPath);
    return fullPath;
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('Intent Action is: $_intentAction\n'),
              FutureBuilder(
                  future: _getLocalFile("image.jpg"),
                  builder:
                      (BuildContext context, AsyncSnapshot<String> snapshot) {
                    return snapshot.data != null
                        ? GestureDetector(
                            child: Image.file(File(snapshot.data!)),
                            onTap: () async {
                              if (_intentAction == IntentAction.pick) {
                                _mediaExtensionPlugin
                                    .setResult("file://${snapshot.data!}");
                              }
                            },
                          )
                        : Container();
                  }),
              GestureDetector(
                child: Text(
                  'Set as',
                  style: Theme.of(context).textTheme.headline4,
                ),
                onTap: () async {
                  var tempDir = await getTemporaryDirectory();
                  String fullPath = "${tempDir.path}/image.jpg'";
                  await _downloadHelper.downloadToFile(imgUrl, fullPath);
                  try {
                    final isDOne = await _mediaExtensionPlugin.setAs(
                        "file://$fullPath", "image/*");
                    debugPrint("Is done $isDOne");
                  } on PlatformException {
                    debugPrint("Some error");
                  }
                },
              ),
              GestureDetector(
                child: Text(
                  'Edit',
                  style: Theme.of(context).textTheme.headline4,
                ),
                onTap: () async {
                  var tempDir = await getTemporaryDirectory();
                  String fullPath = "${tempDir.path}/image.jpg'";
                  await _downloadHelper.downloadToFile(imgUrl, fullPath);
                  try {
                    final isDOne = await _mediaExtensionPlugin.edit(
                        "file://$fullPath", "image/*");
                    debugPrint("Is done $isDOne");
                  } on PlatformException {
                    debugPrint("Some error");
                  }
                },
              ),
              GestureDetector(
                child: Text(
                  'Open With',
                  style: Theme.of(context).textTheme.headline4,
                ),
                onTap: () async {
                  var tempDir = await getTemporaryDirectory();
                  String fullPath = "${tempDir.path}/image.jpg'";
                  await _downloadHelper.downloadToFile(imgUrl, fullPath);
                  try {
                    final result = await _mediaExtensionPlugin.openWith(
                        "file://$fullPath", "image/*");
                    debugPrint("Is done $result");
                  } on PlatformException {
                    debugPrint("Some error");
                  }
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}          

Issues/Upcoming Changes

  • Implement setResults method for multiple selection of images for ACTION_PICK intent.

Credits

This plugin wouldn't be possible without the following:

  • aves : Most of the methods in the plugin are inspired from this repository

License