Problem receiving Intents with Zebra Data wedge API - android

We are currently using a zebra device for company asset management so we are developing a small prototype android app to scan RFID tags. I've read from the data wedge API that the app can get scanned output has an intent broadcast.
But the app is unable to receive any intents.
Device : Zebra MC33
Data wedge version : 7.3
I've tried using the following
Profile Settings:
Intent Action : my.prototype.app.SCAN
Intent Delivery Type: Broadcast Intent.
Intent Category: Default.
Added to Associated Apps
AndroidManifest.xml
<receiver
android:name=".ScanIntentReceiver"
android:enabled="true"
android:exported="true" />
ScanIntentReceiver.kt
abstract class ScanIntentReceiver : BroadcastReceiver() {
abstract fun onReceiveScan(data: ScannerOutput)
override fun onReceive(p0: Context?, p1: Intent?) {
Timber.d("S1: Broadcast Scan Intent Received.")
p0?.let { context ->
p1?.let { intent ->
when (intent.action) {
BuildConfig.intentAction -> {
try {
val data = parseData(intent, context)
Timber.d("Data received: $data")
onReceiveScan(data)
} catch (ex: Exception) {
Timber.d("Parsing error")
Timber.d(ex)
}
}
else -> {
Timber.d("No Suitable Action.")
}
}
}
}
}
}
Also tried using the "Send via Start Activity"
Profile Settings:
Intent Action : my.prototype.app.SCAN
Intent Delivery Type: Send via StartActivity.
Intent Category: Default.
Added to Associated Apps
AndroidManifest.xml
<activity
android:name=".activity.ScanActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="${intentAction}" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
ScanActivity.kt
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
Timber.d("Received Intent via Activity.")
intent?.let {
try {
val data = parseData(it, this)
viewModel.processOutput(data)
} catch (ex: Exception) {
Timber.e(ex)
}
}
}
Any help is appreciated. Thanks in advance.
UPDATE:
private fun parseData(intent: Intent, ctx: Context): ScannerOutput {
val decodedSource =
intent.getStringExtra(ctx.getString(R.string.datawedge_intent_key_source))
val decodedData =
intent.getStringExtra(ctx.getString(R.string.datawedge_intent_key_data))
val decodedLabelType =
intent.getStringExtra(ctx.getString(R.string.datawedge_intent_key_label_type))
....
}
UPDATE:
val filter = IntentFilter()
filter.addCategory(Intent.CATEGORY_DEFAULT)
filter.addAction(BuildConfig.intentAction)
registerReceiver(scanIntentReceiver, filter)

Let's clarify things a bit. If you want to read RFID tags using the MC33R, then you must use Zebra RFID API3, not intents. Zebra is considering using the intents also for RFID, but at the moment the best option is to use the SDK, not the intent Broadcaster/Receiver. If you intend to use the barcode scanner, then the official (new) way of doing it is through intents. To get the intents you must configure a profile in the Data Wedge, you must activate intent broadcasing and specify the intent action in the profile, if you do so, you'll receive the intent. Look for the following settings in the data Wedge profile (default profile is good):
Intent Output = ON
Intent action = my.prototype.app.SCAN
Intent distribution (or delivery): Broadcast
I can assure you that these settings will work for the barcode scanner, but in case you want to use the RFID antenna, download the API3 SDK from Zebra Developer site and follow the examples.
***UPDATE
val filter = IntentFilter()
filter.addCategory(Intent.CATEGORY_DEFAULT)
filter.addAction("my.prototype.app.SCAN")//here set the action (same as in DataWedge app)
this.requireContext().registerReceiver(this, filter)
Implement
BroadcastReceiver
and add:
override fun onReceive(context: Context?, intent: Intent?) {
//Receives readings from barcode scanner
val action = intent!!.action
if (action == "my.prototype.app.SCAN") {
val decodedData = intent.getStringExtra("com.symbol.datawedge.data_string")
if (decodedData != null) {
//decodedData is your barcode
}
}
}

Related

Context-Registered Receiver not being triggered by Custom Action

I have a problem: the Broadcast Receiver in one of my apps isn't having its onReceive callback triggered by a custom action broadcast sent by my service from a separate app.
The broadcast receiver is context-registered, meaning I don't want to declare it in a manifest file anywhere because I don't want broadcasts launching my app. The broadcast is being sent from a service completely separate from the app, so we're dealing with inter-process communication here and a local broadcast won't do.
My suspicion is that i'm not correctly matching the intent action string declared in my broadcast sender (the Service) with my broadcast receiver (the App).
Looking at my code below, what am I doing incorrectly?
ScannerService.kt
Intent().also { intent ->
intent.action = "com.foo.bar.example.package.ScannerService.SCANNER_EVENT"
intent.putExtra("barcode", barcode)
intent.setPackage("com.nu.rms")
sendBroadcast(intent)
Timber.d("Sent broadcast")
}
AppActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
registerBroadcastReceivers()
}
private fun registerBroadcastReceivers() {
val broadcastReceiver = ScannerBroadcastReceiver()
val filter = IntentFilter().apply { addAction("SCANNER_EVENT") }
registerReceiver(broadcastReceiver, filter)
Timber.d("Registered broadcast receiver")
}
class ScannerBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Timber.d("Received broadcast")
StringBuilder().apply {
append("Action: ${intent.action}\n")
append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}\n")
toString().also { log -> Timber.d(log) }
}
}
}
Try to use a manifest-declared receiver (eg. in case permissions may have to be added there):
<receiver
android:name=".ScannerBroadcastReceiver"
android:permission="android.permission.INTERNET">
<intent-filter>
<action android:name="com.foo.bar.example.package.ScannerService.SCANNER_EVENT"/>
</intent-filter>
</receiver>
When using a context-registered receiver, the action might be ScannerService.SCANNER_EVENT or verbosely com.foo.bar.example.package.ScannerService.SCANNER_EVENT.

Sending text data from Android to IOS using NFC [duplicate]

This question already has answers here:
NDEF Message with HCE Android
(2 answers)
Closed 4 years ago.
I know that at the moment there is no API in IOS 11 to write data to NFC tags, but it's possible to read data from NFC tags and want to pass text from Android device to iPhone.
I assumed that it's possible to write NdefMessage and it will be received on IOS, but it just doesnt work for me. There is no Intent received when I start NFC scanning on IOS (using NfcActions application).
Source code of my main activity:
class MainActivity : AppCompatActivity() {
var nfc: NfcAdapter? = null
#SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
hintLabel.text = "Initializing..."
nfc = NfcAdapter.getDefaultAdapter(this)
if (nfc == null) hintLabel.text = "NFC is not available on this device"
else hintLabel.text = "NFC initialized"
}
override fun onResume() {
super.onResume()
nfc?.enableNFCInForeground(this, javaClass)
}
override fun onPause() {
super.onPause()
nfc?.disableForegroundDispatch(this)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
val pathPrefix = "/testuser:nfctest"
val isSuccess = createNFCMessage(pathPrefix, "Hello World", intent)
if(isSuccess)
hintLabel.text = "Successfully wrote data"
else
hintLabel.text = "Couldnt write any data"
}
fun createNFCMessage(pathPrefix: String, payload: String, intent: Intent?) : Boolean {
val nfcRecord = NdefRecord(NdefRecord.TNF_EXTERNAL_TYPE, pathPrefix.toByteArray(), ByteArray(0), payload.toByteArray())
val nfcMessage = NdefMessage(arrayOf(nfcRecord))
intent?.let {
val tag = it.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
return writeMessageToTag(nfcMessage, tag)
}
return false
}
fun <T>NfcAdapter.enableNFCInForeground(activity: Activity, classType: Class<T>) {
val pendingIntent = PendingIntent.getActivity(activity, 0,
Intent(activity,classType).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0)
val filters = arrayOf(IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED))
val techList = arrayOf(arrayOf(Ndef::class.java.name), arrayOf(NdefFormatable::class.java.name))
this.enableForegroundDispatch(activity, pendingIntent, filters, techList)
}
private fun writeMessageToTag(nfcMessage: NdefMessage, tag: Tag?): Boolean {
// This functions writes given NdefMessage to the tag, if it's possible
// and returns whether it was successful or not
}
And I also added following permissions and intent filter to main manifest
<uses-permission android:name="android.permission.NFC" />
<uses-feature
android:name="android.hardware.nfc"
android:required="false" />
<application ...>
<activity ...>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:host="ext"
android:pathPrefix="/testuser:nfctest"
android:scheme="vnd.android.nfc"
android:mimeType="text/plain"/>
</intent-filter>
</activity>
</application>
What am I doing wrong and how to properly pass data from Android device to iPhone using NFC?
Apparently the usual peer-2-peer way to send NDEF messages from one phone to another only works between 2 android devices.
To send a message from Android to iOS you need to use host card emulation.
Basically the Android phone needs to emulate a Tag 4 Type (based on NDEF Forum specification), for the iPhone to read the content.
Hope that gets you on the right track ...
For anyone who stuck on this issue, I have read NFCForum-TS-Type-4-Tag which proposed by #Michael Roland. The whole idea is correct. All you need is to simulate the process SEND and RECEIVED commands to convert the byte array to a NDEF message. I created two repositories, one conclude the whole package about converting string to a NDEF message and the other one is a iOS Reader NDEF TAG to verify whether Android HCE is correct or not.
So Good luck!
Also see the response by Michael Roland to NDEF Message with HCE Android.

Bluetooth can't find custom service UUID in SDP query

I've got a bit of my app that is dedicated to sharing files between devices over bluetooth using a quick, ad-hoc protocol that I put together. Currently, in the containing Activity I begin discovery, and add any device that I find into a RecyclerView. Here is the code for the BroadcastReceiver that is handling that:
private val scanReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == BluetoothDevice.ACTION_FOUND) {
val dev = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
Log.d(TAG, "Got device ${dev.name} with address ${dev.address}")
if (dev.name != null) {
Log.d(TAG, "Found nonnull device name, adding")
if (!viewAdapter.dataset.any { it.name == dev.name }) {
viewAdapter.dataset.add(dev)
viewAdapter.notifyDataSetChanged()
}
}
}
}
}
I wanted to modify this in such a way that it would only add devices who were broadcasting with the service UUID that I set up in the server portion of the app. After doing some research I came to this method that I could use to get the UUIDs of the services on the device. I integrated that into my BroadcastReceiver as such
private val scanReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
BluetoothDevice.ACTION_FOUND -> {
val dev = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
Log.d(TAG, "Got device ${dev.name} with address ${dev.address}")
if (dev.name != null) {
dev.fetchUuidsWithSdp()
}
}
//TODO: Untested code
BluetoothDevice.ACTION_UUID -> {
val id = intent.getParcelableExtra<ParcelUuid>(BluetoothDevice.EXTRA_UUID)
if (id.uuid == ShareServerSocket.SERVICE_UUID) {
val dev = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
if (!viewAdapter.dataset.any { it.name == dev.name }) {
viewAdapter.dataset.add(dev)
viewAdapter.notifyDataSetChanged()
}
}
}
}
}
}
(With requisite modifications to the IntentFilter I'm registering it with).
The code in the new branch gets called, I validated that with some debugging output. However, the ParcelUuid[] that I am given never contains the UUID of my service, and the device therefore never gets added. If I keep the entire setup the same on the device acting as a server, and bypass the new check on the client, I am able to connect and interact just fine. I'm unsure as to why my service wouldn't be being shown at this point.
P.S. I did also check the SDP cache, my service UUID is not there, either.
It turns out I was running into the same issue as described in Strange UUID reversal from fetchUuidsWithSdp. Stealing that workaround made it work.

How to direct users for enabling accessibility service for my app

I know It's impossible to enable the Accessibility service for apps programmatically, so I'd like to direct users to this screen:
System settings --> Accessibility --> app name --> enable/disable screen.
Is that possible ?
You can get them to the Accessibility screen on most devices using ACTION_ACCESSIBILITY_SETTINGS. However:
that may not work on all devices, so you will want to just send them to Settings as a fallback, if you get an ActivityNotFoundException
there is no way to get them straight to any given app, let alone the enable/disable screen
You can at least make it reach the app, making the app item blink. It should work for most devices, or at least those that are like of Pixel devices:
fun <T : AccessibilityService> getRequestAccessibilityPermissionIntents(context: Context, accessibilityService: Class<T>): Array<Intent> {
var intent = Intent("com.samsung.accessibility.installed_service")
if (intent.resolveActivity(context.packageManager) == null) {
intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
}
val extraFragmentArgKey = ":settings:fragment_args_key"
val extraShowFragmentArguments = ":settings:show_fragment_args"
val bundle = Bundle()
val showArgs = "${context.packageName}/${accessibilityService.canonicalName!!}"
bundle.putString(extraFragmentArgKey, showArgs)
intent.putExtra(extraFragmentArgKey, showArgs)
intent.putExtra(extraShowFragmentArguments, bundle)
return arrayOf(intent, Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY))
}
Usage:
private fun requestAccessibilityPermission() {
getRequestAccessibilityPermissionIntents(this, MyAccessibilityService::class.java).forEach { intent ->
try {
startActivity(intent)
return
} catch (e: Exception) {
}
}
//TODO do something here in case it failed
}

Preferred way to attach AudioEffect to global mix?

In Android, AudioEffect API, all of the builtin effects such as Equalizer come with a warning
"NOTE: attaching an Equalizer to the global audio output mix by use of session 0 is deprecated. "
If this is deprecated, then what is the replacement API? My goal is to attach an effect to the global output mix...
Yes it is deprecated, because of side-effects isues.
The Android Developers website states that the second parameter of the Equalizer class should be :
A system wide unique audio session identifier. The Equalizer will be
attached to the MediaPlayer or AudioTrack in the same audio session.
You should use this instead :
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource( _your_data_source_ );
Equalizer equalizer = new Equalizer(0, mediaPlayer.getAudioSessionId());
equalizer.setEnabled(true);
/* Do your stuff ... */
mediaPlayer.start();
There is no alternative to attach an AudioEffect to the global output. What you should do instead is register a broadcast receiver that receives all audio sessions, so you can apply audio effects to that. An example can be found here. The intent containing the session ID is obtained in this BroadcastReceiver. Remember that this only works if you registered the receiver in the manifest.
Alternatively you could register a receiver programmatically like this in your service onCreate():
IntentFilter()
.apply { addAction(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION) }
.apply { addAction(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION) }
.run { registerReceiver(mAudioSessionReceiver, this) } `
where mAudioSessionReceiver looks something like this:
private val mAudioSessionReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent == null || context == null) {
return
}
val sessionStates = arrayOf(
AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION,
AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION
)
if (sessionStates.contains(intent.action)) {
val service = Intent(context, WaveletService::class.java)
val sessionID = intent.getIntExtra(AudioEffect.EXTRA_AUDIO_SESSION, AudioEffect.ERROR)
service
.apply { action = intent.action }
.apply { putExtra(AudioEffect.EXTRA_AUDIO_SESSION, sessionID) }
context.startService(service)
}
}
}`
Then, you can obtain the intent in onStartCommand:
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent == null)
return START_STICKY
val sessionID = intent.getIntExtra(AudioEffect.EXTRA_AUDIO_SESSION, AudioEffect.ERROR)
when (intent.action) {
AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION -> {
// create new instance of the AudioEffect using the sessionID
}
AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION -> {
// Release instance of the AudioEffect connected to this sessionID
}
}
return START_REDELIVER_INTENT
}`
Lastly, don't forget to unregister the receiver in onDestroy():
unregisterReceiver(mAudioSessionReceiver)`
According to Android, you can use ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION to receive the id of a playing audio session:
Intent to signal to the effect control application or service that a new audio session is opened and requires audio effects to be applied.
I tried adding the constant in the manifest, but it didn't work:
<receiver android:name=".receivers.AudioSessionReceiver">
<intent-filter>
<action android:name="android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION"/>
</intent-filter>
</receiver>
If you figure out how to use the constant, the intent will contain the audio session id, which you can use to apply the equalizer settings you want. (If you found a work-around in all those years, would you mind sharing?)

Categories

Resources