so I have an NFC tag that stores a URL and I am building an android app that reads that tag and starts the app to the Main activity. After that I have a WebView that displays that website. My problem here is, each tag has a different url/path (lets say they are clothing items and each point to a product on the website). How do I get the NDEF message from the tag (the url) and pass it to that parameter of the webview? Thank your for your help, here I will leave my code.
I have followed the android documentation on NFC both, basic and advanced, googled every page and searched everything on reddit, however I could not find any answer...
This is my Main Activity, the one that opens as soon as it reads the tag:
class MainActivity : AppCompatActivity() {
private var adapter: NfcAdapter? = null
// Pending intent for NFC intent foreground dispatch.
// Used to read all NDEF tags while the app is running in the foreground.
private var nfcPendingIntent: PendingIntent? = null
// Optional: filter NDEF tags this app receives through the pending intent.
//private var nfcIntentFilters: Array<IntentFilter>? = null
private val KEY_LOG_TEXT = "logText"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initNfcAdapter()
if (intent != null)
{
processIntent(intent)
}
}
private fun initNfcAdapter() {
val nfcManager = getSystemService(Context.NFC_SERVICE) as NfcManager
adapter = nfcManager.defaultAdapter
}
override fun onResume() {
super.onResume()
enableNfcForegroundDispatch()
}
private fun enableNfcForegroundDispatch() {
try {
val intent = Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
val nfcPendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
adapter?.enableForegroundDispatch(this, nfcPendingIntent, null, null)
} catch (ex: IllegalStateException) {
}
}
override fun onPause() {
disableNfcForegroundDispatch()
super.onPause()
}
private fun disableNfcForegroundDispatch() {
try {
adapter?.disableForegroundDispatch(this)
} catch (ex: IllegalStateException) {
}
}
private fun processIntent(checkIntent: Intent) {
if(checkIntent.action == NfcAdapter.ACTION_NDEF_DISCOVERED) {
val rawMessages = checkIntent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
}
}
}
This is the webview activity:
class Webview : AppCompatActivity() {
private lateinit var webview: Webview1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_webview)
val myWebView: WebView = findViewById(R.id.webview)
myWebView.webViewClient = object : WebViewClient () {
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
if (url != null) {
view?.loadUrl(url)
}
return true
}
}
myWebView.loadUrl("https://website.com")
myWebView.settings.javaScriptEnabled=true
myWebView.settings.allowContentAccess=true
myWebView.settings.domStorageEnabled=true
myWebView.settings.useWideViewPort=true
myWebView.settings.setAppCacheEnabled(true)
}
}
I want to pass the NDEF message to myWebVIew.loadUrl().
Related
class MainActivity : AppCompatActivity() {
// private var nfcAdapter: NfcAdapter? = null
lateinit var text: Button
#SuppressLint("SuspiciousIndentation")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// nfcAdapter = NfcAdapter.getDefaultAdapter(this);
text = findViewById(R.id.helo) as Button
text.setOnClickListener {
val intent = Intent(this, MyHostApduService::class.java)
Log.e("TAGGG","HELLO")
startService(intent)
}
}
}`
`class MyHostApduService : HostApduService() {
private val SELECT_APDU = byteArrayOf(
0x00.toByte(),
0xA4.toByte(),
0x04.toByte(),
0x00.toByte(),
0x08.toByte(),
0xF0.toByte(),
0x01.toByte(),
0x02.toByte(),
0x03.toByte(),
0x04.toByte(),
0x05.toByte(),
0x06.toByte()
)
private val HELLO_WORLD_APDU = "Hello World".toByteArray()
override fun processCommandApdu(commandApdu: ByteArray?, extras: Bundle?): ByteArray? {
return if (Arrays.equals(SELECT_APDU, commandApdu)) {
HELLO_WORLD_APDU
} else {
ByteArray(0)
}
}
override fun onDeactivated(reason: Int) {}
}
I need to emulate NFC card with specific content/record on my android device. Content on card should be a text something like "t100200". It s just a part of wider project and i could not find working example or tutorial. So i am not sure if is something like this possible. Thanks for any help or advice
Integrating Jitsi Sample in my project. but meeting unable to establish getting Cannot join, view is null error in logs. can anyone suggest what could be the issue?
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
private val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
onBroadcastReceived(intent)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val serverURL: URL = try {
URL("https://meet.jit.si")
} catch (e: MalformedURLException) {
e.printStackTrace()
throw RuntimeException("Invalid server URL!")
}
val defaultOptions = JitsiMeetConferenceOptions.Builder()
.setServerURL(serverURL)
// When using JaaS, set the obtained JWT here
//.setToken("MyJWT")
// Different features flags can be set
//.setFeatureFlag("toolbox.enabled", false)
//.setFeatureFlag("filmstrip.enabled", false)
.setFeatureFlag("welcomepage.enabled", false)
.build()
JitsiMeet.setDefaultConferenceOptions(defaultOptions)
registerForBroadcastMessages()
binding.button4.setOnClickListener{
onButtonClick()
}
}
override fun onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver)
super.onDestroy()
}
private fun onButtonClick() {
val text = binding.conferenceName.text.toString()
if (text.isNotEmpty()) {
// Build options object for joining the conference. The SDK will merge the default
// one we set earlier and this one when joining.
val options = JitsiMeetConferenceOptions.Builder()
.setRoom(text)
// Settings for audio and video
//.setAudioMuted(true)
//.setVideoMuted(true)
.build()
// Launch the new activity with the given options. The launch() method takes care
// of creating the required Intent and passing the options.
JitsiMeetActivity.launch(this, options)
}
}
private fun registerForBroadcastMessages() {
val intentFilter = IntentFilter()
/* This registers for every possible event sent from JitsiMeetSDK
If only some of the events are needed, the for loop can be replaced
with individual statements:
ex: intentFilter.addAction(BroadcastEvent.Type.AUDIO_MUTED_CHANGED.action);
intentFilter.addAction(BroadcastEvent.Type.CONFERENCE_TERMINATED.action);
... other events
*/
for (type in BroadcastEvent.Type.values()) {
intentFilter.addAction(type.action)
}
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, intentFilter)
}
// Example for handling different JitsiMeetSDK events
private fun onBroadcastReceived(intent: Intent?) {
if (intent != null) {
val event = BroadcastEvent(intent)
when (event.type) {
BroadcastEvent.Type.CONFERENCE_JOINED -> Timber.i("Conference Joined with url%s", event.getData().get("url"))
BroadcastEvent.Type.PARTICIPANT_JOINED -> Timber.i("Participant joined%s", event.getData().get("name"))
else -> Timber.i("Received event: %s", event.type)
}
}
}
// Example for sending actions to JitsiMeetSDK
private fun hangUp() {
val hangupBroadcastIntent: Intent = BroadcastIntentHelper.buildHangUpIntent()
LocalBroadcastManager.getInstance(this.applicationContext).sendBroadcast(hangupBroadcastIntent)
}
}
added below dependency.
dependencies {
// Jitsi Meet
implementation "org.jitsi.react:jitsi-meet-sdk:5.1.0"
}
Sharing log information also. this sample I am integrating into kotlin multiplatform project.
please suggest to resolve this issue.
I was developing some Conference application and got confused on how to handle PARTICIPANT_LEFT.action in code. Here is my code
private var roomName: String? = null
private lateinit var anotherUser:String
private var username: String? = SharedUserObject.userID
private var jitsiMeetView: JitsiMeetView? = null
#SuppressLint("LogNotTimber")
#Suppress("DEPRECATION")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_call)
anotherUser = intent.getStringExtra("AnotherUser").toString()
SharedUserObject.checkCallActivity = true
// get data from Two Activities (IncomingCall , CallFrom )
// create jitsi call view
// if (!SharedUserObject.checkCallActivity) {
dataFromIntent
jitsiMeetView = JitsiMeetView(this#CallActivity)
val conferenceOptions = videoChatOptions
jitsiMeetView!!.join(conferenceOptions)
setContentView(jitsiMeetView)
jitsiMeetView!!.listener = this
// }
val intentFilter = IntentFilter()
intentFilter.addAction(BroadcastEvent.Type.PARTICIPANT_LEFT.action)
LocalBroadcastManager.getInstance(this).registerReceiver(BroadcastReceiver(applicationContext), intentFilter)
}
override fun requestPermissions(
strings: Array<String>,
i: Int,
permissionListener: PermissionListener
) {}
// this run when user accept the call and set it busy
#SuppressLint("LogNotTimber")
override fun onConferenceJoined(map: Map<String, Any>) {
SharedUserObject.inCall = true
}
#SuppressLint("LogNotTimber")
// this run when user end the call and set it not busy
override fun onConferenceTerminated(map: Map<String, Any>) {
SharedUserObject.inCall = false
//
SharedUserObject.checkCallActivity = false
jitsiMeetView?.leave()
jitsiMeetView?.dispose()
jitsiMeetView = null
finish()
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
override fun onConferenceWillJoin(map: Map<String, Any>) {
//LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(muteBroadcastIntent)
}
private val dataFromIntent: Unit
get() {
roomName = intent.getStringExtra("RoomName").toString()
username = ""
}
// Set call subject here. Connection with jitsi call server and create Call.
private val videoChatOptions: JitsiMeetConferenceOptions
get() {
var videoChatUrl: URL? = null
try {
videoChatUrl = URL("https://meet.jit.si")
} catch (e: Exception) {
e.printStackTrace()
}
val meetUserInfo = JitsiMeetUserInfo()
meetUserInfo.displayName = SharedUserObject.userID
return JitsiMeetConferenceOptions.Builder()
.setServerURL(videoChatUrl)
.setAudioOnly(true)
.setAudioMuted(false)
.setUserInfo(meetUserInfo)
.setSubject(roomName) // Set call subject here. use to display phone number here.
.setRoom(roomName)
.build()
}
override fun onDestroy() {
super.onDestroy()
SharedUserObject.checkCallActivity = false
jitsiMeetView?.leave()
jitsiMeetView?.dispose()
jitsiMeetView = null
JitsiMeetActivityDelegate.onHostDestroy(this)
}
}
I need that when user leaves the Conference then the Conference should end. I tried using BroadcastEvent.Type.PARTICIPANT_LEFT.action
but no success was received. Please guide me if there is some other way to implement it.
I have built a QR scanning application (written in Kotlin, for Android). It scans for a QR code and then returns the URL after its scanned like so.
However, I wish to take it a step further and actually launch the return value of the QR code into a search engine of an internet application and have it display the results of what the QR code had intended. How would I get the application to get the returned URL and redirect the user to the intended place?
Here is my MainActivity.kt for reference:
class MainActivity : AppCompatActivity() {
private lateinit var codeScanner: CodeScanner
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val scannerView = findViewById<CodeScannerView>(R.id.scanner_view)
codeScanner = CodeScanner(this, scannerView)
codeScanner.camera = CodeScanner.CAMERA_BACK
codeScanner.formats = CodeScanner.ALL_FORMATS
codeScanner.autoFocusMode = AutoFocusMode.SAFE
codeScanner.scanMode = ScanMode.SINGLE
codeScanner.isAutoFocusEnabled = true
codeScanner.isFlashEnabled = false
codeScanner.decodeCallback = DecodeCallback {
runOnUiThread {
Toast.makeText(this, "Scan result: ${it.text}", Toast.LENGTH_LONG).show()}
}
scannerView.setOnClickListener {
codeScanner.startPreview()
}
}
override fun onResume() {
super.onResume()
codeScanner.startPreview()
}
override fun onPause() {
codeScanner.releaseResources()
super.onPause()
}
}
I used this answer to build a searchWeb function within my MainActivity.kt class like so;
fun searchWeb(query: String) {
val url = "http://www.google.com"
val intent = Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url))
startActivity(intent)
}
I then called the function within my codeScanner.decodeCallback like so;
codeScanner.decodeCallback = DecodeCallback {
runOnUiThread {
Toast.makeText(this, "Scan result: ${it.text}", Toast.LENGTH_LONG).show()
}
searchWeb(it.text)
}
After doing this, the implicit intent was launched and the QR application launched the internet browser.
I am developing an Android app using Kotlin and it is supposed to read the NFC tag, start my app and open the URL inside of the app.
However, whenever I am inside the app and I tap the NFC tag it opens Chrome. It is supposed to open the URL inside of it.
Here is my activity:
class MainActivity : AppCompatActivity() {
var TAG = "NFC supported"
var TAG2 = "NFC enabled"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var nfcAdapter = NfcAdapter.getDefaultAdapter(this)
Log.d("NFC supported", (nfcAdapter != null).toString())
Log.d("NFC enabled", (nfcAdapter?.isEnabled).toString())
if (intent != null) {
processIntent(intent)
}
val intent = Intent(this, javaClass).apply {
addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
}
var pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
val ndef = IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED).apply {
try {
addDataType("*/*") /* Handles all MIME based dispatches.
You should specify only the ones that you need. */
} catch (e: IntentFilter.MalformedMimeTypeException) {
throw RuntimeException("fail", e)
}
}
var intentFiltersArray = arrayOf(ndef)
val techListsArray = arrayOf(arrayOf<String>(NfcF::class.java.name))
}
override fun onNewIntent(intent: Intent?){
super.onNewIntent(intent)
if(intent != null){
processIntent(intent)
}
}
private fun processIntent(checkIntent: Intent) {
if(checkIntent.action == NfcAdapter.ACTION_NDEF_DISCOVERED) {
val rawMessages = checkIntent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
}
}
}
Any guesses?
You need to use enableForegroundDispatch to receive NFC Intent's when your App is in the foreground, the Manifest Filters is just to tell the System you want your App to be in the list of App to be consider to be launched when it sees a Tag of the correct type and your App is not running (Then the Intent should be processed in onCreate
See https://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc#foreground-dispatch
So create the Pending Intent and correct Intent Filers and enable/disable Foreground dispatch in onResume and onPause, once that is done Tag's seen while running will be delivered to onNewIntent
Update:
There looks nothing wrong with the documentation parts, but you still have not quite put it together correct.
So I've taken your code and re-arranged it a bit to show a fully working example (and tested with an NFcA NDEF tag)
import android.app.PendingIntent
import android.content.Intent
import android.content.IntentFilter
import android.nfc.NfcAdapter
import android.nfc.tech.NdefFormatable
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
class MainActivity : AppCompatActivity() {
var TAG = "NFC supported"
var TAG2 = "NFC enabled"
private var nfcAdapter : NfcAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
nfcAdapter = NfcAdapter.getDefaultAdapter(this)
Log.d("NFC supported", (nfcAdapter != null).toString())
Log.d("NFC enabled", (nfcAdapter?.isEnabled).toString())
if (intent != null) {
processIntent(intent)
}
}
override fun onNewIntent(intent: Intent?){
super.onNewIntent(intent)
if(intent != null){
processIntent(intent)
}
}
private fun processIntent(checkIntent: Intent) {
if(checkIntent.action == NfcAdapter.ACTION_NDEF_DISCOVERED) {
val rawMessages = checkIntent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
Log.d("processIntent", "NDEF Found")
}
}
public override fun onPause() {
super.onPause()
nfcAdapter?.disableForegroundDispatch(this)
}
public override fun onResume() {
super.onResume()
val intent = Intent(this, javaClass).apply {
addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
}
var pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
val ndef = IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED).apply {
try {
addDataType("*/*") /* Handles all MIME based dispatches.
You should specify only the ones that you need. */
} catch (e: IntentFilter.MalformedMimeTypeException) {
throw RuntimeException("fail", e)
}
}
var intentFiltersArray = arrayOf(ndef)
val techListsArray = arrayOf(arrayOf<String>(NdefFormatable::class.java.name))
nfcAdapter?.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray)
}
}
Note I changed from NfcF to NdefFormatable::class.java.name in the techlist as this is more useful if only dealing with NDEF type data (As NFcF type tags are less common) but really if the NFC Tag has NDEF data on it the this can valve can be set to null in the enableForegroundDispatch call