简体   繁体   English

在onNewIntent执行之前如何拦截NFC标签

[英]how to intercept NFC tag before onNewIntent executes

I have an app that captures NFC tags. 我有一个捕获NFC标签的应用程序。 The problem i have had in the past is that users hover over the tag in an unsteady manner causeing the NFC adapter to trigger twice. 我过去遇到的问题是,用户以不稳定的方式悬停在标签上,导致NFC适配器触发两次。

I have done a few things to counter this. 我已经做了几件事来应对这个问题。

manifest: 表现:

<activity
    android:name=".NfcActivity"
    android:screenOrientation="portrait"
    android:launchMode="singleTask" 
    android:noHistory="true"
    android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation">

    <intent-filter>


        <action android:name="android.nfc.action.NDEF_DISCOVERED" />

        <category android:name="android.intent.category.DEFAULT" />

        <data android:mimeType="text/plain" />
    </intent-filter>
</activity>

This sets the NFC capturing Activity to be the only instance in the stack and there to be no history. 这会将NFC捕获活动设置为堆栈中的唯一实例,并且没有历史记录。 I've overridden all the config changes that can stop and relaunch this activity, the latter can lead to intent data being redelivered to the activity, making it look like a duplicate scan. 我已经覆盖了所有可以停止并重新启动此活动的配置更改,后者可能导致将意图数据重新传递到该活动,使其看起来像重复扫描。

In the Activity itself i have overridden onNewIntent to do nothing but show a bad scan screen. 在活动本身中,我已覆盖onNewIntent,但只显示了错误的扫描屏幕,什么也没做。 I also understand that onNewIntent should mirror onCreate from a functionality standpoint, but because previous versions of the app have sent 2 scans to the next Activity, i just want the NFC capturing code in one place, onCreate. 从功能的角度来看,我还理解onNewIntent应该反映onCreate,但是由于该应用程序的先前版本已将2次扫描发送到下一个Activity,因此我只希望NFC在一个地方捕获代码onCreate。

In onCreate i do further tests to counter against hovering over a tag and creating a bad scan. 在onCreate中,我进行了进一步的测试,以防止将鼠标悬停在标签上并创建错误的扫描。

  • I check the launched form history flag in the intent. 我检查了意图中启动的表单历史记录标志。 Android can kill an app when low on memory and relaunch later REDELIVERING the original intent. 内存不足时,Android可以终止应用程序,然后重新启动以重新释放原始意图。 This can cause what seems like a duplicate scan. 这可能会导致看起来像是重复扫描。
  • In onCreate i check that the user STILL has the phone connected to the tag. 在onCreate中,我检查用户是否仍将手机连接到标签。 This proves the user is not hovering over the tag with ndefTag.connect(); 这证明用户没有使用ndefTag.connect()将鼠标悬停在标签上。

The app seems to work fine but on one particular phone (Samsung Galaxy Young 2), if the user places the phone on the tag for say a few seconds the the NFC adapter seems to fire a few times in a row. 该应用程序似乎可以正常运行,但可以在一部特定的手机(三星Galaxy Young 2)上使用,如果用户将手机放在标签上几秒钟,则NFC适配器似乎连续触发了几次。

When this happens the original scan is cancelled. 发生这种情况时,原始扫描将被取消。 The reason for this is oncreate processes the tag but when a subsequent scan happens(by hovering, by accident), onPause -> onNewIntent runs. 这样做的原因是oncreate处理标签,但是在随后的扫描发生时(由于悬停,偶然发生),将运行onPause-> onNewIntent。 So the Activity jumps out of onCreate and stops processing the tag. 因此,活动从onCreate跳出并停止处理标记。 onNewIntent shows a failed scan screen and launches the menu screen. onNewIntent显示失败的扫描屏幕并启动菜单屏幕。

The above isn't too much of a problem as all that happens is the user must re-scan the tag. 上面的问题不大,因为发生的一切是用户必须重新扫描标签。

What i would like to happen is: 我想发生的是:

When onCreate runs, the tag is processed no matter what, even if onNewintent executes. 当onCreate运行时,即使执行onNewintent,也无论如何处理标记。 Is there a way maybe to intercept the intent before is reaches onNewintent and onPause? 有没有办法在到达onNewintent和onPause之前拦截意图?

Maybe there is a global flag i can use, that can be checked first to say that onCreate is still running and onNewIntent shouldn't, or more importantly onPause isn't called making onCreate stop running. 也许有一个我可以使用的全局标志,可以首先检查该标志以表明onCreate仍在运行,而onNewIntent不应该运行,或更重要的是,不叫onPause不使onCreate停止运行。

import java.util.List;
import java.util.concurrent.ExecutionException;

import org.ndeftools.Message;
import org.ndeftools.Record;

import android.app.Activity;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.os.Vibrator;
import android.util.Log;
import android.widget.Toast;

public class NfcActivity extends Activity {

    private static final String TAG = NfcActivity.class.getName();

    protected NfcAdapter nfcAdapter;
    protected PendingIntent nfcPendingIntent;

    Handler handler;
    Runnable runnable;

    Handler failHandler;
    Runnable failRunnable;

    Parcelable[] messages;

    Intent i;

    Tag tag;
    String tagId;

    boolean nfcConnected;

    ProgressDialog progressDialog;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.nfcactivitylayout);
        Log.e(TAG, "oncreate");
        nfcConnected = false;

        // initialize NFC
        nfcAdapter = NfcAdapter.getDefaultAdapter(this);
        nfcPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, this.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);

        tag = null;
        tagId = null;

        i = getIntent();

        if ((i.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {

            //check to see if Android has previously killed the app and relaunched it from History
            //and delivered the original intent.
            //if it has do not process and launch the menu screen
                Intent processPayloadIntent = new Intent(NfcActivity.this, NfcscannerActivity.class);
                processPayloadIntent.setAction("QRCODE_ACTION"); 
                processPayloadIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(processPayloadIntent);

            }else{



        tag = i.getParcelableExtra(NfcAdapter.EXTRA_TAG);

        tagId = bytesToHexString(tag.getId());

        Log.e(TAG, "tagID = " + tagId);

        Log.e(TAG, "oncreate intent action = " + i.getAction());




        //The activity has captured tag data, prove the user is not hovering over the tag and is doing a good scan
        //hovering can trigger the adapter twice

        AsyncNfcConnect asnc = new AsyncNfcConnect();
        try {
            asnc.execute().get();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }




        Log.e(TAG, "nfcConnected!!!!!!!!!!!!!!!!!!!!!!!!! = " + nfcConnected);

        if(nfcConnected == true){


            int buildVersionSdk = Build.VERSION.SDK_INT;
            int buildVersionCodes = Build.VERSION_CODES.GINGERBREAD;

            Log.e(TAG, "buildVersionSdk = " + buildVersionSdk
                    + "buildVersionCodes = " + buildVersionCodes);

            int themeVersion;
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD) {

                themeVersion = 2;

            } else {

                themeVersion = 1;
            }

            try{

            progressDialog = new ProgressDialog(this, themeVersion);
            progressDialog.setTitle("NFC Tag Scanned");
            progressDialog.setMessage("Processing tag...");
            progressDialog.setIndeterminate(true);
            progressDialog.show();

            }catch(Exception e){        }




        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(i.getAction()) || NfcAdapter.ACTION_TAG_DISCOVERED.equals(i.getAction())) {            

            if(NfcScannerApplication.isCanScanNfcTag()){

            messages = i.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
            if (messages != null) {


                //setContentView(R.layout.successfulnfc);

                NfcScannerApplication.startNfcTimer();
                //Toast.makeText(this, "NFC timer set", Toast.LENGTH_LONG).show();

                Log.e(TAG, "Found " + messages.length + " NDEF messages"); // is almost always just one

                vibrate(); // signal found messages :-)



                initHandler();
                handler.postDelayed(runnable,  2000);

      }else{

          Toast.makeText(this, "Data on tag was not correct", Toast.LENGTH_LONG).show();

              try{

                handler.removeCallbacks(runnable);
                Log.e(TAG, "just removed callback to runnable that reads nfc tag data");

                }catch(Exception e){

                }




                initFailHandler();
                failHandler.postDelayed(failRunnable,  1);



      }

            }else{


                try{

                    handler.removeCallbacks(runnable);
                    Log.e(TAG, "just removed callback to runnable that reads nfc tag data");

                    }catch(Exception e){

                    }




                    initFailHandler();
                    failHandler.postDelayed(failRunnable,  1);


            }



        } else {

            Toast.makeText(this, "Tag not recognized correctly", Toast.LENGTH_LONG).show();

                try{

                handler.removeCallbacks(runnable);
                Log.e(TAG, "just removed callback to runnable that reads nfc tag data");

                }catch(Exception e){

                }




                initFailHandler();
                failHandler.postDelayed(failRunnable,  1);

        }


        }else{


            try{

                Toast.makeText(this, "Phone wasn't connected to Tag", Toast.LENGTH_LONG).show();

                handler.removeCallbacks(runnable);
                Log.e(TAG, "just removed callback to runnable that reads nfc tag data");

                }catch(Exception e){

                }




                initFailHandler();
                failHandler.postDelayed(failRunnable,  1);



        }//end of NFC connect test


        }//end of launched from history check


    }//end of onCreate





    @Override
    protected void onStart() {
        super.onStart();
        Log.e(TAG, "onStart");
    }





    @Override
    protected void onStop() {
        super.onStop();
        Log.e(TAG, "onStop");
    }





    @Override
    public void onNewIntent(Intent intent) {
        Log.e(TAG, "onNewIntent");

                            Toast.makeText(this, "Bad scan!!!", Toast.LENGTH_LONG).show();



                            initFailHandler();
                            failHandler.postDelayed(failRunnable, 1);



    }//end of onNewIntent






    public void enableForegroundMode() {
        Log.e(TAG, "enableForegroundMode");

        IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED); // filter for all
        IntentFilter[] writeTagFilters = new IntentFilter[] {tagDetected};
        nfcAdapter.enableForegroundDispatch(this, nfcPendingIntent, writeTagFilters, null);
    }

    public void disableForegroundMode() {
        Log.e(TAG, "disableForegroundMode");

        nfcAdapter.disableForegroundDispatch(this);
    }





    @Override
    protected void onResume() {
     super.onResume();
     Log.e(TAG, "onResume");

        enableForegroundMode();
    }

    @Override
    protected void onPause() {
        Log.e(TAG, "onPause");

        super.onPause();

        disableForegroundMode();

        if(handler != null){
            handler.removeCallbacks(runnable);
        }


    }

    private void vibrate() {
        Log.e(TAG, "vibrate");

        Vibrator vibe = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE) ;
        vibe.vibrate(500);
    }


    public void initHandler(){

          handler = new Handler();
          runnable = new Runnable() {
                public void run() {
                    processTag();

                }

                private void processTag() {
                    Log.e(TAG, "about to process tag");

                    try{
                        progressDialog.dismiss();

                    }catch(Exception e){

                        //do nothing
                    }

                    // parse to records
                    for (int i = 0; i < messages.length; i++) {
                        try {
                            List<Record> records = new Message((NdefMessage)messages[i]);

                            Log.e(TAG, "Found " + records.size() + " records in message " + i);

                            for(int k = 0; k < records.size(); k++) {
                                Log.e(TAG, " Record #" + k + " is of class " + records.get(k).getClass().getSimpleName());

                                Record record = records.get(k);

                                NdefRecord ndefRecord = record.getNdefRecord();

                                byte[] arr = ndefRecord.getPayload();

                                String payload = new String(arr);


                                if(payload.length() > 0){

                                payload = payload.substring(3, payload.length());

                                Log.e(TAG, "payload = " + payload);

                                String[] splitPayload = payload.split(",");

                                String tagType = splitPayload[0];
                                String tagCompany = splitPayload[1];
                                String tagClientID = splitPayload[2];
                                String tagClientName = splitPayload[3];

                                if(! tagClientID.equalsIgnoreCase("0") && tagClientID.length() > 0){

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

                                            setContentView(R.layout.successfulnfc);

                                          }
                                        });






                                Intent processPayloadIntent = new Intent(NfcActivity.this, NfcscannerActivity.class);
                                processPayloadIntent.putExtra("payload", payload);
                                processPayloadIntent.putExtra("tagid", tagId);
                                processPayloadIntent.setAction("NFC"); 
                                processPayloadIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                                //processPayloadIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_NEW_TASK);
                                startActivity(processPayloadIntent);
                                finish();
                                overridePendingTransition(0, R.anim.activity_animation_zoom_in);





                                    }else{
                                        Toast.makeText(NfcActivity.this, "Tag data problem/Scan problem.", Toast.LENGTH_LONG).show();

                                        initFailHandler();
                                        failHandler.postDelayed(failRunnable, 1);


                                    }

                                }else{
                                    Toast.makeText(NfcActivity.this, "Tag data problem/Scan problem.", Toast.LENGTH_LONG).show();

                                    initFailHandler();
                                    failHandler.postDelayed(failRunnable, 1);

                                }



                            }
                        } catch (Exception e) {
                            Log.e(TAG, "Problem parsing message", e);
                        }

                    }

                }
            };

        }





    public void initFailHandler(){

          failHandler = new Handler();
          failRunnable = new Runnable() {
                public void run() {

                    returnToMainMenu();

                }

                private void returnToMainMenu() {
                    //Log.e(TAG, "about to return to main menu");

                    try{
                        progressDialog.dismiss();

                    }catch(Exception e){

                        //do nothing
                    }

                    Toast.makeText(NfcActivity.this, "Please check your scanning technique.\nPlease do not hover over tag or swipe...", Toast.LENGTH_LONG).show();

                    failHandler.post(new Runnable(){
                        public void run() {

                            setContentView(R.layout.nfcfail);

                          }
                        });






                    //onBackPressed();
                    Intent processPayloadIntent = new Intent(NfcActivity.this, NfcscannerActivity.class);
                    processPayloadIntent.setAction("QRCODE_ACTION"); 
                    processPayloadIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                    startActivity(processPayloadIntent);
                    finish();
                    //overridePendingTransition(0, R.anim.activity_animation_zoom_in);

                }
            };

        }




    private String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder("0x");
        if (src == null || src.length <= 0) {
            return null;
        }

        char[] buffer = new char[2];
        for (int i = 0; i < src.length; i++) {
            buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F, 16);
            buffer[1] = Character.forDigit(src[i] & 0x0F, 16);
            System.out.println(buffer);
            stringBuilder.append(buffer);
        }

        return stringBuilder.toString();
    }



private class AsyncNfcConnect extends AsyncTask<String, Void, String> {






        @Override
        protected String doInBackground(String... params) {

            NfcActivity.this.nfcConnected = false;
            String result;
            Ndef ndefTag = Ndef.get(tag);

            try {
                Log.e(TAG, "about to test connect()********************************************");
                ndefTag.connect();  // this should already perform an IO operation and should therefore fail if there is no tag
                Log.e(TAG, "Ndef.connect() connected!********************************************");
                NdefMessage ndefMsg = ndefTag.getNdefMessage();  // this reads the current NDEF message from the tag and consequently causes an IO operation

                NfcActivity.this.nfcConnected = true;
                result = "OK";
                return result;

            } catch (Exception e) {
                // there is no tag or communication with tag dropped
                Log.e(TAG, "There a problem with connecting to the tag using Ndef.connect(");
                NfcActivity.this.nfcConnected = false;
                result = "NOTOK";
                return result;
            } finally {
                try {
                    ndefTag.close();
                } catch (Exception e) {
                }
            }


        }



    }//end of Async


}

You seem to insist on not handling NFC intents in onNewIntent() . 您似乎坚持不处理onNewIntent() NFC意图。 I would suggest that onCreate() and onNewIntent() both call a common procedure for scanning the tag. 我建议onCreate()onNewIntent()都调用扫描标签的通用过程。 In that way, both entry points would follow the same code path. 这样,两个入口点将遵循相同的代码路径。

Your claim that the app "jumps out of onCreate" is probably just a figure of speech? 您声称应用程序“跳出onCreate”可能只是一种表达方式? What happens is that your tag scanning AsyncNfcConnect runs on a separate thread as a background task (as it should). 发生的情况是,标签扫描AsyncNfcConnect作为后台任务在单独的线程上运行(应如此)。 That task is created in onCreate() and continues running after onCreate() has finished (you could add add a Log statement at the end of onCreate() to check). 该任务在onCreate()创建,并在onCreate()完成后继续运行(您可以在onCreate()的末尾添加一个Log语句以进行检查)。 When the connection with the tag is lost somehow and the tag is rediscovered, onNewIntent() is called, as you observed. 正如您所观察到的,当与标签的连接以某种方式丢失并重新发现了标签时,将调用onNewIntent()

In any case there is no way to prevent this from happening, so your app has to be able to handle it. 无论如何,都无法阻止这种情况的发生,因此您的应用必须能够处理它。 To detect this and deal with it, you could set some flag inside your activity to indicate that your background task is running or has run. 要检测并处理它,您可以在活动中设置一些标志,以指示您的后台任务正在运行或已经运行。 You could also store the information whether an exception has occurred and simply try scanning the tag again when it is rediscovered (this probably requires that you implement the suggestion I made above). 您还可以存储是否发生异常的信息,并在重新发现标签后尝试再次扫描标签(这可能需要您实施我在上文中提出的建议)。 If you want to make your app even more failure proof, you could also store the ID of the last scanned tag to positively identify it again when it is rediscovered after you have successfully scanned it (or not). 如果要使您的应用程序更能防止故障,还可以存储上次扫描的标签的ID,以便在成功扫描(或不成功)后重新发现该标签时再次对其进行肯定标识。 When exceptions keep occurring with the same tag, you could indicate his after a certain number of times to the user (eg by suggesting that the device be positioned differently wrt the tag). 当同一标签持续发生异常时,您可以在一定次数后向用户指示他的用户(例如,通过建议该设备在标签上放置不同的位置)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM