androidandroid-intentandroid-usb

How to receive Intent when USB Accessory is attached if application is already running


I was able to read up here about how to launch my app when a USB accessory is attached, and that much all works fine: I have an IntentFilter built into my manifest which will launch the appropriate activity each time the specified accessory is attached. Here is how that looks:

<activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:exported="true">
        <intent-filter>
            <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
        </intent-filter>

        <meta-data
            android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
            android:resource="@xml/accessory_filter" />
</activity>

However, I am having issues if my Activity is already running when the accessory is attached. Within MainActivity, I look for the USB_ACCESSORY_ATTACHED Intent in two places: onCreate, and onNewIntent, like so:

protected void onCreate(Bundle savedInstanceState) {
    //Check for some other actions first
    if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(intent.getAction()))
        usbAttached(intent);

}
protected void onNewIntent(Intent intent) {
    if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(intent.getAction()))
        usbAttached(intent);
}

However, neither onCreate nor onNewIntent is being called when the accessory is plugged in if MainActivity is already running. In that case, I need to close my app before plugging in the accessory, which would be a hassle for users. What would be the appropriate way to receive the Intent from my USB accessory if my activity is already running? Would I need to implement a separate listener within the activity itself?


Solution

  • If you look at the documentation of onNewIntent() it reads:

    protected void onNewIntent (Intent intent)

    Added in API level 1
    This is called for activities that set launchMode to "singleTop" in their package, or if a client used the FLAG_ACTIVITY_SINGLE_TOP flag when calling startActivity(Intent). In either case, when the activity is re-launched while at the top of the activity stack instead of a new instance of the activity being started, onNewIntent() will be called on the existing instance with the Intent that was used to re-launch it.
    ...

    So you need to set the launchMode of your Activity to singleTop in your manifest, otherwise you will receive no calls to onNewIntent(). The result should look something like this:

    <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:launchMode="singleTop"
            android:exported="true">
               ... 
    </activity>
    

    If you think about it that behavior actually makes a lot of sense. Next time look at the documentation before asking here. Don't assume how stuff works if you don't know.