I have a simple Android Kotlin app, and part of what it does is listen for when power is connected and disconnected and perform an action
This is my old code, and it worked totally fine while targeting devices below Oreo.
AndroidManifest.xml
<receiver android:name=".ChargingUtil$PlugInReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
</intent-filter>
</receiver>
ChargingUtil.kt
class ChargingUtil (context: Context){
/*... Some other charging-related functions here ... */
class PlugInReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("thisistest", "Power was changed")
// Here I do some logic with `intent.action`
}
}
}
There have been some changes to how to implement Broadcasts, in later Android versions: https://developer.android.com/guide/components/broadcasts
What I've tried so far:
So my question is:
How to call a function when power is connected/ disconnected? While taking account of the additional restrictions that systems running Android 8 or later impose on manifest-declared receivers.
Note: I am using Kotlin, and would like to avoid the use of deprecated packages
I am a bit of a noob when it comes to Android, so sorry if there is actually an obvious solution what I just missed. Thanks in advance for any help.
A broadcast receiver (receiver) is an Android component which allows you to register for system or application events. All registered receivers for an event are notified by the Android runtime once this event happens.
An intent filter is an expression in an app's manifest file that specifies the type of intents that the component would like to receive. For instance, by declaring an intent filter for an activity, you make it possible for other apps to directly start your activity with a certain kind of intent.
The question you have referred already has the answer.
Use ACTION_BOOT_COMPLETED
to start a foreground service after boot. This service should register ACTION_POWER_CONNECTED
broadcast. You can also start this service in a separate process. As soon as the power is connected/disconnected, you will receive the broadcast in service class where you can run your required method.
public class MyService extends Service {
private String TAG = this.getClass().getSimpleName();
@Override
public void onCreate() {
Log.d(TAG, "Inside onCreate() API");
if (Build.VERSION.SDK_INT >= 26) {
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
mBuilder.setSmallIcon(R.drawable.ic_launcher);
mBuilder.setContentTitle("Notification Alert, Click Me!");
mBuilder.setContentText("Hi, This is Android Notification Detail!");
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// notificationID allows you to update the notification later on.
mNotificationManager.notify(100, mBuilder.build());
startForeground(100, mBuilder.mNotification);
IntentFilter filter1=new IntentFilter();
filter1.addAction(Intent.ACTION_POWER_CONNECTED);
registerReceiver(myBroadcastReceiver,filter1);
}
}
@Override
public int onStartCommand(Intent resultIntent, int resultCode, int startId) {
Log.d(TAG, "inside onStartCommand() API");
return startId;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "inside onDestroy() API");
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
BroadcastReceiver myBroadcastReceiver =new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// call your method
}
};
}
You can also register a JobScheduler
with setRequiresCharging
true. this will start the call JobScheduler's job when the power state is changed.
ComponentName serviceComponent = new ComponentName(context, TestJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(0, serviceComponent);
builder.setMinimumLatency(1 * 1000); // wait at least
builder.setOverrideDeadline(3 * 1000); // maximum delay
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
builder.setRequiresDeviceIdle(true);
builder.setRequiresCharging(true);
JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
jobScheduler.schedule(builder.build());
Starting from Android 8.0 (API level 26 and higher), you can't use static receivers to receive most Android system broadcasts read here ,
A BroadcastReceiver is either a static receiver or a dynamic receiver, depending on how you register it:
To register a receiver statically, use the <receiver>
element in your
AndroidManifest.xml
file. Static receivers are also called manifest-declared receivers.
To register a receiver dynamically, use the app context or activity context. The receiver receives broadcasts as long as the registering context is valid, meaning as long as the corresponding app or activity is running. Dynamic receivers are also called context-registered receivers.
So you need to register your Receiver dynamically , go to AndroidManifest.xml
and delete the <receiver>
tag , and register it at your activity .
private MyBatteryReceiver mReceiver = new MyBatterReceiver();
Then use IntentFilter
for the power actions :
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
filter.addAction(Intent.ACTION_POWER_CONNECTED)
All you have left is to register and dismiss your register
this.registerReceiver(mReceiver, filter); // using activity context.
this.unregisterReceiver(mReceiver); // implement on onDestroy().
Thanks to @Derek's answer, plus a couple of changes I got this to work. Posting the working solution to here, in case it's any help to anyone else.
PowerConnectionReciever.kt
import android.content.Intent
import android.content.BroadcastReceiver
import android.os.IBinder
import android.content.IntentFilter
import android.app.Service
import android.content.Context
class PowerConnectionService : Service() {
private var connectionChangedReceiver: BroadcastReceiver =
object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// This block gets run whenever the power connection state is changed
when {
intent.action == Intent.ACTION_POWER_CONNECTED ->
powerWasConnected()
intent.action == Intent.ACTION_POWER_DISCONNECTED ->
powerWasDisconnected()
}
}
}
override fun onCreate() {
val connectionChangedIntent = IntentFilter()
connectionChangedIntent.addAction(Intent.ACTION_POWER_CONNECTED)
connectionChangedIntent.addAction(Intent.ACTION_POWER_DISCONNECTED)
registerReceiver(connectionChangedReceiver, connectionChangedIntent)
}
override fun onStartCommand(
resultIntent: Intent, resultCode: Int, startId: Int): Int {
return startId
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(connectionChangedReceiver)
}
override fun onBind(intent: Intent): IBinder? {
return null
}
private fun powerWasConnected() {
// Do whatever you need to do when the power is connected here
}
private fun powerWasDisconnected() {
// And here, do whatever you like when the power is disconnected
}
}
Then within my MainActivity.kt file, added this function, which is called within the onCreate
hook.
private fun startPowerConnectionListener() {
val serviceComponent = ComponentName(this, PowerConnectionService::class.java)
val builder = JobInfo.Builder(0, serviceComponent)
builder.setMinimumLatency((200)) // wait time
builder.setOverrideDeadline((200)) // maximum delay
val jobScheduler = this.getSystemService(JobScheduler::class.java)
jobScheduler.schedule(builder.build())
}
The only other changes that I needed, were in the AndroidMainifest.xml
A permission for the FOREGROUND_SERVICE
is added as a first-level item:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
And of course it's also required to register the new PowerConnectionService
, this goes within the relevant activity
node
<service
android:name=".PowerConnectionService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true">
</service>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With