How to share files to another app using Xamarin iOS? - ios

I am using the below code to share the files to other apps using xamarin iOS. but i didnt get any response. Is there any other option to share files to other apps using Xamarin iOS?
Ref: http://xamarinhelp.com/share-dialog-xamarin-forms/
public async Task Show(string title, string message, string filePath)
{
var items = new NSObject[] { NSObject.FromObject(title), NSUrl.FromFilename(filePath) };
var activityController = new UIActivityViewController(items, null);
var vc = GetVisibleViewController();
NSString[] excludedActivityTypes = null;
if (excludedActivityTypes != null && excludedActivityTypes.Length > 0)
activityController.ExcludedActivityTypes = excludedActivityTypes;
if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
if (activityController.PopoverPresentationController != null)
{
activityController.PopoverPresentationController.SourceView = vc.View;
}
}
await vc.PresentViewControllerAsync(activityController, true);
}
UIViewController GetVisibleViewController()
{
var rootController = UIApplication.SharedApplication.KeyWindow.RootViewController;
if (rootController.PresentedViewController == null)
return rootController;
if (rootController.PresentedViewController is UINavigationController)
{
return ((UINavigationController)rootController.PresentedViewController).TopViewController;
}
if (rootController.PresentedViewController is UITabBarController)
{
return ((UITabBarController)rootController.PresentedViewController).SelectedViewController;
}
return rootController.PresentedViewController;
}

Related

Xamarin Android - Custom Map Image Async

I'm following this example:
CustomMap
In the example the image is informed directly(On here):
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="#drawable/xamarin" />
I changed it to:
<ImageView
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center"
android:id="#+id/InfoWindowFoto"/>
and:
var infoFoto = view.FindViewById<ImageView>(Resource.Id.InfoWindowFoto);
if (infoFoto != null) GetImageBitmapFromUrl(infoFoto, customPin.local.Url_Imagem);
and:
private void GetImageBitmapFromUrl(ImageView img, string url)
{
using (var webClient = new WebClient())
{
var imageBytes = webClient.DownloadData(url);
if (imageBytes != null && imageBytes.Length > 0)
{
var bitmap = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length);
img.SetImageBitmap(bitmap);
}
}
}
if the image is synchronous, it works with delay :(
If I change to asynchronous the image is not show:
var infoFoto = view.FindViewById<ImageView>(Resource.Id.InfoWindowFoto);
if (infoFoto != null) GetImageBitmapFromUrl(infoFoto, customPin.local.Url_Imagem);
private async void GetImageBitmapFromUrl(ImageView img, string url)
{
using (var webClient = new WebClient())
{
var imageBytes = await webClient.DownloadDataTaskAsync(url);
if (imageBytes != null && imageBytes.Length > 0)
{
var bitmap = await BitmapFactory.DecodeByteArrayAsync(imageBytes, 0, imageBytes.Length);
img.SetImageBitmap(bitmap);
}
}
}
I don't know how to update a view to show an image after downloading it. I also tried Picasso with the same result:
var infoFoto = view.FindViewById<ImageView>(Resource.Id.InfoWindowFoto);
Picasso.Get().Load(customPin.local.Url_Imagem).Into(infoFoto);
Can you help me fix this please
EDIT:
also tried with FFImageloading:
if (infoFoto != null) ImageService.Instance.LoadUrl(customPin.local.Url_Imagem).Into(infoFoto);
When I click on the pin the image does not appear. But if I click off the pin and the view closes, and I click on the pin again for the view to open the image appears quickly!
I need to know if it is possible to update the view inside the map after the image is downloaded...
My sample: Sample Project
The info window that is drawn is not a live view. The view is rendered as an image (using View.draw(Canvas)) at the time it is returned. This means that any subsequent changes to the view will not be reflected by the info window on the map.
To update the info window later (for example, after an image has loaded), call showInfoWindow().
You could try the below codes:
private Bitmap bitmap;
public Android.Views.View GetInfoContents(Marker marker)
{
var inflater = Android.App.Application.Context.GetSystemService(Context.LayoutInflaterService) as Android.Views.LayoutInflater;
if (inflater != null)
{
Android.Views.View view;
var customPin = GetCustomPin(marker);
if (customPin == null)
{
throw new Exception("Local não localizado");
}
if (customPin.local.Name.Equals("Xamarin"))
{
view = inflater.Inflate(Resource.Layout.XamarinMapInfoWindow, null);
}
else
{
view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
}
var infoTitle = view.FindViewById<TextView>(Resource.Id.InfoWindowTitle);
if (infoTitle != null) infoTitle.Text = marker.Title;
var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle);
if (infoSubtitle != null) infoSubtitle.Text = marker.Snippet;
var infoNote = view.FindViewById<TextView>(Resource.Id.InfoNote);
if(infoNote != null) infoNote.Text = customPin.local.Note;
var infoFoto = view.FindViewById<ImageView>(Resource.Id.InfoWindowFoto);
if (infoFoto != null && bitmap == null)
{
GetImageBitmapFromUrlAsync(infoFoto, customPin.local.Url_Imagem, marker);
}
else
{
infoFoto.SetImageBitmap(bitmap);
}
return view;
}
return null;
}
private async void GetImageBitmapFromUrlAsync(ImageView img, string url, Marker marker)
{
using (var webClient = new WebClient())
{
var imageBytes = await webClient.DownloadDataTaskAsync(url);
if (imageBytes != null && imageBytes.Length > 0)
{
bitmap = await BitmapFactory.DecodeByteArrayAsync(imageBytes, 0, imageBytes.Length);
marker.ShowInfoWindow();
}
}
}
if your image_urls are not the same,you could use Dictionary to store the bitmap,use the url as the key,the bitmap as the value.

Xamarin iOS camera and photos

I am taking a picture with the iOS camera and trying to extract metadata from the image. This is my code:-
partial void BtnCamera_TouchUpInside(UIButton sender)
{
UIImagePickerController imagePicker = new UIImagePickerController();
imagePicker.PrefersStatusBarHidden();
imagePicker.SourceType = UIImagePickerControllerSourceType.Camera;
// handle saving picture and extracting meta-data from picture //
imagePicker.FinishedPickingMedia += Handle_FinishedPickingMedia;
// present //
PresentViewController(imagePicker, true, () => { });
}
protected void Handle_FinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e)
{
try
{
// determine what was selected, video or image
bool isImage = false;
switch (e.Info[UIImagePickerController.MediaType].ToString())
{
case "public.image":
isImage = true;
break;
}
// get common info
NSUrl referenceURL = e.Info[new NSString("UIImagePickerControllerReferenceURL")] as NSUrl;
if (referenceURL != null)
Console.WriteLine("Url:" + referenceURL.ToString());
I am able to initiate the camera, take the picture and then however when I click 'use photo'... The referenceURL comes back as NULL... How can I get the url, such that to extract GPS coordinates of the photo and such other attributes ?
I had a tremendous amount of trouble with the URL. It can be a file, it can be a web url and it acts different on every device. My app crashed and burned so many times with my test group. I finally found a way to get the Metadata from the data. There are multiple ways to get the DateTaken, Width and Height as well as GPS Coordinates. In addition, I needed the Camera MFG and the Model.
string dateTaken = string.Empty;
string lat = string.Empty;
string lon = string.Empty;
string width = string.Empty;
string height = string.Empty;
string mfg = string.Empty;
string model = string.Empty;
PHImageManager.DefaultManager.RequestImageData(asset, options, (data, dataUti, orientation, info) => {
dateTaken = asset.CreationDate.ToString();
// GPS Coordinates
var coord = asset.Location?.Coordinate;
if (coord != null)
{
lat = asset.Location?.Coordinate.Latitude.ToString();
lon = asset.Location?.Coordinate.Longitude.ToString();
}
UIImage img = UIImage.LoadFromData(data);
if (img.CGImage != null)
{
width = img.CGImage?.Width.ToString();
height = img.CGImage?.Height.ToString();
}
using (CGImageSource imageSource = CGImageSource.FromData(data, null))
{
if (imageSource != null)
{
var ns = new NSDictionary();
var imageProperties = imageSource.CopyProperties(ns, 0);
if (imageProperties != null)
{
width = ReturnStringIfNull(imageProperties[CGImageProperties.PixelWidth]);
height = ReturnStringIfNull(imageProperties[CGImageProperties.PixelHeight]);
var tiff = imageProperties.ObjectForKey(CGImageProperties.TIFFDictionary) as NSDictionary;
if (tiff != null)
{
mfg = ReturnStringIfNull(tiff[CGImageProperties.TIFFMake]);
model = ReturnStringIfNull(tiff[CGImageProperties.TIFFModel]);
//dateTaken = ReturnStringIfNull(tiff[CGImageProperties.TIFFDateTime]);
}
}
}
}
}
}
The little helper function
private string ReturnStringIfNull(NSObject inObj)
{
if (inObj == null) return String.Empty;
return inObj.ToString();
}
You can request a PHAsset from the reference Url and that will contain some metadata. You can request the image data to obtain more.
Note: If you need full EXIF, you need to check to ensure the image on on the device (could be iCloud-based), download it if needed, and then load the image data with the ImageIO framework (lots of SO postings cover this).
public void ImagePicker_FinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e)
{
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);
});
}
PHAsset phAsset;
if (e.ReferenceUrl == null)
{
e.OriginalImage?.SaveToPhotosAlbum((image, error) =>
{
if (error == null)
{
var options = new PHFetchOptions
{
FetchLimit = 1,
SortDescriptors = new[] { new NSSortDescriptor("creationDate", true) }
};
phAsset = PHAsset.FetchAssets(options).FirstOrDefault() as PHAsset;
ImageData(phAsset);
}
});
}
else
{
phAsset = PHAsset.FetchAssets(new[] { e.ReferenceUrl }, null).FirstOrDefault() as PHAsset;
ImageData(phAsset);
}
}
Note: Make sure you have request runtime photo library authorization PHPhotoLibrary.RequestAuthorization) and have set the Privacy - Photo Library Usage Description string in your info.plist to avoid a nasty privacy crash

UIImagePickerController crashesdue to memory issues in ios 10

I get random memory crashes with no memory warning. I have updated my ios to 10.0.2, my Xamarin to 6.2. I added camera usage description and photo library usage description, as suggested.
I have the following code in my FinishedPickingMedia callback
public async override void FinishedPickingMedia(UIImagePickerController picker, NSDictionary info)
{
try
{
// determine what was selected, video or image
bool isImage = false;
switch (info[UIImagePickerController.MediaType].ToString())
{
case "public.image":
isImage = true;
break;
case "public.video":
break;
}
if (isImage)
{
UIImage originalImage = info[UIImagePickerController.OriginalImage] as UIImage;
if (originalImage != null)
{
UIImageOrientation OrIn = originalImage.Orientation;
Debug.WriteLine("scaling image");
var originalImage1 = await Task.Run(() => ScaleAndRotateImage.ScaleAndRotateImageView(originalImage, OrIn));
if (originalImage1 != null)
{
var Data = originalImage1.AsJPEG(0.0f);
if (Data != null)
{
UIImage resizedImage = UIImage.LoadFromData(Data);
if (originalImage != null)
{
originalImage.Dispose();
originalImage = null;
}
originalImage1.Dispose();
originalImage1 = null;
Data.Dispose();
Data = null;
GC.Collect();
#if DEBUG
var Process1 = Process.GetCurrentProcess();
if (Process1 != null)
{
Debug.WriteLine(string.Format("memory allocated by FinishedPickingMedia = {0}", Process1.WorkingSet64));
}
#endif
camController.ShowPhoto(resizedImage);
}
if (info != null)
{
info.Dispose();
info = null;
}
GC.Collect();
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine("Exception Occured in FinishedPickingMedia method due to " + ex.Message);
}
finally
{
// dismiss the picker
picker.DismissModalViewController(true);
picker.Dispose();
picker = null;
GC.Collect();
}
}
This seams to happen randomly, my application needs to capture more than 200 photos (archive) I researched on the internet and this seams to be an issue with iOS 10 and this control

Showing "Import with Instagram" in UIActivityViewController

I am trying to add Instagram in "Share To" functionality in my app. I have seen the Instagram's iPhone hooks documents. I have created custom UIActivty which works fine but my question is, is there a way to add "Import with Instagram" functionality as it can be seen in iOS's Photos app iOS Photo App:
In my app for some reason, it does not show that "Import with Instagram". my app Share view :
I do not want to share only with Instagram so no ".igo"
EDIT: All of this is specifically for iOS versions < 10. For some reasons Instagram Share Extension works fine (for my app) in devices with iOS >= 10.
EDIT: I am trying to share image and video with ".jpeg" and ".mov" formats respectively
I have seen/read that Instagram added share extension in release 8.2, so technically all the apps should show "Instagram" in share tray, i.e. it can be seen in Google Photos app.
public void NativeShareImage(UIView sourceView, CGRect sourceRect,
UIImage image, string shareCaption, string emailSubject)
{
string filename = Path.Combine(FileSystemUtils.GetTemporaryDataPath(), "Image.jpg");
NSError err = null;
using(var imgData = image.AsJPEG(JpgImageQuality))
{
if(imgData.Save(filename, false, out err))
{
Logger.Information("Image saved before native share as {FileName}", filename);
}
else
{
Logger.Error("Image NOT saved before native share as to path {FileName}. {Error}", filename, err.Description);
return;
}
}
// this are the items that needs to be shared
// Instagram ignores the caption, that is known
var activityItems = new List<NSObject>
{
new NSString(shareCaption),
new NSUrl(new Uri(filename).AbsoluteUri)
};
// Here i add the custom UIActivity for Instagram
UIActivity[] applicationActivities =
{
new InstagramActivity(image, sourceRect, sourceView),
}
var activityViewController = new UIActivityViewController(activityItems.ToArray(), applicationActivities);
activityViewController.SetValueForKey(new NSString(emailSubject), new NSString("subject"));
activityViewController.CompletionWithItemsHandler = (activityType, completed, returnedItems, error) =>
{
UserSharedTo(activityType, completed);
};
// Hide some of the less used activity types so that Instagram shows up in the list. Otherwise it's pushed off the activity view
// and the user has to scroll to see it.
activityViewController.ExcludedActivityTypes = new[] { UIActivityType.AssignToContact, UIActivityType.CopyToPasteboard, UIActivityType.Print };
if(UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone)
{
PresentViewController(activityViewController, true, null);
}
else
{
activityViewController.ModalPresentationStyle = UIModalPresentationStyle.Popover;
PresentViewController(activityViewController, true, null);
// Get the popover presentation controller and configure it.
UIPopoverPresentationController presentationController = activityViewController.PopoverPresentationController;
presentationController.PermittedArrowDirections = UIPopoverArrowDirection.Down;
presentationController.SourceRect = sourceRect;
presentationController.SourceView = sourceView;
}
}
// when opening custom activity use ".igo" to only show instagram
public class InstagramActivity : UIActivity
{
public InstagramActivity(UIImage imageToShare, CGRect frame, UIView view, string shareCaption = "")
{
_ImageToShare = imageToShare;
_Frame = frame;
_View = view;
}
public override UIImage Image { get { return UIImage.FromBundle("Instagram"); } }
public override string Title { get { return "Instagram"; } }
public override NSString Type { get { return new NSString("PostToInstagram"); } }
public string Caption { get; set; }
public override bool CanPerform(NSObject[] activityItems)
{
return UIApplication.SharedApplication.CanOpenUrl(NSUrl.FromString("instagram://app"));
}
public override void Prepare(NSObject[] activityItems)
{
}
public override void Perform()
{
string filename = Path.Combine(FileSystemUtils.GetTemporaryDataPath(), "Image.igo");
NSError err = null;
using(var imgData = _ImageToShare.AsJPEG(JpgImageQuality))
{
if(imgData.Save(filename, false, out err))
{
Logger.Information("Instagram image saved as {FileName}", filename);
}
else
{
Logger.Error("Instagram image NOT saved as to path {FileName}. {Error}", filename, err.Description);
Finished(false);
return;
}
}
var url = NSUrl.FromFilename(filename);
_DocumentController = UIDocumentInteractionController.FromUrl(url);
_DocumentController.DidEndSendingToApplication += (o, e) => Finished(true);
_DocumentController.Uti = "com.instagram.exclusivegram";
if(!string.IsNullOrEmpty(ShareCaption))
{
_DocumentController.Annotation = NSDictionary.FromObjectAndKey(new NSString(ShareCaption), new NSString("InstagramCaption"));
}
_DocumentController.PresentOpenInMenu(_Frame, _View, true);
}
UIImage _ImageToShare;
CGRect _Frame;
UIView _View;
UIDocumentInteractionController _DocumentController;
}

Xamarin IOS: Change tint color in share window

I am using this code for sharing in Xamarin IOS:
public void Share(string title, string content)
{
if (UIApplication.SharedApplication.KeyWindow == null || UIApplication.SharedApplication.KeyWindow.RootViewController == null)
return;
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(content))
return;
var rootController = UIApplication.SharedApplication.KeyWindow.RootViewController;
var imageToShare = UIImage.FromBundle("icon_120.png");
var itemsToShare = new NSObject[] { new NSString(content), imageToShare };
var shareStatusController = new UIActivityViewController(itemsToShare, null)
{
Title = title
};
//shareStatusController.NavigationController.NavigationBar.TintColor = UIColor.Black;
//rootController.NavigationController.NavigationBar.TintColor = UIColor.Black;
//shareStatusController.NavigationBar.TintColor = UIColor.FromRGB(0, 122, 255);
rootController.PresentViewController(shareStatusController, true, null);
Mvx.Resolve<IAnalyticsService>().LogEvent("Share button clicked.");
}
When i choose mail and i am redirected here http://take.ms/3KE4F. But color of cancel and send buttons is white (in NavigationBar). How can i change color of text of this button?
I found article
Cannot set text color of Send and Cancel buttons in the mail composer when presented from the UIActivityViewController in iOS7. But dont know howto apply this using Xamarin.
Thanks for any help.
Try this out:
public void Share(string title, string content)
{
if (UIApplication.SharedApplication.KeyWindow == null || UIApplication.SharedApplication.KeyWindow.RootViewController == null)
return;
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(content))
return;
//Share Barbutton tint
UIBarButtonItem.AppearanceWhenContainedIn (new [] {typeof(UINavigationBar)}).TintColor = UIColor.Cyan;
var rootController = UIApplication.SharedApplication.KeyWindow.RootViewController;
var imageToShare = UIImage.FromBundle("icon_120.png");
var itemsToShare = new NSObject[] { new NSString(content), imageToShare };
var shareStatusController = new UIActivityViewController(itemsToShare, null)
{
Title = title
};
shareStatusController.CompletionHandler += (NSString arg1, bool arg2) => {
// Set it back to old theme theme
UIBarButtonItem.AppearanceWhenContainedIn (new [] {typeof(UINavigationBar)}).TintColor = UIColor.White;
};
rootController.PresentViewController(shareStatusController, true, null);
Mvx.Resolve<IAnalyticsService>().LogEvent("Share button clicked.");
}
You basically set the the button tint before you present the controller then set it back in completion handler.

Resources