-1

I have built an app widget that simply fetches data at regular intervals so it has no activities; not even a config activity. Is it possible to register a broadcast receiver using the following code without the use of an activity?

context.registerReceiver(networkChangeReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));

Basically I want the widget to automatically refresh whenever a device has reconnected to a network instead of waiting for the next refresh.

I have tried doing this in the AppWidgetProvider's onEnabled() and onUpdate() methods but it throws a android.content.ReceiverCallNotAllowedException so, I don't know where else I can do this.

The manifest isn't an option either as ConnectivityManager.CONNECTIVITY_ACTION is ignored SDK 24+.

Ken
  • 499
  • 6
  • 18

2 Answers2

0

First of all let's determine lifecycle of appwidgets. AppWidgetProvider is a BroadcastReceiver by default, so it can live for a short time (onReceive() have 10 seconds timeout - so onUpdate() and onEnabled(), and all other callbacks of AppWidgetProvider will be executed on onReceive() callback). So it's not an option to register a dynamic broadcast receiver. You have only one option (since you don't have any activity and you want to update your widget without ui) - foreground service (yes you need a notification for oreo+ devices, but where is no other ways). You can register your receiver and update on ConnectivityManager.CONNECTIVITY_ACTION and update widget like:

val intent = Intent(this, YourAppWidgetProvider::class.java)
intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
val ids = AppWidgetManager.getInstance(application).getAppWidgetIds(ComponentName(getApplicationContext(), YourAppWidgetProvider::class.java)
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
sendBroadcast(intent)

And in your case it's the only way to update widget if you need to react on dynamic receiver action.

HeyAlex
  • 1,666
  • 1
  • 13
  • 31
  • Thanks for your answer and pointing out the changes to services v8.0+. I'm working with both pre-Oreo and post so having a notification when it's not needed isn't very pretty... But it's pointed me in the right direction I think. – Ken Mar 24 '20 at 18:02
-1

I attempted to use a Service to solve my problem but as HeyAlex pointed out, Services work differently in Oreo v8.0+ of Android and requires you to have a notification appear which is very unsightly as far as UI is concerned.

After doing some further research I discovered a separate Stackoverflow thread regarding these changes:

Android 8.0: java.lang.IllegalStateException: Not allowed to start service Intent

This might not be the best solution as it's deprecated and no longer maintained but, JobServiceIntent works with Oreo and is backwards compatible. You will need the following permissions in the manifest:

<!-- < 8.0 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- >= 8.0, inside application -->
        <service
            android:name="com.example.ListenerService"
            android:permission="android.permission.BIND_JOB_SERVICE" />

I then have my service extending JobIntentService instead of Service and it all works; the network listener is setup without the need of any activity and no notification on Android 8+.

public class ListenerService extends JobIntentService {

    public static final int JOB_ID = 1;

    @Override
    protected void onHandleWork(@NonNull Intent intent) {
         getApplicationContext().registerReceiver(networkChangeReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));

    }

    public static void enqueueWork(Context context, Intent intent) {
        ListenerService.context = context;
        enqueueWork(context, ListenerService.class, JOB_ID, intent);
    }

    private static final BroadcastReceiver networkChangeReceiver = new BroadcastReceiver() {
        // usual broadcast methods in here such as onReceive
    }

}

The service can be triggered using:

ListenerService.enqueueWork(context,intent);
Ken
  • 499
  • 6
  • 18