Невозможно запустить намерение при чтении NFC с помощью LockTaskMode

#android #nfc

#Android #nfc

Вопрос:

Я пытаюсь прочитать теги NFC в своем приложении для Android, теги NFC представляют собой простую карточку с простым текстом на ней, после просмотра документации по Android и просмотра некоторых других руководств я получил следующий код в моем AndroidManifest:

 <uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />

    <activity
        android:name=".pterm" // activity where i would be able to read NFC
        android:screenOrientation="portrait"
        android:theme="@style/SplashScreen">
        <intent-filter>
            <action android:name="android.nfc.action.NDEF_DISCOVERED" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="text/plain" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.nfc.action.TAG_DISCOVERED" />
        </intent-filter>
    </activity>
  

И в моей деятельности я добавил следующий код:

   @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        if (nfcRead) {
            readFromIntent(intent);
        }
    }

    private void readFromIntent(Intent intent) {
        String action = intent.getAction();
        if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)
                || NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)
                || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
            Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
            NdefMessage[] messages = null;
            if (rawMessages != null) {
                messages = new NdefMessage[rawMessages.length];
                for (int i = 0; i < rawMessages.length; i  ) {
                    messages[i] = (NdefMessage) rawMessages[i];
                    NdefRecord[] records = messages[i].getRecords();
                    //if you are sure you have text then you don't need to test TNF
                    for(NdefRecord record: records){
                        processRecord(record);
                    }
                }
            }
        }
    }

    public void processRecord(NdefRecord record) {

        short tnf = record.getTnf();
        switch (tnf) {

            case NdefRecord.TNF_WELL_KNOWN: {
                if (Arrays.equals(record.getType(), NdefRecord.RTD_TEXT)) {
                    String yourtext = processRtdTextRecord(record.getPayload());
                    Log.e("NFC:", yourtext);
                } else if (Arrays.equals(record.getType(), NdefRecord.RTD_URI)) {
                    return;
                } else if (Arrays.equals(record.getType(), NdefRecord.RTD_SMART_POSTER)) {
                    return;
                } else {
                    return;
                }
            }
            case NdefRecord.TNF_MIME_MEDIA: {
                if (record.toMimeType().equals("MIME/Type")) {
                    // handle this as you want
                } else {
                    //Record is not our MIME
                }
            }
            // you can write more cases
            default: {
                //unsupported NDEF Record
            }
        }
    }

    private String processRtdTextRecord(byte[] payload) {
        String textEncoding = ((payload[0] amp; 128) == 0) ? "UTF-8" : "UTF-16";
        int languageCodeLength = payload[0] amp; 0063;

        String text = "";
        try {
            text = new String(payload, languageCodeLength   1, payload.length - languageCodeLength - 1, textEncoding);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            Log.e("UnsupportedEncoding", e.toString());
        }
        return text;
    }
  

Но когда я пытаюсь прочитать метку NFC, событие onNewIntent даже не запускается, но NFC считывается, когда устройство издает звук уведомления NFC..

Цель приложения — считывать NFC только тогда, когда запущен пользовательский AletDialog, после того, как NFC прочитал значение, оно должно быть помещено в EditText, и новое значение может быть прочитано снова только при повторном запуске диалогового окна.

Приложение использует LockTaskMode.

Ответ №1:

Обновить:

Исходя из комментария, в котором находится приложение, LockTaskMode это может изменить способность системы повторно запускать приложения для доставки intent к нему (у меня нет опыта LockTaskMode )

Я предлагаю вам не использовать старые API NFC, которые должны использовать запуск или повторный запуск приложений, когда вы находитесь в ограничительном LockTaskMode режиме.

Я предлагаю вам попробовать более новый и гораздо лучший enableReaderMode NFC API для чтения карт. https://developer.android.com/reference/android/nfc/NfcAdapter#enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle)

Этот enableReaderMode NFC API вызывает обратный вызов в вашем приложении, который запускает новый поток для обработки входящих данных NFC, нет запуска или повторного запуска вашего приложения и, следовательно, может не возникнуть проблем LockTaskMode .

пример использования enableReaderMode API.

 
public class MainActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback{

private NfcAdapter mNfcAdapter;

@Override
    protected void onResume() {
        super.onResume();
        enableNfc();
    }

    private void enableNfc(){
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if(mNfcAdapter!= null amp;amp; mNfcAdapter.isEnabled()) {
            // READER_PRESENCE_CHECK_DELAY is a work around for a Bug in some NFC implementations.
            Bundle options = new Bundle();
            options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250);

            // Ask for all type of cards to be sent to the App as they all might contain NDEF data
            mNfcAdapter.enableReaderMode(this,
                    this,
                    NfcAdapter.FLAG_READER_NFC_A |
                            NfcAdapter.FLAG_READER_NFC_B |
                            NfcAdapter.FLAG_READER_NFC_F |
                            NfcAdapter.FLAG_READER_NFC_V |
                            NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS,
                    options);
        } else {
            NfcMessage();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if(mNfcAdapter!= null)
            mNfcAdapter.disableReaderMode(this);
    }


// This gets run in a new thread on Tag detection
// Thus cannot directly interact with the UI Thread
public void onTagDiscovered(Tag tag) {

// Get the Tag as an NDEF tag Technology
Ndef mNdef = Ndef.get(tag);

        // only process if there is NDEF data on the card
        if (mNdef != null) {
             NdefMessage mNdefMessage = mNdef.getCachedNdefMessage();

             // Now process the Ndef message
             ....

             // If success of reading the correct Ndef message
             // Make a notification sound (as we disabled PLATFORM_SOUNDS when enabling reader Mode)
             // As getting a confirmation sound too early causes bad user behaviour especial when trying to write to cards
        }

        // Ignore other Tag types.

}


....

  

Оригинал
Я оставлю оригинальный ответ здесь, поскольку он показывает здесь работу системного приложения NFC, которое enableForegroundDispatch напрямую передает intent с данными NFC в нем в ваше приложение, повторно запуская его, приостанавливая и возобновляя его, что запускает onNewIntent
Это также объясняет, что если у вас этого нет enableForegroundDispatch , то системное приложение NFC передает намерение NFC в ОС Android, что заставляет его находить приложение, которое может обрабатывать этот тип намерений, что он и делает, просматривая все файлы манифеста приложения на предмет соответствия фильтра намерений.

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

onNewIntent для

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

Из https://developer.android.com/reference/android/app/Activity#onNewIntent (android.контент.Намерение)

Таким образом, в этом сценарии, когда ваше приложение не запущено, оно не может быть повторно запущено, поэтому onCreate вместо этого необходимо зафиксировать намерение.

Итак, onCreate введите что-то вроде

         Intent intent = getIntent();

        // Check to see if the App was started because of the Intent filter in the manifest, if yes read the data from the Intent
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
          readFromIntent(intent);
        }

  

Теперь, если вы хотите скрыть получение данных NFC во время работы приложения, вам необходимо использовать enableForegroundDispatch и onNewIntent

https://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc#foreground-dispatch

Что можно сделать, создав фильтр намерений onResume и включив отправку переднего плана (отправка переднего плана должна быть отключена, если ваше приложение не находится на переднем плане, т.е. onPause Как показано).

например, что-то вроде

 
private NfcAdapter adapter;

public void onResume() {
    super.onResume();
    pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
            getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
    IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
    try {
        // Only look for plain text mime type
        ndef.addDataType("text/plain");
    }
    catch (MalformedMimeTypeException e) {
        throw new RuntimeException("fail", e);
    }
    intentFiltersArray = new IntentFilter[] {ndef, };


    adapter = NfcAdapter.getDefaultAdapter(this);
    adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, null);
}

@Override
public void onPause() {
    super.onPause();
    adapter.disableForegroundDispatch(this);

}

  

Комментарии:

1. Я не заинтересован в открытии приложения, когда NFC был прочитан, а просто для того, чтобы иметь возможность считывать данные с карты nfc при запуске AlertDialog и устанавливать его данные в EditText, после попытки установить Intent в onCreate или с помощью enableForegroundDispatch и onNewIntent в debug я все еще не могу прочитать readFromIntent метод, и приложение все еще пытается перезапустить (но, поскольку оно находится в режиме LockTaskMode, оно просто отправляет сообщение, чтобы разблокировать его)

2. Обновленный ответ, основанный на новой информации, которую вы используете LockTaskMode , поскольку это может помешать чтению NFC с использованием старого метода, основанного на намерениях.

3. Я должен был также сказать, что, вероятно, лучше всегда фиксировать все типы NFC в действии, а затем выбирать только для его обработки, если в данный момент отображается диалоговое окно предупреждения. Это означает, что ОС не будет пытаться что-либо делать (в том числе воспроизводить звук обнаруженного тега), когда диалоговое окно предупреждения не отображается (еще одним преимуществом enableReaderMode является контроль звука обнаруженного тега)

4. На самом деле я добавил enableNFC() в свой метод, в котором я вызываю dialog.show(), и я вызываю disableReaderMode() его отклонение, когда новый тег был прочитан, и он соответствует .equals тому, что я ищу, я делаю второе уведомление и принимаю прочитанные данные, спасибо!