Подтвердить что ты не робот

Android 3.1 USB-хост - BroadcastReceiver не получает USB_DEVICE_ATTACHED

Я работал через описание и образцы для хоста USB на сайте developer.android.com для обнаружения подключенных и отсоединенных USB-устройств.

Если я использую фильтр-замысел в файле манифеста для запуска приложения при подключении устройства, он отлично работает: подключается, устройство обнаружено, андроид запрашивает разрешение на запуск приложения, информация об устройстве отображается в стол.

Приложение, которое я разрабатываю, не должно запускаться/завершаться, только если устройство подключено/отсоединено (например, в целях управления данными). Кроме того, я не хочу, чтобы открытое диалоговое окно появлялось, если приложение уже запущено. Поэтому я решил не запускать деятельность напрямую, если устройство подключено, но зарегистрировать BroadcastReceiver, который (позже) должен уведомить об этом, если устройство находится в состоянии/отсоединено. Этот приемник распознает действие отсоединения просто отлично, но не прилагается.

Мне не хватает атрибута разрешения или данных или что-то в этом роде? Учебник и примеры ничего не говорят о дополнительных необходимых атрибутах.

Вот файл манифеста:

<?xml version="1.0" encoding="utf-8"?>
<manifest 
  xmlns:android="http://schemas.android.com/apk/res/android"
  package="de.visira.smartfdr"
  android:versionCode="1"
  android:versionName="1.0">

<uses-sdk android:minSdkVersion="12" />
<uses-feature android:name="android.hardware.usb.host" />

<application android:icon="@drawable/icon" android:label="@string/app_name">


    <receiver android:name=".usb.Detector">
        <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" />
    </receiver>
</application>

И приемник:

public class FDRDetector extends BroadcastReceiver{

@Override
public void onReceive(Context context, Intent intent) {
    String action = intent.getAction();

    Toast.makeText(context, "Action: " + action, 3).show();
            // pops up only if action == DETACHED
}

Я не понимаю, почему работает тот же фильтр намерений, если я использую их для активности, но не если они применяются к получателю? Даже если я настрою приемник и фильтр в коде, прикрепления не распознаются.

Моя рабочая среда: IDE: Eclipse 3.7 с Android Plugin

Устройство: Acer Iconia Tab A500

Android: 3.1

Заранее спасибо

4b9b3361

Ответ 1

Ага! Я понял. У меня была та же самая проблема.

Суть его в том, что если вы запускаете приложение автоматически, когда устройство подключено (используя файл манифеста), то появляется система Android, которая получает намерение ACTION_USB_DEVICE_ATTACHED, а затем, поскольку оно знает, что ваше приложение хочет запустить в этой ситуации он фактически отправляет вашему приложению намерение android.intent.action.MAIN. Он никогда не отправляет действие ACTION_USB_DEVICE_ATTACHED в ваше приложение, потому что он считает, что он уже знает, что ваше приложение хочет сделать в этой ситуации.

Я только что определил проблему, и я думаю, что у меня есть решение, но я могу сказать вам, что я нашел:

Даже если ваше приложение работает и на переднем плане, когда вы подключаете USB-устройство, а система Android получает намерение ACTION_USB_DEVICE_ATTACHED, оно будет вызывать onResume() в вашей активности.

К сожалению, вы не можете просто сделать это:

@Override
public void onResume() {
    super.onResume();

    Intent intent = getIntent();
    Log.d(TAG, "intent: " + intent);
    String action = intent.getAction();

    if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
        //do something
    } 
}

Потому что намерение вернется как android.intent.action.MAIN, а не ACTION_USB_DEVICE_ATTACHED.

Досадно, вы также получите android.intent.action.MAIN, если вы просто покинете приложение, но не отключите USB. Я предполагаю, что устройство будет спать, и его пробуждение будет делать то же самое.

Итак, из того, что я нашел, вы не можете получить намерение напрямую, но, похоже, вы можете полагаться на onResume(), вызываемый при подключении USB-устройства, поэтому решение должно просто проверить, чтобы увидеть если USB подключен каждый раз, когда вы получаете onResume. Вы также можете установить флаг, когда USB отключен, так как, конечно, USB-разъединение полностью срабатывает.

Таким образом, ваш широковещательный приемник может выглядеть так:

// BroadcastReceiver when remove the device USB plug from a USB port  
BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
         if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {                
        usbConnected=false;             
        }
    }
};

У вас есть это внутри onCreate:

  // listen for new devices
 IntentFilter filter = new IntentFilter();
 filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
 registerReceiver(mUsbReceiver, filter);

Это происходит внутри тега активности в вашем манифесте:

        <intent-filter>
            <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
        </intent-filter>

        <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
            android:resource="@xml/device_filter" />

У вас будет файл device_filter.xml в папке /res/xml/, который выглядит так:

<?xml version="1.0" encoding="utf-8"?>

<resources>
    <usb-device vendor-id="1027" product-id="24577" />  
    <usb-device vendor-id="1118" product-id="688" /> 
</resources>

(конечно же, с любыми идентификаторами поставщиков и идентификаторами продуктов, которые вам нужны)

И тогда ваш onCreate выглядит примерно так:

@Override
public void onResume() {
    super.onResume();

    Intent intent = getIntent();
    Log.d(TAG, "intent: " + intent);
    String action = intent.getAction();


    if (usbConnected==false ) {
        //check to see if USB is now connected
    } 
}

У меня нет специального кода для проверки того, подключен ли USB, поскольку я на самом деле еще не вникал в это. Я использую библиотеку, которая будет просто подключаться, если это возможно, поэтому для моего приложения я могу просто начать этот цикл, и я в порядке.

Кроме того, вероятно, важно установить стартовый режим вашей активности в манифесте на "singleTask", чтобы он не запускался снова, когда он уже запущен, или же подключение USB-устройства просто запустит второй экземпляр вашего приложения!

Таким образом, весь мой тег активности в моем манифесте выглядит так:

    <activity
        android:label="@string/app_name"
        android:name="com.awitness.common.TorqueTablet"
        android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen"
        android:screenOrientation="landscape"
        android:configChanges="orientation|keyboardHidden" 
        android:launchMode="singleTask"
        >
        <intent-filter >
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.HOME"/>
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter> 

        <intent-filter>
            <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
        </intent-filter>

        <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
            android:resource="@xml/device_filter" />

    </activity>

В любом случае, я надеюсь, что это поможет кому-то! Я был удивлен, что уже не смог найти решение для этого!

Ответ 2

Просто для продолжения проницательного комментария @Gusdor (+1): я выполнил проверку onNewIntent(), которая, как указывает @Gusdor, вызывается, когда ваша активность launchMode установлена ​​как singleTask или singleTop. Затем вместо проверки булевых флагов, как предлагает принятый ответ, просто передайте намерение вашему приемнику вещания USB с помощью LocalBroadcastManager. Например,

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(intent.getAction())) {
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }
}

Затем, где бы вы ни регистрировали существующий (системный) широковещательный приемник USB, просто зарегистрируйте один и тот же приемник с локальным экземпляром диспетчера вещания, т.е.

@Override
protected void onResume() {
    super.onResume();
    myContext.registerReceiver(myUsbBroadcastReceiver, myIntent); // system receiver
    LocalBroadcastManager.getInstance(myContext).registerReceiver(myUsbBroadcastReceiver, intent); // local receiver
}

@Override
protected void onPause() {
    super.onResume();
    myContext.unregisterReceiver(myUsbBroadcastReceiver); // system receiver
    LocalBroadcastManager.getInstance(myContext).unregisterReceiver(myUsbBroadcastReceiver); // local receiver
}

Вы можете отправить другую систему, а не локальную трансляцию, но я не думаю, что вы сможете использовать действие UsbManager.ACTION_USB_ACCESSORY_ATTACHED (система увидит это как потенциальный риск для безопасности), поэтому вам придется определите свое собственное действие. Ничего страшного, но зачем беспокоиться, тем более, что на локальных вещаниях нет IPC.

Ответ 3

Создание приемника вещания внутри приложения, а не манифеста, позволяет вашему приложению обрабатывать отдельные события во время его работы. Таким образом, отдельные события отправляются только в приложение, которое в настоящее время запущено и не транслируется во все приложения.