Search code examples
androidnfcintentfilter

Possible to prevent app launch by NFC intent from within an app?


I have an app for which the requirements are to launch it on detection of a non-NDEF NFC tag, so I'm using the TECH_DISCOVERED filter on my main activity to do so:

<intent-filter>
    <action android:name="android.nfc.action.TECH_DISCOVERED" />
</intent-filter>

This works fine, however some users complain that their phone case doubles as a holder for their credit cards / smart cards and hence the app is unintentionally launching when they close their phone case. These users don't want to have to disable the device NFC setting (and that can't be done programmatically) so my question is: is it possible to programmatically stop an app launching by NFC intent from within that app?

The best idea I can come up with is to have the NFC intent launch a non-UI Activity (one that doesn't call setContentView) and have this check if some persistent flag has been set (by a UI control in the main activity) and if the flag is set, do not launch the main activity.

Is there an easier/more elegant solution?


Solution

  • My solution to this was to launch a headless (invisible) activity via the NFC intent and use a shared preference (set by a UI switch via the main activity) to determine whether to launch the main activity.

    AndroidManifest.xml:

    <activity android:name="com.mypackage.NFCLaunchActivity" android:theme="@android:style/Theme.Translucent.NoTitleBar">
        <intent-filter>
            <action android:name="android.nfc.action.TECH_DISCOVERED" />
        </intent-filter>
        <meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" />
    </activity>
    

    res/nfc_tech_filter.xml:

    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
        <tech-list>
            <tech>android.nfc.tech.NfcA</tech>
        </tech-list>
    </resources>
    

    MainActivity.java:

    public static String SETTINGS_NAME = "settings";
    public static String shouldLaunchByNFC = "launchWithNfc";
    
    // Call on changing UI state
    protected void setShouldLaunchByNFC(boolean enableLaunch) {
        setSettingBoolean(this, shouldLaunchByNFC, enableLaunch);
    }
    
    // Call to set initial UI state
    protected boolean getShouldLaunchByNFC() {
        return getSettingBoolean(this, shouldLaunchByNFC, true);
    }
    
    public static void setSettingBoolean(Activity activity, String name, boolean value){
        SharedPreferences settings = activity.getSharedPreferences(SETTINGS_NAME, MODE_PRIVATE);
        SharedPreferences.Editor editor = settings.edit();
        editor.putBoolean(name, value);
        editor.commit();
    }
    
    public static boolean getSettingBoolean(Activity activity, String name, boolean defaultValue){
        SharedPreferences settings = activity.getSharedPreferences(SETTINGS_NAME, MODE_PRIVATE);
        return settings.getBoolean(name, defaultValue);
    }
    

    NFCLaunchActivity.java:

    import static com.mypackage.MainActivity.getSettingBoolean;
    import static com.mypackage.MainActivity.shouldLaunchByNFC;
    
    public class NFCLaunchActivity extends Activity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // setContentView explicitly omitted
    
            boolean launchWithNfc = getSettingBoolean(this, shouldLaunchByNFC, true);
            if(launchWithNfc){
                Context context = this.getApplicationContext();
                Intent intent = new Intent();
                intent.setClassName(context, context.getPackageName() + ".MainActivity");
                context.startActivity(intent);
            }
            finish();
        }
    }