Visual Studio Xamarin iOS ThreadAccessException calling a Task UIKit method [duplicate] - c#

I am having a issue accesing a text box in a view controller .cs file
async partial void loginUser(UIButton sender)
{
// Show the progressBar as the MainActivity is being loade
Console.WriteLine("Entered email : " + txtEmail.Text);
// Create user object from entered email
mCurrentUser = mJsonHandler.DeserialiseUser(txtEmail.Text);
try
{
Console.WriteLine("Starting network check");
// Calls email check to see if a registered email address has been entered
if (EmailCheck(txtEmail.Text) == true)
{
await CheckPassword();
}
else
{
UIAlertView alert = new UIAlertView()
{
Title = "Login Alert",
Message = "Incorrect email or password entered"
};
alert.AddButton("OK");
alert.Show();
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("An error has occured: '{0}'", ex);
}
It is within this funciton that it complains it cannot access a text box which is on a aynsc method
public Task CheckPassword()
{
return Task.Run(() =>
{
// Creates instance of password hash to compare plain text and encrypted passwords.
PasswordHash hash = new PasswordHash();
// Checks password with registered user password to confirm access to account.
if (hash.ValidatePassword(txtPassword.Text ,mCurrentUser.password)==true)
{
Console.WriteLine("Password correct");
UIAlertView alert = new UIAlertView()
{
Title = "Login Alert",
Message = "Password Correct Loggin In"
};
alert.AddButton("OK");
alert.Show();
//insert intent call to successful login here.
}
else
{
UIAlertView alert = new UIAlertView()
{
Title = "Login Alert",
Message = "Incorrect email or password entered"
};
alert.AddButton("OK");
alert.Show();
}
Console.WriteLine("Finished check password");
});
}
Its this line the error occurs:
txtPassword.Text
The error is as follows:
UIKit.UIKitThreadAccessException: UIKit Consistency error: you are
calling a UIKit method that can only be invoked from the UI thread.
Also my Password Correct does not show even though if it is a correct password.
Do i have to run the UI Alerts on a seperate thread?

Any UIKit methods must be called from the UI thread (or Main thread, Main queue, etc.). This ensures consistency in the UI. Xamarin adds a check to all UIKit methods in debug mode and throws that exception if you try to use a background thread to change the UI.
The solution is simple: only modify the UI from the UI thread. That essentially means if you're using a class with "UI" in front it, you should probably do it from the UI thread. (That's a rule of thumb and there are other times to be on the UI thread).
How do I get my code on this mythical UI thread? I'm glad you asked. In iOS, you have a few options:
When in a subclass of NSObject, InvokeOnMainThread will do the trick.
From anywhere, CoreFoundation.DispatchQueue.MainQueue.DispatchAsync will always work.
Both of those methods just accept an Action, which can be a lambda or a method.
So in your code, if we add an InvokeOnMainThread (because I think this is in your UIViewController subclass)...
public Task CheckPassword()
{
return Task.Run(() =>
{
// Creates instance of password hash to compare plain text and encrypted passwords.
PasswordHash hash = new PasswordHash();
// Checks password with registered user password to confirm access to account.
InvokeOnMainThread(() => {
if (hash.ValidatePassword(txtPassword.Text ,mCurrentUser.password)==true)
{
Console.WriteLine("Password correct");
UIAlertView alert = new UIAlertView()
{
Title = "Login Alert",
Message = "Password Correct Loggin In"
};
alert.AddButton("OK");
alert.Show();
//insert intent call to successful login here.
}
else
{
UIAlertView alert = new UIAlertView()
{
Title = "Login Alert",
Message = "Incorrect email or password entered"
};
alert.AddButton("OK");
alert.Show();
}
});
Console.WriteLine("Finished check password");
});
}

Maybe this helps someone. So I will add what solve my issue, that was the same of rogue.
Follow the code that avoid this error of consistency in xamarin forms when used in iOS
await Task.Run(async () =>
{
await Device.InvokeOnMainThreadAsync(async () =>
{
await MaterialDialog.Instance.SnackbarAsync(message: "Bla bla bla",
msDuration: MaterialSnackbar.DurationShort).ConfigureAwait(false);
}).ConfigureAwait(false);
}).ConfigureAwait(false);

Related

How to call a function inside Firebase email login Task in Unity?

On My unity app, inside login using email and password, I am using firebase login. My problem is when I try to call a function or execute a group code inside the task nothing happen. Not even an error. How to call a function inside this.
public void signin()
{
auth.SignInWithEmailAndPasswordAsync(email.text, password.text).ContinueWith(task => {
if (task.IsCanceled)
{
Debug.LogError("SignInWithEmailAndPasswordAsync was canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError("SignInWithEmailAndPasswordAsync encountered an error: " + task.Exception);
return;
}
Firebase.Auth.FirebaseUser newUser = task.Result;
//========Checking Email Verified Or Not======
if(auth.CurrentUser.IsEmailVerified== true)
{
Debug.LogFormat("User signed in successfully: {0} ({1})",newUser.DisplayName, newUser.UserId);
//=================Here is the Problem this is not calling==========
MyFunction();
//==============================================
}
else
{
print("Email Not Verified");
auth.SignOut();
}
//===========
});
}
In general the most common issue with these kind of things is that most of the Unity API can only be used on the main thread.
The Task.ContinueWith is not guaranteed to be executed in the main thread but might happen in any async background thread/task
Therefore Firebase provides an extension method ContinueWithOnMainThread you should use instead, which makes sure the inner codeblock is executed on the Unity main thread.
So simply replace
auth.SignInWithEmailAndPasswordAsync(email.text, password.text).ContinueWith(task => {
with
auth.SignInWithEmailAndPasswordAsync(email.text, password.text).ContinueWithOnMainThread(task => {
make sure you have using Firebase.Extensions at the top of your file

Show AlertDialog on HttpClient exception (Xamarin Android)

My Xamarin Android app utilizes a Web service, which it connects to using HttpClient. On no connection (for exmaple when the user has no cell nor WiFi connection), an excpetion is thrown. I'm using async/await to get the data from the server. Here is an excerpt from my code:
public async Task<String> doLogin(string username, string password)
{
String url = Constants.loginEndpoint + username + "/" + password + "/";
var uri = new Uri(string.Format(url, string.Empty));
return_string = "";
try
{
var response = await GetAsync(uri);
if (response.IsSuccessStatusCode)
{
return_string = "success";
// Process the positive response here
else
{ }
}
catch (Exception ex)
{
throw new ConnectionException();
}
return return_string;
}
I defined a custon ConnectionException and want to show an AlertDialog to the user to inform them, that the request failed due to no connection. After the user clicks OK I want to close the app. I tried to show the alert dialog in the following way, but it's not working:
public class ConnectionException : Exception
{
public ConnectionException()
{
AlertDialog.Builder alert = new AlertDialog.Builder(myApp.Context);
alert.SetTitle("Failure");
alert.SetMessage("Request failed. No connection.");
alert.SetPositiveButton("OK", (senderAlert, args) =>
{
});
Dialog dialog = alert.Create();
dialog.Show();
}
public ConnectionException(string message)
: base(message)
{ }
public ConnectionException(string message, Exception innerException)
: base(message, innerException)
{ }
}
Is this the right approach? Probably not, as it's not working. I would appreciate any help on how to achieve this. Also, I've not given it too much thought, but is this a preferred way to handle such exceptions?
Assuming that your myApp.Context is an Activity and it has no back stack, you can just call Finish()
var context = myApp.Context; // this needs to be an Activity-based context...
context.RunOnUiThread(() =>
{
var alertDialog = new AlertDialog.Builder(context)
.SetTitle("Failure")
.SetMessage("Request failed. No connection.")
.SetPositiveButton("OK", (senderAlert, args) =>
{
context.Finish();
})
.Create();
alertDialog.Show();
});
Are you reusing this exception in several places, or is this a one off?
If your only using this exception once, there is no real reason to build your own.
You may as well just capture the exception and post your alert from inside your catch.
I know that's not as pretty of a way to write the catch, but if it works why not use it.
Side note:
DisplayAlert may be easier for you as well. It'll be a one liner.
Example:
await DisplayAlert("Failure","Request failed. No connection.", "Ok");
The way you are handling possible errors contains multiple issues and is not the right approach for several reasons.
First: Your code doesn't follow C-Sharp conventions and contains several code-smells. I show you a better and more accepted style.
1) Methods in C# normally starts with an uppercase letter. doLogin becomes Login
2) To create a new Uri instance you do not need to format your url-string. The string.Empty won't be used. So the code can be simplified into await GetAsync(new Uri(...));
3) The return_string seems not to be used in any way outside the method. It is string.Empty or "success". Why not switch it to bool? That way you can easily check if the login was successful. The return-type becomes bool instead of string.
The method looks now like this:
public async Task<bool> Login(string username, string password)
{
//TODO: Do parameter check for username and password
try
{
var response = await GetAsync(new Uri(Constants.loginEndpoint + username + "/" + password + "/"));
if (response.IsSuccessStatusCode)
{
// Process the positive response here
return true;
}
else
{
return false;
}
}
catch (Exception ex)
{
throw new ConnectionException();
}
return false;
}
Second, as mentioned by #Jason, an exception should not contain any UI or business logic. Consider the following, which will break your current implementation.
public async Task<bool> Login(string username, string password)
{
var connectionEx = new ConnectionException();
try
{
...
}
catch (Exception ex)
{
throw connectionEx;
}
...
}
Now your user will see the exception even so there wasn't any.
The last thing is that I recommend not to catch the exception just to throw your custom exception. The reason is, that there might be other things that raise an exception too. For example something is null in the positive response handling.
Depending on how the Login method is used, for example directly in an Android Activity, I would do something like that:
public async Task Login(string username, string password)
{
//TODO: Do parameter check for username and password
try
{
var response = await GetAsync(new Uri(Constants.loginEndpoint + username + "/" + password + "/"));
if (response.IsSuccessStatusCode)
{
// Process the positive response here
}
else
{
var alertDialog = new AlertDialog.Builder(context)
.SetTitle("Failure")
.SetMessage("Request failed.")
.SetPositiveButton("OK", (senderAlert, args) =>
{
Finish();
})
.Create();
alertDialog.Show();
}
}
catch (Exception ex)
{
var alertDialog = new AlertDialog.Builder(context)
.SetTitle("Failure")
.SetMessage("Something went wrong (" + ex.Message +")")
.SetPositiveButton("OK", (senderAlert, args) =>
{
Finish();
})
.Create();
alertDialog.Show();
}
}

Kicking Discord Users Through Bot in CS

I'm creating a discord bot, and I'm struggling to figure out how to kick a user. I can't find code or documentation on this. Here's the code
private void MuteUserCommand()
{
commands.CreateCommand("stfu")
.Parameter("PersonToKick", ParameterType.Required)
.Do(async (e) =>
{
string persontomute = e.GetArg("PersonToKick");
if (e.User.ServerPermissions.MuteMembers == false)
{
await e.Channel.SendMessage("Yoo cahnot tells " + persontomute + " to shut up, iz not enuff poweh!");
}
else
{
await e.Channel.SendMessage("Shut up " + persontomute + "!");
//How do I kick persontomute here?
}
});
}
Update: I'm using Discord.NET 0.9.6
This is the command I use to kick people, checking for the correct permissions etc. (Make sure the bot has kick permissions itself ofcourse)
_client.GetService<CommandService>().CreateGroup("user", cgb =>
{
cgb.CreateCommand("kick")
.Description("Kick a user from the Server.")
.Parameter("User", ParameterType.Required)
.AddCheck((command, user, channel) => !paused)
.Do(async e =>
{
try
{
if (e.User.ServerPermissions.KickMembers)
{
User user = null;
try
{
// try to find the user
user = e.Server.FindUsers(e.GetArg("User")).First();
}
catch (InvalidOperationException)
{
await e.Channel.SendMessage($"Couldn't kick user {e.GetArg("User")} (not found).");
return;
}
// double safety check
if (user == null) await e.Channel.SendMessage($"Couldn't kick user {e.GetArg("User")} (not found).");
await user.Kick();
await e.Channel.SendMessage($"{user.Name} was kicked from the server!");
}
else
{
await e.Channel.SendMessage($"{e.User.Name} you don't have the permission to kick.");
}
}
catch (Exception ex)
{
// needs a better error handling haven't changed it since i tested it xD
await e.Channel.SendMessage(ex.Message);
}
});
You seem to be using Discord.Net. You need to get the user from the server and then kick them, as explaned in the documentation.
EDIT: my bad, I took this snippet from a friend's bot and forgot it used a custom extension. I added a "vanilla" way, taken straight from the sample bot.
var user = await _client.FindUser(e, e.Args[0], e.Args[1]); //args[0]would be the username, args[1] would be the discriminator (the random number that follows the discord id)
if (user == null) return;
await user.Kick();
Old
string err;
User user = e.Server.Users.FindUser(persontomute, out err);
if(user != null)
{
await user.Kick();
await e.Channel.SendMessage("Shut up " + persontomute + "!");
}

Can' t send Email through android app

I'm using C# for Visual studio, and i wanted to send an Email through my app, but i always get an error
here's my code
Button button = FindViewById<Button>(Resource.Id.myButton);
button.Click += delegate
{
var email = new Intent(Android.Content.Intent.ActionSend);
email.PutExtra(Android.Content.Intent.ExtraEmail, new string[] { "person1#gmail.com", "person2#gmail.com" });
email.PutExtra(Android.Content.Intent.ExtraCc, new string[] { "person3#gmail.com" });
email.PutExtra(Android.Content.Intent.ExtraSubject, "Hello Email");
email.PutExtra(Android.Content.Intent.ExtraText, "Hello user");
email.SetType("message/rfc822");
StartActivity(email);
};
and i always get this error
Android.Content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.SEND typ=message/rfc822 flg=0x1 (has clip) (has extras) }
can someone help me?
I means that it can't find an activity to handle the action SEND.
Are you running this on code in a simulator? Try it on a physical device (make sure it has a mail client installed).
Also, I would surround your code in a try/catch block, to avoid crashing if there's no activity available to handle your intent.
** Update **
Here's an example. Put the try/catch block inside your delegate.
try
{
var email = new Intent(Android.Content.Intent.ActionSend);
email.PutExtra(Android.Content.Intent.ExtraEmail, new string[] { "person1#gmail.com", "person2#gmail.com" });
email.PutExtra(Android.Content.Intent.ExtraCc, new string[] { "person3#gmail.com" });
email.PutExtra(Android.Content.Intent.ExtraSubject, "Hello Email");
email.PutExtra(Android.Content.Intent.ExtraText, "Hello user");
email.SetType("message/rfc822");
StartActivity(email);
}
catch (Exception ex)
{
// Either ignore or log the error.
}

Can I get a phone number by user id via Telegram Bot API?

I am using Telegram Bot API for sending instant messages to users.
I have installed nuget package. This package is recommend by telegram developers.
I have created a telegram bot and successfully got access to it by using code. When I send messsage to bot, bot gets some info about sender.
I need the phone numbers of users to identify them in our system and send the information back to them.
My question is Can i get a user phone number by telegramUserId?
I'm doing it for user convenience. If I could to get a user phone number I should't have to ask for it from the user.
Now my command like this:
debt 9811201243
I want
debt
It's possible with bots 2.0 check out bot api docs.
https://core.telegram.org/bots/2-0-intro#locations-and-numbers
https://core.telegram.org/bots/api#keyboardbutton
No, unfortunately Telegram Bot API doesn't return phone number. You should either use Telegram API methods instead or ask it explicitly from the user. You cannot get "friends" of a user as well.
You will definitely retrieve the following information:
userid
first_name
content (whatever it is: text, photo, etc.)
date (unixtime)
chat_id
If user configured it, you will also get last_name and username.
With Telegram Bot API, you can get the phone number only when you request it from the user, but the user does not have to write the number, all he must do is to press a button in the conversation and the number will be sent to you.
When user clicks on /myNumber
The user has to confirm:
You will get his number
This. is the console output:
Take a look at this Simple console application, but you need to do some changes to handle the number:
In Handler.ch add the following lines to BotOnMessageReceived
if (message.Type == MessageType.Contact && message.Contact != null)
{
Console.WriteLine($"Phone number: {message.Contact.PhoneNumber}");
}
This is the piece of code needed in case the repository is deleted someday:
Program.cs
public static class Program
{
private static TelegramBotClient? bot;
public static async Task Main()
{
bot = new TelegramBotClient(/*TODO: BotToken hier*/);
User me = await bot.GetMeAsync();
Console.Title = me.Username ?? "My awesome bot";
using var cts = new CancellationTokenSource();
ReceiverOptions receiverOptions = new() { AllowedUpdates = { } };
bot.StartReceiving(Handlers.HandleUpdateAsync,
Handlers.HandleErrorAsync,
receiverOptions,
cts.Token);
Console.WriteLine($"Start listening for #{me.Username}");
Console.ReadLine();
cts.Cancel();
}
}
Handlers.cs
internal class Handlers
{
public static Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken)
{
var errorMessage = exception switch
{
ApiRequestException apiRequestException => $"Telegram API Error:\n[{apiRequestException.ErrorCode}]\n{apiRequestException.Message}",
_ => exception.ToString()
};
Console.WriteLine(errorMessage);
return Task.CompletedTask;
}
public static async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
{
var handler = update.Type switch
{
UpdateType.Message => BotOnMessageReceived(botClient, update.Message!),
_ => UnknownUpdateHandlerAsync(botClient, update)
};
try
{
await handler;
}
catch (Exception exception)
{
await HandleErrorAsync(botClient, exception, cancellationToken);
}
}
private static async Task BotOnMessageReceived(ITelegramBotClient botClient, Message message)
{
Console.WriteLine($"Receive message type: {message.Type}");
if (message.Type == MessageType.Contact && message.Contact != null)
{
// TODO: save the number...
Console.WriteLine($"Phone number: {message.Contact.PhoneNumber}");
}
if (message.Type != MessageType.Text)
return;
var action = message.Text!.Split(' ')[0] switch
{
"/myNumber" => RequestContactAndLocation(botClient, message),
_ => Usage(botClient, message)
};
Message sentMessage = await action;
Console.WriteLine($"The message was sent with id: {sentMessage.MessageId}");
static async Task<Message> RequestContactAndLocation(ITelegramBotClient botClient, Message message)
{
ReplyKeyboardMarkup requestReplyKeyboard = new(
new[]
{
// KeyboardButton.WithRequestLocation("Location"), // this for the location if you need it
KeyboardButton.WithRequestContact("Send my phone Number"),
});
return await botClient.SendTextMessageAsync(chatId: message.Chat.Id,
text: "Could you please send your phone number?",
replyMarkup: requestReplyKeyboard);
}
static async Task<Message> Usage(ITelegramBotClient botClient, Message message)
{
const string usage = "/myNumber - to send your phone number";
return await botClient.SendTextMessageAsync(chatId: message.Chat.Id,
text: usage,
replyMarkup: new ReplyKeyboardRemove());
}
}
private static Task UnknownUpdateHandlerAsync(ITelegramBotClient botClient, Update update)
{
Console.WriteLine($"Unknown update type: {update.Type}");
return Task.CompletedTask;
}
}

Categories

Resources