Skip to content

Commit

Permalink
Refined NfcAdapterController to inject NfcControllerFactory internall…
Browse files Browse the repository at this point in the history
…y. This means that NfcViewModel no longer needs the factory class passed in.
  • Loading branch information
h0ker committed Oct 4, 2024
1 parent 261a57b commit aa1f415
Show file tree
Hide file tree
Showing 14 changed files with 154 additions and 46 deletions.
9 changes: 5 additions & 4 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ plugins {

android {
namespace = "com.carbidecowboy.intra_example"
compileSdk = 34
compileSdk = 35

defaultConfig {
applicationId = "com.hoker.intra_example"
minSdk = 28
targetSdk = 35
versionCode = 131
versionName = "1.3.1"
versionCode = 133
versionName = "1.3.3"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand Down Expand Up @@ -57,6 +57,7 @@ dependencies {

implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.12.0")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
implementation("androidx.navigation:navigation-compose:2.7.7")
implementation("androidx.compose.ui:ui:1.6.7")
implementation("androidx.compose.material:material:1.6.7")
Expand All @@ -68,5 +69,5 @@ dependencies {
implementation("androidx.constraintlayout:constraintlayout-compose-android:1.1.0-alpha13")

implementation(project(":intra"))
implementation("com.github.CarbideCowboy:Supra:0.0.6")
implementation("com.github.h0ker:Supra:0.0.9")
}
2 changes: 2 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.NFC"/>

<application
android:allowBackup="true"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,69 @@
package com.hoker.intra_example.presentation

import android.os.Bundle
import android.os.PersistableBundle
import android.widget.Toast
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import com.hoker.intra.domain.NfcActivity
import com.carbidecowboy.supra.presentation.scaffolds.SupraGyroScaffold
import com.hoker.supra.presentation.scaffolds.SupraGyroScaffold
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collectLatest

@AndroidEntryPoint
class IntraExampleActivity: NfcActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {

val viewModel: IntraExampleViewModel = hiltViewModel()
val context = LocalContext.current

LaunchedEffect(Unit) {
viewModel.errorFlow.collectLatest { errorMessage ->
Toast.makeText(context, errorMessage, Toast.LENGTH_SHORT).show()
}
}

SupraGyroScaffold(
borderColor = Color.Black,
backgroundColor = Color.DarkGray
borderColor = Color.DarkGray,
backgroundColor = Color.Black
) {

Column(
modifier = Modifier.padding(16.dp)
) {
if (viewModel.jwtText == null) {
Text(
text = "Scan to get JWT",
color = Color.White,
fontSize = 24.sp,
fontWeight = FontWeight.Bold
)
}
viewModel.jwtText?.let { jwt ->
Text(
text = "JWT:",
color = Color.White,
fontSize = 24.sp,
fontWeight = FontWeight.Bold
)
Text(
text = jwt,
color = Color.White,
fontSize = 16.sp
)
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.hoker.intra_example.presentation

import android.nfc.Tag
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.viewModelScope
import com.hoker.intra.domain.NfcAdapterController
import com.hoker.intra.domain.NfcController
import com.hoker.intra.domain.NfcViewModel
import com.hoker.intra.domain.OperationResult
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class IntraExampleViewModel @Inject constructor(
nfcAdapterController: NfcAdapterController
): NfcViewModel(nfcAdapterController) {

private val _jwtText: MutableState<String?> = mutableStateOf(null)
val jwtText: String?
get() { return _jwtText.value }

private val _errorChannel = Channel<String>(Channel.BUFFERED)
var errorFlow = _errorChannel.receiveAsFlow()

override fun onNfcTagDiscovered(tag: Tag, nfcController: NfcController) {
viewModelScope.launch(Dispatchers.IO) {
nfcController.withConnection(tag) {
when (val result = nfcController.getVivokeyJwt(tag)) {
is OperationResult.Success -> {
_jwtText.value = result.data
}
is OperationResult.Failure -> {
_jwtText.value = null
_errorChannel.trySend(result.exception?.message ?: "Error getting JWT")
}
}
}
}
}
}
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ buildscript {
}

plugins {
id("com.android.application") version "8.4.0" apply false
id("com.android.application") version "8.6.0" apply false
id("org.jetbrains.kotlin.android") version "1.9.24" apply false
id("com.android.library") version "8.4.0" apply false
id("com.android.library") version "8.6.0" apply false
id("com.google.devtools.ksp") version "1.9.10-1.0.13" apply false
}
4 changes: 2 additions & 2 deletions intra/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ afterEvaluate {
publications {
create<MavenPublication>("release") {
from(components["release"])
groupId = "com.github.CarbideCowboy"
groupId = "com.github.h0ker"
artifactId = "Intra"
version = "1.3.1"
version = "1.3.3"
}
}
}
Expand Down
16 changes: 8 additions & 8 deletions intra/src/main/java/com/hoker/intra/data/IsodepControllerImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -139,17 +139,14 @@ class IsodepControllerImpl @Inject constructor(
command[6] = 0x00.toByte()
command[7] = 0x00.toByte()

val isoDep = IsoDep.get(tag)
isoDep.connect()

//TODO: Add error check on this result
isoDep.transceive(NDEF_SEL)
isoDep?.transceive(NDEF_SEL)

// send part 1 command
val part1Result = isoDep.transceive(command)
val part1Result = isoDep?.transceive(command)

var piccChallenge = part1Result
piccChallenge = piccChallenge.copyOfRange(0, 16)
piccChallenge = piccChallenge?.copyOfRange(0, 16)
println("PICC Challenge:\n${Hex.encodeHexString(piccChallenge)}\n\n")

val challengeRequest = ChallengeRequest(
Expand Down Expand Up @@ -179,8 +176,7 @@ class IsodepControllerImpl @Inject constructor(
command[37] = 0x00.toByte()

Log.i("Part 2 Command", Hex.encodeHexString(command))
val response = isoDep.transceive(command)
isoDep.close()
val response = isoDep?.transceive(command)
Log.i("Response", Hex.encodeHexString(response))

val responseString = Hex.encodeHexString(response)
Expand Down Expand Up @@ -267,6 +263,10 @@ class IsodepControllerImpl @Inject constructor(
)
}

override fun getMaxTransceiveLength(): Int? {
return isoDep?.maxTransceiveLength
}

override suspend fun writeNdefMessage(tag: Tag, message: NdefMessage): OperationResult<Unit> {
return try {
val ndef = Ndef.get(tag)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ class NfcAControllerImpl @Inject constructor(
}
}

override fun getMaxTransceiveLength(): Int? {
return nfcA?.maxTransceiveLength
}

override suspend fun getNdefCapacity(ndef: Ndef): OperationResult<Int> {
return try {
val result = ndef.maxSize
Expand Down
10 changes: 5 additions & 5 deletions intra/src/main/java/com/hoker/intra/data/NfcVControllerImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,6 @@ class NfcVControllerImpl @Inject constructor(
OperationResult.Failure()
}

val nfcV = NfcV.get(tag)
nfcV.connect()

// truncate challenge to 10 bytes
// challenge string into hex
val challengeBytes: ByteArray =
Expand All @@ -199,8 +196,7 @@ class NfcVControllerImpl @Inject constructor(
challengeBytes.copyInto(command, UID_BYTE_LENGTH + 5, 0)
// connect and send command
Log.i("Command", Hex.encodeHexString(command))
val response = nfcV.transceive(command)
nfcV.close()
val response = nfcV?.transceive(command)
Log.i("Response", Hex.encodeHexString(response))

val sessionRequest = SessionRequest(
Expand Down Expand Up @@ -231,6 +227,10 @@ class NfcVControllerImpl @Inject constructor(
}
}

override fun getMaxTransceiveLength(): Int? {
return nfcV?.maxTransceiveLength
}

override suspend fun getNdefMessage(ndef: Ndef): OperationResult<NdefMessage?> {
return try {
val result = ndef.cachedNdefMessage
Expand Down
7 changes: 5 additions & 2 deletions intra/src/main/java/com/hoker/intra/di/NfcModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,11 @@ abstract class NfcModule {

@Provides
@Singleton
fun provideNfcAdapterController(nfcAdapter: NfcAdapter?): NfcAdapterController {
return NfcAdapterController(nfcAdapter)
fun provideNfcAdapterController(
nfcAdapter: NfcAdapter?,
nfcControllerFactory: NfcControllerFactory
): NfcAdapterController {
return NfcAdapterController(nfcAdapter, nfcControllerFactory)
}
}

Expand Down
2 changes: 2 additions & 0 deletions intra/src/main/java/com/hoker/intra/domain/NfcActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.os.Bundle
import android.os.PersistableBundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import com.hoker.intra.di.NfcModule
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.delay
import javax.inject.Inject
Expand All @@ -12,6 +13,7 @@ import javax.inject.Inject
abstract class NfcActivity : ComponentActivity() {

@Inject lateinit var nfcAdapterController: NfcAdapterController
@Inject lateinit var nfcControllerFactory: NfcModule.NfcControllerFactory

override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
Expand Down
28 changes: 22 additions & 6 deletions intra/src/main/java/com/hoker/intra/domain/NfcAdapterController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ import android.nfc.NfcAdapter
import android.nfc.Tag
import android.os.Bundle
import android.util.Log
import com.hoker.intra.di.NfcModule
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.receiveAsFlow
import javax.inject.Inject

open class NfcAdapterController @Inject constructor(
private val nfcAdapter: NfcAdapter?
private val nfcAdapter: NfcAdapter?,
private val nfcControllerFactory: NfcModule.NfcControllerFactory
) {
private var onTagDiscoveredListener: ((Tag?) -> Unit)? = null
private val listenerMap = LinkedHashMap<String, Pair<String, (Tag?) -> Unit>>()
private var onTagDiscoveredListener: ((Tag, NfcController) -> Unit)? = null
private val listenerMap = LinkedHashMap<String, Pair<String, (Tag, NfcController) -> Unit>>()
private val _onScanChannel = Channel<Unit>(Channel.BUFFERED)
val scanEvent = _onScanChannel.receiveAsFlow()

Expand All @@ -26,8 +28,15 @@ open class NfcAdapterController @Inject constructor(
adapter.enableReaderMode(
activity,
{ tag ->
_onScanChannel.trySend(Unit)
onTagDiscoveredListener?.invoke(tag)
when (val result = nfcControllerFactory.getController(tag)) {
is OperationResult.Success -> {
_onScanChannel.trySend(Unit)
onTagDiscoveredListener?.invoke(tag, result.data)
}
is OperationResult.Failure -> {
Log.i(this@NfcAdapterController::class.simpleName, "There was an error constructing the NfcController")
}
}
},
flags,
options
Expand All @@ -46,14 +55,21 @@ open class NfcAdapterController @Inject constructor(
fun setOnTagDiscoveredListener(
uuid: String,
className: String,
listener: (Tag?) -> Unit
listener: (Tag, NfcController) -> Unit
) {
listenerMap.remove(uuid)
listenerMap[uuid] = className to listener
updateListener()
logCurrentListeners()
}

fun setOnTagDiscoveredListener(
listener: (Tag, NfcController) -> Unit
) {
listenerMap.clear()
onTagDiscoveredListener = listener
}

fun removeOnTagDiscoveredListener(uuid: String) {
listenerMap.remove(uuid)
updateListener()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ interface NfcController {
suspend fun getNdefCapacity(ndef: Ndef): OperationResult<Int>
suspend fun getNdefMessage(ndef: Ndef): OperationResult<NdefMessage?>
suspend fun checkConnection(): OperationResult<Boolean>
fun getMaxTransceiveLength(): Int?
suspend fun withNdefConnection(tag: Tag, operations: suspend (ndef: Ndef) -> Unit): OperationResult<Unit> {
return try {
close()
Expand Down
Loading

0 comments on commit aa1f415

Please sign in to comment.