2

Based on a checkbox preference I enable a bunch of receivers and I broadcast to them so they schedule a bunch of alarms for themselves. EDIT : The receivers both set up the alarms and wait for the Broadcast from the AlarmManager (as is clear from the onReceive posted).

SettingsActivity

@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
    if (newValue instanceof Boolean) {
        boolean enable = (Boolean) newValue;
        Monitor.enableMonitoring(getApplicationContext(), enable);
        return true;
    }
    return false;
}

Monitor.enableMonitoring

public static void enableMonitoring(Context ctx, boolean enable) {
    Resources resources = ctx.getResources();
    CharSequence ac_setup_alarm = resources
            .getText(R.string.intent_action_setup_alarm);
    CharSequence ac_cancel_alarm = resources
            .getText(R.string.intent_action_cancel_alarm);
    // if only I could do the above in a static way someclass once and for all
    Intent i = new Intent("" + (enable ? ac_setup_alarm : ac_cancel_alarm));
    if (enable) {
        Log.d(_tag, "enable receivers / setup alarms int : " + i);
        _enableDisableReceivers(ctx, enable); // enable == true
        ctx.sendBroadcast(i);
    } else {
        Log.d(_tag, "cancel alarms / disable receivers int : " + i);
        ctx.sendBroadcast(i);
        _enableDisableReceivers(ctx, enable); // enable != true (will disable)
    }
}

questions

  1. Why

    Intent i = new Intent("" + (enable ? ac_setup_alarm : ac_cancel_alarm)); won't get received while

    Intent i = new Intent("" + (enable ? ac_setup_alarm : ac_cancel_alarm), Uri.EMPTY, ctx, BatteryMonitoringReceiver.class); will get received as expected ?
    Manifest :

    <receiver
        android:name="di.k23b.hw3.receivers.BatteryMonitoringReceiver"
        android:enabled="false" >
        <intent-filter>
            <action android:name="@string/intent_action_setup_alarm" />
            <action android:name="@string/intent_action_cancel_alarm" />
            <action android:name="@string/intent_action_monitor" />
        </intent-filter>
    </receiver>  
    

    I would expect implicit intents to propagate to a registered receiver - why do I have to make those implicit intents explicit ? I expect this to be easy to answer - I just overlooked/didn't quite get something in the docs :)

  2. Do I need to make sure that the call to _enableDisableReceivers(ctx, true); will actually enable the receivers before I broadcast the Intent ? Similarly do I have to wait till the intent is received before I call _enableDisableReceivers(ctx, false); to disable the receivers ? If yes how should I go about that ?

  3. (Bonus off the first answer I got) It is not possible to use LocalBroadcastManager with the AlarmaManager - With a boot receiver ? (I guess no)

For completeness :

private static void _enableDisableReceivers(Context ctx, boolean enable) {
    Log.d(_tag, "enable/disable receivers");
    for (Class<? extends BaseReceiver> receiver : RECEIVERS)
        BaseReceiver.enable(ctx, enable, receiver);
}

where :

public static void enable(Context context, boolean enable,
        Class<? extends BaseReceiver> receiver) {
    PackageManager pacman = context.getPackageManager();
    final ComponentName componentName = new ComponentName(context, receiver);
    Log.d(_tag, componentName.toString());
    final int state = (enable) ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
            : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
    pacman.setComponentEnabledSetting(componentName, state,
            PackageManager.DONT_KILL_APP);
    Log.d(_tag,
            "pacman :" + pacman.getComponentEnabledSetting(componentName));
}

And for mind boggling completeness (BaseMonitoringReceiver) :

@Override
final public void onReceive(Context context, Intent intent) {
    // FIXME : possible NPE below ?
    final String action = intent.getAction(); // can intent==null ?
    Log.w(TAG, "" + action);
    resources = context.getResources();
    ac_setup_alarm = resources.getText(R.string.intent_action_setup_alarm);
    ac_cancel_alarm = resources
            .getText(R.string.intent_action_cancel_alarm);
    ac_monitor = resources.getText(R.string.intent_action_monitor);
    if (ac_setup_alarm.equals(action) || ac_cancel_alarm.equals(action)) {
        monitoringIntent = new Intent(context, this.getClass());
        monitoringIntent.setPackage(RECEIVERS_PACKAGE_NAME);// TODO: needed?
        final boolean enable = ac_setup_alarm.equals(action);
        setupAlarm(context, enable);
    } else if (ac_monitor.equals(action)) {
        // monitoring - got broadcast from ALARM
        Class<? extends WakefulIntentService> serviceClass = getService();
        WakefulIntentService.sendWakefulWork(context, serviceClass);
    } else {
        Log.w(TAG, "Received bogus intent : " + intent);
        return;
    }
}

Subclasses (like the BatteryMonitoringReceiver in the manifest) just override getService() - neat no ? Notice onReceive is final.

Community
  • 1
  • 1
Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361

1 Answers1

0

This is not an answer - just wanted to ask questions that won't fit in a comment.

What you are doing - just enabling a receiver for long enough to send it an intent - looks pretty unusual to me. Are you sure that is what you want to do? I'm used to having to enable/disable receivers for system intents, but not for custom intents.

Regarding Q#2, why is there an issue of trying to reduce the window during which the receiver is enabled? Why not enable the receiver before sending the intent, and then don't disable it until after you have received the intent?

I'm assuming that all of this is happening in the same process - it sounds like that is the case. And, if that is the case, why bother declaring your receiver in your manifest at all? You could also consider switching to LocalBroadcastManager.

Edit:

Question 3. As you suspect it is not possible to use LBM with Alarm Manager. See this post.

Community
  • 1
  • 1
Tom
  • 17,103
  • 8
  • 67
  • 75
  • I am about to implement the WakefulIntent pattern (see http://stackoverflow.com/questions/15451347/android-design-background-long-running-service-or-alarmmanager) - "just enabling a receiver for long enough to send it an intent" - _what_ ? "Why not enable the receiver before sending the intent, and then don't disable it until after you have received the intent?" - _that's what I am doing but not sure I have to wait somehow and if yews **how** - that is Q2_ ? Yes same process but receivers need to receive from AlarmManager - can a LocalReceiver receive from AM (I was wondering actually) ? – Mr_and_Mrs_D Apr 01 '13 at 12:34
  • Oh, OK, now I understand. You are right that my suggestion of LBM would not work when using Alarm Manager. I saw that you added that to question so I added it above. Sorry, I don't know the answer to your questions (1&2). – Tom Apr 01 '13 at 14:25
  • Thanks for following up :) – Mr_and_Mrs_D Apr 01 '13 at 14:39