Check whether Internet is on or off in Xamarin Android - android

I am working on Xamarin Android Application.Before proceed to my next fragment I want to check Internet Connection and inform user about it ? How can i implement that ?And how to refresh whole fragment after user switch-on the internet?
Any advice or suggestion will be appreciated !

To get the network status you could use the following method in your activity:
public bool IsOnline()
{
var cm = (ConnectivityManager)GetSystemService(ConnectivityService);
return cm.ActiveNetworkInfo == null ? false : cm.ActiveNetworkInfo.IsConnected;
}
If I understood you correctly from this sentence: And how to refresh whole fragment after user switch-on the internet, You want to detect, whenever any changes in the connection status happens, Therefore you absolutely need to use broadcast receivers.
First of all you should implement a broadcast receiver with a simple Event named ConnectionStatusChanged as follows:
[BroadcastReceiver()]
public class NetworkStatusBroadcastReceiver : BroadcastReceiver
{
public event EventHandler ConnectionStatusChanged;
public override void OnReceive(Context context, Intent intent)
{
if (ConnectionStatusChanged != null)
ConnectionStatusChanged(this, EventArgs.Empty);
}
}
Then in your activity (in OnCreate() method for example, It doesn't matter) create an instance of that receiver and register it:
var _broadcastReceiver = new NetworkStatusBroadcastReceiver();
_broadcastReceiver.ConnectionStatusChanged += OnNetworkStatusChanged;
Application.Context.RegisterReceiver(_broadcastReceiver,
new IntentFilter(ConnectivityManager.ConnectivityAction));
Here is the body of the event handler:
private void OnNetworkStatusChanged(object sender, EventArgs e)
{
if(IsOnline()){
Toast.MakeText(this, "Network Activated", ToastLength.Short).Show();
// refresh content fragment.
}
}
To cut the long story short, NetworkStatusBroadcastReceiver receives any change in the network status of the device and invokes the ConnectionStatusChanged (When user enables data traffic or WiFi connection), Then you catch that event and check for network status using IsOnline() method. Very simple.

You can use the MVVMCross plugin : Connectivity
It wil expose a boolean
/// <summary>
/// Gets if there is an active internet connection
/// </summary>
bool IsConnected { get; }
and a delegate on change state
/// <summary>
/// Event handler when connection changes
/// </summary>
event ConnectivityChangedEventHandler ConnectivityChanged;

Try this :
NetworkStatus internetStatus = Reachability.InternetConnectionStatus();
if(!Reachability.IsHostReachable("http://google.com")) {
// Put alternative content/message here
}
else
{
// Put Internet Required Code here
}

Related

getAccessibilityButtonController Android Accessibility Service

I have been looking at the new methods available for Accessibility in Android O. I ran across this new method called getAccessibilityButtonController, I am unsure precisely what it does and an intended use. I know that in Android O there is a navigation button that can be used for an accessibility service. Does this accessibility button only launch the accessibility service, or could it have other functionality within the service such as to do specific tasks? I am curious possible uses for the accessibility and the getAccessibilityButtonController methods. Thank you for your time.
It can do pretty much anything you want it to. From the android accessibility doc, the button allows you to register a callback that has an onClicked method. If you enable the button and provide said callback you can execute whatever you'd like in the context of that callback.
Edit: The android documentation has been updated so the following should no longer be necessary.
Note that if you read the doc there's currently an example that has a call to getAccessibilityButtonController() within onCreate(). This is incorrect because the controller isn't valid until onServiceConnected is called. I've modified the example below to show something that should work.
private AccessibilityButtonController mAccessibilityButtonController;
private AccessibilityButtonController
.AccessibilityButtonCallback mAccessibilityButtonCallback;
private boolean mIsAccessibilityButtonAvailable;
#Override
protected void onServiceConnected() {
mAccessibilityButtonController = getAccessibilityButtonController();
mIsAccessibilityButtonAvailable =
mAccessibilityButtonController.isAccessibilityButtonAvailable();
if (!mIsAccessibilityButtonAvailable) {
return;
}
AccessibilityServiceInfo serviceInfo = getServiceInfo();
serviceInfo.flags
|= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
setServiceInfo(serviceInfo);
mAccessibilityButtonCallback =
new AccessibilityButtonController.AccessibilityButtonCallback() {
#Override
public void onClicked(AccessibilityButtonController controller) {
Log.d("MY_APP_TAG", "Accessibility button pressed!");
// Add custom logic for a service to react to the
// accessibility button being pressed.
}
#Override
public void onAvailabilityChanged(
AccessibilityButtonController controller, boolean available) {
if (controller.equals(mAccessibilityButtonController)) {
mIsAccessibilityButtonAvailable = available;
}
}
};
if (mAccessibilityButtonCallback != null) {
mAccessibilityButtonController.registerAccessibilityButtonCallback(
mAccessibilityButtonCallback, null);
}
}

Cast custom channel callback never called

I'm working on an Android app that communicates with a Cast receiver app.
Connecting to the app works (I can see the app appear on the tv), but I'm having difficulties getting the custom channel to work.
In the onCreate of my Activity I get the CastContext and add my SessionManagerLister.
mCastContext = CastContext.getSharedInstance(this);
mCastContext.getSessionManager().addSessionManagerListener(getSessionManagerListener(), CastSession.class);
getSessionManagerListener() returns the listener where I register my MessageReceivedCallback:
private SessionManagerListener<CastSession> getSessionManagerListener()
{
return new SessionManagerListener<CastSession>()
{
#Override
public void onSessionStarted(CastSession castSession, String s)
{
try
{
castSession.setMessageReceivedCallbacks("urn:x-cast:be.myappname.player.cast.v1", new Cast.MessageReceivedCallback()
{
#Override
public void onMessageReceived(CastDevice castDevice, String s, String s1)
{
System.out.println("never reaches this callback");
}
});
}
catch (IOException e)
{
e.printStackTrace();
}
}
... other methods omitted ...
}
}
When I tap the Toolbar cast button I can select a device, which triggers the onSessionStarted in the SessionManagerListener (this also starts the receiver app on the tv). I then add the MessageReceivedCallback, but its callback never gets called.
Inspecting my Cast device in Chrome does show the data I'm expecting to receive, it just never seems to reach my Android code.
cast_receiver.js:67 [667.202s] [cast.receiver.IpcChannel] IPC message
[667.202s] [cast.receiver.IpcChannel] IPC message sent: {"namespace":"urn:x-cast:be.myappname.player.cast.v1","senderId":"7c442884-74e6-a388-243c-58b4ab3a4527.3471:com.google.sample.cast.refplayer.tutorial-512","data":"{\"type\":\"login request\"}"}
A colleague is working on the iOS app and that one does receive the callback.
Try the following in onSessionStarted
CastContext cc = CastContext.getSharedInstance(this);
SessionManager sm = cc.getSessionManager();
if (sm != null) {
CastSession cs = sm.getCurrentCastSession();
if (cs != null) {
try {
MyCastChannel mcc = new MyCastChannel();
cs.setMessageReceivedCallbacks("urn:x-cast:be.myappname.player.cast.v1",mcc);
}
catch (IOException e) {
}
}
}
public class MyCastChannel implements Cast.MessageReceivedCallback
{
#Override
public void onMessageReceived(CastDevice castDevice, String namespace, String message)
{
// do your thing
}
}
I had the same problem, this is how I managed to get the message to be sent:
context.sendCustomMessage(namespace, undefined, JSON.stringify({
"a": "b"
}));
This is the javascript on the receiver side. So you need the "undefined" param and also use JSON.stringify(), otherwise the message gets silently dropped.
The undefined means "send to all", but you should probably specify sender-id there.
This is in the v3 API.
In my case, it was more subtle.
The callback worked absolutely fine when the cast session was initiated for the first time. When the user presses the cast button the receiver is registered for the message callback.
override fun onSessionStarted(castSession: CastSession?, p1: String?) {
liveViewModel.requestPause()
castSession?.let {
setCastChannelReceiver(it, this#myActivity)
loadRemoteMedia(it, buildChromeCastInfo())
}
}
fun setCastChannelReceiver(castSession: CastSession?, receiver: CastMessageReceiver) {
castSession?.let {
castChannel.addReceiver(receiver, castSession)
it.setMessageReceivedCallbacks(castChannel.nameSpace, castChannel)
}
}
Although when the user use to kill the Activity which initiated the cast session and then after traversing other parts of app use to again visit the Activity, the callback failed to work.
Remember, when the user visits the Activity for the second time, the CastSession is already connected. As a result the onSessionStarted(castSession: CastSession, p1: String) method is never called.
I was under the assumption that once the receiver has been registered for the session, it need not be registered again. But still for some reason the callback never worked.
As a final resort, just to be assured I re-registered the receiver in the OnCreate() of the Activity.
override fun onCreate(out:Bundle){
....
setCastChannelReceiver(castSession, receiver)
....
}
fun setCastChannelReceiver(castSession: CastSession?, receiver: CastMessageReceiver) {
castSession?.let {
castChannel.addReceiver(receiver, castSession)
it.setMessageReceivedCallbacks(castChannel.nameSpace, castChannel)
}
}
And it worked!!
NOTE: For me, the communication between the Sender(Android App) and Cast Receiver only occurred when the string messages were in JSON format.

Why does Android AppOps (privacy manager) send duplicate events when user change his/her privacy settings?

When a user changes his/her privacy settings through AppOps (e.g. denying an application access to phone contacts), AppOpsManager sends to anyone who listens what the users have changed (i.e. the package name and the operation (e.g. Read contacts)).
So I wrote a listener to do so. However, we the user make only one change, I receive too many duplicate events (e.g. 10 events that the user decided to deny Angry Bird access to his/her location) and then the app crashes.
Here is my code to register listners for each pair of package & operation:
public void startWatchingOperations(AppOpsManager appOps, List<AppOpsManager.PackageOps> opsforapps) {
SharedPreferences myAppListnerPreferences = getSharedPreferences(APP_OPS_PREFERENCES, Activity.MODE_PRIVATE);
for (AppOpsManager.PackageOps o:opsforapps) {
List<OpEntry> opEntry = o.getOps();
//if I already assigned a listener to this pari of package & operation, then skip
if (myAppListnerPreferences.getBoolean(o.getPackageName(), false)==false) {
for (OpEntry entry:opEntry) {
//for each pair of package & operation, assign a new listener
ChangePrivacySettingsListener opsListner = new ChangePrivacySettingsListener(getApplicationContext());
appOps.startWatchingMode(entry.getOp(),o.getPackageName(),opsListner);
}
myAppListnerPreferences.edit().putBoolean(o.getPackageName(), true).apply();
}
}
}
Here is a snippet of the listener
public class ChangePrivacySettingsListener implements AppOpsManager.Callback {
public void opChanged(int op, String packageName) {
AppOpsManager appOps= (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
PackageManager pkg = context.getPackageManager();
try {
//this is an object to store the event: package name,
// the operation that has been changed, & time stamp
PrivacySetting privacySetting = new PrivacySetting();
privacySetting.setPackageName(packageName);
privacySetting.setOperation(OPERATIONS_STRINGS[op]);
privacySetting.setDecisionTime(Calendar.getInstance(TimeZone.getDefault()).getTimeInMillis());
privacySetting.setUserId(userId);
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Her is the part of AppOpsManager.java that allows me to listen to user's changes.
public class AppOpsManager {
final HashMap<Callback, IAppOpsCallback> mModeWatchers
= new HashMap<Callback, IAppOpsCallback>();
public void startWatchingMode(int op, String packageName, final Callback callback) {
synchronized (mModeWatchers) {
IAppOpsCallback cb = mModeWatchers.get(callback);
if (cb == null) {
cb = new IAppOpsCallback.Stub() {
public void opChanged(int op, String packageName) {
callback.opChanged(op, packageName);
}
};
mModeWatchers.put(callback, cb);
}
try {
mService.startWatchingMode(op, packageName, cb);
} catch (RemoteException e) {
}
}
}
I double checked to ensure that I've never assigned more than one listener to each pair of package & operation.
I would appreciate hints about potential causes.
Here is a link to AppOpsManager.java
Try moving the deceleration of ChangePrivacySettingsListener opsListner to be out side of the for block:
public void startWatchingOperations(AppOpsManager appOps, List<AppOpsManager.PackageOps> opsforapps) {
ChangePrivacySettingsListener opsListner;
SharedPreferences myAppListnerPreferences = getSharedPreferences(APP_OPS_PREFERENCES, Activity.MODE_PRIVATE);
for (AppOpsManager.PackageOps o:opsforapps) {
List<OpEntry> opEntry = o.getOps();
//if I already assigned a listener to this pari of package & operation, then skip
if (myAppListnerPreferences.getBoolean(o.getPackageName(), false)==false) {
for (OpEntry entry:opEntry) {
//for each pair of package & operation, assign a new listener
opsListner = new ChangePrivacySettingsListener(getApplicationContext());
appOps.startWatchingMode(entry.getOp(),o.getPackageName(),opsListner);
}
myAppListnerPreferences.edit().putBoolean(o.getPackageName(), true).apply();
}
}
}
And please let me know what happened?
Just in case this is helpful to someone, up to at least Android Oreo, calling AppOpsManager.startWatchingMode(op, packageName, callback) will cause callback to be invoked when the setting is changed (1) for the op with any package, AND (2) for any AppOps setting changes with packageName. This can be seen from the AppOpsService.java source, particularly AppOpsService.startWatchingMode() which registers the callback, AppOpsService.setMode() which calls the callback when the AppOps setting is changed.
For example, if you register a callback with startWatchingMode(appOps1, package1, callback) and startWatchingMode(appOps2, package1, callback),
when there is a change in the setting for appOps3 for package1, the callback will be called twice since you have registered for package1 two times. If there is a change in appOps1 for package1, the callback will be invoked 3 times, because you have registered once for appOps1, and twice for package1.
The solution is to register either for the set of AppOps you are interested in (without duplications), with the packageName parameter set to null, or register for the set of packages you are interested in, with op parameter set to AppOpsManager.OP_NONE.
Also you need to ensure that all listeners are unregistered (e.g. in onDestroy of your activity) using stopWatchingMode. Otherwise, the callback entries will accumulate across Activity lifecycles (until the app is terminated) and you will start getting duplicates. This also means that you should keep references to all the listeners created.

Listener for enable/disable mobile data (not connected or disconnnected)

I test some actions (see below).
ConnectivityManager.CONNECTIVITY_ACTION
WifiManager.NETWORK_STATE_CHANGED_ACTION
PhoneStateListener.LISTEN_DATA_CONNECTION_STATE (it is not actually action)
PhoneStateListener.LISTEN_DATA_CONNECTION_STATE (it is not actually action)
But they listen only state (connected or disconnected).
When wifi disconnected, It can listen (enable mobile data -> connnected -> broadcast - > listener)
When wifi copnnnected, It cannot listen (enable mobile data -> connetivity does not changed!)
I need wheather mobile data settings is enable or not
Can I listen mobile data enabled or disabled event?
While there's no broadcast by the system for this, we can actually use a ContentObserver to get notified of when the user toggles the Mobile Data setting.
e.g:
ContentObserver mObserver = new ContentObserver(new Handler()) {
#Override
public void onChange(boolean selfChange, Uri uri) {
// Retrieve mobile data value here and perform necessary actions
}
};
...
Uri mobileDataSettingUri = Settings.Secure.getUriFor("mobile_data");
getApplicationContext()
.getContentResolver()
.registerContentObserver(mobileDataSettingUri, true,
observer);
Don't forget to unregister the observer appropriately! E.g.
getContentResolver().unregisterContentObserver(mObserver);
So, after digging into it a bit, it doesn't seem like any broadcasts are sent out when that value is changed. Even the mobile network settings fragment in the Android Settings app doesn't listen for changes; it only checks in onCreate() and onResume(). So it seems you can't listen for changes, but you can get the current state. Unfortunately, it's a private API so you'll have to use reflection:
public static boolean isMobileDataEnabled(Context ctx) {
try {
Class<?> clazz = ConnectivityManager.class;
Method isEnabled = clazz.getDeclaredMethod("getMobileDataEnabled", null);
isEnabled.setAccessible(true);
ConnectivityManager mgr = (ConnectivityManager)
ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
return (Boolean) isEnabled.invoke(mgr, null);
} catch (Exception ex) {
// Handle the possible case where this method could not be invoked
return false;
}
}

Android - Listening to NFC Adapter State Changed

I am trying to build an application which uses NFC. The goal is to display a DialogFragment containing a button link to go the settings and change it manually and when the feature is enabled, disable the DialogFragment.
Problem: If the user enables/disables NFC using the icon in the pull down notifications tray , then the onPause/onResume doesn't get called and misses the condition entirely.
I am sure there is a receiver that I can register to instead and respond appropriately in real time. Any ideas, thoughts or reference will be greatly appreciated!
The following code checks if the state is enabled/disabled. I am also responding to it appropriately in the onResume event.
NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE);
NfcAdapter adapter = manager.getDefaultAdapter();
if(adapter != null && adapter.isEnabled()) {
detector = new NfcDetector(this);
detector.setListener(this);
onNfcFeatureFound();
}
else {
onNfcFeatureNotFound();
}
For others looking at this post, the code below will take the user directly into settings to enable/disable NFC:
startActivity(new Intent(android.provider.Settings.ACTION_NFC_SETTINGS));
Thought I should post the answer for other people looking for the same problem, since I wasn't able to find one easily.
Add the following code to your activities onCreate() method:
IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
this.registerReceiver(mReceiver, filter);
Inner private class declared within your activity (or anywhere else you like):
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED)) {
final int state = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE,
NfcAdapter.STATE_OFF);
switch (state) {
case NfcAdapter.STATE_OFF:
break;
case NfcAdapter.STATE_TURNING_OFF:
break;
case NfcAdapter.STATE_ON:
break;
case NfcAdapter.STATE_TURNING_ON:
break;
}
}
}
};
#Override
public void onDestroy() {
super.onDestroy();
// Remove the broadcast listener
this.unregisterReceiver(mReceiver);
}
// The following check needs to also be added to the onResume
#Override
protected void onResume()
super.onResume();
// Check for available NFC Adapter
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
if(adapter != null && adapter.isEnabled()) {
createNfcDetector();
//NFC is available on device, but disabled
}
else {
//NFC Is available and enabled
}
}
You can use ACTION_ADAPTER_STATE_CHANGED to receive a broadcast message when the state of the adapter changes, but that option is only available in API 18 and above. See this for the documentation.
For prior to 18, I don't know of a way to do this unfortunately.
Also, as an aside, the android.provider.Settings.ACTION_NFC_SETTINGS will work on API levels 16 and above. For prior versions, the NFC settings are under "wireless settings". Take a look at the ensureSensorIsOn method at the bottom of this blog post for a code sample that checks against the API level and redirects to the correct settings pane.

Categories

Resources