Google Login and retrieving profile info in Windows Phone 8.1 WinRT app - winapi

I am trying to add Google login in my universal app,For Windows 8.1 app,it's easy to do but in case of Windows Phone 8.1 WinRT app,
Here is the code I did:
private String simpleKey = "YOUR_SIMPLE_API_KEY"; // Should keep this secret
private String clientID = "ffffff- n12s9sab94p3j3vp95sdl7hrm2lbfk3e.apps.googleusercontent.com";
private string CALLBACKuri = "writeprovidedcallbackuri";
private String clientSecret = "LYffff2Q6MbgH623i"; // Keep it secret!
private String callbackUrl = "urn:ietf:wg:oauth:2.0:oob";
private String scope = "https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/userinfo.email";
public GooglePlusLoginPage()
{
this.InitializeComponent();
refreshToken = null;
code = null;
access_token = null;
renderArea = this;
Auth();
}
public void Auth()
{
Windows.Storage.ApplicationData.Current.LocalSettings.Values["code"] = "";
if (access_token == null)
{
if (refreshToken == null && code == null)
{
try
{
String GoogleURL = "https://accounts.google.com/o/oauth2/auth?client_id=" + Uri.EscapeDataString(clientID) + "&redirect_uri=" + Uri.EscapeDataString(callbackUrl) + "&response_type=code&scope=" + Uri.EscapeDataString(scope);
System.Uri StartUri = new Uri(GoogleURL);
// When using the desktop flow, the success code is displayed in the html title of this end uri
System.Uri EndUri = new Uri("https://accounts.google.com/o/oauth2/approval?");
WebAuthenticationBroker.AuthenticateAndContinue(StartUri, EndUri, null, WebAuthenticationOptions.None);
// await Task.Delay(2);
}
catch (Exception Error)
{
((GooglePlusLoginPage)renderArea).SendToLangingPage();
}
}
}
//codeToAcccesTok();
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
string name = e.Parameter as string;
IsGplusLogin = true;
// When the navigation stack isn't restored navigate to the ScenarioList
}
private void OutputToken(String TokenUri)
{
string access_token = TokenUri;
}
public void ContinueWebAuthentication(WebAuthenticationBrokerContinuationEventArgs args)
{
WebAuthenticationResult result = args.WebAuthenticationResult;
if (result.ResponseStatus == WebAuthenticationStatus.Success)
{
string response = result.ResponseData.ToString();
code = response.Substring(response.IndexOf("=") + 1);
Windows.Storage.ApplicationData.Current.LocalSettings.Values["code"] = code;
// TODO: switch off button, enable writes, etc.
}
else if (result.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
{
//TODO: handle WebAuthenticationResult.ResponseErrorDetail.ToString()
}
else
{
((GooglePlusLoginPage)renderArea).SendToLangingPage();
// This could be a response status of 400 / 401
// Could be really useful to print debugging information such as "Your applicationID is probably wrong"
//TODO: handle WebAuthenticationResult.ResponseStatus.ToString()
}
codeToAcccesTok();
}
interface IWebAuthenticationContinuable
{
/// <summary>
/// This method is invoked when the web authentication broker returns
/// with the authentication result
/// </summary>
/// <param name="args">Activated event args object that contains returned authentication token</param>
void ContinueWebAuthentication(WebAuthenticationBrokerContinuationEventArgs args);
}
private async void codeToAcccesTok()
{
string oauthUrl = "https://accounts.google.com/o/oauth2/token";
HttpClient theAuthClient = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, oauthUrl);
// default case, we have an authentication code, want a refresh/access token
string content = "code=" + code + "&" +
"client_id=" + clientID + "&" +
"client_secret=" + clientSecret + "&" +
"redirect_uri=" + callbackUrl + "&" +
"grant_type=authorization_code";
if (refreshToken != null)
{
content = "refresh_token=" + refreshToken + "&" +
"client_id=" + clientID + "&" +
"client_secret=" + clientSecret + "&" +
"grant_type=refresh_token";
}
request.Method = HttpMethod.Post;
request.Content = new StreamContent(new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(content)));
request.Content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
try
{
HttpResponseMessage response = await theAuthClient.SendAsync(request);
parseAccessToken(response);
}
catch (HttpRequestException)
{
}
}
public async void parseAccessToken(HttpResponseMessage response)
{
string content = await response.Content.ReadAsStringAsync();
//content="{\n \"error\" : \"invalid_request\",\n \"error_description\" : \"Missing required parameter: code\"\n}";
if (content != null)
{
string[] lines = content.Replace("\"", "").Replace(" ", "").Replace(",", "").Split('\n');
for (int i = 0; i < lines.Length; i++)
{
string[] paramSplit = lines[i].Split(':');
if (paramSplit[0].Equals("access_token"))
{
access_token = paramSplit[1];
}
if (paramSplit[0].Equals("refresh_token"))
{
refreshToken = paramSplit[1];
Windows.Storage.ApplicationData.Current.LocalSettings.Values["refreshToken"] = refreshToken;
}
}
//access_token="ya29.aAAvUHg-CW7c1RwAAACtigeHQm2CPFbwTG2zcJK-frpMUNqZkVRQL5q90mF_bA";
if (access_token != null)
{
getProfile();
}
else
{
((GooglePlusLoginPage)renderArea).SendToLangingPage();
// something is wrong, fix this
}
}
}
private async void ParseProfile(HttpResponseMessage response)
{
string content = await response.Content.ReadAsStringAsync();
if (content != null)
{
var serializer = new DataContractJsonSerializer(typeof(UserEmail));
UserInfo = serializer.ReadObject(new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(content))) as UserEmail;
((GooglePlusLoginPage)renderArea).RenderUser();
WebView wb = new WebView();
var url = "http://accounts.google.com/Logout";
wb.Navigate(new Uri(url, UriKind.RelativeOrAbsolute));
}
}
public async void getProfile()
{
httpClient = new HttpClient();
var searchUrl = "https://www.googleapis.com/oauth2/v2/userinfo";
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + access_token);
try
{
HttpResponseMessage response = await httpClient.GetAsync(searchUrl);
ParseProfile(response);
}
catch (HttpRequestException hre)
{
// DebugPrint(hre.Message);
}
}
public async void RenderUser()
{
GridProgressRing.Visibility = Visibility.Visible;
Imageuri = UserInfo.picture.ToString().Replace("sz=50", "sz=150");
displayname = UserInfo.name;
Google_Id = UserInfo.id;
emailid = UserInfo.Email;
first_name = displayname;
uniqueName = Imageuri.ToString();
string Imagefile = "";
if (ShareMenuClass.CheckInternetConnection())
{
Imagefile = await ShareMenuClass.ToBase64String(Imageuri);
}
if (first_name.Contains(' '))
{
string[] dfdf = new string[2];
dfdf = first_name.Split(' ');
first_name = dfdf[0];
last_name = dfdf[1];
}
password = "google user";
string DataString = "<UserRegistration>" + "<FirstName>" + first_name + "</FirstName><LastName>" + last_name + "</LastName><Platform>Windows 8</Platform>" +
"<UUID>" + getDeviceId() + "</UUID><EmailId>" + emailid + "</EmailId><Password>" + password + "</Password><Photo>" + Imagefile +
"</Photo><OrganiztionName>" + organization_name + "</OrganiztionName><Location>indore</Location><AppId>2</AppId><querytype>register</querytype></UserRegistration>";
if (ShareMenuClass.CheckInternetConnection())
{
string Front = "<UserRegistration xmlns=\"www.XMLWebServiceSoapHeaderAuth.net\"> <UserRegistrationXml>";
string Back = "</UserRegistrationXml></UserRegistration>";
DataString = DataString.Replace("<", "<");
DataString = DataString.Replace(">", ">");
DataString = Front + DataString + Back;
string RecivedString = await ShareMenuClass.CallWebService("UserRegistration", DataString);
bool flagtoFillDefaultProgress = true;
if (RecivedString.Contains("Email Id is already registered"))
{
flagtoFillDefaultProgress = false;
string SoapXml = "<getuserProgressInfo><EmailId>" + emailid + "</EmailId><AppId>2</AppId></getuserProgressInfo>";
Front = "<getuserProgress xmlns=\"www.XMLWebServiceSoapHeaderAuth.net\"><getuserProgressInfoXml>";
Back = "</getuserProgressInfoXml></getuserProgress>";
SoapXml = SoapXml.Replace("<", "<");
SoapXml = SoapXml.Replace(">", ">");
SoapXml = Front + SoapXml + Back;
RecivedString = await ShareMenuClass.CallWebService("getuserProgress", SoapXml);
}
if (RecivedString.Contains("success"))
{
txtplswait.Text = "Configuring your account...";
RecivedXml.RecivedStringToObserCollection(RecivedString);
//if (flagtoFillDefaultProgress)
//{
await System.Threading.Tasks.Task.Delay(25);
await RecivedXml.FillMyHalfList();
//}
RecivedXml.SerializeRecivedRecivedollection();
ShareMenuClass.Google_Loging = true;
if (RecivedXml.WholeRecivedData[0].response == "success")
{
StorageFile storagefile = await ApplicationData.Current.LocalFolder.CreateFileAsync("IsGoogleUser.txt", CreationCollisionOption.ReplaceExisting);
RecivedXml.SerializeSignedUserInfo(RecivedXml.WholeRecivedData[0].Id);
Quizstatemodleobj.GetOverallQuizProgressForAllUserAndFillThisUserList(RecivedXml.WholeRecivedData[0].Id);
await System.Threading.Tasks.Task.Delay(25);
GridProgressRing.Visibility = Visibility.Collapsed;
Frame.Navigate(typeof(TrainingModulesPage));
}
}
else
{
MessageDialog msg1 = new MessageDialog("Somthing went wrong.Try again later!");
await msg1.ShowAsync();
Frame.Navigate(typeof(RegistrationPage));
}
}
else
{
MessageDialog msg1 = new MessageDialog("You are not connected to internet!");
await msg1.ShowAsync();
Frame.Navigate(typeof(RegistrationPage));
}
}
public Page renderArea { get; set; }
public string refreshToken { get; set; }
public string code { get; set; }
Here in ContinueWebAuthentication which is triggered after user accepts to let the app get the profile info the value of "code" is not the desired one,In W8.1 app the value of "code" is correct but here it is not.
Due to this I am unable to get the user profile info

I finally figured this out.
Change the "redirect_uri" query parameter in the "StartUri" parameter of the AuthenticateAndContinue method to http://localhost
Change the CallbackURL (or "EndUri") parameter of the "AuthenticateAndContinue" method to also equal http://localhost
After many hours this is what worked for me. I found the answer by browsing the code at: http://code.msdn.microsoft.com/windowsapps/Authentication-using-bb28840e and specifically looking at the class "GoogleService.cs" in the Authentication.Shared project of the solution.
Hope this helps.

Related

Poper way to make a HTTPClient PostAsync and GetAsync?

I want to make a proper HTTPClient request. I have a code but I am always getting so may exceptions like:
Java.IO.IOException: Socket closed
System.OperationCanceledException: The operation was canceled.
Java.Net.SocketException: Connection reset
Java.Net.SocketException: Software caused connection abort
Java.Net.UnknownHostException: android_getaddrinfo failed: EAI_NODATA (No address associated with hostname)
Java.Net.UnknownHostException: Unable to resolve host "tbs.scratchit.ph": No address associated with hostname
Java.IO.IOException: isConnected failed: ETIMEDOUT (Connection timed out)
Java.Net.SocketException: recvfrom failed: ECONNRESET (Connection reset by peer)
I am always getting these kinds of exceptions, errors.
I am starting to wonder how can I create a Post Async and GetAsync properly to avoid these errors in the future?
Here is how I create a HTTP Client:
1. I have a class call Constants, in there I will declare a new HTTP Client so that I only have 1 HTTPClient across my project
public class Constants
{
public static HttpClient client = new HttpClient();
}
2. I have a function(s) that gets data from my server through a PHP API by sending the parameters through JSON.
public async void FirstTimeSyncUser(string host, string database, string contact, string ipaddress)
{
try
{
syncStatus.Text = "Checking internet connection";
string apifile = "first-time-sync-user-api.php";
if (CrossConnectivity.Current.IsConnected)
{
syncStatus.Text = "Initializing first-time user sync";
var db = DependencyService.Get<ISQLiteDB>();
var conn = db.GetConnection();
var getData = conn.QueryAsync<UserTable>("SELECT * FROM tblUser WHERE ContactID = ? AND Deleted != '1'", contact);
var resultCount = getData.Result.Count;
var current_datetime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
int count = 1;
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore
};
if (resultCount == 0)
{
syncStatus.Text = "Getting user data from the server";
var link = "http://" + ipaddress + "/" + Constants.apifolder + "/api/" + apifile;
string contentType = "application/json";
JObject json = new JObject
{
{ "Host", host },
{ "Database", database },
{ "ContactID", contact }
};
Constants.client.DefaultRequestHeaders.ConnectionClose = true;
var response = await Constants.client.PostAsync(link, new StringContent(json.ToString(), Encoding.UTF8, contentType));
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
if (!string.IsNullOrEmpty(content))
{
try
{
var dataresult = JsonConvert.DeserializeObject<List<UserData>>(content, settings);
var datacount = dataresult.Count;
for (int i = 0; i < datacount; i++)
{
syncStatus.Text = "Syncing user " + count + " out of " + datacount;
var item = dataresult[i];
var userid = item.UserID;
var usrpassword = item.UsrPassword;
var usertypeid = item.UserTypeID;
var userstatus = item.UserStatus;
var lastsync = DateTime.Parse(current_datetime);
var lastupdated = item.LastUpdated;
var deleted = item.Deleted;
var insertdata = new UserTable
{
UserID = userid,
UsrPassword = usrpassword,
ContactID = contact,
UserTypeID = usertypeid,
UserStatus = userstatus,
LastSync = lastsync,
LastUpdated = lastupdated,
Deleted = deleted
};
await conn.InsertOrReplaceAsync(insertdata);
count++;
}
synccount += "Total synced user: " + count + "\n";
var logType = "App Log";
var log = "Initialized first-time sync (<b>User</b>) <br/>" + "App Version: <b>" + Constants.appversion + "</b><br/> Device ID: <b>" + Constants.deviceID + "</b>";
int logdeleted = 0;
Save_Logs(contact, logType, log, database, logdeleted);
Preferences.Set("userchangeslastcheck", current_datetime, "private_prefs");
FirstTimeSyncSystemSerial(host, database, contact, ipaddress);
}
catch
{
var retry = await DisplayAlert("Application Error", "Syncing failed. Failed to send the data.\n\n Error:\n\n" + content + "\n\n Do you want to retry?", "Yes", "No");
if (retry.Equals(true))
{
FirstTimeSyncUser(host, database, contact, ipaddress);
}
else
{
First_Time_OnSyncFailed();
}
}
}
else
{
Preferences.Set("userchangeslastcheck", current_datetime, "private_prefs");
FirstTimeSyncSystemSerial(host, database, contact, ipaddress);
}
}
else
{
var retry = await DisplayAlert("Application Error", "Syncing failed. Server is unreachable.\n\n Error:\n\n"+ response.StatusCode +" Do you want to retry?", "Yes", "No");
if (retry.Equals(true))
{
FirstTimeSyncUser(host, database, contact, ipaddress);
}
else
{
First_Time_OnSyncFailed();
}
}
}
else
{
SyncUserClientUpdate(host, database, contact, ipaddress);
}
}
else
{
var retry = await DisplayAlert("Application Error", "Syncing failed. Please connect to the internet to sync your data. Do you want to retry?", "Yes", "No");
if (retry.Equals(true))
{
FirstTimeSyncUser(host, database, contact, ipaddress);
}
else
{
First_Time_OnSyncFailed();
}
}
}
catch (Exception ex)
{
Crashes.TrackError(ex);
var retry = await DisplayAlert("Application Error", "Syncing failed. Failed to send the data.\n\n Error:\n\n" + ex.Message.ToString() + "\n\n Do you want to retry?", "Yes", "No");
if (retry.Equals(true))
{
FirstTimeSyncUser(host, database, contact, ipaddress);
}
else
{
First_Time_OnSyncFailed();
}
}
}
3. After getting the data I needed it will execute another function with another POSTASYNC Call. In my code above when I got the user data from my server it will execute the next function which is FirstTimeSyncSystemSerial(host, database, contact, ipaddress);
What am I doing wrong? and How can I improve this so that I can avoid these exceptions?
Debug your code to find out where the exception is thrown.
Put a try catch blocked around that block of code. Then catch all the expected exceptions and try loopback again for a number of time.
You can Make a Generic Custom Service call That Can be Called Anywhere When You Need
public class RestClient : IRestClient
{
private const string TokenHeaderKey = "Any Token Header";
private HttpClient _httpclient = new HttpClient();
public async Task<T> GetAsync<T>(string url, string token) where T : new()
{
var responseContent = await ExecuteRequest(
async () =>
{
try
{
AddTokenToDefaultRequestsHeader(token);
return await _httpclient.GetAsync(url);
}
finally
{
ClearAuthenticationHeader();
}
});
return await Deserialize<T>(responseContent);
}
private void AddTokenToDefaultRequestsHeader(string token)
{
_httpclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(TokenHeaderKey, token);
}
private void ClearAuthenticationHeader()
{
_httpclient.DefaultRequestHeaders.Authorization = null;
}
private static async Task<HttpContent> ExecuteRequest(Func<Task<HttpResponseMessage>> requestFunc)
{
HttpResponseMessage response = null;
try
{
response = await requestFunc();
if (!response.IsSuccessStatusCode)
{
var message = $"Executed HTTP request returned status code {response.StatusCode} and reason phrase {response.ReasonPhrase}";
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
throw new Exception(message);
}
throw new Exception(message);
}
return response.Content;
}
catch (Exception exception)
{
if (exception is HttpRequestException || exception is WebException || exception is TaskCanceledException)
{
throw new Exception(
"Could not connect to service.");
}
throw;
}
}
private static async Task<T> Deserialize<T>(HttpContent responseContent) where T : new()
{
try
{
var responseContentString = await responseContent.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(responseContentString);
}
catch (Exception exception)
{
if (exception is TaskCanceledException || exception is JsonException)
{
throw new Exception("Could not deserialize response content.", exception);
}
throw;
}
}
And Add an App settings class
public class AppSettings : IAppSettings
{
public string Server => "server url";
public string ServerEndPoint => "End point";
public string Token => "Token If you Have any";
}
Then Call Like this
public class Servicecall
{
private readonly IRestClient _restClient;
private readonly IAppSettings _appSettings;
public PatientService(IRestClient restClient, IAppSettings appSettings)
{
_restClient = restClient;
_appSettings = appSettings;
}
public async Task<IList<PatientViewModel>> GetPatients()
{
var url = _appSettings.Server + _appSettings.EndPoint ;
var token = _appSettings.Token;
return GetPatientList(await _restClient.GetAsync<List<ListModelClass>>(url, token));
}
public IList<Model> GetPatientList(IList<ListModelClass> List)
{
return List.Select(p => new Model(p)).ToList();
}
}
This way You can Call deferent services without typing a lot of boilercodes
This way You can Call Services with real ease

The specified string is not in the form required for an e-mail address.i am getting Emails from Database

this is method
#region StuDetailsMail
public List StuDetailsMail(string Stu_Name, string Stu_Email, string Stu_Mobile)
{
List data = new List();
SqlParameter[] sqlprm = new SqlParameter[3];
SqlDataReader dr = null;
try {
sqlprm[0] = new SqlParameter(DBProcedures.sqlparam_Name, Stu_Name);
sqlprm[1] = new SqlParameter(DBProcedures.sqlpram_Email, Stu_Email);
sqlprm[2] = new SqlParameter(DBProcedures.sqlpram_Mobile_Num, Stu_Mobile);
dr = objDAL.ExecuteReader(DBProcedures.Usp_StuEmail, ref sqlprm, false);
while (dr.Read())
{
InstEmails list1 = new InstEmails()
{
Emaillist = dr["email"].ToString(),
};
data.Add(list1);
};
foreach (var emailId in data)
{
string MessageBody = "studentname" + Stu_Name + " is Intersted" + "studentmail is" + Stu_Email;
string MailSubject = "this is from Traininghubs";
obj.SendEmailToInstitute(emailId.ToString(), MessageBody, MailSubject);
}
}
catch(Exception e)
{
}
return data;
}
#endregion

How to use RESTful service in Xamarin.Forms to fetch JSON data

Since I am new to Xamarin, I would really appreciate if someone could explain me how to consume RESTful service (returning JSON data) using Xamarin Forms with the simplest example.
You can use JSON.net and HTTP request, here is a quick example of how to use the movie database which is a free movie database GET and POST examples, hope this helps:
const string _baseUrl = "http://api.themoviedb.org/3/";
const string _pageString = "&page=";
//GET Example
public static async Task<ObservableCollection<Movie>> GetTopRatedMoviesAsync(int page = 1)
{
HttpClient client = new HttpClient();
//_apiKey = themoviedb api key, page = page size 1 = first 20 movies;
string topRatedUrl = _baseUrl + "movie/top_rated?" + _apiKey + _pageString + page;
HttpResponseMessage result = await client.GetAsync (topRatedUrl, CancellationToken.None);
if (result.IsSuccessStatusCode) {
try{
string content = await result.Content.ReadAsStringAsync ();
ObservableCollection<Movie> MovieList = GetJsonData(content);
//return a ObservableCollection to fill a list of top rated movies
return MovieList;
}catch(Exception ex){
//Model Error
Console.WriteLine (ex);
return null;
}
}
//Server Error or no internet connection.
return null;
}
static ObservableCollection<Movie> GetJsonData(string content){
JObject jresponse = JObject.Parse (content);
var jarray = jresponse ["results"];
ObservableCollection<Movie> movieList = new ObservableCollection<Movie> ();
foreach (var jObj in jarray) {
Movie newMovie = new Movie();
newMovie.Title = (string)jObj["title"];
newMovie.PosterPath = _baseImgUrl + (string)jObj["poster_path"];
newMovie.HighResPosterPath = _baseImgUrl + (string)jObj["poster_path"];
newMovie.Id = (int)jObj["id"];
newMovie.Overview = (string)jObj["overview"];
newMovie.VoteCount = (double)jObj["vote_count"];
newMovie.ReleaseDate = (DateTime)jObj["release_date"];
newMovie.VoteAverage = (float)jObj["vote_average"];
movieList.Add(newMovie);
}
return movieList;
}
//POST Example:
public static async Task<bool> PostFavoriteMovieAsync(Movie movie)
{
try
{
HttpClient client = new HttpClient();
//_sessionId = string with the movie database session.
string tokenUrl = _baseUrl + "account/id/favorite?" + _apiKey + _sessionId;
string postBody = JsonConvert.SerializeObject(new Favorite{
favorite = !movie.Favorite,
media_id = movie.Id,
});
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.PostAsync (tokenUrl,
new StringContent (postBody, Encoding.UTF8, "application/json"));
if(response.IsSuccessStatusCode)
return true;
else
return false;
}
catch(Exception ex){
Console.WriteLine (ex.Message);
return false;
}
}

Download and Save PDF for viewing

Im trying to download a PDF document from my app and display it in IBooks or at least make it available to read some how when its completed downloading.
I followed the download example from Xamarin which allows me download the PDF and save it locally. Its being save in the wrong encoding also.
This is what I've tried so far.
private void PdfClickHandler()
{
var webClient = new WebClient();
webClient.DownloadStringCompleted += (s, e) => {
var text = e.Result; // get the downloaded text
string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string localFilename = $"{_blueways}.pdf";
// writes to local storage
File.WriteAllText(Path.Combine(documentsPath, localFilename), text);
InvokeOnMainThread(() => {
new UIAlertView("Done", "File downloaded and saved", null, "OK", null).Show();
});
};
var url = new Uri(_blueway.PDF);
webClient.Encoding = Encoding.UTF8;
webClient.DownloadStringAsync(url);
}
Do not use DownloadStringAsync for "binary" data, use DownloadDataAsync:
Downloads the resource as a Byte array from the URI specified as an asynchronous operation.
private void PdfClickHandler ()
{
var webClient = new WebClient ();
webClient.DownloadDataCompleted += (s, e) => {
var data = e.Result;
string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string localFilename = $"{_blueways}.pdf";
File.WriteAllBytes (Path.Combine (documentsPath, localFilename), data);
InvokeOnMainThread (() => {
new UIAlertView ("Done", "File downloaded and saved", null, "OK", null).Show ();
});
};
var url = new Uri ("_blueway.PDF");
webClient.DownloadDataAsync (url);
}
// Retrieving the URL
var pdfUrl = new Uri("url.pdf"); //enter your PDF path here
// Open PDF URL with device browser to download
Device.OpenUri(pdfUrl);
//First Create Model Class FileDownload
public class FileDownload
{
public string FileUrl { get; set; }
public string FileName { get; set; }
}
//Create a view in xaml file for button on which we need to perform download functionality
<ImageButton BackgroundColor="Transparent" Clicked="DownloadFile_Clicked" x:Name="ImgFileReportDownload_ViewResult" IsVisible="False">
<ImageButton.Source>
<FontImageSource Glyph=""
Color="#1CBB8C"
Size="30"
FontFamily="{StaticResource FontAwesomeSolid}">
</FontImageSource>
</ImageButton.Source>
</ImageButton>
//Created a method in xaml.cs to download File on the click of button
private async void DownloadFile_Clicked(object sender, EventArgs e)
{
var status = await Permissions.CheckStatusAsync<Permissions.StorageWrite>();
if (status == PermissionStatus.Granted)
{
Uri uri = new Uri(fileReportNameViewResult);
string filename = System.IO.Path.GetFileName(uri.LocalPath);
FileDownload fileDownload = new FileDownload();
fileDownload.FileName = filename;
fileDownload.FileUrl = fileReportNameViewResult;
MessagingCenter.Send<FileDownload>(fileDownload, "Download");
}
else
{
status = await Permissions.RequestAsync<Permissions.StorageWrite>();
if (status != PermissionStatus.Granted)
{
await DisplayAlert("Permission Denied!", "\nPlease go to your app settings and enable permissions.", "Ok");
return;
}
}
}
//In MainActivity.cs , create a method
private void MessagingCenter()
{
Xamarin.Forms.MessagingCenter.Subscribe<FileDownload>(this, "Download", (s) =>
{
NotificationID += 4;
var intent = new Intent(this, typeof(Service.DownloadManager));
intent.PutExtra("url", s.FileUrl);
intent.PutExtra("name", s.FileName);
_layout.SetMinimumHeight(3000);
_layout.Bottom = 350; ;
Snackbar.Make(_layout, "Document is Downloading.", Snackbar.LengthShort)
.Show();
StartService(intent);
});
}
//Create a class DownloadManager.cs in Service folder , copy all the below code and paste , just change the Namespace
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace App.Droid.Service
{
[Service]
public class DownloadManager : Android.App.Service
{
AndroidNotificationManager NotificationManager = new AndroidNotificationManager();
public override IBinder OnBind(Intent intent)
{
return null;
}
public override void OnCreate()
{
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
Task.Run(() =>
{
int messageId = ++MainActivity.NotificationID;
string url = intent.GetStringExtra("url");
string filename = intent.GetStringExtra("name");
string extension = url.Substring(url.LastIndexOf('.'));
if (!filename.EndsWith(extension))
{
filename += extension;
}
NotificationManager.ScheduleNotification(filename, "", messageId);
String TempFileName = "";
try
{
HttpWebRequest Http = (HttpWebRequest)WebRequest.Create(url);
WebResponse Response = Http.GetResponse();
long length = Response.ContentLength;
var stream = Response.GetResponseStream();
string baseDir = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).AbsolutePath;
//string baseDir = Android.App.Application.Context.GetExternalFilesDir(Android.OS.Environment.DirectoryDownloads).AbsolutePath;
//string baseDir = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
//string baseDir = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
baseDir = Path.Combine(baseDir, filename.Substring(filename.LastIndexOf('/') + 1).Replace(' ', '_'));
Directory.CreateDirectory(baseDir);
//string filePath = Path.Combine(documentsPath, name);
if (filename.Length > 18)
{
TempFileName = filename.Substring(0, 18) + "...";
}
else
{
TempFileName = filename;
}
FileInfo fi = new FileInfo(Path.Combine(baseDir, filename.Substring(filename.LastIndexOf('/') + 1).Replace(' ', '_')));
var fis = fi.OpenWrite();
long count = 0;
int begpoint = 0;
bool iscancelled = false;
MessagingCenter.Subscribe<CancelNotificationModel>(this, "Cancel", sender =>
{
if (messageId == sender.ID)
{
iscancelled = true;
}
});
while (true)
{
try
{
if (iscancelled == true)
{
break;
}
// Read file
int bytesRead = 0;
byte[] b = new byte[1024 * 1024];
bytesRead = stream.Read(b, begpoint, b.Length);
if (bytesRead == 0)
break;
fis.Write(b, 0, bytesRead);
fis.Flush();
count += bytesRead;
System.Diagnostics.Debug.WriteLine(count + "-" + length);
if (count >= length)
break;
NotificationManager.ChangeProgress(TempFileName, (int)((count * 100) / length), messageId);
}
catch (Exception ex)
{
Http = (HttpWebRequest)WebRequest.Create(url);
WebHeaderCollection myWebHeaderCollection = Http.Headers;
Http.AddRange(count, length - 1);
Response = Http.GetResponse();
stream = Response.GetResponseStream();
}
}
fis.Close();
NotificationManager.RemoveNotification(messageId);
if (iscancelled == false)
{
new AndroidNotificationManager().DownloadCompleted(filename, "Download Completed", Path.Combine(baseDir, filename), ++messageId);
}
}
catch (Exception ex)
{
NotificationManager.RemoveNotification(messageId);
NotificationManager.FileCancelled(filename, "Download Cancelled, Please try again", ++messageId);
}
});
return StartCommandResult.NotSticky;
}
public override void OnDestroy()
{
}
}
public class CancelNotificationModel
{
public int ID { get; set; }
}
}
//Create a class AndroidNotificationManager.cs in Service folder
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using AndroidX.Core.App;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xamarin.Essentials;
using AndroidApp = Android.App.Application;
namespace App.Droid.Service
{
public class AndroidNotificationManager
{
const string channelId = "default";
const string channelName = "Default";
const string channelDescription = "The default channel for notifications.";
const int pendingIntentId = 0;
public const string TitleKey = "title";
public const string MessageKey = "message";
bool channelInitialized = false;
NotificationManager manager;
NotificationCompat.Builder builder;
public event EventHandler NotificationReceived;
public void Initialize()
{
CreateNotificationChannel();
}
public void RemoveNotification(int messageid)
{
manager.Cancel(messageid);
}
public int ScheduleNotification(string title, string message, int messageId, bool isInfinite = false)
{
if (!channelInitialized)
{
CreateNotificationChannel();
}
Intent intent = new Intent(AndroidApp.Context, typeof(MainActivity));
intent.PutExtra(TitleKey, title);
intent.PutExtra(MessageKey, message);
PendingIntent pendingIntent = PendingIntent.GetActivity(AndroidApp.Context, pendingIntentId, intent, PendingIntentFlags.OneShot);
builder = new NotificationCompat.Builder(AndroidApp.Context, channelId)
.SetContentTitle(title)
.SetContentText(message)
.SetPriority(NotificationCompat.PriorityLow)
.SetVibrate(new long[] { 0L })
.SetProgress(100, 0, isInfinite)
.SetSmallIcon(Resource.Drawable.checkcircle);
var notification = builder.Build();
manager.Notify(messageId, notification);
return messageId;
}
public void ChangeProgress(string filename, int progress, int messageId)
{
try
{
var actionIntent1 = new Intent();
actionIntent1.SetAction("Cancel");
actionIntent1.PutExtra("NotificationIdKey", messageId);
var pIntent1 = PendingIntent.GetBroadcast(Android.App.Application.Context, 0, actionIntent1, PendingIntentFlags.CancelCurrent);
var ProgressBuilder = new NotificationCompat.Builder(AndroidApp.Context, channelId)
.SetSmallIcon(Resource.Drawable.checkcircle)
.SetContentTitle(filename)
.SetVibrate(new long[] { 0L })
.AddAction(Resource.Drawable.checkcircle, "Cancel", pIntent1)
.SetPriority(NotificationCompat.PriorityLow)
.SetProgress(100, progress, false)
.SetContentText(progress + "%")
.SetAutoCancel(false);
System.Diagnostics.Debug.WriteLine(progress);
manager.Notify(messageId, ProgressBuilder.Build());
}
catch
{
}
}
public void DownloadCompleted(string filenametitle, string Message, string filepath, int messageId)
{
try
{
if (!channelInitialized)
{
CreateNotificationChannel();
}
var CompletedBuilder = new NotificationCompat.Builder(AndroidApp.Context, channelId)
.SetContentTitle(filenametitle)
.SetContentText(Message)
.SetAutoCancel(true)
.SetSmallIcon(Resource.Drawable.checkcircle);
Intent it = OpenFile(filepath, filenametitle);
if (it != null)
{
PendingIntent contentIntent =
PendingIntent.GetActivity(AndroidApp.Context,
pendingIntentId,
it,
PendingIntentFlags.OneShot
);
CompletedBuilder.SetContentIntent(contentIntent);
}
var notification = CompletedBuilder.Build();
manager.Notify(messageId, notification);
}
catch (Exception ex)
{
}
}
public void FileCancelled(string filenametitle, string Message, int messageId)
{
if (!channelInitialized)
{
CreateNotificationChannel();
}
var CompletedBuilder = new NotificationCompat.Builder(AndroidApp.Context, channelId)
.SetContentTitle(filenametitle)
.SetContentText(Message)
.SetAutoCancel(true)
.SetSmallIcon(Resource.Drawable.checkcircle)
.SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate);
var notification = CompletedBuilder.Build();
manager.Notify(messageId, notification);
}
public void ReceiveNotification(string title, string message)
{
}
void CreateNotificationChannel()
{
manager = (NotificationManager)AndroidApp.Context.GetSystemService(AndroidApp.NotificationService);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var channelNameJava = new Java.Lang.String(channelName);
var channel = new NotificationChannel(channelId, channelNameJava, NotificationImportance.Low)
{
Description = channelDescription
};
manager.CreateNotificationChannel(channel);
}
channelInitialized = true;
}
public Intent OpenFile(string filePath, string fileName)
{
try
{
string application = "";
string extension = fileName.Substring(fileName.IndexOf('.'));
switch (extension.ToLower())
{
case ".doc":
case ".docx":
application = "application/msword";
break;
case ".pdf":
application = "application/pdf";
break;
case ".xls":
case ".xlsx":
application = "application/vnd.ms-excel";
break;
case ".jpg":
case ".jpeg":
case ".png":
application = "image/jpeg";
break;
case ".mp4":
application = "video/mp4";
break;
default:
application = "*/*";
break;
}
Java.IO.File file = new Java.IO.File(filePath);
bool isreadable =
file.SetReadable(true);
string ApplicationPackageName = AppInfo.PackageName;
var context = Android.App.Application.Context;
var component = new Android.Content.ComponentName(context, Java.Lang.Class.FromType(typeof(AndroidX.Core.Content.FileProvider)));
var info = context.PackageManager.GetProviderInfo(component, Android.Content.PM.PackageInfoFlags.MetaData);
var authority = info.Authority;
Android.Net.Uri uri = AndroidX.Core.Content.FileProvider.GetUriForFile(Android.App.Application.Context, authority, file);
Intent intent = new Intent(Intent.ActionView);
System.IO.File.AppendAllText((filePath + "backdebug.txt"), System.Environment.NewLine + "Point 3 uri done ");
intent.SetDataAndType(uri, application);
intent.AddFlags(ActivityFlags.GrantReadUriPermission);
intent.AddFlags(ActivityFlags.NoHistory);
intent.AddFlags(ActivityFlags.NewTask);
System.IO.File.AppendAllText((filePath + "backdebug.txt"), System.Environment.NewLine + "Point 4open file last ");
return intent;
}
catch (Exception ex)
{
Intent it = new Intent();
it.PutExtra("ex", ex.Message);
System.IO.File.AppendAllText((filePath + "backdebug.txt"), System.Environment.NewLine + "Point 4 uri done " + ex.Message);
return it;
}
}
}
}
Here is the sample code to download file in PCL Xamarin from remote server.
I have used PCLStorage library package which is available in Nuget. You just need download and install in your project.
public async void Downloadfile(string Url)
{
try
{
Uri url = new Uri(Url);
var client = new HttpClient();
IFolder rootfolder = FileSystem.Current.LocalStorage;
IFolder appfolder = await rootfolder.CreateFolderAsync("Download", CreationCollisionOption.OpenIfExists);
IFolder dbfolder = await appfolder.CreateFolderAsync("foldername", CreationCollisionOption.OpenIfExists);
IFile file = await dbfolder.CreateFileAsync(strReport_name, CreationCollisionOption.ReplaceExisting);
using (var fileHandler = await file.OpenAsync(PCLStorage.FileAccess.ReadAndWrite))
{
var httpResponse = await client.GetAsync(url);
byte[] dataBuffer = await httpResponse.Content.ReadAsByteArrayAsync();
await fileHandler.WriteAsync(dataBuffer, 0, dataBuffer.Length);
}
}
catch (Exception ex)
{
throw ex;
}
}

Unauthorizedaccessexception{"Invalid cross-thread access."}...is occur

i want to short my url with bitly but an exception is occur when i want to set out string to my text block
private void button1_Click(object sender, RoutedEventArgs e)
{
ShortenUrl(textBox1.Text);
}
enum Format
{
XML,
JSON,
TXT
}
enum Domain
{
BITLY,
JMP
}
void ShortenUrl(string longURL)
{
Format format = Format.XML;
Domain domain = Domain.BITLY;
string _domain;
//string output;
// Build the domain string depending on the selected domain type
if (domain == Domain.BITLY)
_domain = "bit.ly";
else
_domain = "j.mp";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
string.Format(#"http://api.bit.ly/v3/shorten?login={0}&apiKey={1}&longUrl={2}&format={3}&domain={4}",
"username", "appkey", HttpUtility.UrlEncode(longURL), format.ToString().ToLower(), _domain));
request.BeginGetResponse(new AsyncCallback(GetResponse), request);
}
void GetResponse(IAsyncResult result)
{
XDocument doc;
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
string responseString = reader.ReadToEnd();
doc = XDocument.Load(reader.BaseStream);
}
//// var x = from c in doc.Root.Element("data").Elements()
// where c.Name == "url"
// select c;
//XElement n = ((IEnumerable<XElement>)x).ElementAt(0);
// textBox2.Text = ((IEnumerable<String>)x).ElementAt(0);
lista = (from Born_rich in doc.Descendants("url")
select new a()
{
shrtenurl = Born_rich.Value
}).ToList();
output = lista.ElementAt(0).shrtenurl;
textBox2.Text = output;
//
//
// textBox2.Text = s;
}
List<a> lista = new List<a>();
String output;
}
public class a
{
public String shrtenurl { set; get; }
}
The calback from HttpWebRequest occurs on a non-UI thread. If you want to change soemthing in the UI you must do it on the UI thread. Fortunatley there is an easy way to do this. You simply use the dispatcher to invoke the code in question on the UI.
Dispatcher.BeginInvoke(() => textBox2.Text = output);

Resources