SDK

Scannables

This functionality will allow your app to present content after they have scanned a NFC Tag or QR Code (we use QR Codes whenever the device does not support NFC). This could be extremely useful if you looking to extend physical objects with engaging content pretty much the same way you do when sending a notification.

Before you can start using this functionality you will need to create one or more tags in our dashboard. This is covered in our guides located here.

For this functionality you'll need to add the following to your /app/build.gradle file:

dependencies {
    //...more dependencies
    implementation 're.notifica:notificare-scannable:2.4.0' // make sure you use always the latest version
    // implementation 're.notifica:notificare-scannable-hms:2.4.0' // if you need your app to run on devices with HMS too
}

Then let's add the following to your AndroidManifest.xml file:

<activity
    android:name="re.notifica.ui.ScannableActivity"
    android:configChanges="keyboardHidden|orientation|screenSize"
    android:hardwareAccelerated="true">
</activity>

In most cases you will want to start scanning a tag whenever the user opens your app and clicks a button in your UI. This would be done from one your app's activities or fragments like this:

public class MyScanningActivity extends ActionBarBaseActivity implements Notificare.OnNotificareReadyListener {


    private static final int SCANNABLE_REQUEST_CODE = 9001;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        Notificare.shared().addNotificareReadyListener(this);
        //... more code

    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        Notificare.shared().removeNotificareReadyListener(this);
    }

    @Override
    public void onNotificareReady(NotificareApplicationInfo notificareApplicationInfo) {

        myButton.setOnClickListener(view -> {
            Notificare.shared().startScannableActivity(this, SCANNABLE_REQUEST_CODE);
        });

    }
    
    //...more code
}
class MyScanningActivity : ActionBarBaseActivity(), OnNotificareReadyListener {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Notificare.shared().addNotificareReadyListener(this)
        //... more code
    }

    override fun onDestroy() {
        super.onDestroy()
        Notificare.shared().removeNotificareReadyListener(this)
    }

    override fun onNotificareReady(notificareApplicationInfo: NotificareApplicationInfo) {
        val myButton = Button(this)
        myButton.setOnClickListener { view: View? ->
            Notificare.shared().startScannableActivity(this, SCANNABLE_REQUEST_CODE)
        }

        //...more code
    }

    companion object {
        private const val SCANNABLE_REQUEST_CODE = 9001
    }
}

Starting this built-in activity whenever a user clicks a button will be enough to start a scanning session. If the user's device supports NFC that activity will show a text indicating that the user should tap the tag. If by any chance the device does not support NFC or NFC is not enabled, the activity will fallback to scanning Qr Codes and present the built-in camera.

Once the user taps a NFC tag or scans a Qr Code, your app will be responsible for handling the results. In that activity you would then implement the following:

public class MyScanningActivity extends ActionBarBaseActivity implements Notificare.OnNotificareReadyListener {


    private static final int SCANNABLE_REQUEST_CODE = 9001;

    //...more code

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == SCANNABLE_REQUEST_CODE) {
            if (resultCode == CommonStatusCodes.SUCCESS) {
                if (data != null) {
                    NotificareScannable scannable = Notificare.shared().extractScannableFromActivityResult(data);
                    if (scannable != null) {
                        if (scannable.getNotification() != null) {
                            Notificare.shared().openNotification(this, scannable.getNotification());
                        } else {
                            Log.i(TAG, "scannable with type " + scannable.getType());
                        }
                    } else {
                        Toast.makeText(this, "scannable not found", Toast.LENGTH_LONG).show();
                    }
                } else {
                    Toast.makeText(this, "scan did not return any results", Toast.LENGTH_LONG).show();
                }
            } else if (resultCode == CommonStatusCodes.CANCELED) {
                Toast.makeText(this, "scan was canceled", Toast.LENGTH_LONG).show();
            } else {
                Log.w(TAG, "error result: " + resultCode);
            }
        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }


    //...more code

}
class MyScanningActivity : ActionBarBaseActivity(), OnNotificareReadyListener {
    public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        if (requestCode == SCANNABLE_REQUEST_CODE) {
            if (resultCode == CommonStatusCodes.SUCCESS) {
                if (data != null) {
                    val scannable = Notificare.shared().extractScannableFromActivityResult(data)
                    if (scannable != null) {
                        if (scannable.notification != null) {
                            Notificare.shared().openNotification(this, scannable.notification)
                        } else {
                            Log.i(TAG, "scannable with type " + scannable.type)
                        }
                    } else {
                        Toast.makeText(this, "scannable not found", Toast.LENGTH_LONG).show()
                    }
                } else {
                    Toast.makeText(this, "scan did not return any results", Toast.LENGTH_LONG).show()
                }
            } else if (resultCode == CommonStatusCodes.CANCELED) {
                Toast.makeText(this, "scan was canceled", Toast.LENGTH_LONG).show()
            } else {
                Log.w(TAG, "error result: $resultCode")
            }
        } else {
            super.onActivityResult(requestCode, resultCode, data)
        }
    } 

    companion object {
        private const val SCANNABLE_REQUEST_CODE = 9001
    }

    //...more code
}

The code above would make sure that errors were handled correctly as well as it would present the content scanned to the user.

In Android devices with support for NFC, tags can also be scanned even when your app is not being used. This means that you will need to add some extra configurations if you would like your app to respond to NFC tags scanned when it is is inactive or in the background.

To signal Android that your app can handle these tags, you need to declare the following in any of your activities:

<activity android:name=".MyActivity">
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:scheme="com.mydomain" />
    </intent-filter>
</activity>

And then in that activity you'll want to handle the results of the scanning session. To do that, add the following code:

public class MyActivity extends ActionBarBaseActivity implements Notificare.OnNotificareReadyListener {


    private static final int SCANNABLE_REQUEST_CODE = 9001;

    //...more code

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_my);

        //...more code

        //Let's handle the intent
        handleIntent(getIntent());
    }


    protected void handleIntent(Intent intent) {
        Uri data = intent.getData();
        if (data != null && intent.getAction() != null && intent.getAction().equals(NfcAdapter.ACTION_NDEF_DISCOVERED)) {
            Notificare.shared().fetchScannable(data.toString(), new NotificareCallback<NotificareScannable>() {

                @Override
                public void onSuccess(NotificareScannable notificareScannable) {
                    if (notificareScannable != null) {
                        if (notificareScannable.getNotification() != null) {
                            Notificare.shared().openNotification(MainActivity.this, notificareScannable.getNotification());
                        } else {
                            Toast.makeText(MainActivity.this, "scannable found", Toast.LENGTH_LONG).show();
                        }
                    }
                }

                @Override
                public void onError(NotificareError notificareError) {
                    Toast.makeText(MainActivity.this, "scannable not found", Toast.LENGTH_LONG).show();
                }
            });
        } else {
            //Handle unidentifiable data
        }
    }


    //...more code

}
class MyScanningActivity : ActionBarBaseActivity(), OnNotificareReadyListener {

    //...more code

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_my)

        //...more code

        //Let's handle the intent
        handleIntent(intent)
    }

    protected fun handleIntent(intent: Intent) {
        val data = intent.data
        if (data != null && intent.action != null && intent.action == NfcAdapter.ACTION_NDEF_DISCOVERED) {
            Notificare.shared().fetchScannable(data.toString(), object : NotificareCallback<NotificareScannable?> {
                override fun onSuccess(notificareScannable: NotificareScannable?) {
                    if (notificareScannable != null) {
                        if (notificareScannable.notification != null) {
                            Notificare.shared().openNotification(this@Test, notificareScannable.notification)
                        } else {
                            Toast.makeText(this@Test, "scannable found", Toast.LENGTH_LONG).show()
                        }
                    }
                }

                override fun onError(notificareError: NotificareError) {
                    Toast.makeText(this@Test, "scannable not found", Toast.LENGTH_LONG).show()
                }
            })
        } else {
            //Handle unidentifiable data
        }
    } 

    companion object {
        private const val SCANNABLE_REQUEST_CODE = 9001
    }

    //...more code
}

This will make sure that as soon as the NFC tag is scanned your app will be able to handle results and present the content accordingly.