Detecting when a USB device is detached on Android - android

I've got an Android app that needs to detect when a USB peripheral is attached or detached. It works fine when the peripheral is first attached, but I don't receive any notification (i.e., I don't receive an Intent whose action is ACTION_USB_DEVICE_DETACHED) when it is subsequently detached.
Here's the relevant part of my AndroidManifest.xml:
<activity android:name=".LauncherActivity">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="#xml/device_filter" />
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" android:resource="#xml/device_filter" />
</activity>
It may also be worth noting that LauncherActivity only exists to start a Service when the device is attached, and to stop the service when it is detached. In either case, LauncherActivity always finishes itself immediately. All of this occurs in LauncherActivity.onCreate.
Any ideas?

USB_DEVICE_DETACHED is a broadcast intent, thus you may want to declare the BroadcastReceiver in manifest with the appropriate intent-filter for detached action, also with meta-data attached.
Same goes for USB_ACCESSORY_DETACHED, for who is interested.
Summary:
USB_XXX_ATTACHED is an activity intent
USB_XXX_DETACHED is a broadcast intent
(where XXX = DEVICE | ACCESSORY)
See: http://developer.android.com/guide/components/intents-filters.html
"There is no overlap within these messaging systems: Broadcast intents are delivered only to broadcast receivers, never to activities or services"

Try with USB_STATE as below.
It will fire both attached and detatched to same receiver and in receiver you can identify whether is was attached or detatched event.
IntentFilter filter = new IntentFilter();
filter.addAction("android.hardware.usb.action.USB_STATE");
Receiver:
public class USBReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
if (intent.getExtras().getBoolean("connected")) {
//do your stuff
}
}
}

So, I never got the ACTION_USB_DEVICE_DETACHED Intent to go to LauncherActivity; I don't know what the deal is there, probably something I don't properly understand about intent filters or the Activity lifecycle callbacks.
The solution I ended up using comes from the post linked by Pratik. I basically took everything about USB_DEVICE_DETACHED out of AndroidManifest.xml. Then, in the onCreate method of the Service, I registered a BroadcastReceiver like this:
#Override
public void onCreate() {
detachReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_DETACHED))
stopSelf();
}
};
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
registerReceiver(detachReceiver, filter);
}
A little clunky, and I'm still curious why just adding USB_DEVICE_DETACHED to the <intent-filter> of LauncherActivity wasn't working, but it does what I need.

The fix that worked for me was to unregister in onPause() and register again in onResume():
#Override
public void onPause()
{
super.onPause();
stopIOManager();
if(m_UsbReceiver!=null) unregisterReceiver(m_UsbReceiver);
}
#Override
public void onResume()
{
super.onResume();
startIOManager();
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
registerReceiver(m_UsbReceiver, filter);
}
However, although the app seems to always receive the DETACH event, once in a while it doesn't get the ATTACHED event. I then need to plug and and unplug the USB connector, and it usually works after one or two attempts. I've put the blame of this strange behaviour on the OS, without certainty.

For what it's worth, I had this same issue because the activity is paused then resumed when the device is disconnected.
Since the receiver is unregistered in the OnPause() method just prior to receiving the ACTION_USB_DEVICE_DETACHED, your app never gets notified.

Related

Not able to get actions to fire USER_PRESENT or SCREEN_ON Android Emulator

I want to know when a user has turned their phone on eg. click the power button/possibly actually unlocked their phone. The ultimate goal is to update a widget's display after calling an API vs. using scheduled updates.
The issue is I've been scouring threads and I have not been able to get my logs to fire in Logcat. I have tried both Receiver and Service methods. Is what Is what I am attempting possible?
I have also tried setting the READ_PHONE_STATE permission in the manifest.
Some code context of what I have:
Manifest:
<receiver android:name=".CustomBroadcastRecveiver">
<intent-filter>
<action android:name="android.intent.action.USER_PRESENT">
<action android:name="android.intent.action.SCREEN_ON">
</intent-filter>
</receiver>
Optional service in manifest that I tried:
<service android:naem=".CustomService"/>
CustomBroadcastReceiver:
public class CustomBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("MyTag", "broadcast receive"); // this does not run
if (intent.getAction().equals(Intent.ACTION_USER_PRESENT) {
Log... // this doesn't run, same for ACTION_SCREEN_ON that would be in a new if block
}
}
}
I'm open to other alternatives. I can provide more context if necessary. I can expand more on my service implementation if that is the way to go assuming what I'm after is possible.
Edit
To add more context on how I used Elletlar's answer
In my MainActivity I added:
private static Context context;
Then inside onCreate:
context = getApplicationContext();
// instantiate my specific receiver
// set the code from answer below
Note: I am API 29 if it makes any difference.
From the "android.intent.action.SCREEN_ON" docs, "You cannot receive this through components declared in manifests, only by explicitly registering for it with
Context#registerReceiver(BroadcastReceiver, IntentFilter).
Example of registering explicitly:
myReceiver = new MyReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
registerReceiver(myReceiver, filter);

Receiver stops when force close

Hello I've made an app with a receiver to listen to incoming calls ,
My problem is that when i close (swipe off from list of apps) the receiver doesn't work anymore.
first thing i tried is the receiver itself defined in the android manifest like so:
<receiver
android:name=".demo.CallReceiver"
android:exported="true"
android:enabled="true">
<intent-filter android:priority="999">
<action android:name="android.intent.action.PHONE_STATE"/>
</intent-filter>
<intent-filter android:priority="999">
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
This works only when the app is open or in the background .
I looked online and saw this - https://stackoverflow.com/a/46889335/7079340
so i made a Service of my own like so (in the manifest):
<service android:name=".Service.CallService" android:enabled="true"
android:exported="false"> <intent-filter>
<action android:name="com.package.name.IRemoteConnection" />
</intent-filter>
</service>
and the class :
public class CallService extends Service {
private static BroadcastReceiver m_Receiver;
#Override
public IBinder onBind(Intent arg0)
{
Log.e("SERVICELOG","bind");
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("SERVICELOG","start command");
return START_STICKY;
}
#Override
public void onCreate()
{
Log.e("SERVICELOG","create");
Receiver();
}
#Override
public void onDestroy()
{
Log.e("SERVICELOG","destroy");
try{
unregisterReceiver(m_Receiver);}catch (Exception e){
Log.e("SERVICELOG"," "+e.getMessage());
}
}
private void Receiver()
{
m_Receiver = new CallReceiver();
}
}
Started it in the oncreate of my Splashscreen and it prints the logs and it works !
Any way to make this work without a service I'm afraid of battery issues and so on ? Thanks !
Android has had many changes from Marshmallow and above about apps that implicitly listener for broadcast in their manifest. The reasoning for this is because several apps would register for broadcast and a new process would be spun up for all the registered app's broadcast receiver to run in (very expensive) thus causing battery drain. What made this worse, was that users have no control over this behavior because the broadcast receiver couldn't be unregistered. To fix this, the engineering team behind Android, only allows for a select few broadcast to be implicitly registered. One is the Device Boot broadcast intent. By stopping apps from implicitly registering Broadcast's, apps have to be manually launched by the user to listen for intents they'd like to be notified of. This prevent several unnecessary apps from waking up to attempt to handle intent.
As for your concern, about "battery issues" I would recommend to you to use the preferred pattern of explicitly registering a BroadcastReciever in your Service and just performance tune to get your code to be as performant as possible. Services are definitely not free, but they aren't automatically heavy objects just by having one started and running; plus they do exist for this exact purpose. Just remember to not do unnecessary work in your service and you should be off to the right start.
When you close your app, it goes directly to onDestroy method. In your service code, you implemented this method. So, in your method you did stop your service programmatically. In short, you must remove it.

Different broadcastreceiver depending on activity life

Some time ago, I was able to have 2 broadcastreceivers, one declared in my manifest, and the other one, declared in my activity.
Like here : What is the best way to handle callback from IntentService
But, since I have changed for LocalBroadcastReceiver, and changed sendOrderedBroadcast with sendBroadcast method, only the one registered in activity is only receiving the broadcast. I have read that localbroadcastreceiver can not be registered in manifest.
So how to wake up a broadcastreceiver which is not registered in an activity ?
I found my own answer, so I give the response for those who can be interested in.
I have two BroadcastReceiver class, one used when application is wake up, with :
ActivityBroadcastReceiver myReceiver = new ActivityBroadcastReceiver();
#Override
protected void onResume() {
LocalBroadcastManager.getInstance(this).registerReceiver(myReceiver, new IntentFilter("com.mybroadcast"));
super.onResume();
}
and
#Override
protected void onPause() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(myReceiver);
super.onPause();
}
and in my manifest file :
<receiver
android:name="com.broadcastreceiver.NotificationBroadcastReceiver"
android:enabled="true" >
<intent-filter>
<action android:name="com.mybroadcast" />
</intent-filter>
</receiver>
To send my broadcast :
if(!LocalBroadcastManager.getInstance(context).sendBroadcast(broadcast))
context.sendBroadcast(broadcast);
I agree that with this, context.sendBroadcast() , everyone can received my broadcast, but in my code, sendBroadcast send only non-sensible data. That is the best solution I got for now.

How do I communicate information from a BroadcastReceiver to an Activity in Android

I'm trying to write something similar to a custom app store, and I'm having a very hard time hearing the results of the installation.
I've tried a variety of things, and none of them have functioned as it appears they should.
In my main activity class, I have the installation code as follows:
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setDataAndType(Uri.fromFile(apkFile),
"application/vnd.android.package-archive");
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, getApplicationInfo().packageName);
startActivityForResult(intent, resultIndex);
That's pretty straightforward. The first thing I tried to get the result is to override the onActivityResult function in my main activity. That function never gets called (a logger statement on the first line never prints).
Hence, I tried creating a receiver in the manifest.
<receiver android:name=".PackageReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="package" />
</intent-filter>
</receiver>
And then in the PackageReceiver class, I overrode the onReceive function. This worked in the sense that the PackageReceiver's function did get called, but I'm now stuck with the problem of how to notify the main activity that something has happened (for the purposes of my project, I need to know). I can't instantiate the PackageReciever; Android does that for me because it's in the manifest.
I tried creating a new broadcast, so my PackageReceiver's onReceive function looks like this:
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_INSTALL_PACKAGE) ||
action.equals(Intent.ACTION_PACKAGE_ADDED) ||
action.equals(Intent.ACTION_PACKAGE_CHANGED) ){
logger.debug("Caught action of type " + action);
Intent i2 = new Intent();
i2.setAction("MY.CUSTOM.ACTION");
context.sendBroadcast(i2);
}
}
Then, over in my main activity, I created a new BroadcastReceiver:
BroadcastReceiver myReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
logger.warn("I made it!!!! HOORAY!!");
}
};
And registered that receiver in the onResume (and unregistered in the onPause) function, like this:
#Override
protected void onResume() {
IntentFilter filter = new IntentFilter();
filter.addAction("MY.CUSTOM.ACTION");
registerReceiver(myReceiver, filter);
logger.debug("Registering receiver in onResume.");
super.onResume();
}
It sounded like a nice solution, but "myReceiver" never has its onRecieve function called. I've tried making sure I don't unregister when I don't want to by simply removing the unregistration, moving the registration to the onCreate method of the activity, and none of that works. The net result there is that exceptions get thrown in the logger saying I've leaked an IntentFilter, "Did you forget to unregister?" (No, I very purposefully didn't unregister... but... nevermind).
I'm running out of ideas as to how to bridge this gap. Also, I can't just poll the installed applications and check version numbers. For reasons I can't get into here, we don't use version numbers. I need to know whether or not the user completed the installation that I kicked off.
How can I send information from the PackageReceiver to the main activity? Or, how can I catch the result of the installation in the main activity itself?
Thanks to all who give it a stab. I checked other stackoverflow questions, but none seem to have the answers I'm looking for.
If you register myReceiver in onResume() and unregister it in onPause() it will never get called because when the package installation occurs your app is paused.
You need to register MyReceiver in onCreate() and don't unregister it until onDestroy().
Also, you don't need so many components to do this. In your main activity, just before you go off to install a package you can register the receiver to listen for the package installer results:
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addCategory(Intent.CATEGORY_DEFAULT);
filter.addDataScheme("package");
registerReceiver(myReceiver, filter);
// Now launch the package installer...

Android: how to launch my app upon receiving a specific Broadcast?

I am trying to get my app automatically launched when the phone gets connected to wifi. Here's the code I am using to both set the Broadcast receiver and to specify that once the broadcast is received I want the "Connected" activity to be launched:
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
receiver = new BroadcastReceiver(){
public void onReceive(Context context, Intent intent){
Intent intent2 = new Intent(getApplicationContext(),com.example.package.Connected.class);
startActivity(intent2);
}
};
registerReceiver(receiver,intentFilter);
Unfortunately it's not working. The logcat says my activity has "leaked IntentReceiver".
Does anyone know how I can solve this?
EDIT: I also tried to register the receiver via the Manifest file. I added this code to the manifest:
<receiver android:name="com.example.package.receiver">
<intent-filter>
<action android:name="android.net.wifi.STATE_CHANGE" />
</intent-filter>
</receiver>
And then this code to my main activity:
private BroadcastReceiver receiver = new BroadcastReceiver(){
public void onReceive(Context context, Intent intent){
Intent intent2 = new Intent(getApplicationContext(),com.example.package.Connected.class);
context.startActivity(intent2);
}
};
But now my app crashes once the phone connects to wifi. Logcat says "RuntimeException: Unable to instantiate receiver".
Any ideas how to solve it?
I am trying to get my app automatically launched when the phone gets connected to wifi.
Register your BroadcastReceiver in the manifest, using a <receiver> element, and have the receiver call startActivity() on the Context supplied in the onReceive() method.
Note that users may not appreciate your popping up an activity just because the device connected to WiFi.
as Per link and Activity has leaked IntentReceiver
Unregister the Broadcast Receiver that you created in the onCreate()
In the onRestart() re-register a brand new Broadcast Receiver.

Categories

Resources