安卓11上NFC无法检测到

3

我需要制作一个应用程序,它可以扫描NFC卡片,并在同时拍照并将此信息发送到服务器,但我的问题是,Android 11上的设备无法检测到我的NFC标签,而Android 6到Android 9上的设备可以。当我按下按钮(以拍照(in、in2b和out2b)时),我启用前台调度,然后仅在onPause方法中禁用它。另外,当我的应用程序运行时,其他应用程序(例如NFC工具)无法检测到任何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>

如果您的应用程序未运行,其他在此设备上运行的应用程序是否能够检测到您的标签?如果它们不能,则说明您的标签类型与Android 11或设备之间存在某些低级不兼容性。如果其他应用程序可以检测到标签,因为它们可能不使用相机,请尝试禁用您的应用程序对相机的使用,并查看您的NFC是否正常工作。如果是这样,那么您就知道在相机处于活动状态时使用NFC必须遵守一些新的限制。 - CommonsWare
1
你说得对,非常感谢,我关闭了相机后,NFC 就正常工作了 :) 我在这个主题上找到了另一个话题: https://dev59.com/xVsW5IYBdhLWcg3wwZjG看起来设备不能同时使用相机和检测 NFC 标签。 - ZETROV93XB47
1个回答

2
最近有一些关于这个问题的疑问,似乎与设备有关。旧版相机API的文档称,某些设备可能会阻塞您的应用程序的主事件循环,并且由于您正在使用的旧版NFC API需要暂停和恢复您的应用程序才能接收NFC数据,因此存在这种情况。

如果您的应用程序针对API 19及以上版本,则建议始终使用更新且更好的NFC API,称为enableReaderMode,而不是enableForegroundDispatch

这个新的API可以给您更多的控制权,而且不需要暂停和恢复您的应用程序,数据在它自己的线程中处理,不会被相机阻塞主事件循环所影响。

请注意,我没有尝试将enableReaderMode与相机操作一起使用,我只是总是将其用于NFC操作,因为这是一种更好的处理NFC的方法。


1
Android 11不允许同时使用NFC和相机输入,正如线程中所讨论的那样。enableReaderMode无法解决这个问题。我很好奇原帖作者是否解决了他/她的问题? - Nilzor
进一步的问题表明,NFC和相机不能同时工作是非常硬件特定的,并且是一些硬件制造商的设计选择,而不是所有的制造商都是如此。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接