How do I get last picture from gallery (camera roll) using flutter?
I would like to display this photo as a thumbnail like this:
I know that it's possible in android Get uri of picture taken by camera and in IOS Swift - how to get last taken 3 photos from photo library?
Is there a tool, lib, media API that could help me with that?
You can try using this library. photo_manager
List<AssetEntity> assets = [];
_fetchAssets() async {
final albums = await PhotoManager.getAssetPathList(type: RequestType.all);
final recentAlbum = albums.first;
final recentAssets = await recentAlbum.getAssetListRange(
start: 0, // start at index 0
end: 1, // end at a very big index (to get all the assets)
);
print(recentAssets);
setState(() => assets = recentAssets);
}
Use platform channels ,, as you know it's native solution
https://medium.com/flutter-io/flutter-platform-channels-ce7f540a104e
https://flutter.io/docs/development/platform-integration/platform-channels
If you're willing to do this through a dialog, you can use the image picker plugin which allows you to go through the camera's image gallery and the user can select the image (including the last image).
If you want this done completely programmatically, someone implemented a pull request for this very feature here: https://github.com/flutter/plugins/pull/676, but it hasn't been accepted because it needs some tests. You could copy the code from that PR, or add some tests, and then get it accepted into the repo!
Related
I have a Flutter app that can view mp4 files from a URL. (Using a video controller playing directly from the URL.) I want the user to be able to share them if they wish. As best I can tell the file has to actually exist on the device so I have broken down the steps for now into download file, invoke share.
I'm using this guide: https://retroportalstudio.medium.com/saving-files-to-application-folder-and-gallery-in-flutter-e9be2ebee92a
I need to work on ios and android. The problem is that on ios neither the filename I get from the dio downloader nor the ImageGallerySaver seem to "work" when passed to the system ShareSheet.
I'm using the flutter extensions dio, share_plus, cross_file, image_gallery_saver as I've seen recommended in various places.
File saveFile = File(directory.path + "/$fileName");
developer.log("starting download...");
await dio.download(url, saveFile.path,
onReceiveProgress: (value1, value2) {
developer.log("got progress " + value1.toString());
setState(() {
downloadProgress = value1 / value2;
});
});
_permaFile = saveFile.path;
if (Platform.isIOS) {
var galleryResult = await ImageGallerySaver.saveFile(saveFile.path,
isReturnPathOfIOS: true);
developer.log("gallery save result = " + galleryResult.toString());
_permaFile = galleryResult['filePath'];
}
After getting a directory we use dio to download the file, do some log chirping, and then save the name to an object member called _permaFile.
Then the share button triggers:
void _shareAction() async {
final box = context.findRenderObject() as RenderBox?;
final files = <XFile>[];
if (_permaFile == null) {
return;
}
developer.log("sharing file: " + _permaFile.toString());
files.add(XFile(_permaFile!));
await Share.shareXFiles(files,
text: "Event",
// subject: "Subject for Event",
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size);
}
This works on android device... after I download I hit share, and I can share the video to a third-party app like WhatsApp.
On ios the ShareSheet is invoked but when I share I only get the text "Event", not the video file that goes along with it.
Note that I have tried both results... setting the _permaFile to be what comes back from ImageGallerySaver but also just using what the dio downloader gives back.
Note also that the ImageGallerySaver seems to work: the video really does land and is there in the ios video lib. If I go into the Photos app I can share from there to WhatsApp and have the video get sent.
In each case I get errors like this:
[ShareSheet] error fetching item for URL:file:/var/mobile/Media/DCIM/100APPLE/IMG_0021.MP4 -- file:/// : (null)
[ShareSheet] error fetching file provider domain for URL:file:/var/mobile/Media/DCIM/100APPLE/IMG_0021.MP4 -- file:/// : (null)
[ShareSheet] error loading metadata for
documentURL:file:/var/mobile/Media/DCIM/100APPLE/IMG_0021.MP4 --
file:/// error:Error Domain=NSFileProviderInternalErrorDomain Code=0
"No valid file provider found from URL
file:/var/mobile/Media/DCIM/100APPLE/IMG_0021.MP4 -- file:///."
UserInfo={NSLocalizedDescription=No valid file provider found from URL
file:/var/mobile/Media/DCIM/100APPLE/IMG_0021.MP4 -- file:///.}
In order to test this further I built the share_plus demo app:
https://github.com/fluttercommunity/plus_plugins/tree/main/packages/share_plus/share_plus
I modified it to share videos to see what was different. The share plus example (sp_example) works for sharing videos that have been selected by the picker.
For this reason I think the problem is something I'm missing about ios video filenames/formats and possibly a built-in conversion step that happens.
Here are what the filenames look like that I see in my app:
dio download result:
file:///var/mobile/Containers/Data/Application/223BF2B9-DDF0-490E-932F-09D5F03B98B3/Library/Caches/test.mp4
ImageGallerySaver result:
file:///var/mobile/Media/DCIM/100APPLE/IMG_0019.MP4
This is what video filenames look like when they are picked and shared in sp_example:
/private/var/mobile/Containers/Data/Application/E5CB4D7C-6CDF-4AA2-8134-C4322ED7C886/tmp/trim.E6633D68-44E3-4853-A29E-A71AC95A0913.MOV
Note that it has been converted to MOV extension and the user gets trim step right in the picker that results in trim in the name.
For my purposes I don't want to go through the picker, the user is on the screen showing the video and they shouldnt have to repick, so where do I get the post-conversion ios filename that references what I just saved?
I'm writing a simple app in Ionic 3, for submitting forms with few attachments (picture from the camera, picture from gallery and one 20 s movie). After form submits into the server, forms are processing and receiving statuses. Statuses are fetching through the app to inform the user. The whole form which was submitted is saving into SQLite to allow the user an access to all his forms and attachments (pictures and movies). For this purpose, I'm copying pictures and movie into Cordova file plugin:
let imageData = await this.camera.getPicture(options);
let dir = imageData.split('/');
let fileName = dir[dir.length - 1];
dir.pop();
let fromDirectory = dir.join('/');
let toDirectory: string = this.file.syncedDataDirectory;
await this.file.copyFile(fromDirectory, fileName, toDirectory, fileName);
My camera options:
const options: CameraOptions = {
quality: 50,
destinationType: this.camera.DestinationType.FILE_URI,
sourceType: this.camera.PictureSourceType.PHOTOLIBRARY,
mediaType: this.camera.MediaType.PICTURE,
encodingType: this.camera.EncodingType.JPEG
}
Everything is working on Android but on iOS I'm receiving an error with code: 12, PATH_EXISTS_ERR. Error occurs only with pictures. I'm copying the movie in the same way as pictures:
await this.file.copyFile(fromDirectory, movieName, toDirectory, movieName)
In the first session, I'm adding one picture from the camera and one from the gallery (Camera Roll), and all is working fine. The problem occurs in a new session when I'm trying to add other pictures. If I added two pictures in the previous session, in the second session I receive twice error: PATH_EXISTS_ERR, and third approach to adding a picture is a success. It looks like in one session file plugin is writing picture with index 001, 002 etc... But in new session file plugin is trying to write a new picture again with index 001, but this path already exists.
I can not find any replication of this error, probably I'm using file plugin in the wrong way, but on Android, everything is working perfectly with the same way of copying the file into file plugin (one except I'm copying files into this.file.dataDirectory). If I will not find a solution probably I will transform pictures into base64 and I will store them as a string.
I will be really grateful for any advice!
I am trying to share image from gallery and want to get it in my ionic application. I have tried a lot but still not understanding this.
I have tried following plugins:
https://github.com/protonet/cordova-plugin-share-extension-helper
https://github.com/markmarijnissen/cordova-plugin-share
How to share content/data through other apps in an iOS app like we do in an Android app with Intent.ACTION_SEND?
http://www.technetexperts.com/mobile/share-extension-in-ios-application-overview-with-example/
https://github.com/LokeshPatel/iOS-Phonegap-app-share-extension
After some detailed analysis, this is what I could conclude:
In Android, you could just get your application added in the share list using cordova-plugin-intent as described in this SO Post. You can also achieve this by adding intent filter in the activity as described here
In iOS, this is bit tricky as there are no straight forward plugins or readymade solution available to achieve this. But the best possible link i could get related to adding app in iOS share menu is getting listed in share menu The link includes apple documentation to do this and also some tweaking in Info.plist to achieve this.
You can use this plugin https://github.com/wymsee/cordova-imagePicker
The usage has been described there. It gives you the path of the image.
You can use that path or read the imagefile to get base64 equivalent of it. e.g.
window.imagePicker.getPictures(
function(results) {
for (var i = 0; i < results.length; i++) {
createImage(results[i])
}
}, function (error) {
console.log('Error: ' + error);
})
createImage(fileURL){
var img = new Image();
img.crossOrigin = 'Anonymous';
img.src = fileURL;
img.onload = function(){
// processing
}}
So I have app where I want to let users to share screenshot of score to facebook etc... I'm using SocialShare widget. In documentation it says to set path to image like this: "image:fileToShare.nativePath", but I'm not really sure how to set it. Another problem is that I need to share picture that has always different name, it saves screenshots with names like tia7828157.png,tia107997596.png... in folder in device internal memory in pictures/enigmania/ I'm new to appcelerator, so I dont know if there is something like wildcard I could use for this? Thanks for any help.
This is my code so far which I know is wrong, I know the widget works because it shares text without problem:
function shareTextWidget(e){
// share text status
var socialWidget=Alloy.createWidget('com.alcoapps.socialshare');
socialWidget.share({status:"Enigmania kvíz",androidDialogTitle:"hoho",image:test.png/pictures/enigmania});
}
You should use Ti.Filesystem class methods/properties to get the path of any file located on internal or external storage.
Also aware of the permissions of reading storage on Android 6+. Use Storage Permissions before accessing any file on Android 6+.
Simple code snippet to create a directory on internal storage at this location: pictures/enigmania and then write an image file of captured view in this directory.
function shareTextWidget(e){
var directory = Ti.Filesystem.getFile(Ti.Filesystem.externalStorageDirectory, 'pictures/enigmania');
!directory.exists() && directory.createDirectory();
var fileToShare = Ti.Filesystem.getFile(directory.resolve(), 'screen.jpg');
fileToShare.write($.SCREENSHOT_VIEW.toImage()); // write the blob image to created file
var socialWidget=Alloy.createWidget('com.alcoapps.socialshare');
socialWidget.share({status:"Enigmania kvíz",androidDialogTitle:"hoho",image:fileToShare.nativePath});
}
This code should work without any issues.
Note that $.SCREENSHOT_VIEW is the View ID for which you will take screenshot, so it depends on you how you maintain your View positions in order to capture correct screenshot, but point is to use Ti.UI.View toImage() method to capture the screenshot of particular view.
Let me know if this works for you or not, else we can look into other methods by getting your exact requirements. Good Luck!!!!
My understanding currently is that:
CameraUI
I can use the CameraUI to access the built in camera for MediaType.VIDEO and that delegates to the built-in video camera app and lets me record a video. My app does that now.
When I stop recording and click the "Use" button, I am returned to my app and theoretically I have a valid MediaPromise.
iOS does -not- provide a valid/usable url/filename to the recorded video (or to photos) and so I would have to use a Loader to bring-in/use/access the 'recorded' video... AND... iOS does not actually create a file anywhere on the device, most importantly, in the Camera Roll where one would expect by the normal behavior when uses the system native camera/video app.
The documentation says that the Loader can load various image types and SWFs but nothing about video data, so I conclude from that that I cannot actually use the CameraUI to generate a valid MediaPromise that I can then pass to a Loader class or similar to read in the information created by the system camera and then manipulate (upload, save to applicationStorageDirectory, and/or display in one of the two video player components available in the API).
CameraRoll
I can have video entities in the iOS Camera Roll but the AS3/Air3.5 CameraRoll class won't let me view/access/reference them in any way.
Normal File I/O
All my attempts to use the Air3.5 File classes to browse to the storage location of the iOS Camera Roll have been rebuffed.
------- Questions -------
Am I correct in believing that there is a way to take video but no way to use the video that's been captured. (No way to use the resulting MediaPromise successfully).
I believe you can take video and access it using Android, but there's nothing in the documentation that says that you cannot using iOS.
Am I correct in believing that iOS sandboxes apps so that they cannot browse to video/photo storage using standard File I/O, but only through the apparently non-workable means I've tried (CameraUI & CameraRoll)
Am I wrong to think that these should be rather obvious NEEDS that one can achieve using the XCode Objective C++ etc route but the AIR Mobile Framework does not allow either because of Apple blocking functionality or because Adobe has failed to meet reasonable expectations?
One item of ironic note to convey. If I use the iOS system camera app to record a video, a thumnail of that video then appears in the Gallery/Camera Roll, and of course, I can share it or view it, or whatever... If I use AIR's CameraRoll.browseForImage(), provided I haven't used the camera to take another image, when it shows me the folder where the pictures are stored, the folder icon uses the thumbnail of the last object added... in this case, the video I took, but if I then enter the folder, the video cannot be found. It's teasing us. It knows it's there, but it is apparently forbidden fruit.
I can't answer all your questions, so this entry may not be acceptable, but I found this page while searching a solution for some the problems you described and thought that someone else may find this answer (partially) useful.
To save the movie you just took you need to open and read the data from the promise.
The iOS won't save the file anywere, so the MediaPromise.file is always null.
This is my solution to the problem:
private var camera:CameraUI;
private var dataInput:IDataInput;
public function recordVideo():void
{
// Start the camera and ask for a video
camera = new CameraUI();
camera.addEventListener(MediaEvent.COMPLETE, onCameraComplete);
camera.launch(MediaType.VIDEO);
}
private function onCameraComplete(event:MediaEvent):void
{
// event.data is a MediaPromise and MediaPromise.open() returns a IDataInput
// Let's cast it to a dispatcher and check when it's complete
dataInput = event.data.open();
var dispatcher:IEventDispatcher = IEventDispatcher(dataInput);
dispatcher.addEventListener(Event.COMPLETE, onDataInputComplete);
}
private function onDataInputComplete(event:Event):void
{
// We can do whatever we want with the data, so we'll store it in a File
var file:File = new File();
var bytes:ByteArray = new ByteArray();
var stream:FileStream = new FileStream();
// Reading the data from the opened MediaPromise
dataInput.readBytes(bytes);
stream.open(file, FileMode.WRITE);
stream.writeBytes(bytes, 0, bytes.bytesAvailable);
stream.close();
}
Also, I'm still looking for a way to put the movie in the CameraRoll