I'm using Xamarin forms, and in my application has a button to share the problem that is being in my android application I got doing with Intent but in my application ios'm not knowing how to do, someone could help me?
using android
public async Task<bool> Share(ImageSource image)
{
Intent shareIntent = new Intent(Intent.ActionSend);
bitmapToShare = await GetBitmap (image);
if (bitmapToShare != null) {
CreateDirectoryForPictures("Xpto");
var filePath = System.IO.Path.Combine (dir.AbsolutePath, string.Format("xpto_{0}.png",Guid.NewGuid()));
var stream = new FileStream (filePath, FileMode.Create);
bitmapToShare.Compress (Bitmap.CompressFormat.Png, 100, stream);
stream.Close ();
Java.IO.File file = new Java.IO.File (filePath);
shareIntent.SetType ("image/*");
shareIntent.PutExtra (Intent.ExtraStream, Android.Net.Uri.FromFile (file));
shareIntent.AddFlags (ActivityFlags.GrantReadUriPermission);
Forms.Context.StartActivity (Intent.CreateChooser (shareIntent, "Compartilhar"));
}
return true;
}
private static async Task ShareImageAsyc(ImageSource image, string message, string url = null)
{
var handler = image.GetHandler();
if (handler == null) return;
var uiImage = await handler.LoadImageAsync(image);
var items = new List<NSObject> { new NSString(message ?? string.Empty) };
if (!url.IsNullOrEmpty())
items.Add(new NSString(url));
items.Add(uiImage);
var controller = new UIActivityViewController(items.ToArray(), null);
UIApplication.SharedApplication.KeyWindow.RootViewController.GetTopViewController()
.PresentViewController(controller, true, null);
}
From: https://github.com/jimbobbennett/JimLib.Xamarin/blob/master/JimLib.Xamarin.ios/Sharing/Share.cs
Related
I followed example from here (https://learn.microsoft.com/en-gb/xamarin/xamarin-forms/app-fundamentals/custom-renderer/hybridwebview#invoke-c-from-javascript) to setup WebView for my project and I can invoke C# code from WebView page event, that is working fine.
However, before sending a request I have to setup a Cookie and that cookie should be passed to remote server. I followed several examples from net I am getting it to work for Android but iOS its not working.
Code I got from another Stackoverflow question as follows.
Android Working
var cookieManager = CookieManager.Instance;
cookieManager.SetAcceptCookie(true);
cookieManager.RemoveAllCookie();
var cookies = UserInfo.CookieContainer.GetCookies(new System.Uri(AppInfo.URL_BASE));
for (var i = 0; i < cookies.Count; i++)
{
string cookieValue = cookies[i].Value;
string cookieDomain = cookies[i].Domain;
string cookieName = cookies[i].Name;
cookieManager.SetCookie(cookieDomain, cookieName + "=" + cookieValue);
}
iOS Not Working
// Set cookies here
var cookieUrl = new Uri(AppInfo.URL_BASE);
var cookieJar = NSHttpCookieStorage.SharedStorage;
cookieJar.AcceptPolicy = NSHttpCookieAcceptPolicy.Always;
foreach (var aCookie in cookieJar.Cookies)
{
cookieJar.DeleteCookie(aCookie);
}
var jCookies = UserInfo.CookieContainer.GetCookies(cookieUrl);
IList<NSHttpCookie> eCookies =
(from object jCookie in jCookies
where jCookie != null
select (Cookie) jCookie
into netCookie select new NSHttpCookie(netCookie)).ToList();
cookieJar.SetCookies(eCookies.ToArray(), cookieUrl, cookieUrl);
I have tried code from WebView documentation here, Cookie section (https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/webview?tabs=macos#cookies)
I'll really appreciate if anybody can point out what I am doing wrong any hints.
Thanks.
Update
In my HybridWebViewRenderer method I am adding my custom Cookie as follows.
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
userController.RemoveAllUserScripts();
userController.RemoveScriptMessageHandler("invokeAction");
HybridWebView hybridWebView = e.OldElement as HybridWebView;
hybridWebView.Cleanup();
}
if (e.NewElement != null)
{
string cookieDomain = new System.Uri(((HybridWebView)Element).Uri).Host;
foreach (var c in NSHttpCookieStorage.SharedStorage.Cookies)
{
Console.WriteLine("Cookie (Delete)" + c.Name);
NSHttpCookieStorage.SharedStorage.DeleteCookie(c);
}
var cookieDict = new NSMutableDictionary();
cookieDict.Add(NSHttpCookie.KeyDomain, new NSString("." + cookieDomain));
cookieDict.Add(NSHttpCookie.KeyName, new NSString("ABC"));
cookieDict.Add(NSHttpCookie.KeyValue, new NSString("123e4567-e89b-12d3-a456-426652340003"));
cookieDict.Add(NSHttpCookie.KeyPath, new NSString("/"));
cookieDict.Add(NSHttpCookie.KeyExpires, DateTime.Now.AddDays(1).ToNSDate());
var myCookie = new NSHttpCookie(cookieDict);
NSHttpCookieStorage.SharedStorage.SetCookie(myCookie);
string filename = $"{hybridView.Uri}";
var request = new NSMutableUrlRequest(new NSUrl(filename));
var wkNavigation = LoadRequest(request);
}
}
In AppDelegate I have added.
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
NSHttpCookieStorage.SharedStorage.AcceptPolicy = NSHttpCookieAcceptPolicy.Always;
return base.FinishedLaunching(app, options);
}
Still no luck :( .........
You need to set the cookie in the shared storage.
Set your shared storage policy to always accept your own cookies.
In your ApplicationDelegate:
NSHttpCookieStorage.SharedStorage.AcceptPolicy = NSHttpCookieAcceptPolicy.Always;
We have middleware in a web API, which we use to filter the resposne body from a controller
After updating our service to .net 5, replacing the output fails with
System.InvalidOperationException: Headers already sent.
at Microsoft.AspNetCore.Server.HttpSys.Response.CheckResponseStarted()
at Microsoft.AspNetCore.Server.HttpSys.FeatureContext.ConsiderEnablingResponseCache()
at Microsoft.AspNetCore.Server.HttpSys.FeatureContext.OnResponseStart()
at Microsoft.AspNetCore.Server.HttpSys.FeatureContext.CompleteAsync()
at Microsoft.AspNetCore.Server.HttpSys.RequestContext.Execute()
at Microsoft.AspNetCore.Server.HttpSys.RequestContext.Execute()
Our middleware to filter the output looks something like this
internal class FilterOutput : IMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var originalBodyStream = context.Response.Body;
var tempResponseBody = new MemoryStream();
context.Response.Body = tempResponseBody;
context.Response.OnStarting(async state =>
{
await FilterResponse(context, tempResponseBody, originalBodyStream);
}, context);
await next(context);
}
private async Task FilterResponse(HttpContext context, MemoryStream tempResponseBody, Stream originalBodyStream)
{
if (context.Response.StatusCode == 200)
{
var output = Encoding.UTF8.GetString(tempResponseBody.GetBuffer());
var newOutput = output.Filter(null);
var updatedStream = GenerateStreamFromString(newOutput);
await updatedStream.CopyToAsync(originalBodyStream);
context.Response.Body = originalBodyStream;
return;
}
await tempResponseBody.CopyToAsync(originalBodyStream);
}
public static Stream GenerateStreamFromString(string s)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
}
I realize net 5 is propably more asynchronous and sends headers earlier
Is there a way to stop that, so I can modify the output body in middleware?
I have a small png image I like to show in an imageview using Xamarin.Android.
I am downloading the file using the following code:
private void Download()
{
var url = "https://hns.d7u.de/v4/images/hvvstoerungen_facebook.png";
var directory = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + "/myapp/";
var fileName = url.Substring(url.LastIndexOf("/") +1);
var path = directory + fileName;
System.Net.WebClient wC = new System.Net.WebClient();
wC.Headers.Add(System.Net.HttpRequestHeader.AcceptEncoding, "gzip");
wC.DownloadDataCompleted += WC_DownloadDataCompleted;
wC.DownloadDataAsync(new Uri(url), path);
}
private void WC_DownloadDataCompleted(object sender, System.Net.DownloadDataCompletedEventArgs e)
{
var path = e.UserState.ToString();
var bytes = e.Result;
if (File.Exists(path))
File.Delete(path);
if (!File.Exists(path))
File.WriteAllBytes(path, bytes);
}
It is stored at /data/user/0/myapp/files/hns/hvvstoerungen_facebook.png and a File.Exists(...) returns a true for that path. So I am sure, that the file is downloaded and it exists.
When I want to show it in the ImageView, I do it like this:
if (System.IO.File.Exists(imageFilePath))
{
Android.Net.Uri andrUri = Android.Net.Uri.Parse(imageFilePath);
ImageIcon.SetImageURI(andrUri);
//Also not working:
//Bitmap bitmap = BitmapFactory.DecodeFile(imageFilePath);
//ImageIcon.SetImageBitmap(bitmap);
//And also not working:
//Android.Net.Uri andrUri = Android.Net.Uri.Parse(imageFilePath);
//Bitmap bmp = BitmapFactory.DecodeStream(Android.App.Application.Context.ContentResolver.OpenInputStream(andrUri));
//ImageIcon.SetImageBitmap(bmp);
}
The Output windows shows the following when the image should be shown:
02-01 23:41:24.770 E/Drawable(19815): Unable to decode stream:
android.graphics.ImageDecoder$DecodeException: Failed to create image
decoder with message 'unimplemented'Input contained an error. 02-01
23:41:24.770 W/ImageView(19815): resolveUri failed on bad bitmap uri:
/data/user/0/myapp/files/hns/hvvstoerungen_facebook.png
But I cannot figured out what exactly this means.
One additional thing is: If I run the app in a brand new Android Emulator instance, this image and all other of its kind are not shown.
If I run the app in an old Android Emulator instance, where the app was already running before but on Android.Forms basis, the old images that were known by the old project are shown while the newly downloaded images are not. All images are in the same folder and I cannot see any differences between them.
Does anyone has an Idea?
Edit:
My working version has the following Download() Method instead:
private void Download()
{
var noCompression = new string[] { ".png", ".jpg", ".jpeg", ".gif", ".zip", ".7z", ".mp3", ".mp4" };
var url = "https://hns.d7u.de/v4/images/hvvstoerungen_facebook.png";
var directory = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + "/myapp/";
var fileName = url.Substring(url.LastIndexOf("/") +1);
var path = directory + fileName;
System.Net.WebClient wC = new System.Net.WebClient();
if (!noCompression.Contains(url.Substring(url.LastIndexOf('.'))))
wC.Headers.Add(System.Net.HttpRequestHeader.AcceptEncoding, "gzip");
wC.DownloadDataCompleted += WC_DownloadDataCompleted;
wC.DownloadDataAsync(new Uri(url), path);
}
You could try the code below.
Download the image from Url:
public Bitmap GetImageBitmapFromUrl(string url)
{
Bitmap imageBitmap = null;
using (var webClient = new WebClient())
{
var imageBytes = webClient.DownloadData(url);
if (imageBytes != null && imageBytes.Length > 0)
{
imageBitmap = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length);
}
}
return imageBitmap;
}
Usage:
bitmap = GetImageBitmapFromUrl("https://hns.d7u.de/v4/images/hvvstoerungen_facebook.png");
And save the image as png:
void ExportBitmapAsPNG(Bitmap bitmap)
{
var folderPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
filePath = System.IO.Path.Combine(folderPath, "test.png");
var stream = new FileStream(filePath, FileMode.Create);
bitmap.Compress(Bitmap.CompressFormat.Png, 100, stream);
stream.Close();
}
Usage:
ExportBitmapAsPNG(bitmap);
Check the file exists or not and set into the imageview:
if (File.Exists(filePath))
{
Bitmap myBitmap = BitmapFactory.DecodeFile(filePath);
imageview.SetImageBitmap(myBitmap);
}
I am trying to refresh my listview when an item is removed from it, but every time it gives me this error:
System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: index.
Before updating the ObservableCollection, I do this:
Groups = new ObservableCollection<RequestGroups>();
And then I fill it with this:
var temp = (JArray)resultJson["data"];
JArray jarr = temp;
foreach (JObject contents in jarr.Children<JObject>())
{
Requests obj = new Requests();
obj.Id = (int)contents["id"];
Client c = new Client();
c.address = contents["address"].ToString();
c.phone = contents["phone"].ToString();
c.name = contents["user"].ToString();
obj.Client = c;
obj.Date = contents["date"].ToString();
obj.Duration = contents["duration"].ToString();
obj.DurationText = "Duración: "+contents["duration"].ToString()+"h";
obj.Price = "$" + contents["price"].ToString();
String[] cDate = obj.Date.Split(' ');
String cHour = cDate[1]+" "+cDate[2];
obj.Hour = cHour;
String[] date = cDate[0].Split('-');
String title = months[date[1]] + " " + date[0];
obj.Title = title;
bool flag = false;
foreach(RequestGroups rqG in Groups){
if(rqG.Title.Equals(title)){
rqG.Add(obj);
flag = true;
}
}
if(!flag){
RequestGroups rq = new RequestGroups(title, date[1] + "-" + date[0]);
rq.Add(obj);
Device.BeginInvokeOnMainThread(() =>
{
Groups.Add(rq);
});
}
}
This is where I remove the items:
private async Task UpdateRequest(int status,int idEvent)
{
HttpClient hTTPClient = new HttpClient();
var client = new HttpClient();
client.Timeout = TimeSpan.FromSeconds(60);
client.BaseAddress = new Uri(Utils.baseUrl);
Dictionary<string, string> dataToSend = new Dictionary<string, string>();
dataToSend.Add("session", Utils.loginKey);
dataToSend.Add("eventId", idEvent+"");
dataToSend.Add("status", status.ToString());
string jsonData = Newtonsoft.Json.JsonConvert.SerializeObject(dataToSend, new KeyValuePairConverter());
var contentVar = new StringContent(jsonData, Encoding.UTF8, "application/json");
try
{
HttpResponseMessage response = await client.PostAsync("/UpdateEvent", contentVar);
var result = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
string contents = await response.Content.ReadAsStringAsync();
var resultJson = JObject.Parse(result);
if ((int)resultJson["status"] == 0)
{
await base.DisplayAlert((string)resultJson["msg"], "", "OK");
return;
}
else if ((int)resultJson["status"] == 1)
{
//I'm currently trying to reload the whole view, before this was calling the method above.
await this.mainPage.Navigation.PushAsync(new NavigationPage(new MasterMenu.MainPage()));
await getRequests();
}
else
{
await base.DisplayAlert("Error procesando la solicitud, intente más tarde", "", "Ok");
return;
}
}
}
catch (Exception ex)
{
Debug.WriteLine("Error update request: {0}", ex);
}
await Task.Delay(1);
}
If I leave it like that, UI will not update. Please help me, as I've been struggling with this issue for 2 days now. It happens exclusively on iOS.
The issue was fixed after updating iOS. The problem was caused because of a buggy iOS version that had problems indexing objects. After updating, everything ran as smoothly as usual. If anyone runs into this issue (exclusively on iOS), try updating both iOS and Xamarin Forms.
have the following which works on Win10 phone in a pcl.
But i cannot get the same code to return OK on samsung s7 with android 7.0
project is xamarin forms.
nuget for system.net.http is 2.2.29.
I've include the same nuget in my UWP for the win10 phone and android projects.
i've also changed the user to include be "domain\user", "domain#user", "user#domain"
var httpClientHandler = new System.Net.Http.HttpClientHandler()
{
Credentials = credentials.GetCredential(new Uri(location), "NTLM")
};
I've tried and alternative to setting the httpClientHandler.Credentials.
var credentials = new NetworkCredentials("user", "pass", "domain");
var location = "http://apps.mysite.com/api#/doit";
var httpClientHandler = new HttpClientHandler{
Credentials = credentials
}
using (var httpClient = new HttpClient(httpClientHandler, true))
{
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
try
{
var httpResponseMessage = await httpClient.GetAsync(location);
if (httpResponseMessage.StatusCode != System.Net.HttpStatusCode.OK)
{
//handle error
}
else
{
//do something
}
}
catch (Exception)
{}
finally
{}
}
another strange thing. when i run this on android, the code hits the await httpclient.getasync(location);
and the immediately jumps to the finally.
I hava a simple form with username & password Entry Fields, plus an OK button.
all three controls are bound to a viewmodel. the OK button via an ICommand.
this code and the view live in the PCL. which has a reference to Microsoft.Net.Http.
I have Android and Universal Windows Xamarin forms builds that consume the PCL.
Android Properties. Default httpClient, SSL/TLS Default. supported arch armeabi, armeabi-v7a;x86
Android Manifest: Camera, flashlight and internet
private bool calcEnabled = false;
private ICommand okCommand;
private string message = string.Empty;
private string validatingMessage = "Validating!";
private string unauthorizedMessage = "Invalid Credentials!";
private string authenticatedMessage = "Validated";
private bool validating = false;
public ICommand OkCommand => okCommand ?? (okCommand = new Command<object>((o) => clicked(o), (o) => CalcEnabled));
protected async void clicked(object state)
{
try
{
Validating = true;
Message = validatingMessage;
var credentials = new
System.Net.NetworkCredential(Helpers.Settings.UserName, Helpers.Settings.Password, "www.domain.com");
var location = "http://apps.wwwoodproducts.com/wwlocator#/information";
var httpClientHandler = new System.Net.Http.HttpClientHandler()
{
Credentials = credentials.GetCredential(new Uri(location), "NTLM") };
using (var httpClient = new System.Net.Http.HttpClient(httpClientHandler))
{
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
try
{
var httpResponseMessage = await
httpClient.GetAsync(location);
if (httpResponseMessage.StatusCode != System.Net.HttpStatusCode.OK)
{
Message = unauthorizedMessage;
}
else
{
Message = authenticatedMessage;
Messenger.Default.Send<bool>(true);
}
}
catch (Exception)
{
Message = unauthorizedMessage;
}
finally
{
Validating = false;
}
}
}
catch (Exception)
{
throw;
}
}