Xamarin - using async/await and a ProgressDialog - c#

Im building an android app using Xamarin and C#.
The app use restsharp to connect to my server and pull the information I need.
Im trying to build a register page, and I want to check if the user exist.
I want to do this in the background while the user see a ProgressDialog.
This is my code:
if (!string.IsNullOrEmpty(PhoneNumber) && !string.IsNullOrEmpty(Password)
&& !string.IsNullOrEmpty(LicenceId) && LicenceImage.Length > 1)
{
ProgressDialog mDialog = new ProgressDialog(this);
mDialog.SetMessage("Loading data...");
mDialog.SetCancelable(false);
mDialog.Show();
bool checkExistance = await api.CheckIfExist(PhoneNumber);
if (checkExistance)
{
Android.Support.V7.App.AlertDialog.Builder alert = new Android.Support.V7.App.AlertDialog.Builder(this);
alert.SetTitle("");
}
else
{
Intent intent = new Intent(this, typeof(RegisterDone));
StartActivity(intent);
}
}
The ProgressDialog shows but than nothing happedns.
I tried to do it in other ways but still dosent work.
What is the propper way to do it? Thx in advance

I want to check if the user exist. I want to do this in the background while the user see a ProgressDialog
In Android, you could use Handler to implement this feature. When you display a ProgressDialog, you could use Handler open a new thread to execute the verification. When you get the result, send a message to Handler, in Handler class, you could get the result and do your logic. Here is an example :
Display the ProgressDialog and use Handler open a new thread to execute the verification :
mDialog = new ProgressDialog(this);
mDialog.SetMessage("Loading data...");
mDialog.SetCancelable(false);
mDialog.Show();
Action action = () =>
{
checkExistance();
};
handler = new MyHandler(this);
handler.Post(action);
Check if the user exist and return a value, send message to MyHandler :
private async void checkExistance()
{
bool checkExistance = api.CheckIfExist(PhoneNumber);
Message msg = new Message();
msg = handler.ObtainMessage();
if (checkExistance)
{
msg.Arg1 = 0;//tell MyHandler exist
}
else
{
msg.Arg1 = 1;//tell MyHandler didnt exist
}
handler.SendMessage(msg);
}
Handle message in MyHandler :
public class MyHandler : Handler
{
private MainActivity mainActivity;
public MyHandler(MainActivity mainActivity)
{
this.mainActivity = mainActivity;
}
public override void HandleMessage(Message msg)
{
switch (msg.Arg1)
{
case 0:
//true
mainActivity.Exist();
mainActivity.mDialog.Dismiss();
break;
case 1:
//false
mainActivity.Regist();
mainActivity.mDialog.Dismiss();
break;
default:
break;
}
base.HandleMessage(msg);
}
}
Exist() method and Regist() in MainActivity :
public void Exist()
{
Android.Support.V7.App.AlertDialog.Builder alert = new Android.Support.V7.App.AlertDialog.Builder(this);
alert.SetTitle("");
}
public void Regist()
{
Intent intent = new Intent(this, typeof(RegisterDone));
StartActivity(intent);
}
Effect :

Related

How to use PendingIntent instead of an Intent with ConnectivityManager.ConnectivityAction with a callback in C# Xamarin API 28?

I need to make a simple callback in Xamarin, to check if the network status is connected or disconnected.
I have so far been doing it with this code:
class NetworkControl : INetworkControl
{
private readonly INetworkControl.ICallback _callback;
private readonly Context _context;
private readonly NetworkBroadcastReceiver _receiver = new NetworkBroadcastReceiver();
public NetworkControl(INetworkControl.ICallback callback, Context context)
{
_callback = callback;
_context = context;
IntentFilter filter = new IntentFilter(ConnectivityManager.ConnectivityAction);
context.RegisterReceiver(_receiver, filter);
}
public INetworkControl.ICallback Callback => _callback;
public INetworkControl.NetworkStatus Status
{
get
{
var current = Connectivity.NetworkAccess;
if (current == NetworkAccess.Internet)
{
return INetworkControl.NetworkStatus.Connected;
}
return INetworkControl.NetworkStatus.Disconnected;
}
}
}
class NetworkBroadcastReceiver : BroadcastReceiver
{
private static String TAG = "NetworkBroadcastReceiver";
public override void OnReceive(Context context, Intent intent)
{
if (ShellBridge.Instance != null)
{
if (intent.Action.Equals(ConnectivityManager.ConnectivityAction))
{
NetworkInfo ni = (NetworkInfo)intent.Extras.Get(ConnectivityManager.ExtraNetworkInfo);
if (ni.isConnected)
{
// do something if connected
ShellBridge.Instance.NetworkBridge.Callback.NetworkStatusChanged(INetworkControl.NetworkStatus.Connected);
} else
{
ShellBridge.Instance.NetworkBridge.Callback.NetworkStatusChanged(INetworkControl.NetworkStatus.Connected);
}
}
}
}
The problem is, the function ConnectivityManager.ConnectivityAction in the Intent creating is depricated, and will soon be obsolete. After searching, I found that the pendingIntent should be used for that, but I could not find any valid example of how to use it.
The closest to what I need is this:
https://stackoverflow.com/questions/58588132/how-to-use-registernetworkcallback-with-pendingintent
But, it has not all the information I need.
I need it to be all programmatically, without changing the manifest, for, my app should be a fore- and background app.
Please help, and thank you for your time.
You can take a look at NetworkCallback .
public class ConnectionStateMonitor : NetworkCallback
{
NetworkRequest networkRequest;
public ConnectionStateMonitor()
{
networkRequest = new NetworkRequest.Builder().
AddTransportType(TransportType.Cellular).
AddTransportType(TransportType.Wifi).Build();
}
public void enable(Context context) {
ConnectivityManager connectivityManager = context.GetSystemService(Context.ConnectivityService) as ConnectivityManager;
connectivityManager.RegisterNetworkCallback(networkRequest, this);
}
public override void OnAvailable(Network network)
{
//network available
}
public override void OnLost(Network network)
{
//network lost
}
}
Usage
You just need to instantiate the class ConnectionStateMonitor and enable it , you could detect the network status with the method OnAvailable and OnLost .
ConnectionStateMonitor m = new ConnectionStateMonitor ();
m.enable(context);
Refer
https://github.com/xamarin/Essentials/issues/512
ConnectivityManager.CONNECTIVITY_ACTION deprecated
You don't need to reinvent the wheel. You can achieve all that with Xamarin Essentials' Connectivity.
Besides checking if there is a connectivity like this:
var current = Connectivity.NetworkAccess;
if (current == NetworkAccess.Internet)
{
// Connection to internet is available
}
you can also track when the connectivity type changes:
public class ConnectivityTest
{
public ConnectivityTest()
{
// Register for connectivity changes, be sure to unsubscribe when finished
Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
}
void Connectivity_ConnectivityChanged(object sender, ConnectivityChangedEventArgs e)
{
var access = e.NetworkAccess;
var profiles = e.ConnectionProfiles;
}
}

How to update the database?

I have an application that accepts push notifications. I have to write the push notifications to the database. The problem is that in the database it writes only the last message (after I re-enter the application) and when the application is in the background and when in the foreground. What can be done to keep the database up-to-date when new data arrives?
I accept push, after which I write its data into variables
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class MyFirebaseMessagingService : FirebaseMessagingService
{
const string TAG = "MyFirebaseMsgService";
public override void HandleIntent(Intent intent)
{
try
{
if (intent.Extras != null)
{
var builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
foreach (string key in intent.Extras.KeySet())
{
builder.AddData(key, intent.Extras.Get(key).ToString());
}
this.OnMessageReceived(builder.Build());
}
else
{
base.HandleIntent(intent);
}
}
catch (Exception)
{
base.HandleIntent(intent);
}
}
public override void OnMessageReceived(RemoteMessage message)
{
Log.Debug(TAG, "From: " + message.From);
Log.Debug(TAG, "Notification Message Body: " + message.GetNotification().Body);
SendNotification(message.GetNotification().Body, message.Data, message.GetNotification().Title);
// Here I write the push - notification data into variables
X.Instance.title = message.GetNotification().Title;
X.Instance.body = message.GetNotification().Body;
}
private void OnStartCommand()
{
throw new NotImplementedException();
}
public class X
{
public static X Instance = new X();
public string title;
public string body;
}
Here in this class I fill the database:
[Service]
[Activity(Label = "Message")]
public class FormMessageActivity : Activity
{
ListView lstData;
List<Message> lstSource = new List<Message>();
DataBase db;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.FormMessage);
db = new DataBase();
db.CreateDataBase();
lstData = FindViewById<ListView>(Resource.Id.listView);
Message message = new Message()
{
//And here I add to the database
Some = MyFirebaseMessagingService.X.Instance.title,
Notification = MyFirebaseMessagingService.X.Instance.body
};
if (message.Notification != null)
{
db.InsertIntoTableMessage(message);
LoadData();
}
LoadData();
lstData.ItemClick += (s, e) =>
{
//Set background for selected item
for (int i = 0; i < lstData.Count; i++)
{
if (e.Position == i) StartActivity(typeof(MessageActivity));
}
};
}
public void LoadData()
{
lstSource = db.SelectTableMessage();
var adapter = new ListViewAdapter(this, lstSource);
lstData.Adapter = adapter;
}
}
From Your Code it seems like you're creating a DB instance in the onCreate and inputing data in same onCreate method. I can't see how your db is implemented or what kind of db it is, but i would suggest you input your notification in the Db in the OnMessageReceived handler which is called every-time you receive a new notification and not in your onCreate because onCreate is called sparingly and due to certain device configurations like orientation changes.

Xamarin - MediaPicker not showing the videoPicker for iOS

I tried doing this in my main app I'm writing but can't seem to get it to work. So I created a Single View Application to try it out and figure it out but I still seem to be stuck. He's what i got.
public partial class HelloViewController : UIViewController
{
partial class VideoPickerController : UIViewController
{
MediaPicker videoPicker;
readonly TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
public VideoPickerController (IntPtr handle) : base (handle)
{
}
public override void DidReceiveMemoryWarning ()
{
// Releases the view if it doesn't have a superview.
base.DidReceiveMemoryWarning ();
// Release any cached data, images, etc that aren't in use.
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
var picker = new MediaPicker ();
PresentViewController (picker.GetPickPhotoUI (), true, null);
}
}
}
Basically all I get is a white screen and nothing happens. Thanks!
I don't think you need need to to manually present it. This is code that I used and it automatically presents the UI for me. This code is not in it's own viewcontroller, it is just in whatever viewcontroller that I have the button it to take the pic. There are similar methods for the photo album and for taking videos.
Here is a link to the github page where I have this sample app hosted.
var picker = new Xamarin.Media.MediaPicker();
btnCamera.Hidden = !picker.IsCameraAvailable;
btnCamera.TouchUpInside += async (sender, e) =>
{
try
{
MediaFile file =
await picker.TakePhotoAsync(new StoreCameraMediaOptions());
processImage(file);
}
catch { }
};
btnPhoto.Hidden = !picker.PhotosSupported;
btnPhoto.TouchUpInside += async (sender, e) =>
{
try
{
MediaFile file = await picker.PickPhotoAsync();
processImage(file);
}
catch { }
};
private void processImage(MediaFile file)
{
if (file != null)
{
viewModel.Image = file.GetStream();
viewModel.ImagePath = file.Path;
setImage();
}
}
private void setImage()
{
try
{
System.Diagnostics.Debug.WriteLine(viewModel.ImagePath);
if (!string.IsNullOrEmpty(viewModel.ImagePath) &&
System.IO.File.Exists(viewModel.ImagePath))
{
imgImage.Image = new UIImage(NSData.FromFile(viewModel.ImagePath));
}
else if (viewModel.Image != null && viewModel.Image.Length != 0)
{
imgImage.Image = new UIImage(NSData.FromStream(viewModel.Image));
}
}
//just don't load image
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}

Handling events after receiving a MSMQ message (thread issue?)

I created two separate Windows Forms applications in C# that use MSMQ for communicating. Here's how it works, it looked simple enough though:
App1 sends a details request to App2.
App2 creates an event to open the window.
App2 opens a "details" window.
The only problem I have is that when received the message, the "details" window freezes after appearing.
As I handle MSMQ messages handling in an object that uses threads, I suspect the problem comes from there... But I have no experience in handling MSMQ messages or specific events handling between parts of an application.
Here's part of the code I use for App2:
/*Class declared in the Core namespace*/
public class TaskMessageQueueHandler
{
public TaskMessageQueueHandler()
{
this.Start();
}
private Thread m_thread;
private ManualResetEvent m_signal;
public event System.EventHandler messageReceived;
public void Start()
{
m_signal = new ManualResetEvent(false);
m_thread = new Thread(MSMQReceiveLoop);
m_thread.Start();
}
public void Stop()
{
m_signal.Set();
}
protected virtual void SendEvent(object sender, EventArgs e)
{
if (messageReceived != null)
messageReceived(this.message, e);
}
public string message;
private void MSMQReceiveLoop()
{
bool running = true;
MessageQueue queue = new MessageQueue(#".\Private$\queue1");
while (running)
{
try
{
var message = queue.Receive();
message.Formatter = new XmlMessageFormatter(new String[] { "System.String,mscorlib" });
this.message = message.Body.ToString();
string m = this.message;
SendEvent(m, System.EventArgs.Empty);
if (m_signal.WaitOne(10))
{
running = false;
}
}
catch
{
Console.WriteLine("ERROR");
running = false;
}
}
}
}
/*Main process, in the Program namespace*/
[...]
Core.TaskMessageQueueHandler tmqh = new Core.TaskMessageQueueHandler();
EventListener el = new EventListener();
tmqh.messageReceived += new System.EventHandler(el.ShowDetails);
[...]
/* Class in the Program namespace */
class EventListener
{
public void ShowDetails(object sender, EventArgs e)
{
int numero = int.Parse(sender as string);
Details details = new Details(numero);
details.Show();
}
}
Where did I go wrong? Where did I go right?
Thanks a lot,
Stephane.P
EDIT: if the MSMQ handler is stopped with Stop() anywhere around the event sending, the details window appears then disappears right away...
EDIT2: After the workaround given by Slugart, I managed to make this work:
class EventListener
{
Main control;
public EventListener(Main main)
{
control = main;
}
public void ShowDetails(object sender, EventArgs e)
{
int numero = int.Parse(sender as string);
control.Invoke((Action)(() => ShowDetails(numero)));
}
private void ShowDetails(int numero)
{
Details details = new Details(numero);
details.Show();
}
}
Which is used like:
Core.TaskMessageQueueHandler tmqh = new Core.TaskMessageQueueHandler();
EventListener el = new EventListener(this);
tmqh.messageReceived += new System.EventHandler(el.ShowDetails);
You're creating and displaying a form Details on a thread other than the main GUI thread and not an STA thread at that.
Your EventListener should have a reference to a running form (your main form perhaps) and then call form.Invoke() on it.
class EventListener
{
Control control; // A valid running winforms control/form created on an STA thread.
public void ShowDetails(object sender, string message)
{
int numero = int.Parse(message);
control.Invoke(() => ShowDetails(numero))
}
private void ShowDetails(int numero)
{
Details details = new Details(numero);
details.Show();
}
}
Also sending your event data as the sender is not really following the Event pattern that has been put in front of you. You want to use the EventArgs parameter for this, use the EventHandler delegate (EventHandler in your case).

Handle a message I just sent with ActiveMQ and C#

I'm a beginner at using ActiveMQ with C#. I've created a simple windows form with one button and one label. When I click on the button, i send a message to the queue and the label is initialized with the message I just sent. Of course, I could initialize my label directly but I want my form to rather consume the message from the queue in order to update my label.
The problem is I don't manage to handle the message in the same form to update my label. My consumer code is not called at all and yet, its initialized in the Load event of my form.
Here's the code
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
InitializeHandlerAMQ();
}
private void InitializeHandlerAMQ()
{
Tchat tchat = null;
IDestination dest = _session.GetQueue(QUEUE_DESTINATION);
using(IMessageConsumer consumer = _session.CreateConsumer(dest))
{
IMessage message;
while((message = consumer.Receive(TimeSpan.FromMilliseconds(2000))) != null)
{
var objectMessage = message as IObjectMessage;
if(objectMessage != null)
{
tchat = objectMessage.Body as Tchat;
if (tchat != null)
{
textBox2.Text += string.Format("{0}{1}", tchat.Message, Environment.NewLine);
}
}
}
}
}
If I close my windows form and restart it, then my label is well updated but I don't want to close it and re open it.
Do you have any ideas guys ?
Try creating a class with an event delegate like this.
A subscriber class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Apache.NMS;
using Apache.NMS.ActiveMQ;
using Apache.NMS.ActiveMQ.Commands;
namespace Utilities
{
public delegate void QMessageReceivedDelegate(string message);
public class MyQueueSubscriber : IDisposable
{
private readonly string topicName = null;
private readonly IConnectionFactory connectionFactory;
private readonly IConnection connection;
private readonly ISession session;
private readonly IMessageConsumer consumer;
private bool isDisposed = false;
public event QMessageReceivedDelegate OnMessageReceived;
public MyQueueSubscriber(string queueName, string brokerUri, string clientId)
{
this.topicName = queueName;
this.connectionFactory = new ConnectionFactory(brokerUri);
this.connection = this.connectionFactory.CreateConnection();
this.connection.ClientId = clientId;
this.connection.Start();
this.session = connection.CreateSession();
ActiveMQQueue topic = new ActiveMQQueue(queueName);
//this.consumer = this.session.CreateDurableConsumer(topic, consumerId, "2 > 1", false);
this.consumer = this.session.CreateConsumer(topic, "2 > 1");
this.consumer.Listener += new MessageListener(OnMessage);
}
public void OnMessage(IMessage message)
{
ITextMessage textMessage = message as ITextMessage;
if (this.OnMessageReceived != null)
{
this.OnMessageReceived(textMessage.Text);
}
}
#region IDisposable Members
public void Dispose()
{
if (!this.isDisposed)
{
this.consumer.Dispose();
this.session.Dispose();
this.connection.Dispose();
this.isDisposed = true;
}
}
#endregion
}
}
Winforms
In your windows form Subscribe to the queue like this
MyQueueSubscriber QueueSubscriber = new MyQueueSubscriber(QueueName, ActiveMQHost, QueueClientId);
QueueSubscriber.OnMessageReceived += new QMessageReceivedDelegate(QueueSubscriber_OnMessageReceived);
static void QueueSubscriber_OnMessageReceived(string message)
{
SetText(message);
}
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.labelname.value = text;
}
}
Resources:
Unfortunately there are not that many resources to teach C# & ActiveMQ. Try using http://activemq.apache.org/nms/ as this was quite good.
Try looking at a small article from http://www.codersource.net/MicrosoftNet/CAdvanced/PublishSubscribeinCusingActiveMQ.aspx. Disclaimer: This is my website and the article was written by me. Sorry for the self publicity. But I feel this is relevant to the topic.

Categories

Resources