USB-хост Android 3.1 - BroadcastReceiver не получает USB_DEVICE_ATTACHED - PullRequest
34 голосов
/ 08 августа 2011

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

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

Приложение, которое я разрабатываю, не должно запускаться / завершаться, только если устройство подключено / отключено (например, в целях управления данными). Также я не хочу, чтобы открытое диалоговое окно появлялось, если приложение уже запущено. Поэтому я решил не начинать действие напрямую, если устройство подключено, а зарегистрировать 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

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

Android: 3.1

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

Ответы [ 3 ]

36 голосов
/ 22 марта 2012

Aha! Я понял. У меня была точно такая же проблема.

Суть этого в том, что если ваше приложение запускается автоматически при подключении устройства (с помощью файла манифеста), то, по-видимому, система 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" />

В вашей папке / res / xml / будет находиться файл device_filter.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>

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

8 голосов
/ 27 марта 2014

Просто для продолжения из проницательного комментария @ 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 при локальных трансляциях.

4 голосов
/ 12 марта 2012

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...