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

Я пытаюсь прочитать теги 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] & 128) == 0) ? "UTF-8" : "UTF-16";
        int languageCodeLength = payload[0] & 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.


person NiceToMytyuk    schedule 20.10.2020    source источник


Ответы (1)


Обновление:

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

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

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

Этот enableReaderMode API NFC вызывает обратный вызов в вашем приложении, который запускает новый поток для обработки входящих данных 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 && 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.

}


....

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

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

onNewIntent для

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

Из https://developer.android.com/reference/android/app/Activity#onNewIntent(android.content.Intent)

Таким образом, в этом сценарии, когда ваше приложение не работает, его нельзя повторно запустить, поэтому намерение должно быть зафиксировано в 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

Это можно сделать, создав Intent Filter в 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);

}

person Andrew    schedule 20.10.2020
comment
Я не заинтересован в открытии приложения, когда NFC был прочитан, а просто для того, чтобы иметь возможность считывать данные с карты nfc, когда активирован AlertDialog, и устанавливать его данные в EditText после попытки установить Intent в onCreate или с помощью используя enableForegroundDispatch и onNewIntent в отладке, я все еще не могу прочитать метод readFromIntent, и приложение все еще пытается перезапуститься (но, поскольку оно находится в LockTaskMode, оно просто отправляет сообщение, чтобы разблокировать его) - person NiceToMytyuk; 20.10.2020
comment
Обновлен ответ на основе новой информации о том, что вы используете LockTaskMode, так как это может помешать чтению NFC с использованием старого метода, основанного на намерениях. - person Andrew; 20.10.2020
comment
Я также должен был сказать, что, вероятно, лучше всего всегда захватывать все типы NFC в действии, а затем выбирать только для обработки, если в это время отображается диалоговое окно предупреждения. Это означает, что ОС не будет пытаться что-либо делать (включая воспроизведение звука обнаружения тега), когда диалоговое окно предупреждения не отображается (еще одно преимущество enableReaderMode — управление звуком обнаружения тега). - person Andrew; 20.10.2020
comment
На самом деле я добавил enableNFC() в свой метод, где я вызываю dialog.show(), и я вызываю disableReaderMode() при его отклонении, когда новый тег был прочитан, и его шаблон .equals соответствует тому, что я ищу. сделать звуковое уведомление и принять прочитанные данные, спасибо! - person NiceToMytyuk; 20.10.2020