We have an app that scans for Bluetooth devices. The code responsible for scanning should only run when bluetooth is enabled. Also we want to disable/enable this feature at any point in time.
We choose to implement a BroadcastReceiver that registers for the BluetoothAdapter.ACTION_STATE_CHANGED broadcast.
Here some of the problems we encountered:
Programmatically enable the BroadcastReceiver:
public void registerForBroadcasts(Context context) {
IntentFilter bluetooth = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
context.registerReceiver(this, bluetooth);
}
When the app is killed, the BroadcastReceiver is also not active anymore. So if the user multi-tasks-swipes the app to death, it is not being woken up again.
We have full control, when to start the BroadcastReceiver.
Declare the BroadcastReceiver in the Manifest
<receiver android:name="com.mypackage.BroadcastReceiver">
<intent-filter>
<action android:name="android.bluetooth.adapter.action.STATE_CHANGED"/>
</intent-filter>
</receiver>
The BroadcastReceiver is active right after the app start.
The BroadcastReceiver cannot be disabled.
Declare the BroadcastReceiver in the Manifest as disabled + enable it programmatically
<receiver android:name="com.mypackage.BroadcastReceiver"
android:enabled="false" >
<intent-filter>
<action android:name="android.bluetooth.adapter.action.STATE_CHANGED"/>
</intent-filter>
</receiver>
Then enable the component if you need it.
public void registerForBroadcasts(Context context) {
ComponentName component = new ComponentName(context, BroadcastReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(
component,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
My tests have shown, the state is persisted with the system, so the BroadcastReceiver will stay enabled. It combines the advantages of both methods.
BroadcastReceiver can be disabled
BroadcastReceiver can be on or off by default
BroadcastReceiver keeps activation even if app is disabled and phone rebooted.
Am I missing something, does this method seem legit?
It is possible to enable/disable the receiver programatically.
To Enable programatically
PackageManager pm = Re_editActivity.this.getPackageManager();
ComponentName componentName = new ComponentName(currentActivity.this, name_of_your_receiver.class);
pm.setComponentEnabledSetting(componentName,PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
To Disable programatically
PackageManager pm = Re_editActivity.this.getPackageManager();
ComponentName componentName = new ComponentName(currentActivity.this, name_of_your_receiver.class);
pm.setComponentEnabledSetting(componentName,PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
Manifest receiver
<receiver android:name="name_of_your_receiver" android:enabled="false">
<intent-filter >
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
The BroadcastReceiver cannot be disabled.
Sure it can. Use PackageManager and setComponentEnabledSetting(), as you did in your third scenario.
Am I missing something, does this method seem legit?
It is very legit, at least in terms of managing the BroadcastReceiver. I don't know if there are any Bluetooth-specific hiccups, though I would doubt it. This technique is used for various broadcasts, such as only listening to ACTION_BOOT_COMPLETED when you really do have something that needs to be done at boot time.
Related
I have a Broadcast Receiver in my app:
public class Ringmodechange extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
}
}
My AndroidManifest.xml:
<receiver
android:name=".Ringmodechange"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.media.RINGER_MODE_CHANGED" />
</intent-filter>
</receiver>
Is it possible to register and unregister a BroadcastReceiver with java code? This receiver is working in the background.
Actually, I want to register and unregister the receiver everytime a user clicks a button.
Yes, it is possible using a ComponentName and the PackageManager.
Enabling your BroadcastReceiver:
ComponentName component = new ComponentName(context, Ringmodechange.class)
context.getPackageManager().setComponentEnabledSetting(component, PackageManager. COMPONENT_ENABLED_STATE_ENABLED , PackageManager.DONT_KILL_APP);
To disable it:
ComponentName component = new ComponentName(context, Ringmodechange.class)
context.getPackageManager().setComponentEnabledSetting(component, PackageManager. COMPONENT_ENABLED_STATE_DISABLED , PackageManager.DONT_KILL_APP);
There are a couple of good posts covering this topic.
Essentially, in your onStart and onStop lifecycle methods is where you should be making your changes.
In the documentation for BroadcastReceivers, they discuss the lifecycle of the broadcasts as well as security implications.
I want to start my application at startup in Android 4.0. To do that, I wrote some codes and these are completely the same with the #Ahmad's codes (in the answer). However, although I select my application as always, when tablet opens, it asks 'What do you prefer?' (Android's default launcher or my application). I don't want it to ask that question and it must start my application automatically.
Use the BOOT_COMPLETED Intent.
Broadcast Action: This is broadcast once, after the system has
finished booting. It can be used to perform application-specific
initialization, such as installing alarms. You must hold the
RECEIVE_BOOT_COMPLETED permission in order to receive this broadcast.
In your Manifest:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
Set up a Broadcastreceiver:
<receiver android:name="com.example.MyBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
This is how your BroadcastReceiver could look like:
public class MyBroadcastreceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, MyActivity.class);
startActivity(i);
}
}
I need to create a BroadcastReceiver which performs certain task immediately each time the device boots up. Also, when a certain button is clicked, the receiver should stop starting on boot. Can someone help me to manage that?
All you need to solve the first part of your question is to make a BroadcastReceiver for it and declare it in your Manifest as:
<receiver android:name=".MyBootReceiver"
android:enabled="true"
>
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
The QUICKBOOT_POWERON is necessary for some devices that don't send the BOOT_COMPLETED broadcast. HTC devices like to use the quickboot one instead.
For the second part of your question, there are a few different ways you could accomplish this. You could simply set a value in SharedPreferences that your receiver checks every time it fires, and exit immediately if the value dictates such.
You could also disable the receiver in code:
getPackageManager().setComponentEnabledSetting(
new ComponentName( this, MyBootReceiver.class ),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP );
You can enable it using the same method:
getPackageManager().setComponentEnabledSetting(
new ComponentName( this, MyBootReceiver.class ),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP );
I am unsure of the persistence of this method. I use it in one of my apps, but it's not for a boot receiver, and it doesn't have to persist across boots. You'll have to experiment with it if you want to go that route.
I want to remove my app from list of apps and the recent apps list. So I tried to disable my main / launcher activity with the following code:
ComponentName componentToDisable = new ComponentName(context, MainActivity.class);
context.getPackageManager().setComponentEnabledSetting(componentToDisable,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
This does the job. But now I try to reinstall the app and it fails saying "activity MainActivity class doesn't exist". If I uninstall the app, the installation works again. How can I handle this issue? Thanks a lot for your time and help
I found that I have to make the activity enabled before reinstalling it.
This can be done by having a broadcast receiver listen to package_add / remove events and in onReceive make the activity enabled again.
public void onReceive(Context context, Intent intent) {
Log.i("Receiver","got event");
ComponentName componentToDisable = new ComponentName(context,BlockableComponentActivity.class);
context.getPackageManager().setComponentEnabledSetting(componentToDisable,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}
Manifest excerpt for the receiver:
<receiver android:name="PackageChangeReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED"/>
<action android:name="android.intent.action.PACKAGE_REPLACED"/>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<data android:scheme="package"/>
</intent-filter>
Is there a way to detect when a USB flash drive is plugged into an Android device? I'm able to detect an SD card using a broadcast receiver, but it doesn't work for USB. I'd like to avoid polling.
code to register receiver:
private void RegisterUpdateReceiver()
{
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.intent.action.MEDIA_MOUNTED");
intentFilter.addDataScheme("file");
myReceiver = new MyReceiver();
this.registerReceiver(myReceiver, intentFilter);
}
receiver code:
public class MyReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if (action.equals("android.intent.action.MEDIA_MOUNTED"))
{
// react to event
}
}
If detecting attaching and detaching the USB will work "android.hardware.usb.action.USB_DEVICE_ATTACHED" can be used.
Make sure the definition of the receiver and the intent filter is added in the manifest as well.
Android, at the SDK level, has no concept of USB drives. There are no rules for where they should be mounted, broadcasts for when they appear/disappear, etc. Perhaps some standardization in this area will come in future Android releases, but it is not there today.
This is the xml version of the receiver in the AndroidManifest.xml:
<receiver
android:name="MyMountBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
<action android:name="android.intent.action.MEDIA_UNMOUNTED"/>
<data android:scheme="file" />
</intent-filter>
</receiver>
This is how I made it work on Android 6.0.
Basically, what we need to detect is when the drive is mounted which can be done by ACTION_MEDIA_MOUNTED that is "android.intent.action.MEDIA_MOUNTED".
Follow the steps below:
Register a broadcast receiver for "ACTION_USB_DEVICE_ATTACHED"
Register a broadcast receiver for "ACTION_MEDIA_MOUNTED"
Seek permission for using the device when "ACTION_USB_DEVICE_ATTACHED" action is received.
Then you will receive broadcast for "ACTION_MEDIA_MOUNTED"
You can refer: https://developer.android.com/reference/android/content/Intent#ACTION_MEDIA_MOUNTED
Note: Registering broadcast receiver for ACTION_MEDIA_MOUNTED alone does not work. So used broadcast "ACTION_USB_DEVICE_ATTACHED" for permission purpose.