Xamarin iOS ALAssetsLibrary WriteVideoToSavedPhotosAlbum doesn't save to library - ios

I am writing an iOS application using Xamarin and am having an issue getting videos to save to my photo library. I am using the WriteVideoToSavedPhotosAlbum method of the ALAssetsLibrary (code below), but the video is never saved. I do no receive an error, but I also do not receive an asset URL in the completion block either.
Does anyone have any idea what could be going on here?
var url = NSUrl.FromFilename (this.FilePath);
ALAssetsLibrary library = new ALAssetsLibrary ();
library.WriteVideoToSavedPhotosAlbum (url, delegate(NSUrl u, NSError e)
{
if (e != null)
{
new UIAlertView ("Error", "Unable to save video to library", null, "Ok").Show();
}
});
One thing to note is the FilePath value is a video stored in the applications document directory.
Thanks

One thing to note is the FilePath value is a video stored in the applications document directory? What you are looking for exactly? as WriteVideoToSavedPhotosAlbum is use to store video capture using camera because directly we can not get its url so we have to store video or image captured using camera to device first then we can get its url and other information using ALAssetsLibrary
I have done like this and its working for me hope this might help you:
ALAssetsLibrary library = new ALAssetsLibrary();
library.WriteImageToSavedPhotosAlbum(originalImage.CGImage, meta, (assetUrl, error) =>
{
library.AssetForUrl(assetUrl, delegate (ALAsset asset)
{
ALAssetRepresentation representation = asset.DefaultRepresentation;
if (representation != null)
{
string fileName = representation.Filename != null ? representation.Filename : string.Empty;
var filePath = assetUrl.ToString();
var extension = filePath.Split('.')[1].ToLower();
var mimeData = string.Format("image/{0}", extension);
var mimeType = mimeData.Split('?')[0].ToLower();
var documentName = assetUrl.Path.ToString().Split('/')[1];
}
}, delegate (NSError err) {
Console.WriteLine("User denied access to photo Library... {0}", err);
});
});

Related

Xamarin ios Cloud file copy location issue

I had connected iCloud using xamarin forms ios. My file copied to icloud without any error. But it saved in file:///private/var/mobile/Library/Mobile%20Documents/iCloud~com~companyname~MobileDoc/ path.
When i checked inside iCloud my file not showing it. But when i search that file, its location shows as iCloud --> MobileDoc--> File.txt. But when i checked inside the iCloud there are no Folder call MobileDoc.
This is the example i tried Here
This is the code i am using to save the document.
var uburl = NSFileManager.DefaultManager.GetUrlForUbiquityContainer(null);
if (uburl == null)
{
HasiCloud = false;
}
else
{
HasiCloud = true;
iCloudUrl = uburl;
var docsFolder = Path.Combine(iCloudUrl.Path, "Documents");
var filePath = Path.Combine(docsFolder, fileName);
FileService.Save(filePath, attachment.Content);
if (option == false)
FileService.Instance.Open(filePath);
}
Saving iCloud Documents
To add a UIDocument to iCloud you can call UIDocument.Save directly (for new documents only) or move an existing file using NSFileManager.DefaultManager.SetUbiquitious. The example code creates a new document directly in the ubiquity container with this code (there are two completion handlers here, one for the Save operation and another for the Open):
var docsFolder = Path.Combine (iCloudUrl.Path, "Documents"); // NOTE: Documents folder is user-accessible in Settings
var docPath = Path.Combine (docsFolder, MonkeyDocFilename);
var ubiq = new NSUrl (docPath, false);
var monkeyDoc = new MonkeyDocument (ubiq);
monkeyDoc.Save (monkeyDoc.FileUrl, UIDocumentSaveOperation.ForCreating, saveSuccess => {
Console.WriteLine ("Save completion:" + saveSuccess);
if (saveSuccess) {
monkeyDoc.Open (openSuccess => {
Console.WriteLine ("Open completion:" + openSuccess);
if (openSuccess) {
Console.WriteLine ("new document for iCloud");
Console.WriteLine (" == " + monkeyDoc.DocumentString);
viewController.DisplayDocument (monkeyDoc);
} else {
Console.WriteLine ("couldn't open");
}
});
} else {
Console.WriteLine ("couldn't save");
}
According to document ,when you save file to icloud,you need to use UIDocument .This will be Safe and stable.

Saving image file then uploading sometimes not working

So, I am trying to save an image file from the camera then upload it to firebase. Unfortunately, the firebase storage plugin has some errors and doesn't return errors in the catch so I need to check wifi connection before i do so, just in case i need to cache the image and upload it later. Once the image file has uploaded i then create some JSON and send that to firebase database where the app pulls down relevant information.
Note: sometimes this code works and the image is not empty, othertimes the image comesback empty so im guessing its a timing issue?
Future saveImageFile(UploadImage image) async {
await storage.init();
var imageFile = storage.saveImageFile(image.file, image.getName());
instance.setInt("ImageCount", imageCount + 1);
checkConnectionThenUploadImage(imageFile);
}
//From storage class
File saveImageFile(File toBeSaved, String fileName) {
final filePath = '$imageDirectory$fileName';
var file = new File(filePath)
..createSync(recursive: true)
..writeAsBytes(toBeSaved.readAsBytesSync());
return file;
}
checkConnectionThenUploadImage(File image) {
checkConnectivity().then((isConnected) async {
if (!isConnected) {
instance.setBool("hasImagesToUpload", true);
} else {
await saveImageToStorage(image);
}
}).catchError((error) {
print("Error getting connectivity status, was error: $error");
});
}
saveImageToStorage(File imageFile) async {
final fileName = getNameFromFile(imageFile);
final StorageReference ref = FirebaseStorage.instance.ref().child("AllUsers").child(uuid).child(fileName);
final StorageUploadTask uploadTask = ref.putFile(imageFile, const StorageMetadata(contentLanguage: "en"));
final url = (await uploadTask.future).downloadUrl;
if (url != null) { //Normally you could catch an error here but the plugin has a bug so it needs to be checked in other ways
final fireImage = new FireImage(getNameFromFile(imageFile), storage.getDateFromFileName(fileName), imageCount, "", url.toString());
saveImageJsonToDatabase(fireImage);
storage.deleteImageFile(fileName);
} else {
checkConnectionThenUploadImage(imageFile);
}
}
saveImageJsonToDatabase(FireImage image) async {
await storage.init();
storage.saveJsonFile(image);
checkConnectivity().then((isConnected) {
if (!isConnected) {
instance.setBool("hasJsonToUpload", true);
} else {
final DatabaseReference dataBaseReference = FirebaseDatabase.instance.reference().child("AllUsers").child(uuid);
dataBaseReference.child("images").push().set(image.toJson()).whenComplete (() {
storage.deleteJsonFile(basename(image.name));
}).catchError((error) { //catching errors works with firebase database
saveImageJsonToDatabase(image);
});
}
});
}
//From storage class
deleteImageFile(String fileName) async {
final filePath = '$imageDirectory$fileName';
File(filePath).delete();
}
The image gets uploaded and the json is created but when i try to view the image using the download url from firebase storage it says the image is empty. The only clue i have is that this is a timing issue because it only happens occasionally.
Can anyone see where im going wrong?

Xamarin iOS CGImageFromUrl returns null

I am using Xamarin iOS on windows Visual studio to extract metadata from a photo taken by the iphone camera.
private void GetMetaData(NSUrl url)
{
CGImageSource myImageSource;
myImageSource = CGImageSource.FromUrl(url, null);
var ns = new NSDictionary();
var imageProperties = myImageSource.CopyProperties(ns, 0);
var gps = imageProperties.ObjectForKey(CGImageProperties.GPSDictionary) as NSDictionary;
var lat = gps[CGImageProperties.GPSLatitude];
var latref = gps[CGImageProperties.GPSLatitudeRef];
var lon = gps[CGImageProperties.GPSLongitude];
var lonref = gps[CGImageProperties.GPSLongitudeRef];
var loc = String.Format("GPS: {0} {1}, {2} {3}", lat, latref, lon, lonref);
Console.WriteLine(loc);
}
the url being passed into the method is as:- {file:///var/mobile/Media/DCIM/100APPLE/IMG_0006.JPG}
The CGImageSource.FromUrl(url, null) returns null and my app crashes... Can anyone explain to me how I need to fix this?
Edit This is how I am getting the URL for the image.
protected void Handle_FinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e)
{
NSUrl url = null;
try
{
void ImageData(PHAsset asset)
{
if (asset == null) throw new Exception("PHAsset is null");
PHImageManager.DefaultManager.RequestImageData(asset, null, (data, dataUti, orientation, info) =>
{
//Console.WriteLine(data);
Console.WriteLine(info);
url = info.ValueForKey(new NSString("PHImageFileURLKey")) as NSUrl;
// Call method to get MetaData from Image Url //
GetMetaData(url);
});
}
As I said in your last post: Xamarin iOS camera and photos: If you capture photos from camera, the event will not return a ReferenceUrl. But you can get the metadata info with another key:
protected void Handle_FinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e)
{
var metadataInfo = e.Info["UIImagePickerControllerMediaMetadata"]; //or e.MediaMetadata;
}
This will contain some basic information which may be helpful to you. But this NSDictionary doesn't contain the GPS information. Because this picture is created by yourself, you should get the GPS by using CLLocationManager manually:
CLLocationManager manager;
manager = new CLLocationManager();
manager.RequestWhenInUseAuthorization();
manager.LocationsUpdated += (locationSender, e) =>
{
//get current locations
manager.StopUpdatingLocation();
};
manager.StartUpdatingLocation();
Please notice that add the keys in info.plist: NSLocationWhenInUseUsageDescription and NSLocationAlwaysAndWhenInUsageDescription on iOS11, if you want to deploy iOS10 add NSLocationAlwaysUsageDescription.
In this way, there's no need to get metadata from CGImageSource when you pick photos from camera.

AVAudioplayer initialize component throws a null exception

string mediafile;
mediafile=Constants.AudioLink;
NSUrl url1=new NSUrl(mediafile);
var audioplayer=AVAudioPlayer.FromUrl(url1);
URL is getting link correctly but audio player getting value null. It throws an exception below:
Could not initialize an instance of the type 'AVFoundation.AVAudioPlayer': the native 'initWithContentsOfURL:error:' method returned nil.
It is possible to ignore this condition by setting : MonoTouch.ObjCRuntime.Class.ThrowOnInitFailure to false
What can i do, Please give me the solution.
These is a really quick example of loading/playing an .mp3 file that has a build type of BundledResource (I dropped into the Resources folder of the project) and thus is placed in the app's root directory).
I do a quick check via AudioToolbox.AudioSource.Open to make sure the file exists, can be read, and is a valid media file type so I know that AVAudioPlayer will not throw a fatal error trying to load it.
AVAudioPlayer player; // Class level object ref
partial void playButtonTouch (UIButton sender)
{
if (player != null && player.Playing)
player.Stop ();
else {
var mp3File = "WildTurkeysEN-US.mp3";
var mp3URL = new NSUrl (mp3File);
Console.WriteLine (mp3URL.AbsoluteUrl);
var mp3 = AudioToolbox.AudioSource.Open (mp3URL, AudioFilePermission.Read, AudioFileType.MP3);
if (mp3 != null) {
Console.WriteLine (mp3.EstimatedDuration);
player = AVAudioPlayer.FromUrl (mp3URL);
player.Play ();
} else {
Console.WriteLine ( "File could not be loaded: {0}", mp3URL.FilePathUrl );
}
}
}

PCLStorage and binary data

I'm just new in this PCL libraries, I'm developing an iPhone app with Xamarin and I can't find the way to save it on the phone. The closest I get is with PCLStorage but he only saves text.
There is another way that I can save binary files with other procedure.
Thank you.
foreach (images element in json_object)
{
//var nameFile = Path.Combine (directoryname, element.name);
try{
IFile file = await folder_new.GetFileAsync(element.name);
}catch(FileNotFoundException ex ){
RestClient _Client = new RestClient(element.root);
RestRequest request_file = new RestRequest("/images/{FileName}");
request_file.AddParameter("FileName", element.name, ParameterType.UrlSegment);
_Client.ExecuteAsync<MemoryStream>(
request_file,
async Response =>
{
if (Response != null)
{
IFolder rootFolder_new = FileSystem.Current.LocalStorage;
IFile file_new = await rootFolder_new.CreateFileAsync(element.name,CreationCollisionOption.ReplaceExisting);
await file_new.WriteAllTextAsync(Response.Data);
}
});
}
}
Use the IFile.OpenAsync method to get a stream which you can use to read/write binary data. Here's how you would read a file:
IFile file = await folder_new.GetFileAsync(element.name);
using (Stream stream = await file.OpenAsync(FileAccess.Read))
{
// Read stream and process binary data from it...
}

Resources