I am trying to make an app that shows a toast when the device's Bluetooth turned on. I wanna do that even when my app is not running. So I should use a broadcast receiver, add some permissions, an intent-filter to android manifest and make a java class but I don't know the details.
What should I do? Which permissions should I use?
AS far as permissions go, to detect the state change of bluetooth you need to add this to your AndroidManifest.xml.
<uses-permission android:name="android.permission.BLUETOOTH" />
An example receiver would look like this, you add this code to where you want to handle the broadcast, for example an activity:
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive (Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
if(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1)
== BluetoothAdapter.STATE_OFF)
// Bluetooth is disconnected, do handling here
}
}
};
To use the receiver, you need to register it. Which you can do as follows. I register the receiver in my main activity.
registerReceiver(this, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
You could also decide to add all of it to your AndroidManifest.xml. This way you can make a special class for the receiver and handle it there. No need to register the receiver, just make the class and add the below code to the AndroidManifest
<receiver
android:name=".packagename.NameOfBroadcastReceiverClass"
android:enabled="true">
<intent-filter>
<action android:name="android.bluetooth.adapter.action.STATE_CHANGED"/>
</intent-filter>
</receiver>
You have to take following permission.
<uses-permission android:name="android.permission.BLUETOOTH" />
and you have to write this as your intent filter in receiver tag.
<action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
Unfortunately, for apps targeting api 26 or higher, manifest declared broadcast receivers don't work anymore (reference here: https://developer.android.com/guide/components/broadcast-exceptions), with some exceptions.
android.bluetooth.adapter.action.STATE_CHANGED is not in that list.
For bluetooth, you can only listen for changes on:
ACTION_CONNECTION_STATE_CHANGED, ACTION_ACL_CONNECTED, ACTION_ACL_DISCONNECTED
Related
In the AndroidManifest file, I want to capture the BOOT_COMPLETED event when the user re-boots their device. I am adding this permission:
"uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"
I have seen two "intent-filters" used by others on Stackoverflow:
"Intent.ACTION_BOOT_COMPLETED" and
"android.intent.action.BOOT_COMPLETED"
What is the preferred action string here? Please advise and explain.
Intent.ACTION_BOOT_COMPLETED == android.intent.action.BOOT_COMPLETED
They're both the same, because if you look into what the value of Intent.ACTION_BOOT_COMPLETED is, you'll see that it's android.intent.action.BOOT_COMPLETED.
Typically in the Manifest, you'll use android.intent.action.BOOT_COMPLETED due to Intent.ACTION_BOOT_COMPLETED being Java code rather than xml.
But in your code, you can use Intent.ACTION_BOOT_COMPLETED as an alternative due to it being much easier to remember.
Here is a complete solution:
Set the permission in the manifest:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
You need a receiver to run when your system restarts so something like this:
public class StartMyActivityAtBootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
// everything here executes after system restart
}
}
}
Include this receiver in your manifest like below:
<receiver
android:name=".service.StartMyActivityAtBootReceiver"
android:label="StartMyServiceAtBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
How can you make an app that does not contain Activity as main launcher, but contain service as main launcher?
Yes it is possible. Just don't define any activity in your AndroidManifest and you're fine.
What you should do however to be able for your services to be called, is to listen on some phone-actions with a broadcast receiver.
Something like this:
<receiver android:name="com.example.MyBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
</intent-filter>
</receiver>
The actions BOOT_COMPLETED and MY_PACKAGE_REPLACED are triggered when the user's device is rebooted (obviously) but also whenever an upgrade of your application (or initial install) is done (via the Play Store for example).
Then you can start your service again (or schedule an alarm for you service to be triggered x minutes/hours/days/...).
If you don't have any possible user input, another useful action is ACTION_USER_PRESENT. This one will be triggered whenever your user unlocks his phone.
CAUTION:
For the BOOT_COMPLETED action you need to add a permission in the manifest:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
Then in your 'MyBroadcastReciever' class you should not do anything special to check the actions. You know you will only get in there by the actions specified in the manifest, so your broadcast receiver will look something like this:
public class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Do your work here...
}
}
All services that will be ever triggered should be defined in the manifest also! Don't forget about that!
How do we specify in broadcast sending application that which application can receive this broadcast, and in receiving application that which particular application has the permission to send broadcast to its broadcast receiver...
I am new to android..I read the documentation etc on internet but couldn't find the syntax to specify these permissions.
To control who is able to receive the broadcast message, you can use the method sendBroadcast:
public abstract void sendBroadcast (Intent intent, String receiverPermission)
where you precise the name of the required permission. If the receiver does not declare this permission, it will not be able to get the message. For example, the broadcast sender can do:
Intent broadcast = new Intent(this, MyBroadcastReceiver.class);
sendBroadcast(broadcast, "andro.jf.mypermission");
In the manifest of the broadcast sender, a new permission should be declared:
<!-- Declaring the special permission -->
<permission android:name="andro.jf.mypermission"
android:label="my_permission"
android:protectionLevel="dangerous"></permission>
Then, in the application that is supposed to receive this broadcast, you have to declare this permission and say that you use it. In the manifest you can add:
<!-- I use the permission ! -->
<uses-permission android:name="andro.jf.mypermission"/>
and of course, you have to declare your broadcast receiver:
<receiver android:name="MyBroadcastReceiver" android:exported="true" />
You can have a look at this post for a complete example of a custom permission and also the android developer page about this. Be carefull with the order of installation of your apps because the one that defines the permission should be installed first.
If you want to restrict who only can send intents to your broadcast receiver, do it this way:
The broadcast receiver:
<manifest ...>
<!-- Permission declaration -->
<permission android:name="my.app.PERMISSION" />
<receiver
android:name="my.app.BroadcastReceiver"
android:permission="my.app.PERMISSION"> <!-- Permission enforcement for delivering intents to this receiver -->
<intent-filter>
<action android:name="my.app.Action" />
</intent-filter>
</receiver>
...
</manifest>
The broadcast sender:
<manifest ...>
<!-- We declare we own the permission to send broadcast to the above receiver -->
<uses-permission android:name="my.app.PERMISSION" />
...
</manifest>
Sending broadcast from the sender Activity to the receiver:
Intent intent = new Intent();
intent.setAction("my.app.Action");
activity.sendBroadcast(intent);
If you declare the permission like this:
<permission android:protectionLevel="signature" android:name="my.app.PERMISSION" />
Then the sender will be able to use this permission and send broadcasts to receiver only when both the sender and the receiver apps are signed by the same developer certificate.
Declare permission
First you need declare your permission in your AndroidManifest.xml
<permission android:name="YOUR_PERMISSION_STRING" android:protectionLevel="signature"/>
<uses-permission android:name="com.codylab.photogallery.PRIVATE"/>
the android:name value is used as permission value and will used later.
Usage
There are two kinds of permission usages related to broadcast receiver:
(1) Control which application can receive your broadcast:
String PERMISSION_STRING_PRIVATE_RECEIVER = "YOU_NEED_THIS_TO_RECEIVE_THIS_BROADCAST"
sendBroadcast(intent, PERMISSION_STRING_PRIVATE_RECEIVER);
With this usage, you can control only authorized application can handle the broadcast you sent.
(2) Only handle the broadcasts have the specified permission
String PERMISSION_STRING_PRIVATE_BROADCASTER = "ONLY HANDLE BROADCASTS WITH THIS PERMISSION"
IntentFilter filter = new IntentFilter(ACTION_SAMPLE);
registerReceiver(mReceiver, filter, PERMISSION_STRING_PRIVATE_BROADCASTER, null);
With this usage, you can make sure that the broadcaster is authorized.
use an intent filter in receiver tag in manifest
<receiver
android:name="Your receiver"
android:enabled="true"
android:exported="false" >
<intent-filter>
<action android:name="action"/>
<category android:name="category" />
</intent-filter>
</receiver>
To send broadcast to app
Intent intent = new Intent();
intent.setAction("use same action in receiver");
intent.addcategory("use same category in receiver");
context.sendBroadcast(intent);
After half day search and test, based on #JFL's answer, I find the sender app must add both <permission> tag and <uses-permission> tag, then the receiver can receive the broadcast with permission. Otherwise, the receiver app won't receive the broadcast.
I have a broadcast receiver registered in Manifest:
<application ...>
<receiver android:name="com.some.pkg.NewAppReceiver" >
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
</intent-filter>
</receiver>
</appcication>
And the receiver:
public class NewAppReceiver extends BroadcastReceiver {
private static final String TAG = "NewAppReceiver";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Intent: " + intent.getAction());
}
}
And nothing is received when I install APK manually or from the Android Market. Why?
Did you run the app that contains this broadcastReceiver before installing the other apps?
Starting at some API version, broadcastReceivers will not work till you execute the app. Put an activity and execute it.
Also , don't forget to add the following into the broadcastReceiver:
<data android:scheme="package" />
EDIT: On Android 8 and above, if your app targets API 27 or more, it will work partially, so you have to register to those events in code and not in manifest. Here's a list of intents that are still safe to use in manifest: https://developer.android.com/guide/components/broadcast-exceptions.html .
The rest should be used in code. More info here
Since android.intent.action.PACKAGE_ADDED is a System Intent (note that your own app will not receive it at its installation), your BroadcastReceiver will receive messages from sources outside your app. Thus, check you did NOT put: android:exported="false"
You also may need to add:
<data android:scheme="package" />
So, your BroadcastReceiver in your AndroidManifest.xml should look like this:
<application ...>
<receiver android:name=".NewAppReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
</appcication>
If it still doesn't work, you may try to put an higher priority, such as: android:priority="1000"
Take a look at: http://developer.android.com/guide/topics/manifest/receiver-element.html
Registering receiver from manifest would not work from API 26(android 8). Because it had performance impact on older versions.
But we can register receiver from java code and receive updates of removed and added applications.
val intentFilter = IntentFilter()
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED)
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED)
intentFilter.addDataScheme("package")
registerReceiver(YourBroadcastReceiver(), intentFilter)
Are you trying to receive the intent in the application you are installing? The documentation for ACTION_PACKAGE_ADDED says:
Note that the newly installed package does not receive this broadcast.
Another possibility is that this intent might not be delivered to components registered via the manifest but only manually (as described in an answer by Mark Murphy to Stack Overflow question Can't receive broadcasts for PACKAGE intents).
If you try to receive some other package it must be worked.
(As #Savvas noted) If you try to receive your own package's addition you can't receive it. Even if your broadcast receiver has action.PACKAGE_ADDED, receiver's onReceive method isn't triggered.
In this case your best bet is saving this data. By using sharedPreferences, add a key something like "appIsWorkedBefore", and on your launcher Activity's onCreate method set this variable as "true". And you can make your works with respect to this Boolean.
This intent action is no longer available for applications.
This is a protected intent that can only be sent by the system.
https://developer.android.com/reference/android/content/Intent#ACTION_PACKAGE_ADDED
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.