Xamarin.Forms how display internet connection CHANGED? - android

Structure of my app:
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new MdpMainPage();
}
MdpMainPage is a MasterDetailPage:
<MasterDetailPage.Detail>
<NavigationPage>
<x:Arguments>
<pages:HomePage />
</x:Arguments>
</NavigationPage>
</MasterDetailPage.Detail>
My HomePage is a CarouselPage and it contains three pages of type ContentPage.
<CarouselPage>
...some namespaces...
<CarouselPage.Children>
<pages:HomePageA />
<pages:HomePageB />
<pages:HomePageC />
</CarouselPage.Children>
</CarouselPage>
I would like to use JamesMontemagno's ConnectivityPlugin to WATCH for internet connection CHANGES.
DisplayAlert box should pop up when the application starts and it should tell us either:
DisplayAlert("Internet connection found.", "Wait for the application data to update.", "OK");
...or...
DisplayAlert("No internet connection found.", "Application data may not be up to date. Connect to a working network.", "OK");
If there was internet connection at the start of the application and it gets lost during the using of the app, another message box should pop up saying:
DisplayAlert("Internet connection lost.", "Application data may not be up to date. Connect to a working network.", "OK");
If there was no internet connection at the start of the application and somehow later the device connects successfully, the firstly mentioned message box should appear:
DisplayAlert("Internet connection found.", "Wait for the application data to update.", "OK");
I've tried to figure out the correct implementation with the help of the provided Documentation.
Unfortunately, James Montemagno doesn't bother to explain in detail how to use the ConnectivityPlugin, so beginners like myself tend to end up confused.
I know I should use the following code snippets:
/// <summary>
/// Event handler when connection changes
/// </summary>
event ConnectivityChangedEventHandler ConnectivityChanged;
public class ConnectivityChangedEventArgs : EventArgs
{
public bool IsConnected { get; set; }
}
public delegate void ConnectivityChangedEventHandler(object sender, ConnectivityChangedEventArgs e);
CrossConnectivity.Current.ConnectivityChanged += async (sender, args) =>
{
Debug.WriteLine($"Connectivity changed to {args.IsConnected}");
};
...but I don't know where to put them.
I've tried a couple of combinations, but to no avail so far.
Do I put some in the App.xaml and some in the MasterDetailPage?
Or rather one of the Detail pages?
Or in each of the detail pages?
Please don't think I didn't google around. Because I did and everybody seems to have a different opinion on how to flavor the basic Montemagno recipe, which is very confusing.
Could somebody provide the simplest, cleanest way to implement this? Nothing fancy really, just message boxes that inform the user about changes in connectivity.
Help would be much appreciated.
Thank you all.

Let's say you have a dozen pages in your application. It wouldn't make sense to have connectivity code in all of them. A better place to subscribe to the events would be your App.xaml.cs inside the OnStart method (could also be inside the constructor). This is what I have in one of the projects:
protected override void OnStart()
{
CrossConnectivity.Current.ConnectivityChanged += (sender, args) =>
{
MessagingService.Current.SendMessage("connectivityChanged", args.IsConnected);
};
}
MessagingService is from James Montemagno's Xamarin.Forms Toolkit but you can also use Xamarin's Messaging Center.
Then, on each ViewModel of those pages that want to subscribe to this message of a connection change will subscribe to it like this:
MessagingService.Current.Subscribe ("connectivityChanged", async (e) =>
{
//Show a dialog or something to inform about the connectivity change.
});
This way you'll have everything decoupled.
Edit: I just noticed you're probably looking to show the alert from a code behind of the page. You could simply subscribe to the event on your MasterDetailPage like this:
public class MainPageCS : MasterDetailPage
{
public MainPageCS()
{
MessagingService.Current.Subscribe<bool>("connectivityChanged", (args, connected) =>
{
if (connected)
DisplayAlert("Internet connection found.", "Wait for the application data to update.", "OK");
else
DisplayAlert("Internet connection lost.", "Application data may not be up to date. Connect to a working network.", "OK");
});
}
}
Any time the connectivity changes, your App.xaml.cs handles the event and sends a message to the MessagingService which is received by the MasterDetailPage that reacts to it.
Edit 2: Put this into your App.xaml.cs so the connection gets checked only when the app starts.
protected override void OnStart()
{
Device.BeginInvokeOnMainThread(async () =>
{
var isConnected = CrossConnectivity.Current.IsConnected;
await MainPage.DisplayAlert("Connection", $"Connected {isConnected}", "OK");
});
}

Related

How do I handle when user taps on local notification in Xamarin.Forms?

I am so frustrated with the Xamarin documentations. I am looking at doing one of the most basic thing, which is:
When a local notification comes out, a user taps the notification. It launches the App.
How do I handle this so that the app launches and acts according to the notification?
This is the official Xamarin Local Notifications documentation....
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/local-notifications
In Stack Overflow or google searches I can't find any Solutions.
Nothing is mentioned on how to act on a tap to the notification
Ah yes, the docs can be improved! They just assume that you understand it, you should add a pull request or an issue to improve that page!
After you have created:
The INotificationManager interface in the Core project,
The iOS platform changes- iOSNotificationManager, iOSNotificationReceiver, AppDelegate changes
The Android platform changes- AndroidNotificationManager, MainActivity changes, if needed BroadcastReceiver
Then in the constructor of the page where you want to handle the changes, get the instance of the NotificationManager, and then subscribe to the NotificationReceived event which should get called when a user taps on the notification item.
public DashboardPage()
{
InitializeComponent();
notificationManager = DependencyService.Get<INotificationManager>();
notificationManager.NotificationReceived += (sender, eventArgs) =>
{
var evtData = (NotificationEventArgs)eventArgs;
Console.WriteLine("Title & Message: " + evtData.Title + evtData.Message);
};
}
You could try Plugin.LocalNotification too, it has a feature can hanle tap event, like:
public partial class App : Application
{
public App()
{
InitializeComponent();
// Local Notification tap event listener
NotificationCenter.Current.NotificationTapped += OnLocalNotificationTapped;
MainPage = new MainPage();
}
private void OnLocalNotificationTapped(NotificationEventArgs e)
{
// your code goes here
}
}
edit:
On iOS:
You can add code in DidRecieveNotiticationResponse(),like:
public override void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
{
if (response.IsDefaultAction)
{
ProcessNotification(response.Notification);
}
Console.WriteLine("Called");
App.Current.MainPage=new Page();
completionHandler();
}

Hunting a spooky offline.html

I once wrote a quick ‘offline.html‘. Just be be sure I understood how offline PWA worked:
This is offline.html (My App Name)
I thought it worked as expected. So I deleted it and wrote a better one, with a back button, some info etc.
Now when this app is offline it starts on the page the manifest.json days, as expected.
However, if I try to refresh the page that spooky "offline.html" shows up. From nowhere.
I've tried to understand where it lurks, but I can't find it. (The new "offline.htmi" is there when I go to it in the web browser.)
I've tried Firefox offline on my mobile to see if the spoky "offline.html" jumps up there to. It does. 😐
I've cleared the cache in Chrome. The spoky "offline.html" is still there.
Any ideas?
(This is on Android 9)
Offline pages are not exactly stored in caches as regular cached items, there are stored quite differently, relative to the current installed service worker. I tried looking for an article to explain this more but can't walk around any currently.
However you'll have to unregister your service worker or learn how Updating service worker works,
The aim is to remove the lurking offline.html served to the new offline.html that exists.
This is what I finally landed on. It seems to work. The test for 404 is of course not optimal, but I leave it that way for now, hoping that the emerging standard will address this issue soon.
async function setupServiceWorker() {
let reg;
if (navigator.serviceWorker.controller) {
console.log("Active service worker found, no need to register");
// new Popup("registered service worker, .active.scriptURL", reg.active.scriptURL, null, true).show();
reg = await navigator.serviceWorker.getRegistration();
} else {
reg = await navigator.serviceWorker.register("service-worker.js", { scope: "./" });
console.log("Service-worker.js registered, scope: " + reg.scope);
}
try {
if (navigator.onLine) {
const newReg = await reg.update();
}
} catch (err) {
const is404 = err.message.match("404");
new Popup("err", is404 + " " + err.message).show();
if (is404) {
let wasUnregistered;
try {
wasUnregistered = await reg.unregister();
} catch (errUnreg) {
new Popup("unregister() service worker failed", errUnreg.message).show();
}
if (wasUnregistered) {
setTimeout(setupServiceWorker, 1000);
}
}
}
}
setTimeout(setupServiceWorker, 5000);

Xamarin.Forms how display internet connection status?

My HomePage is a CarouselPage and it contains three pages of type ContentPage.
<CarouselPage
...some namespaces...
<CarouselPage.Children>
<pages:HomePageA />
<pages:HomePageB />
<pages:HomePageC />
</CarouselPage.Children>
</CarouselPage>
I'm using JamesMontemagno's ConnectivityPlugin to check if the device has internet connection:
public partial class HomePage : CarouselPage
{
public HomePage()
{
InitializeComponent();
if (IsConnectionAvailable())
{
// download content from external db to device (SQLite db)
DisplayAlert("Internet connection found.", "Wait for the application data to update.", "OK");
}
else
{
DisplayAlert("No internet connection found.", "Application data may not be up to date. Connect to a working network.", "OK");
}
}
public bool IsConnectionAvailable()
{
if (!CrossConnectivity.IsSupported)
return false;
bool isConnected = CrossConnectivity.Current.IsConnected;
//return CrossConnectivity.Current.IsConnected;
return isConnected;
}
}
After splash screen is gone, the message box doesn't show. Based on stepping through code while debugging I assume it kinda shows and then goes away before the splash screen disappears.
So I tried to put the ConnectivityPlugin code into HomePageA:ContentPage, which one of the children of HomePage:CarouselPage. Like so:
public partial class HomePageA : ContentPage
{
public HomePageA()
{
InitializeComponent();
if (IsConnectionAvailable())
{
// download content from external db to device (SQLite db)
DisplayAlert("Internet connection found.", "Wait for the application data to update.", "OK");
}
else
{
DisplayAlert("No internet connection found.", "Application data may not be up to date. Connect to a working network.", "OK");
}
}
public bool IsConnectionAvailable()
{
if (!CrossConnectivity.IsSupported)
return false;
bool isConnected = CrossConnectivity.Current.IsConnected;
//return CrossConnectivity.Current.IsConnected;
return isConnected;
}
private void RegistrationButton_Clicked(object sender, System.EventArgs e)
{
}
}
Now when I run the application, the HomePageA gets displayed, but no message box. Only after I click the BurgerMenu and select Home (effectively selecting HomePageA) the message box pops up.
Same thing happens again:
After splash screen is gone, the message box doesn't show. Based on stepping through code while debugging I assume it kinda shows and then goes away before the splash screen disappears.
Could somebody explain this behavior to me?
How do I make the message box appear AFTER the splash screen goes away?
Thank you all.
===================== U P D A T E =====================
Pavan didn't explain the problem the way I would like to have it explained. For all you good people I found a couple of similar questions and answers with explanations that are adequate.
A decent explanation can be found here and here's another one.
However, I will accept Pavan's answer, because it works and there has been no better answer provided at the time.
DisplayAlert is used in order to do something on the UI thread. and it’s called from a background thread, in order to manipulate the UI, which can only be done on the UI thread.
Try below code for display alert message,
Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
{
App.Current.MainPage.DisplayAlert("Internet connection found.", "Wait for the application data to update.", "OK");
});
it will help you

Best practice for closing websocket in android

I am using this library for connecting to a websocket server from android.
Specifically this part :
AsyncHttpClient.getDefaultInstance().websocket("ws://192.168.2.10:8000/temp" , "my-protocol", new WebSocketConnectCallback() {
#Override
public void onCompleted(Exception ex, WebSocket webSocket) {
if (ex != null) {
ex.printStackTrace();
return;
}
webSocket.send("a string");
webSocket.setStringCallback(new StringCallback() {
#Override
public void onStringAvailable(String s) {
Debug.Log( LOGTAG ,"I got a string: " + s);
}
});
webSocket.close(); // issue here
}
});
I would like to close the socket when I click a button. Now everytime I want to send a message to the socket I open it and close it.
I would like to open it once and keep it alive and close it when I click a close button. My idea was to pass a variable to the WebSocketConnectCallback and make a static variable and based on this variable close the socket.
I would like to know what is the best practice in a situation like this.
Use the Application class (http://developer.android.com/reference/android/app/Application.html):
Inherid your own class for Application and here you can track the socket, open it, close it as you need.
See a tutorial (first google match, maybe there is a better one): http://www.intertech.com/Blog/androids-application-class/
So basically extend Application and add your class in the manifest file as application class.
Your may add a timer that might close the socket after several time while not used.

Android / XMPP: presence listener ignores/drops UNAVAILABLE presences

I'm trying to establish a simple xmpp group chat on Android using the smack/asmack xmpp library. And basically everything is working, except: UNAVAILABLE presences, i.e., when a user leaves a chat room, seem to be ignored. Presences from users entering the group chat are fine.
Both my local Openfire server as well as a xmpp client (Instantbird) tell me that there is a UNAVAILABLE presence when user leaves. Only my stuff has troubles.
Here's the main snippet of my code. In short, when a user (re-)enters the room I see the console output ("presenceListener.processPacket"), when a user leaves nothing happens.
public boolean join(String room, user) {
this.chat = new MultiUserChat(this.xmppConn, room);
this.presenceListener = new PacketListener() {
#Override
public void processPacket(Packet packet) {
System.out.println("presenceListener.processPacket");
if (packet instanceof Presence)
// Handle presence
}
};
this.chat.addParticipantListener(this.presenceListener);
this.messageListener = ...
this.chat.addMessageListener(this.messageListener);
...
try {
this.chat.join(user);
...
} catch (...) {
...
}
}
I would understand when the listener would pick up nothing. But this is too weird for me at the moment. I'm happy about any hint...thanks!
Christian
I found a working solution here.
It uses a PacketFilter with a filter for presence packets, attaching it to the xmpp connection; inspite my solution via attaching a ParticipantListener to the MultiUserChat.
I still don't know why my original attempt fails, but...well...it works now.

Categories

Resources