NFC не обнаружен на Android 11

Мне нужно создать приложение, которое сканирует карту NFC, затем одновременно делает снимок и отправляет эту информацию на сервер, но моя проблема в том, что устройства на Android 11 не обнаруживают мою метку NFC, в отличие от устройств на Android 6. Android 9. Я включаю отправку переднего плана, когда нажимаю кнопку (чтобы сделать снимок (in, in2b и out2b), а затем отключаю его только в методе onPause. Кроме того, когда мое приложение работает, другие приложения (например, NFC Tools) не могу обнаружить ни одного тега NFC, если я не остановлю свое приложение. Если кто-нибудь знает источник проблемы, приветствую помощь, спасибо Вот мой код:



public class ScanRes extends AppCompatActivity {

    private NfcAdapter nfcAdapter;
    private PendingIntent pintent;
    private String nfcID = "";
    private ImageView imgpts;
    private int nbrClicks = 0;
    private TextView battery;
    private String URLAPI;
    private TextView msg_in;
    private TextView msg_out;
    private static boolean netWorskStatus = true;
    private NetworkChangeReceiver connectionState;


    private SurfaceView cameraDisplay;
    private Camera camera;
    SurfaceHolder surfaceHolder;


    //Valeurs Latitude et Longitude init au début
    private String Longitude = null;
    private String Latitude = null;
    private String Accuracy = null;

    private ImageView in;
    private ImageView in2b;
    private ImageView out2b;
    private TextView msg_sans_btn;
    private static final String SCAN_IN = "in";
    private static final String SCAN_OUT = "ou";
    private static final String SCAN = "u";
    private String specialScanReason = "";
    private String selectedChantier = "";
    private String scanType="";
    private TextView in_msg;

    //Variables qui vont stocker les options téléchargées via le serveur
    private int gps_frequency;
    private boolean photo_on;
    private int nbr_bouton;
    private boolean saved;
    private boolean mode_kiosque_on;
    private  boolean gps_force;
    private boolean mode_chantier_on;
    private boolean special_on;
    private boolean gps_on;
    private String nomChantierCourant;
    private String base64Img = "";
    private TextView msg_if_mode_boutons;
    private boolean mode_controle_acces_on;



    private boolean nfcScanRight = false;

    public static Handler handler;     //Attention, il était private avant
    public static HandlerThread handlerThread;
    public static Looper looper;

    private Handler handlerUS;
    public static HandlerThread handlerThreadUS;
    public static Looper looperUS;

    private static final int SPECIAL_SCAN_REQUEST_CODE = 00005;
    private static final int SELECTION_CHANTIERS_REQUEST_CODE = 00010;
    private static final int ADMIN_ACTIVITY_REQUEST_CODE = 5024;
    String chantiers[];
    private TextView chantierCourant;




    private void takePictureAfterScan(Context currentContext, String fileName, int rapportDeDivision){
        try{
            camera.takePicture(null, null, new Camera.PictureCallback() {
                @Override
                public void onPictureTaken(byte[] data, Camera camera) {
                    //startCameraSource();
                    Bitmap im = BitmapFactory.decodeByteArray(data, 0, data.length);
                    Matrix matrix = new Matrix();
                    matrix.postRotate(-90);
                    Bitmap image = Bitmap.createBitmap(im, 0, 0, im.getWidth(), im.getHeight(), matrix, true);
                    Bitmap resized = Bitmap.createScaledBitmap(image, im.getWidth()/rapportDeDivision, im.getHeight()/rapportDeDivision, false);
                    ByteArrayOutputStream byteArrayOutputStreamc = new ByteArrayOutputStream();
                    resized.compress(Bitmap.CompressFormat.JPEG, 50, byteArrayOutputStreamc);
                    byte[] byteArrayc = byteArrayOutputStreamc.toByteArray();
                    //String imgBuffc = Base64.encodeToString(byteArrayc, Base64.NO_WRAP);
                    base64Img = Base64.encodeToString(byteArrayc, Base64.NO_WRAP);
                    //WriteOnce(currentContext, imgBuffc, fileName);
                }
            });
        }catch(Exception e){
            e.printStackTrace();
        }
    }


    private void startCameraSource(){
        //cameraDisplay = (SurfaceView) findViewById(R.id.camera);
        if ((cameraDisplay != null) && (cameraDisplay.getHolder() != null) && (camera != null)) {
            try {
                camera.setPreviewDisplay(cameraDisplay.getHolder());
                camera.startPreview();
            } catch (Exception e) {
            }
        }
    }

    //Fonction initialisant la caméra
    private void initCamera() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 2552);
            return;
        }
        //cameraDisplay = (SurfaceView) findViewById(R.id.camera);
        try {
            if (camera != null)
                camera.release();
            camera =null;
            camera = Camera.open(1);

            camera.setDisplayOrientation(90);
            //cameraDisplay.setVisibility(View.VISIBLE);
            cameraDisplay.getHolder().addCallback(new SurfaceHolder.Callback() {
                @Override
                public void surfaceCreated(SurfaceHolder holder) {
                    startCameraSource();
                }

                @Override
                public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                }

                @Override
                public void surfaceDestroyed(SurfaceHolder holder) {

                }
            });

        } catch (RuntimeException e) {
            if (camera == null)
                //setText("camera error");
                Log.i("CAMERA", "CAMERA ERROR");
        }
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scan_res);

        connectionState = new NetworkChangeReceiver();
        registerNetworkBroadcastReceiver(connectionState);
        surfaceHolder = cameraDisplay.getHolder();

        handlerThread = new HandlerThread("MyHandlerThread", Thread.MAX_PRIORITY);
        handlerThread.start();
        looper = handlerThread.getLooper();
        handler = new Handler(looper);

        handler.post(new Runnable(){
            @Override
            public void run() {
                if(photo_on) {
                    initCamera();
                }
            }
        });


        //Init NFC Adapter
        nfcAdapter = NfcAdapter.getDefaultAdapter(this);
        //If no NfcAdapter, display that the device has no NFC
        if (nfcAdapter == null){
            Toast.makeText(this,"Votre appareil n'est pas compatible avec le NFC", Toast.LENGTH_SHORT).show();
            finish();
        }
        pintent = PendingIntent.getActivity(this,0,new Intent(this,this.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),0);
    }


    @SuppressLint("ClickableViewAccessibility")
    @Override
    protected void onStart(){
        super.onStart();

        in.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                nfcScanRight = true;
                nfcAdapter.enableForegroundDispatch(ScanRes.this,pintent,null,null);
                scanType = SCAN;
                if(photo_on){
                    takePictureAfterScan(ScanRes.this, "base64.txt", 2);

                    handler.post(new Runnable(){
                        @Override
                        public void run() {
                            initCamera();
                            startCameraSource();
                        }
                    });
                }

                Toast.makeText(ScanRes.this, "Passez votre badge",Toast.LENGTH_SHORT).show();
            }
        });

        in2b.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                nfcScanRight = true;
                nfcAdapter.enableForegroundDispatch(ScanRes.this,pintent,null,null);
                scanType = SCAN_IN;
                if(photo_on){
                    takePictureAfterScan(ScanRes.this, "base64.txt", 2);

                    handler.post(new Runnable(){
                        @Override
                        public void run() {
                            initCamera();
                            startCameraSource();
                        }
                    });
                }

                //cancelScan(5, ScanRes.this, ScanRes.this);
                Toast.makeText(ScanRes.this, "Passez votre badge",Toast.LENGTH_SHORT).show();
            }
        });

        out2b.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                nfcScanRight = true;
                nfcAdapter.enableForegroundDispatch(ScanRes.this,pintent,null,null);
                scanType = SCAN_OUT;
                if(photo_on){
                    takePictureAfterScan(ScanRes.this, "base64.txt", 2);

                    handler.post(new Runnable(){
                        @Override
                        public void run() {
                            initCamera();
                            startCameraSource();
                        }
                    });
                }

                //cancelScan(5, ScanRes.this, ScanRes.this);
                Toast.makeText(ScanRes.this, "Passez votre badge",Toast.LENGTH_SHORT).show();
            }
        });


        if(special_on){
            in.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    // TODO Auto-generated method stub
                    scanType = SCAN;
                    if(photo_on){
                        takePictureAfterScan(ScanRes.this, "base64.txt", 2);
                    }
                    Intent ScanSpecial = new Intent(ScanRes.this, ScanSpecial.class);
                    startActivityForResult(ScanSpecial, SPECIAL_SCAN_REQUEST_CODE);
                    return true;
                }
            });

            in2b.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    // TODO Auto-generated method stub
                    scanType = SCAN_IN;
                    if(photo_on){
                        takePictureAfterScan(ScanRes.this, "base64.txt", 2);
                    }
                    Intent ScanSpecial = new Intent(ScanRes.this, ScanSpecial.class);
                    startActivityForResult(ScanSpecial, SPECIAL_SCAN_REQUEST_CODE);
                    return true;
                }
            });

            out2b.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    // TODO Auto-generated method stub
                    scanType = SCAN_OUT;
                    if(photo_on){
                        takePictureAfterScan(ScanRes.this, "base64.txt", 2);
                    }
                    Intent ScanSpecial = new Intent(ScanRes.this, ScanSpecial.class);
                    startActivityForResult(ScanSpecial, SPECIAL_SCAN_REQUEST_CODE);
                    return true;
                }
            });
        }

        Log.i("onstart", "onstart");
    }


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

        handler.post(new Runnable(){
            @Override
            public void run() {

                if(photo_on){
                    initCamera();
                    startCameraSource();
                }
                //if(netWorskStatus) sendUnregisteredScans(ScanRes.this);
            }
        });

        if(netWorskStatus) sendUnregisteredScans(ScanRes.this);

        assert nfcAdapter != null;

        //Dans le cas où l'on a  pas de bouton, on active le foregroundDispatch pour lire la carte sans avoir à appuyer sur un bouton
        if(nbr_bouton == 0){
            nfcScanRight = true;
            nfcAdapter.enableForegroundDispatch(ScanRes.this,pintent,null,null);
        }

        Log.i("onresume", "onresume");
    }

    @Override
    protected void onPause() {
        super.onPause();

        if (nfcAdapter != null) {
            nfcAdapter.disableForegroundDispatch(this);
        }
        Log.i("onpause", "onpause");
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        unRegisterNetworkBroadcastReceiver(connectionState);
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        if(camera != null){
            camera.release();
        }
        Log.i("ondestroy", "ondestroy");
    }


    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        if(nfcScanRight){

            resolveIntent(intent);
            startCameraSource();

        }
        else{
            Toast.makeText(this, "Veuillez appuyer sur l'un des boutons avant de Scanner", Toast.LENGTH_SHORT);
        }
        Log.i("onNewIntent", "onNewIntent");
    }

    private void resolveIntent(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)) {
            Tag tag = (Tag) intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            assert tag != null;
            String nfc = getTagID(tag);

            handler.post(new Runnable(){
                @Override
                public void run() {
                    if(nbr_bouton == 0){
                        takePictureAfterScan(ScanRes.this, "base64.txt", 2);
                    }
                    JSONObject data = new JSONObject();
                    data = JSONDataBuilder(nfcID, base64Img, Latitude, Longitude, Accuracy);

                    if(gps_force && Latitude!= null && Longitude != null && Accuracy != null){
                        if(!sendToServer(data)){
                            Toast.makeText(ScanRes.this,"Vous n'êtes pas connecté à internet, le pointage sera envoyé lorsque vous serez connecté", Toast.LENGTH_SHORT).show();
                            Write(ScanRes.this, data.toString(), "listePointagesNonEnvoyes.txt");
                        }
                    }
                    else{
                        if(!sendToServer(data)){
                            Toast.makeText(ScanRes.this,"Vous n'êtes pas connecté à internet, le pointage sera envoyé lorsque vous serez connecté", Toast.LENGTH_SHORT).show();
                            Write(ScanRes.this, data.toString(), "listePointagesNonEnvoyes.txt");
//                            Write(ScanRes.this, data.toString(), "listePointagesNonEnvoyes.txt");
                        }
                    }
                }
            });
        }
    }


    private String getTagID(Tag tag){
        byte[] id = tag.getId();
        nfcID = toReversedHex(id).replaceAll("\\s", "");
        nfcScanRight = false;
        return  nfcID;
    }

    private String toReversedHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; ++i) {
            if (i > 0) {
                sb.append(" ");
            }
            int b = bytes[i] & 0xff;
            if (b < 0x10)
                sb.append('0');
            sb.append(Integer.toHexString(b));
        }
        return sb.toString();
    }

}

вот содержимое моего файла манифеста:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.digitalnomade.pointeusenomade"
    android:versionCode="2"
    android:versionName="3.0.0">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.NFC" />

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

    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.VIBRATE" />

    <uses-feature android:name="android.hardware.location.gps" />

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:extractNativeLibs="false"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme.NoActionBar">
        <activity android:name=".sccanact"></activity>
        <activity android:name=".SelectionChantiers" />
        <activity android:name=".ScanSpecial" />
        <activity android:name=".Infos" />
        <activity android:name=".Admin" />
        <activity
            android:name=".ScanRes"
            android:configChanges="orientation"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.BATTERY_CHANGED" />
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

person ZETROV93XB47    schedule 14.04.2021    source источник
comment
Обнаруживают ли другие приложения, работающие на этом устройстве, ваш тег, если ваше приложение не запущено? Если они не могут, это указывает на некоторую несовместимость более низкого уровня между вашим типом тега и либо Android 11, либо устройством. Если другие приложения могут, поскольку они, вероятно, не используют камеру, попробуйте отключить использование камеры вашим приложением и посмотрите, работает ли теперь ваш NFC. Если это так, то вы знаете, что должно быть какое-то новое ограничение на использование NFC, когда камера активна.   -  person CommonsWare    schedule 14.04.2021
comment
Вы правы, большое спасибо, я выключил камеру, и NFC стал работать нормально :) Я нашел еще одну тему на эту тему: stackoverflow.com/questions/34607168/ Кажется, что устройства не могут одновременно использовать камеру и обнаруживать тег nfc   -  person ZETROV93XB47    schedule 14.04.2021


Ответы (1)


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

Если вы ориентируетесь на API 19 и выше для своего приложения, я всегда буду использовать более новый и лучший API NFC под названием enableReaderMode вместо enableForegroundDispatch.

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

Примечание. Я не пробовал использовать enableReaderMode с операциями камеры, я просто всегда использую его для операций NFC, потому что это лучший метод работы с NFC.

person Andrew    schedule 14.04.2021
comment
Android 11 запрещает NFC одновременно с вводом данных с камеры, как описано в этом. нить. enableReaderMode не решает этого. Мне любопытно, решил ли ОП свою проблему? - person Nilzor; 31.05.2021