8

I am able to listen for the Intent Intent.ACTION_BATTERY_CHANGED within my activity and it works great. I tried defining the receiver in the manifest:

<receiver android:name="com.beargreaves.battery.BatteryReceiver"> 
    <intent-filter> 
        <action android:name="android.intent.action.BATTERY_CHANGED" />
    </intent-filter> 
</receiver>

but it didn't work, I read a post on here saying that it must be defined programmatically. Rather than registering for the receiver in my Activity, I wanted to achieve this in a Service so that it continually monitors. I have successfully achieved this, and it works, but I wanted to check my working to see if it is the correct approach.

I started by extending BroadcastReceiver:

public class BatteryReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle bundle = intent.getExtras();

        if(null == bundle)
            return;

        boolean isPresent = intent.getBooleanExtra("present", false);
        String technology = intent.getStringExtra("technology");
        int plugged = intent.getIntExtra("plugged", -1);
        int scale = intent.getIntExtra("scale", -1);
        int health = intent.getIntExtra("health", 0);
        int status = intent.getIntExtra("status", 0);
        int rawlevel = intent.getIntExtra("level", -1);
        int level = 0;

        Log.d("Debug","Battery Receiver OnReceive");

        if(isPresent) {
            if (rawlevel >= 0 && scale > 0) {
                level = (rawlevel * 100) / scale;

                Log.d("Debug","BatterReceiver: " + level);

                Toast.makeText(context,"Battery Receiver: " + level + "\t" + status + "Raw: " + rawlevel,Toast.LENGTH_LONG).show();

                if(level <60) {
                    /*
                     * Only invoke the service when level below threshold
                     */
                    Intent i = new Intent(context, BatteryService.class);
                    i.putExtra("level", level);
                    context.startService(i);
                }
            }
        }
    }
}

And then I use a Service to first register the receiver in onCreate() and then handle the events in onStart(). My BroadcastReceiver starts the Service if the level is below a threshold.

public class BatteryService extends Service {
    /*
     * First Call to onStart we don't want to do anything
     */
    boolean avoidFirst = false;
    private BroadcastReceiver receiver;

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
        receiver = new BatteryReceiver();
        registerReceiver(receiver, filter);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        Log.d("Debug","Battery Service On Start");
        int level = intent.getIntExtra("level", -1);

        if(avoidFirst) {
            if(level != -1) {
                Log.d("Debug","Battery Alert Notifying..... " + level);
                SMSManager.sendSMS(BatteryService.this, "<number redacted>", "Battery Level Aleart: " + level);
                stopSelf();
            }
        } else {
            avoidFirst = true;
        }
    }

    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        PreferenceUtil.updatePreference(BatteryService.this, "battery_monitor_on", false);
        unregisterReceiver(receiver);
    }
}

Is this the correct approach? Be that register the receiver in onCreate() and then start the Service when an event is received. First I tried not using a Service but then I have no way of registering the receiver since it can't be achieved in the manifest. Secondly an exception was thrown when I tried to send a text message in the onReceive(). I read that onReceive() shouldn't be starting any threads.

Thanks in advance

Darshan Rivka Whittle
  • 32,989
  • 7
  • 91
  • 109
Bear
  • 1,541
  • 3
  • 20
  • 32

1 Answers1

2

Yes, that is the correct approach. It's quite similar to what I do with my app, which has been working well in production for over a decade (since Android's early days), and I don't see anything that you're missing or getting wrong here. You're correct that you can't register for that broadcast from your Manifest; you do need to do so programmatically. Based on the rest of what you've shared, doing that in a Service rather than an Activity is clearly correct.

Darshan Rivka Whittle
  • 32,989
  • 7
  • 91
  • 109
  • 1
    @Darshan! Can you please add your answer in your answer rather than question because it is quite confusing right now. – Shajeel Afzal Nov 24 '16 at 07:47
  • @ShajeelAfzal I don't understand. This is my answer. My answer was that the approach Bear came to after some missteps, the approach presented in the question, is indeed an appropriate one that is similar to the approach I've used in production for seven years. – Darshan Rivka Whittle Nov 24 '16 at 22:00