Reputation: 681
For the many threads, blogs, examples and tutorials on the topic of Broadcast Receivers and mobile data connectivity i have not seen this question asked or answered.
I believe, based on experimenting with one of my Apps, that the answer to this question is a distinct NO, that while WiFi is enabled, a Broadcast Receiver listening for Mobile Data CONNECTIVITY_CHANGE does not receive a broadcast notification when that event occurs. If i am wrong and have missed something please let me know.
My App is a home screen Widget with two classes, ActiveMobileData is the AppWidgetProvider and ConnectivityChangeReceiver is the BroadcastReceiver. The AppWidgetProvider class is my first App which i put together earlier this year mainly from code widely available in a book, on StackOverflow and on various blogs etc. There is no App just the home screen widget. It simply toggles a home screen icon between red and green to indicate the current mobile data state. It has worked perfectly for several months with about 100 users.
I decided to add the BroadcastReceiver to pick up clicks from Settings. This code is also straight forward - it determines the current state of mobile data, and uses a global boolean variable set by AppWidgetProvider to determine if the home screen icon is red or green. Then it simply ensures that the icon color matches the mobile data state.
It all works except when WiFi is enabled it does not get a notification. If there is a way around this limitation i would appreciate hearing about it.
Following is the code for the widget and then for the receiver. I left out some details to keep it somewhat brief. iconEnabled is the shared global boolean variable ...
public class ActiveMobileData extends AppWidgetProvider {
static boolean iconEnabled;
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() != null)
super.onReceive(context, intent);
else {
context.startService(new Intent(context, ToggleService.class));
}
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[]appWidgetIds) {
context.startService(new Intent(context, ToggleService.class));
}
public static class ToggleService extends IntentService {
public ToggleService() {
super("ActiveMobileData$ToggleService");
}
@Override
protected void onHandleIntent(Intent intent) {
ComponentName cn = new ComponentName(this, ActiveMobileData.class);
AppWidgetManager mgr = AppWidgetManager.getInstance(this);
mgr.updateAppWidget(cn, buildUpdate(this));
}
private RemoteViews buildUpdate(Context context) {
RemoteViews updateViews = new RemoteViews(context.getPackageName(), R.layout.widget);
if (!isMobileDataEnabled(getApplicationContext())) {
updateViews.setImageViewResource(R.id.mobileDataState, R.mipmap.ic_launcher_g);
enableMobileData(getApplicationContext(), true);
iconEnabled = true;
} else {
updateViews.setImageViewResource(R.id.mobileDataState, R.mipmap.ic_launcher_r);
enableMobileData(getApplicationContext(), false);
iconEnabled = false;
}
Intent i = new Intent(this, ActiveMobileData.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
updateViews.setOnClickPendingIntent(R.id.mobileDataState, pi);
return updateViews;
}
public boolean isMobileDataEnabled(Context context) {
// ... the code here is the one that uses Java reflection
}
private void enableMobileData(Context context, boolean enabled) {
// ... the code here is the one that uses Java reflection
}
} // public static class ToggleService
} // public class ActiveMobileData
Following is the code for the BroadcastReceiver ...
public class ConnectivityChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive (Context context, Intent intent) {
handleIntent(context);
}
protected void handleIntent(Context context) {
ComponentName cn = new ComponentName(context, ActiveMobileData.class);
AppWidgetManager mgr = AppWidgetManager.getInstance(context);
mgr.updateAppWidget(cn, buildUpdate(context));
}
private RemoteViews buildUpdate(Context context) {
RemoteViews updateViews = new RemoteViews(context.getPackageName(), R.layout.widget);
if (!ActiveMobileData.iconEnabled && isMobileDataEnabled(context)) {
ActiveMobileData.iconEnabled = true;
updateViews.setImageViewResource(R.id.mobileDataState, R.mipmap.ic_launcher_g);
Intent i = new Intent(context, ActiveMobileData.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
updateViews.setOnClickPendingIntent(R.id.mobileDataState, pi);
} else
if (ActiveMobileData.iconEnabled && !isMobileDataEnabled(context)) {
ActiveMobileData.iconEnabled = false;
updateViews.setImageViewResource(R.id.mobileDataState, R.mipmap.ic_launcher_r);
Intent i = new Intent(context, ActiveMobileData.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
updateViews.setOnClickPendingIntent(R.id.mobileDataState, pi);
}
return updateViews;
}
private boolean isMobileDataEnabled(Context context) {
// ... Identical code to that in the AppWidgetProvider
}
} // class ConnectivityChangeReceiver
Upvotes: 2
Views: 666
Reputation: 2070
I don't know offhand, but I can point you to the 2 best places to look.
Best bet would be to look in detail into the JavaDoc for ConnectivityManager.html#CONNECTIVITY_ACTION and then the source code for the ConnectivityManager that is online on GrepCode
In particular comments within the source code often have very informative information that doesn't exist elsewhere.
Update:
After reading the javadoc for CONNECTIVITY_ACTION again, I believe that you are correct because it say A change in network connectivity has occurred. A default connection has either been established or lost.
NOTE: Default Conn. NOT 'A Conn.' So it only gets launched when the 'default' changes. So if you lose 3g/4g/etc while on WIFI then I don't think this gets launched.
However there 'is' something you 'can' do... (but only when your widget is running) (I'm actually not 100% sure a 'widget' CAN do this... b/c I generally work with teaching services/AIDL/ContentProviders/etc (aka. 'backend' stuff within the platform) But you can put a 'refresh' button on your widget that can query to GET ALL NETWORKS and then parse through all that data and display which networks 'are' active, etc.
Also there is the option. You could make pending intents for your broadcast receiver(s) (I'd recommend just 1 BR and have different payloads so you can sort them for what is being notified) then register each of those pending intents as a call back with the ConnectivityManager to notify it whenever a 'network' that 'matches' the NetworkRequest
exists. This will notify you at least when they 'come alive'...
(this next idea would likely require you to make a service with a separate thread to prevent ANR)
now when they 'die'... you 'could' setup a TCP connection and see when it dies... (not 'good' but might be only 'viable' option) (and if you are are 'generous' with trying to not wake up the phone, the battery impact could be minimal)
Upvotes: 1