Xamarin. Forms => Java.Lang.ClassCastException: 'android.net.Uri$HierarchicalUri cannot be cast to android.net.Uri[]' - xamarin

I use in my web view and use this web view in my Xamarin Forms app, I enable use input in Xamarin Forms and file browser opened on my phone, but after I select any file, I got this Exception:
Java.Lang.ClassCastException: 'android.net.Uri$HierarchicalUri cannot be cast to android.net.Uri[]';
protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
{
if (requestCode == FILECHOOSER_RESULTCODE)
{
if (null == mUploadCallbackAboveL)
return;
Java.Lang.Object result = intent == null || resultCode != Result.Ok
? null
: intent.Data;
mUploadCallbackAboveL.OnReceiveValue(result);
mUploadCallbackAboveL = null;
}
}
enter image description here

finally I fix that with this code:
protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
{
if (requestCode == FILECHOOSER_RESULTCODE)
{
if (resultCode == Result.Ok)
{
Android.Net.Uri result = intent.Data ?? ImageUri;
if (UploadCallbackAboveL != null)
{
UploadCallbackAboveL.OnReceiveValue(new Android.Net.Uri[] { result });
}
}
else
{
UploadCallbackAboveL.OnReceiveValue(null);
}
UploadCallbackAboveL = null;
}
else
{
base.OnActivityResult(requestCode, resultCode, intent);
}
}

Related

Call method when the location of the annotation becomes visible on the custom map Xamarin.Android

This post is continuation of this topic
I need find equivalent method Xamarin.Forms.Maps.iOS.MapRenderer.GetViewForAnnotation for CustomMapRenderer for Android.
As for this documentation the GetViewForAnnotation method for iOS is called when the location of the annotation becomes visible on the map, and is used to customize the annotation prior to display.
I need to find equivalent because I want to show pins on my map with already expanded window message(without click on them).
You could show the InfoWindow directly after you get the marker from your custom pin.
Add the code below when you override the CreateMarker method.
NativeMap.AddMarker(marker).ShowInfoWindow();
The whold custom renderer of Android:
public class CustomMapRenderer : MapRenderer, GoogleMap.IInfoWindowAdapter
{
List<CustomPin> customPins;
public CustomMapRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
NativeMap.InfoWindowClick -= OnInfoWindowClick;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
customPins = formsMap.CustomPins;
}
}
protected override void OnMapReady(GoogleMap map)
{
base.OnMapReady(map);
NativeMap.InfoWindowClick += OnInfoWindowClick;
NativeMap.SetInfoWindowAdapter(this);
}
protected override MarkerOptions CreateMarker(Pin pin)
{
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(pin.Position.Latitude, pin.Position.Longitude));
marker.SetTitle(pin.Label);
marker.SetSnippet(pin.Address);
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
NativeMap.AddMarker(marker).ShowInfoWindow();
return marker;
}
private void OnInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e)
{
var customPin = GetCustomPin(e.Marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
if (!string.IsNullOrWhiteSpace(customPin.Url))
{
var url = Android.Net.Uri.Parse(customPin.Url);
var intent = new Intent(Intent.ActionView, url);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
}
}
CustomPin GetCustomPin(Marker annotation)
{
var position = new Position(annotation.Position.Latitude, annotation.Position.Longitude);
foreach (var pin in customPins)
{
if (pin.Position == position)
{
return pin;
}
}
return null;
}
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("Custom pin not found");
}
if (customPin.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);
var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle);
if (infoTitle != null)
{
infoTitle.Text = marker.Title;
}
if (infoSubtitle != null)
{
infoSubtitle.Text = marker.Snippet;
}
return view;
}
return null;
}
public Android.Views.View GetInfoWindow(Marker marker)
{
return null;
}
}
For more details, you could refer to the MS docs.
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/map-pin

WebView File Chooser stops to respond after cancelled selection

we have implement file chooser for web view. it works successfully when attachment is selected, but fails when cancelled without file specification. The file chooser just stops to react on click
any help is appreciated. Thanks
we use chrome client. it works fine if in all cases, file selection is listed. but even from the first file selection is cancelled, no longer file chooser will work. It is Xamarin.Android app based fully on webview
Our code is:
protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
{
if (requestCode == FILECHOOSER_RESULTCODE)
{
if (null == _mUploadMessage)
return;
// Check that the response is a good one
if (resultCode == Result.Ok)
{
Android.Net.Uri[] results = null;
if (intent == null)
{
// If there is not data, then we may have taken a photo
if (mCameraPhotoPath != null)
{
results = new Android.Net.Uri[] { Android.Net.Uri.Parse(mCameraPhotoPath) };
}
}
else
{
if (intent.DataString != null)
{
results = new Android.Net.Uri[] { Android.Net.Uri.Parse(intent.DataString) };
}
}
_mUploadMessage.OnReceiveValue(results);
_mUploadMessage = null;
}
}
}
Chrome client:
var chrome = new FileChooserWebChromeClient((uploadMsg) =>
{
_mUploadMessage = uploadMsg;
mCameraPhotoPath = null;
Intent takePictureIntent = new Intent(Android.Provider.MediaStore.ActionImageCapture);
//Create the File where the photo should go
File photoFile = null;
try
{
string folder = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
photoFile = new File(folder, "image" + DateTime.Now.Millisecond + ".png");
takePictureIntent.PutExtra("PhotoPath", mCameraPhotoPath);
}
catch (IOException ex)
{
// Error occurred while creating the File
System.Console.WriteLine("" + ex.ToString());
}
// Continue only if the File was successfully created
if (photoFile != null)
{
mCameraPhotoPath = "file:" + photoFile.AbsolutePath;
takePictureIntent.PutExtra(Android.Provider.MediaStore.ExtraOutput,
Android.Net.Uri.FromFile(photoFile));
}
else
{
takePictureIntent = null;
}
Intent contentSelectionIntent = new Intent(Intent.ActionGetContent);
contentSelectionIntent.AddCategory(Intent.CategoryOpenable);
contentSelectionIntent.SetType("image/*");
Intent[] intentArray;
if (takePictureIntent != null)
{
intentArray = new Intent[] { takePictureIntent };
}
else
{
intentArray = new Intent[0];
}
Intent chooserIntent = new Intent(Intent.ActionChooser);
chooserIntent.PutExtra(Intent.ExtraIntent, contentSelectionIntent);
chooserIntent.PutExtra(Intent.ExtraTitle, this.GetStringFromResource(Resource.String.chose_photo));
chooserIntent.PutExtra(Intent.ExtraInitialIntents, intentArray);
base.StartActivityForResult(chooserIntent, HarmonyAndroid.AndroidMainActivity.FILECHOOSER_RESULTCODE);
});
return chrome;
Part 2
class FileChooserWebChromeClient : WebChromeClient
{
Action<IValueCallback> callback;
public FileChooserWebChromeClient(Action<IValueCallback> callback)
{
this.callback = callback;
}
public override bool OnShowFileChooser(WebView webView, IValueCallback filePathCallback, FileChooserParams fileChooserParams)
{
callback(filePathCallback);
return true;
}
public override void OnCloseWindow(WebView window)
{
base.OnCloseWindow(window);
}
}
Part 3
webView.ImprovePerformance();
webView.SetWebViewClient(new HomeWebViewClient(customWebViewClientListener, clientId));
webView.SetWebChromeClient(chrome);
webView.Settings.JavaScriptEnabled = true;
webView.Settings.DomStorageEnabled = true;
webView.SetDownloadListener(new CustomDownloadListener(activity, customDownloadListener));
webView.AddJavascriptInterface(new JavaScriptToCSharpCommunication(activity, javaScriptToCSharpCommunicationListener), Constants.JS_CSHARP_COMMUNICATOR_NAME);
Try to give a null object to the uri callback, when the resultCode is not RESULT_OK.
add in your OnActivityResult method:
if (resultCode != Result.Ok)
{
_mUploadMessage.OnReceiveValue(null);
_mUploadMessage = null;
return;
}

Xamarin: Navigate from MapRenderer Class to ContentPage

I am facing the following issue.
I am developing a Cross Platform Project on Xamarin and I am trying to open a ContentPage from a MapInfoWindow.
The Map ContentPage is inside the Portable Project and inside the Droid Project I have a the following class that is where I am trying to open the ContenPage:
public class CustomMapRenderer: MapRenderer, GoogleMap.IInfoWindowAdapter, IOnMapReadyCallback
{
GoogleMap map;
List<CustomPin> customPins;
bool isDrawn;
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
map.InfoWindowClick -= OnInfoWindowClick;
}
if (e.NewElement != null)
{
var formsMap = (CustomMap)e.NewElement;
customPins = formsMap.CustomPins;
((MapView)Control).GetMapAsync(this);
}
}
public void OnMapReady(GoogleMap googleMap)
{
map = googleMap;
map.InfoWindowClick += OnInfoWindowClick;
map.SetInfoWindowAdapter(this);
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName.Equals("VisibleRegion") && !isDrawn)
{
map.Clear();
if (customPins != null)
{
foreach (var pin in customPins)
{
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(pin.Pin.Position.Latitude, pin.Pin.Position.Longitude));
marker.SetTitle(pin.Pin.Label);
marker.SetSnippet(pin.Pin.Address);
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
map.AddMarker(marker);
}
isDrawn = true;
}
}
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
if (changed)
{
isDrawn = false;
}
}
void OnInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e)
{
var customPin = GetCustomPin(e.Marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
//Here I want to open the content page
}
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("Custom pin not found");
}
if (customPin.Id == "Xamarin")
{
view = inflater.Inflate(Resource.Layout.XamarinMapInfoWindow, null);
}
else
{
view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
}
var infoTitle = view.FindViewById<TextView>(Resource.Id.InfoWindowTitle);
var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle);
if (infoTitle != null)
{
infoTitle.Text = marker.Title;
}
if (infoSubtitle != null)
{
infoSubtitle.Text = marker.Snippet;
}
return view;
}
return null;
}
public Android.Views.View GetInfoWindow(Marker marker)
{
return null;
}
CustomPin GetCustomPin(Marker annotation)
{
var position = new Position(annotation.Position.Latitude, annotation.Position.Longitude);
foreach (var pin in customPins)
{
if (pin.Pin.Position == position)
{
return pin;
}
}
return null;
}
}
So I believe creating an EventHandler in your CustomMap class might be the way to go. In your CustomMap class, add the following:
public event EventHandler InfoTapped;
public virtual void OnInfoTapped(EventArgs e)
{
EventHandler handler = InfoTapped;
if (handler != null)
{
handler(this, e);
}
}
Then in your Forms shared code subscribe to the InfoTapped event and push your new ContentPage:
customMap.InfoTapped += async (sender, e) => {
await Navigation.PushAsync(new ContentPage());
};
Now in your renderer, create a class level field to hold a reference to your CustomMap:
CustomMap formsMap;
And then set this field in your OnElementChanged method:
if (e.NewElement != null)
{
// Remove the var to set your CustomMap field created
// above so you can use it elsewhere in the class
formsMap = (CustomMap)e.NewElement;
...
}
Now you can raise the event you created in CustomMap by calling:
formsMap.OnInfoTapped(e);
E.G.:
void OnInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e)
{
var customPin = GetCustomPin(e.Marker);
if (customPin == null)
{
throw new Exception("Custom pin not found");
}
//Here I want to open the content page
formsMap.OnInfoTapped(e);
}
And the code you added to the customMap.InfoTapped event handler will be called, in this case to push a new page.

Xamarin PCL: Hide soft keyboard on android (Dependecy Service) doesn't work

I trigger the code onfocus of an entry, but the hiding code in Android doesn't hide the soft keyboard.
Interface
public interface IKeyboardService
{
void Hide();
void Show();
}
ViewModel
public void OnFocused()
{
var serv = DependencyService.Get<IKeyboardService>();
if (cp.FindByName<SwitchCell>("SCell").On)
{
serv.Show();
}
else
{
serv.Hide();
}
}
Android Code
[assembly: Dependency(typeof(KeyboardService))]
namespace KeyboardHide.Droid.DependencyServices
{
public class KeyboardService : IKeyboardService
{
public void Hide()
{
var inputMethodManager = Xamarin.Forms.Forms.Context.GetSystemService(Context.InputMethodService) as InputMethodManager;
if (inputMethodManager != null && Xamarin.Forms.Forms.Context is Activity)
{
var activity = Xamarin.Forms.Forms.Context as Activity;
var focusedView = activity.CurrentFocus;
var token = focusedView == null ? null : focusedView.WindowToken;
inputMethodManager.HideSoftInputFromInputMethod(token, HideSoftInputFlags.None);
}
}
public void Show()
{
}
}
}
I also tried this code
code
var inputMethodManager = Xamarin.Forms.Forms.Context.GetSystemService(Context.InputMethodService) as InputMethodManager;
if (inputMethodManager != null && Xamarin.Forms.Forms.Context is Activity)
{
var activity = Xamarin.Forms.Forms.Context as Activity;
var focusedView = activity.CurrentFocus;
var token = focusedView == null ? null : focusedView.WindowToken;
inputMethodManager.ToggleSoftInputFromWindow(token, ShowSoftInputFlags.None, HideSoftInputFlags.None);
}
this worked for me by another project ??
*Edit:
Previous project (Works for another project of mine):
var inputMethodManager = Xamarin.Forms.Forms.Context.GetSystemService(Context.InputMethodService) as InputMethodManager;
if (inputMethodManager != null && Xamarin.Forms.Forms.Context is Activity)
{
var activity = Xamarin.Forms.Forms.Context as Activity;
var focusedView = activity.CurrentFocus;
var token = focusedView == null ? null : focusedView.WindowToken;
inputMethodManager.ToggleSoftInputFromWindow(token, ShowSoftInputFlags.None, HideSoftInputFlags.None);
}
But for my new project it doesn't work. I still see the keyboard, it doesn't hide it and I tried also:
Device.BeginInvokeOnMainThread(() => serv.Hide());
The following Android code works for me (got this code from someone else but cannot remember where now, I assume the Xamarin Forums). It has the added benefit of being able to be await'ed, but if you do not want that, you can remove the TaskCompletionSource stuff:
public Task<bool> DismissKeyboardAsync() {
TaskCompletionSource<bool> result = new TaskCompletionSource<bool>();
try {
View view = ((MainActivity)Forms.Context).CurrentFocus;
IBinder windowToken = view?.WindowToken;
if(windowToken != null) {
((InputMethodManager)Forms.Context.GetSystemService(Context.InputMethodService)).HideSoftInputFromWindow(windowToken, HideSoftInputFlags.None);
result.TrySetResult(true);
return result.Task;
}
result.TrySetResult(false);
} catch(System.Exception exception) {
System.Console.WriteLine("\nIn NativeHelper_Android.DismissKeyboardAsync() - Exception:\n{0}\n", exception);
result.TrySetException(exception);
result.TrySetResult(false);
}
return result.Task;
}
An example without async might be (this is untested code):
public void Hide() {
try {
View view = ((MainActivity)Forms.Context).CurrentFocus;
IBinder windowToken = view?.WindowToken;
if(windowToken != null) {
((InputMethodManager)Forms.Context.GetSystemService(Context.InputMethodService)).HideSoftInputFromWindow(windowToken, HideSoftInputFlags.None);
}
} catch(System.Exception exception) {
System.Console.WriteLine("\nIn NativeHelper_Android.DismissKeyboardAsync() - Exception:\n{0}\n", exception);
}
}

Android camera intent does not return to onActivityResult

I am trying to save a full camera image to a specified Uri when passing the uri to the camera intent. But it does not return to onActvityResult. However, I have tried getting the image using Bundle in the onActivityResult but it returns a thumbnail. I have tried lots of tutorials and they all had this problem. Can u please help!!!
Intent camera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File u = getTempFile();
picUri = Uri.fromFile(u);
camera.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(u));
startActivityForResult(camera, TAKE_PICTURE);
onActivityResult
if (requestCode == TAKE_PICTURE && resultCode == RESULT_OK && null != data)
{
Toast.makeText(this, "Image saved to:\n"+picUri, Toast.LENGTH_LONG).show();
}
Try code given below :
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File f = new File(android.os.Environment
.getExternalStorageDirectory(), "temp.jpg");
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
startActivityForResult(intent, REQUEST_CAMERA);
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_CAMERA) {
File f = new File(Environment.getExternalStorageDirectory()
.toString());
for (File temp : f.listFiles()) {
if (temp.getName().equals("temp.jpg")) {
f = temp;
break;
}
}
String path = f.getAbsolutePath();
} }
}

Resources