I'm trying to fetch my location using GeoClue and GWeather.
I want to display the name of my city in the blank space.
public class Epoch.LabelsGrid : Gtk.Grid {
public Gtk.Label face1_label;
public Gtk.Label face2_label;
public Gtk.Label face3_label;
public Gtk.Label face4_label;
public GClue.Location? geo_location {get; private set; default = null;}
public GWeather.Location location;
private GClue.Simple simple;
construct {
face1_label = new Gtk.Label ("");
face1_label.label = dgettext ("libgweather-locations", location.get_city_name ());
face1_label.halign = Gtk.Align.CENTER;
face1_label.hexpand = true;
face1_label.margin_top = 6;
face1_label.set_ellipsize (END);
face1_label.set_max_width_chars (12);
face2_label = new Gtk.Label ("Paris");
face2_label.set_markup ("<span font_desc='Inter 14'><b>Paris</b></span>");
face2_label.halign = Gtk.Align.CENTER;
face2_label.hexpand = true;
face2_label.margin_top = 6;
face2_label.set_ellipsize (END);
face2_label.set_max_width_chars (12);
face3_label = new Gtk.Label ("London");
face3_label.set_markup ("<span font_desc='Inter 14'><b>London</b></span>");
face3_label.halign = Gtk.Align.CENTER;
face3_label.hexpand = true;
face3_label.margin_top = 6;
face3_label.set_ellipsize (END);
face3_label.set_max_width_chars (12);
face4_label = new Gtk.Label ("New York");
face4_label.set_markup ("<span font_desc='Inter 14'><b>New York</b></span>");
face4_label.halign = Gtk.Align.CENTER;
face4_label.hexpand = true;
face4_label.margin_top = 6;
face4_label.set_ellipsize (END);
face4_label.set_max_width_chars (12);
}
public async void seek () {
try {
simple = yield new GClue.Simple ("com.gihhub.Suzie97.epoch", GClue.AccuracyLevel.CITY, null);
} catch (Error e) {
warning ("Failed to connect to GeoClue2 service: %s", e.message);
return;
}
simple.notify["location"].connect (() => {
on_location_updated.begin ();
});
on_location_updated.begin ();
}
public async void on_location_updated () {
geo_location = simple.get_location ();
location = location.find_nearest_city (geo_location.latitude, geo_location.longitude);
}
}
This is the code.
The code compiles but while executing the program, (com.github.Suzie97.epoch:30386): GWeather-CRITICAL **: 13:21:03.758: gweather_location_get_city_name: assertion 'loc != NULL' failed this warning is displayed.
I found this code snippet on an app by Daniel Fore called Nimbus (Link to nimbus repo)
public void on_location_updated (double latitude, double longitude) {
location = GWeather.Location.get_world ();
location = location.find_nearest_city (latitude, longitude);
if (location != null) {
weather_info.location = location;
weather_info.update ();
stack.visible_child_name = "weather";
}
}
Do I need to do something similar to this?
According to the error message, gweather_location_get_city_name () is emitting an error because the location is NULL (loc != NULL is false). Look at where you're calling get_city_name (). It's at the very top of your constructor, before location is set to anything. You need to wait until you have a location before you try to use it.
Then you should use something like the Nimbus code. It handles the case when simple.get_location () returns NULL (if it can't find your location), and also initializes the GWeather location so it can call find_nearest_city ().
Related
I am trying to implement below java code in c# referring to Android documentation
List<String> skuList = new ArrayList<> ();
skuList.add("premium_upgrade");
skuList.add("gas");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(SkuType.INAPP);
billingClient.querySkuDetailsAsync(params.build(),
new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(BillingResult billingResult,
List<SkuDetails> skuDetailsList) {
// Process the result.
}
});
I have here 2 questions. I thought that i would run this code on a separate thread than UI thread like below to keep my ui responsive while network connection is done. is that the correct approach? QuerySkuDetailsAsync is called async but doesnt implement as async. how should this be working and how to handle in c# because it will fire and forget but Listener to handle the response.
public async Task<List<InAppBillingProduct>> GetProductsAsync(List<string> ProductIds)
{
var getSkuDetailsTask = Task.Factory.StartNew(() =>
{
var prms = SkuDetailsParams.NewBuilder();
var type = BillingClient.SkuType.Inapp;
prms.SetSkusList(ProductIds).SetType(type);
BillingClient.QuerySkuDetailsAsync(prms.Build(), new SkuDetailsResponseListener());
return InAppBillingProducts;
});
return await getSkuDetailsTask;
}
2nd question regarding how to handle with the listener as below. How do I return value from the listener. I need return list of InAppBillingProduct object.
public class SkuDetailsResponseListener : Java.Lang.Object, ISkuDetailsResponseListener
{
public void OnSkuDetailsResponse(BillingResult billingResult, IList<SkuDetails> skus)
{
if (billingResult.ResponseCode == BillingResponseCode.Ok)
{
// get list of Products here and return
}
}
}
FYI. This is how I did it. This is not a complete code but this will give you and idea.
Listener - PCL
============
private async Task EventClicked()
{
var skuList = new List<string>();
skuList.Add("[nameofsubscriptionfoundinyourgoogleplay]");
if (await _billingClientLifecycle.Initialize(skuList, DisconnectedConnection))
{
var firstProduct = _billingClientLifecycle?.ProductsInStore?.FirstOrDefault();
if (firstProduct != null)
{
//purchase here
}
}
}
private void DisconnectedConnection()
{
//Todo.alfon. handle disconnection here...
}
Interface - PCL
===========
public interface IInAppBillingMigratedNew
{
List<InAppBillingPurchase> PurchasedProducts { get; set; }
List<InAppBillingProduct> ProductsInStore { get; set; }
Task<bool> Initialize(List<String> skuList, Action onDisconnected = null);
}
Dependency - Platform Droid
===============
[assembly: XF.Dependency(typeof(InAppBillingMigratedNew))]
public class InAppBillingMigratedNew : Java.Lang.Object, IBillingClientStateListener
, ISkuDetailsResponseListener, IInAppBillingMigratedNew
{
private Activity Context => CrossCurrentActivity.Current.Activity
?? throw new NullReferenceException("Current Context/Activity is null");
private BillingClient _billingClient;
private List<string> _skuList = new List<string>();
private TaskCompletionSource<bool> _tcsInitialized;
private Action _disconnectedAction;
private Dictionary<string, SkuDetails> _skusWithSkuDetails = new Dictionary<string, SkuDetails>();
public List<InAppBillingPurchase> PurchasedProducts { get; set; }
public List<InAppBillingProduct> ProductsInStore { get; set; }
public IntPtr Handle => throw new NotImplementedException();
public Task<bool> Initialize(List<string> skuList, Action disconnectedAction = null)
{
_disconnectedAction = disconnectedAction;
_tcsInitialized = new TaskCompletionSource<bool>();
var taskInit = _tcsInitialized.Task;
_skuList = skuList;
_billingClient = BillingClient.NewBuilder(Context)
.SetListener(this)
.EnablePendingPurchases()
.Build();
if (!_billingClient.IsReady)
{
_billingClient.StartConnection(this);
}
return taskInit;
}
#region IBillingClientStateListener
public void OnBillingServiceDisconnected()
{
Console.WriteLine($"Connection disconnected.");
_tcsInitialized?.TrySetResult(false);
_disconnectedAction?.Invoke();
}
public void OnBillingSetupFinished(BillingResult billingResult)
{
var responseCode = billingResult.ResponseCode;
var debugMessage = billingResult.DebugMessage;
if (responseCode == BillingResponseCode.Ok)
{
QuerySkuDetails();
QueryPurchases();
_tcsInitialized?.TrySetResult(true);
}
else
{
Console.WriteLine($"Failed connection {debugMessage}");
_tcsInitialized?.TrySetResult(false);
}
}
#endregion
#region ISkuDetailsResponseListener
public void OnSkuDetailsResponse(BillingResult billingResult, IList<SkuDetails> skuDetailsList)
{
if (billingResult == null)
{
Console.WriteLine("onSkuDetailsResponse: null BillingResult");
return;
}
var responseCode = billingResult.ResponseCode;
var debugMessage = billingResult.DebugMessage;
switch (responseCode)
{
case BillingResponseCode.Ok:
if (skuDetailsList == null)
{
_skusWithSkuDetails.Clear();
}
else
{
if (skuDetailsList.Count > 0)
{
ProductsInStore = new List<InAppBillingProduct>();
}
foreach (var skuDetails in skuDetailsList)
{
_skusWithSkuDetails.Add(skuDetails.Sku, skuDetails);
//ToDo.alfon. make use mapper here
ProductsInStore.Add(new InAppBillingProduct
{
Name = skuDetails.Title,
Description = skuDetails.Description,
ProductId = skuDetails.Sku,
CurrencyCode = skuDetails.PriceCurrencyCode,
LocalizedIntroductoryPrice = skuDetails.IntroductoryPrice,
LocalizedPrice = skuDetails.Price,
MicrosIntroductoryPrice = skuDetails.IntroductoryPriceAmountMicros,
MicrosPrice = skuDetails.PriceAmountMicros
});
}
}
break;
case BillingResponseCode.ServiceDisconnected:
case BillingResponseCode.ServiceUnavailable:
case BillingResponseCode.BillingUnavailable:
case BillingResponseCode.ItemUnavailable:
case BillingResponseCode.DeveloperError:
case BillingResponseCode.Error:
Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
break;
case BillingResponseCode.UserCancelled:
Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
break;
// These response codes are not expected.
case BillingResponseCode.FeatureNotSupported:
case BillingResponseCode.ItemAlreadyOwned:
case BillingResponseCode.ItemNotOwned:
default:
Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
break;
}
}
#endregion
#region Helper Methods Private
private void ProcessPurchases(List<Purchase> purchasesList)
{
if (purchasesList == null)
{
Console.WriteLine("No purchases done.");
return;
}
if (IsUnchangedPurchaseList(purchasesList))
{
Console.WriteLine("Purchases has not changed.");
return;
}
_purchases.AddRange(purchasesList);
PurchasedProducts = _purchases.Select(sku => new InAppBillingPurchase
{
PurchaseToken = sku.PurchaseToken
})?.ToList();
if (purchasesList != null)
{
LogAcknowledgementStatus(purchasesList);
}
}
private bool IsUnchangedPurchaseList(List<Purchase> purchasesList)
{
// TODO: Optimize to avoid updates with identical data.
return false;
}
private void LogAcknowledgementStatus(List<Purchase> purchasesList)
{
int ack_yes = 0;
int ack_no = 0;
foreach (var purchase in purchasesList)
{
if (purchase.IsAcknowledged)
{
ack_yes++;
}
else
{
ack_no++;
}
}
//Log.d(TAG, "logAcknowledgementStatus: acknowledged=" + ack_yes +
// " unacknowledged=" + ack_no);
}
private void QuerySkuDetails()
{
var parameters = SkuDetailsParams
.NewBuilder()
.SetType(BillingClient.SkuType.Subs)
.SetSkusList(_skuList)
.Build();
_billingClient.QuerySkuDetailsAsync(parameters, this);
}
private void QueryPurchases()
{
if (!_billingClient.IsReady)
{
Console.WriteLine("queryPurchases: BillingClient is not ready");
}
var result = _billingClient.QueryPurchases(BillingClient.SkuType.Subs);
ProcessPurchases(result?.PurchasesList?.ToList());
}
#endregion
}
I have an forms app where i need to pick "1 to many" images from the phone storage.
For this i use the dependency injection system.
My problem is the somewhere i get an Android.netUri that resolves to a file that do not exist... and to a file name that i have never seen before.
The kicker is that if i pick pictures that was takes within the last couple of hours this code works...
Im am at the end of my hoap, i really hope someone can point me to something that i'm doing wrong.
i start the Picker activity with:
[assembly: Dependency(typeof(ImagePickerService))]
namespace MyApp.Droid
{
public class ImagePickerService : Java.Lang.Object, IImagePickerService
{
public async Task OpenGallery()
{
try
{
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Storage);
if (status != PermissionStatus.Granted)
{
if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.Storage))
{
Toast.MakeText(CrossCurrentActivity.Current.Activity, "Need Storage permission to access to your photos.", ToastLength.Long).Show();
}
var results = await CrossPermissions.Current.RequestPermissionsAsync(new[] { Permission.Storage });
status = results[Permission.Storage];
}
if (status == PermissionStatus.Granted)
{
Toast.MakeText(CrossCurrentActivity.Current.Activity, "Pick max 20 images", ToastLength.Long).Show();
var imageIntent = new Intent(Intent.ActionPick);
imageIntent.SetType("image/*");
imageIntent.PutExtra(Intent.ExtraAllowMultiple, true);
imageIntent.SetAction(Intent.ActionPick);
CrossCurrentActivity.Current.Activity.StartActivityForResult(Intent.CreateChooser(imageIntent, "Pick pictures"), 100);
}
else if (status != PermissionStatus.Unknown)
{
Toast.MakeText(CrossCurrentActivity.Current.Activity, "Permission Denied. Can not continue, try again.", ToastLength.Long).Show();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Toast.MakeText(CrossCurrentActivity.Current.Activity, "Error. Can not continue, try again.", ToastLength.Long).Show();
}
}
}
then in my MainActivity.cs i have the OnActivityResult
I have tried to use the ContentResolver.OpenInputStream to get the image bytes with no luck, so this is commented out atm.
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == OPENGALLERYCODE && resultCode == Result.Ok)
{
List<string> images = new List<string>();
if (data != null)
{
ClipData clipData = data.ClipData;
if (clipData != null)
{
for (int i = 0; i < clipData.ItemCount; i++)
{
ClipData.Item item = clipData.GetItemAt(i);
/*
var stream = ContentResolver.OpenInputStream(item.Uri); //This throws "FileNotFound"
byte[] byteArray;
using (var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
byteArray = memoryStream.ToArray();
stream.Close();
stream = null;
}
stream = ContentResolver.OpenInputStream(item.Uri);
var exif = new ExifInterface(stream);
stream.Close();
*/
Android.Net.Uri uri = item.Uri;
var path = GetActualPathFromFile(uri);
if (path != null)
{
var tmpImgPath = RotateToOriginalDimention(path);
images.Add(tmpImgPath);
}
}
}
else
{
Android.Net.Uri uri = data.Data;
var path = GetActualPathFromFile(uri);
if (path != null)
{
var tmpImgPath = RotateToOriginalDimention(path);
images.Add(tmpImgPath);
}
}
MessagingCenter.Send<App, List<string>>((App)Xamarin.Forms.Application.Current, "ImagesSelected", images);
}
}
}
And the GetActualPathFromFile (also in my MainActivity.cs)
The hole func is below but i hit this part of the code and get at "FileNotFound"
(...)
else if ("content".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase))
{
var retval2 = getDataColumn(this, uri, null, null);
if (File.Exists(retval2)) //<----------------------- This returns "false"
{
return retval2;
}
else
{
throw new Exception("file not found " + retval2);
}
}
(...)
The Hole GetActualPathFromFile
private string GetActualPathFromFile(Android.Net.Uri uri)
{
bool isKitKat = Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Kitkat;
if (isKitKat && DocumentsContract.IsDocumentUri(this, uri))
{
// ExternalStorageProvider
if (isExternalStorageDocument(uri))
{
string docId = DocumentsContract.GetDocumentId(uri);
char[] chars = { ':' };
string[] split = docId.Split(chars);
string type = split[0];
if ("primary".Equals(type, StringComparison.OrdinalIgnoreCase))
{
var retval = Android.OS.Environment.ExternalStorageDirectory + "/" + split[1];
if (File.Exists(retval))
{
return retval;
}
else
{
throw new Exception("file not found " + retval);
}
}
}
// DownloadsProvider
else if (isDownloadsDocument(uri))
{
string id = DocumentsContract.GetDocumentId(uri);
Android.Net.Uri contentUri = ContentUris.WithAppendedId(
Android.Net.Uri.Parse("content://downloads/public_downloads"), long.Parse(id));
//System.Diagnostics.Debug.WriteLine(contentUri.ToString());
var retval = getDataColumn(this, contentUri, null, null);
if (File.Exists(retval))
{
return retval;
}
else
{
throw new Exception("file not found " + retval);
}
}
// MediaProvider
else if (isMediaDocument(uri))
{
String docId = DocumentsContract.GetDocumentId(uri);
char[] chars = { ':' };
String[] split = docId.Split(chars);
String type = split[0];
Android.Net.Uri contentUri = null;
if ("image".Equals(type))
{
contentUri = MediaStore.Images.Media.ExternalContentUri;
}
else if ("video".Equals(type))
{
contentUri = MediaStore.Video.Media.ExternalContentUri;
}
else if ("audio".Equals(type))
{
contentUri = MediaStore.Audio.Media.ExternalContentUri;
}
String selection = "_id=?";
String[] selectionArgs = new String[]
{
split[1]
};
var retval = getDataColumn(this, contentUri, selection, selectionArgs);
if (File.Exists(retval))
{
return retval;
}
else
{
throw new Exception("file not found " + retval);
}
}
}
// MediaStore (and general)
else if ("content".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase))
{
// Return the remote address
if (isGooglePhotosUri(uri))
{
var retval = uri.LastPathSegment;
if (File.Exists(retval))
{
return retval;
}
else
{
throw new Exception("file not found " + retval);
}
}
var retval2 = getDataColumn(this, uri, null, null);
if (File.Exists(retval2))
{
return retval2;
}
else
{
throw new Exception("file not found " + retval2);
}
}
// File
else if ("file".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase))
{
var retval = uri.Path;
if (File.Exists(retval))
{
return retval;
}
else
{
throw new Exception("file not found " + retval);
}
}
throw new Exception("file not found ");
}
public static String getDataColumn(Context context, Android.Net.Uri uri, String selection, String[] selectionArgs)
{
ICursor cursor = null;
String column = "_data";
String[] projection =
{
column
};
try
{
cursor = context.ContentResolver.Query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.MoveToFirst())
{
int index = cursor.GetColumnIndexOrThrow(column);
return cursor.GetString(index);
}
}
finally
{
if (cursor != null)
cursor.Close();
}
return null;
}
//Whether the Uri authority is ExternalStorageProvider.
public static bool isExternalStorageDocument(Android.Net.Uri uri)
{
return "com.android.externalstorage.documents".Equals(uri.Authority);
}
//Whether the Uri authority is DownloadsProvider.
public static bool isDownloadsDocument(Android.Net.Uri uri)
{
return "com.android.providers.downloads.documents".Equals(uri.Authority);
}
//Whether the Uri authority is MediaProvider.
public static bool isMediaDocument(Android.Net.Uri uri)
{
return "com.android.providers.media.documents".Equals(uri.Authority);
}
//Whether the Uri authority is Google Photos.
public static bool isGooglePhotosUri(Android.Net.Uri uri)
{
return "com.google.android.apps.photos.content".Equals(uri.Authority);
}
Found out that the real problem was that Google Photos App was not updating and was still showing images that were deleted.
After 2x reboot of the phone, Google Photos app finally updated.
So this looks more like a cache problem with Google Foto than a xamarin problem.
I'm using Xamarin forms and writing a dependency service for the following objectives :
Open iOS files app. (UIDocumentPickerViewController )
Select any kind of a document.
Copy that document into my application Documents directory. (For app access)
Show that document into my application by storing its path into my SQLite DB.
What I am trying to do here is call the Files app from my application on an Entry click and the click event seems to be working well my dependency service calls perfectly but now when I try to use the UIDocumentPickerViewController I am unable to get View controller context in my dependency service to call the PresentViewController method. Now I know about the xamarin forms context but I don't know if it will work here and I don't even know if it would be a smart idea to use it as it has already been marked as obsolete and since I am not from the iOS background, I don't know what would be the right solution for it.
My code is as follows :
public class DocumentPickerRenderer : IDocumentPicker
{
public object PickFile()
{
var docPicker = new UIDocumentPickerViewController(new string[] { UTType.Data, UTType.Content }, UIDocumentPickerMode.Import);
docPicker.WasCancelled += (sender, wasCancelledArgs) =>
{
};
docPicker.DidPickDocumentAtUrls += (object sender, UIDocumentPickedAtUrlsEventArgs e) =>
{
Console.WriteLine("url = {0}", e.Urls[0].AbsoluteString);
//bool success = await MoveFileToApp(didPickDocArgs.Url);
var success = true;
string filename = e.Urls[0].LastPathComponent;
string msg = success ? string.Format("Successfully imported file '{0}'", filename) : string.Format("Failed to import file '{0}'", filename);
var alertController = UIAlertController.Create("import", msg, UIAlertControllerStyle.Alert);
var okButton = UIAlertAction.Create("OK", UIAlertActionStyle.Default, (obj) =>
{
alertController.DismissViewController(true, null);
});
alertController.AddAction(okButton);
PresentViewController(alertController, true, null);
};
PresentViewController(docPicker, true, null);
}
}
My questions:
Is my methodology correct for picking files?
what will be the object that I will be getting as a callback from a file selection and how will I get the callback?
Is there any other way or something available for xamarin forms, some guide or something that allows me to pick documents from my native file systems and gives a brief on how to handle it in both ios and android?
Hello Guys, You can use following code for picking any type of documents to mention in code using iOS Devices-
use follwing interface:
public interface IMedia
{
Task<string> OpenDocument();
}
public Task<string> OpenDocument()
{
var task = new TaskCompletionSource<string>();
try
{
OpenDoc(GetController(), (obj) =>
{
if (obj == null)
{
task.SetResult(null);
return;
}
var aa = obj.AbsoluteUrl;
task.SetResult(aa.Path);
});
}
catch (Exception ex)
{
task.SetException(ex);
}
return task.Task;
}
static Action<NSUrl> _callbackDoc;
public static void OpenDoc(UIViewController parent, Action<NSUrl> callback)
{
_callbackDoc = callback;
var version = UIDevice.CurrentDevice.SystemVersion;
int verNum = 0;
Int32.TryParse(version.Substring(0, 2), out verNum);
var allowedUTIs = new string[]
{
UTType.UTF8PlainText,
UTType.PlainText,
UTType.RTF,
UTType.PNG,
UTType.Text,
UTType.PDF,
UTType.Image,
UTType.Spreadsheet,
"com.microsoft.word.doc",
"org.openxmlformats.wordprocessingml.document",
"com.microsoft.powerpoint.ppt",
"org.openxmlformats.spreadsheetml.sheet",
"org.openxmlformats.presentationml.presentation",
"com.microsoft.excel.xls",
};
// Display the picker
var pickerMenu = new UIDocumentMenuViewController(allowedUTIs, UIDocumentPickerMode.Import);
pickerMenu.DidPickDocumentPicker += (sender, args) =>
{
if (verNum < 11)
{
args.DocumentPicker.DidPickDocument += (sndr, pArgs) =>
{
UIApplication.SharedApplication.OpenUrl(pArgs.Url);
pArgs.Url.StopAccessingSecurityScopedResource();
var cb = _callbackDoc;
_callbackDoc = null;
pickerMenu.DismissModalViewController(true);
cb(pArgs.Url.AbsoluteUrl);
};
}
else
{
args.DocumentPicker.DidPickDocumentAtUrls += (sndr, pArgs) =>
{
UIApplication.SharedApplication.OpenUrl(pArgs.Urls[0]);
pArgs.Urls[0].StopAccessingSecurityScopedResource();
var cb = _callbackDoc;
_callbackDoc = null;
pickerMenu.DismissModalViewController(true);
cb(pArgs.Urls[0].AbsoluteUrl);
};
}
// Display the document picker
parent.PresentViewController(args.DocumentPicker, true, null);
};
pickerMenu.ModalPresentationStyle = UIModalPresentationStyle.Popover;
parent.PresentViewController(pickerMenu, true, null);
UIPopoverPresentationController presentationPopover = pickerMenu.PopoverPresentationController;
if (presentationPopover != null)
{
presentationPopover.SourceView = parent.View;
presentationPopover.PermittedArrowDirections = UIPopoverArrowDirection.Down;
}
}
Now you need to call using following code:
var filePath = await DependencyService.Get<IMedia>().OpenDocument();
For pick document in Android, you can use following code
public class IntentHelper
{
public const int DocPicker = 101;
static Action<string> _callback;
public static async void ActivityResult(int requestCode, Result resultCode, Intent data)
{ if (requestCode == RequestCodes.DocPicker)
{
if (data.Data == null)
{
_callback(null);
}
else
{
var destFilePath = FilePath.GetPath(CurrentActivity, data.Data);
_callback(destFilePath);
}
}
}
public static Activity CurrentActivity
{
get
{
return (Xamarin.Forms.Forms.Context as MainActivity);
}
}
public static void OpenDocPicker(Action<string> callback)
{
_callback = callback;
var intent = new Intent(Intent.ActionOpenDocument);
intent.AddCategory(Intent.CategoryOpenable);
intent.SetType("*/*");
CurrentActivity.StartActivityForResult(intent, RequestCodes.DocPicker);
}
}
For pick document in Android, you can use following code:
public class IntentHelper
{
public const int DocPicker = 101;
static Action<string> _callback;
public static async void ActivityResult(int requestCode, Result resultCode, Intent data)
{
if (requestCode == RequestCodes.DocPicker)
{
if (data.Data == null)
{
_callback(null);
}
else
{
var destFilePath = FilePath.GetPath(CurrentActivity, data.Data);
_callback(destFilePath);
}
}
}
public static Activity CurrentActivity
{
get
{
return (Xamarin.Forms.Forms.Context as MainActivity);
}
}
public static void OpenDocPicker(Action<string> callback)
{
_callback = callback;
var intent = new Intent(Intent.ActionOpenDocument);
intent.AddCategory(Intent.CategoryOpenable);
intent.SetType("*/*");
CurrentActivity.StartActivityForResult(intent, RequestCodes.DocPicker);
}
}
Use below code to access the helper class: public class Media:
IMedia {
public Task<string> OpenDocument() {
var task = new TaskCompletionSource<string>();
try {
IntentHelper.OpenDocPicker((path) => { task.SetResult(path); });
} catch (Exception ex) {
task.SetResult(null);
}
return task.Task;
}
}
Since I was looking for UIDocumentPickerViewController and not UIDocumentMenuViewController the other answer was not what I was looking for :
So this is how I ended up doing it:
Calling the document picker:
var docPicker = new UIDocumentPickerViewController(new string[]
{ UTType.Data, UTType.Content }, UIDocumentPickerMode.Import);
docPicker.WasCancelled += DocPicker_WasCancelled;
docPicker.DidPickDocumentAtUrls += DocPicker_DidPickDocumentAtUrls;
docPicker.DidPickDocument += DocPicker_DidPickDocument;
var _currentViewController = GetCurrentUIController();
if (_currentViewController != null)
_currentViewController.PresentViewController(docPicker, true, null);
Where GetCurrentUIController is the function to get the current UI controller something like this :
public UIViewController GetCurrentUIController()
{
UIViewController viewController;
var window = UIApplication.SharedApplication.KeyWindow;
if (window == null)
{
return null;
}
if (window.RootViewController.PresentedViewController == null)
{
window = UIApplication.SharedApplication.Windows
.First(i => i.RootViewController != null &&
i.RootViewController.GetType().FullName
.Contains(typeof(Xamarin.Forms.Platform.iOS.Platform).FullName));
}
viewController = window.RootViewController;
while (viewController.PresentedViewController != null)
{
viewController = viewController.PresentedViewController;
}
return viewController;
}
For below iOS 11 i added the DidPickDocument event:
private void DocPicker_DidPickDocument(object sender, UIDocumentPickedEventArgs e)
{
try
{
NSUrl filePath = e.Url.AbsoluteUrl;
//This is the url for your document and you can use it as you please.
}
catch (Exception ex)
{
}
}
For above iOS 11 you use the DidPickDocumentUrls since multipick is supported there :
private void DocPicker_DidPickDocumentAtUrls(object sender, UIDocumentPickedAtUrlsEventArgs e)
{
try
{
List<NSUrl> filePath = e.Urls.ToList().Select(y => y.AbsoluteUrl).ToList();
//returns the list of images selected
}
catch (Exception ex)
{
AppLogger.LogException(ex);
}
}
Problem is faced, when we come across huge data items, where while scrolling the cells become black and glitches are seen throughout....any suggestion are appreciated.
Why not try IncrementalLoading to load your data in gridview.
Tutorial
Incase the link stops working
Code Snippet
public interface IIncrementalSource<T>
{
Task<IEnumerable<T>> GetPagedItems(int pageIndex, int pageSize);
}
public class IncrementalLoadingCollection<T, I> : ObservableCollection<I>,
ISupportIncrementalLoading
where T : IIncrementalSource<I>, new()
{
private T source;
private int itemsPerPage;
private bool hasMoreItems;
private int currentPage;
public IncrementalLoadingCollection(int itemsPerPage = 20)
{
this.source = new T();
this.itemsPerPage = itemsPerPage;
this.hasMoreItems = true;
}
public bool HasMoreItems
{
get { return hasMoreItems; }
}
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
var dispatcher = Window.Current.Dispatcher;
return Task.Run<LoadMoreItemsResult>(
async () =>
{
uint resultCount = 0;
var result = await source.GetPagedItems(currentPage++, itemsPerPage);
if (result == null || result.Count() == 0)
{
hasMoreItems = false;
}
else
{
resultCount = (uint)result.Count();
await dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
() =>
{
foreach (I item in result)
this.Add(item);
});
}
return new LoadMoreItemsResult() { Count = resultCount };
}).AsAsyncOperation<LoadMoreItemsResult>();
}
}
What I want to do is simply to upload a photo to a webservice using mono touch/mono droid and mvvmcross, hopefully in a way so I only have to write the code once for both android and IOS :)
My initial idea is to let the user pick an image (in android using an intent) get the path for the image. Then use MvxResourceLoader resourceLoader to open an stream from the path and then use restsharp for creating a post request with the stream.
However I already hit a wall, when the user picks an image the path is e.g. "/external/images/media/13". this path results in a file not found exception when using the MvxResourceLoader resourceLoader.
Any ideas to why I get the exception or is there an better way to achieve my goal?
This is how I ended up doinging it - thank you stuart and to all the links :)
public class PhotoService :IPhotoService, IMvxServiceConsumer<IMvxPictureChooserTask>,IMvxServiceConsumer<IAppSettings>
{
private const int MaxPixelDimension = 300;
private const int DefaultJpegQuality = 64;
public void ChoosePhotoForEventItem(string EventGalleryId, string ItemId)
{
this.GetService<IMvxPictureChooserTask>().ChoosePictureFromLibrary(
MaxPixelDimension,
DefaultJpegQuality,
delegate(Stream stream) { UploadImage(stream,EventGalleryId,ItemId); },
() => { /* cancel is ignored */ });
}
private void UploadImage(Stream stream, string EventGalleryId, string ItemId)
{
var settings = this.GetService<IAppSettings>();
string url = string.Format("{0}/EventGallery/image/{1}/{2}", settings.ServiceUrl, EventGalleryId, ItemId);
var uploadImageController = new UploadImageController(url);
uploadImageController.OnPhotoAvailableFromWebservice +=PhotoAvailableFromWebservice;
uploadImageController.UploadImage(stream,ItemId);
}
}
public class PhotoStreamEventArgs : EventArgs
{
public Stream PictureStream { get; set; }
public Action<string> OnSucessGettingPhotoFileName { get; set; }
public string URL { get; set; }
}
public class UploadImageController : BaseController, IMvxServiceConsumer<IMvxResourceLoader>, IMvxServiceConsumer<IErrorReporter>, IMvxServiceConsumer<IMvxSimpleFileStoreService>
{
public UploadImageController(string uri)
: base(uri)
{
}
public event EventHandler<PhotoStreamEventArgs> OnPhotoAvailableFromWebservice;
public void UploadImage(Stream stream, string name)
{
UploadImageStream(stream, name);
}
private void UploadImageStream(Stream obj, string name)
{
var request = new RestRequest(base.Uri, Method.POST);
request.AddFile("photo", ReadToEnd(obj), name + ".jpg", "image/pjpeg");
//calling server with restClient
var restClient = new RestClient();
try
{
this.ReportError("Billedet overføres", ErrorEventType.Warning);
restClient.ExecuteAsync(request, (response) =>
{
if (response.StatusCode == HttpStatusCode.OK)
{
//upload successfull
this.ReportError("Billedet blev overført", ErrorEventType.Warning);
if (OnPhotoAvailableFromWebservice != null)
{
this.OnPhotoAvailableFromWebservice(this, new PhotoStreamEventArgs() { URL = base.Uri });
}
}
else
{
//error ocured during upload
this.ReportError("Billedet kunne ikke overføres \n" + response.StatusDescription, ErrorEventType.Warning);
}
});
}
catch (Exception e)
{
this.ReportError("Upload completed succesfully...", ErrorEventType.Warning);
if (OnPhotoAvailableFromWebservice != null)
{
this.OnPhotoAvailableFromWebservice(this, new PhotoStreamEventArgs() { URL = url });
}
}
}
//method for converting stream to byte[]
public byte[] ReadToEnd(System.IO.Stream stream)
{
long originalPosition = stream.Position;
stream.Position = 0;
try
{
byte[] readBuffer = new byte[4096];
int totalBytesRead = 0;
int bytesRead;
while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
{
totalBytesRead += bytesRead;
if (totalBytesRead == readBuffer.Length)
{
int nextByte = stream.ReadByte();
if (nextByte != -1)
{
byte[] temp = new byte[readBuffer.Length * 2];
Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
readBuffer = temp;
totalBytesRead++;
}
}
}
byte[] buffer = readBuffer;
if (readBuffer.Length != totalBytesRead)
{
buffer = new byte[totalBytesRead];
Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
}
return buffer;
}
finally
{
stream.Position = originalPosition;
}
}
}
Try:
Issues taking images and showing them with MvvmCross on WP
Need an example of take a Picture with MonoDroid and MVVMCross
https://github.com/Redth/WshLst/ - uses Xam.Mobile for it's picture taking