åå ã¯ãä¹
ãã¶ãã« Androidã¢ããªéçºã¨ãããã¨ã§ãéçºç°å¢ã®æ§ç¯ã¨ãç°¡åãªã¢ããªãä½ã£ã¦ãåããã¦ã¿ã¾ããã
ä»åã¯ãããããã£ã NFC ã®èªã¿è¾¼ã¿ãããã¢ããªãåããã¦ã¿ããã¨æãã¾ããè¯ããã㪠OSS ãæã£ã¦ãã¦åããã¦ã¿ã¾ãã
ããã§ã¯ããã£ã¦ããã¾ãã
åèæç®
ä»åããåèã«ããã¦é ããæ¸ç±ã§ãã
Kotlin ã¯è©³ããç¥ããªãã®ã§ãææ³ãªã©ã¯ä»¥ä¸ã®æ¸ç±ãåèã«ãã¦ãã¾ãã
ã¯ããã«
ãJavaã§ãã¶ã¤ã³ãã¿ã¼ã³ãå¦ã¶ãã®è¨äºä¸è¦§ã§ããè¯ãã£ããåèã«ãã¦ãã ããã
Javaã§ãã¶ã¤ã³ãã¿ã¼ã³ã®è¨äºä¸è¦§
NFCãªã¼ãã¼ãGitHubã§æ¢ã
ã¾ããä»åã®ç®çï¼NFCãªã¼ãã¼ï¼ã«åã£ã OSS ã GitHub ããæ¢ãå¿
è¦ãããã¾ãããéãããã¨ã¦ãè¯ããããªãã®ãè¦ã¤ããã¾ãããæçµæ´æ°æ¥ã 2024/2/20 ã¨ãããã¾ã§å¤ããªãã¦ãKotlin ã§æ¸ããã¦ãã¦ãæ©è½ãã·ã³ãã«ã§æ±ãããããã§ãã
ä»åã¯ãããããããã¡ãã®ã½ã¼ã¹ã³ã¼ãã使ããã¦é ããã¨æãã¾ãã
github.com
Android Studioã«æ¢åã¢ããªãèªã¿è¾¼ã¾ãã
ã¾ããGitHub ã®ã½ã¼ã¹ã³ã¼ãããã¦ã³ãã¼ããã¾ããWindows ã§åããã®ã§ãGit BASH ã§ãé©å½ãªã¨ããã«ã¯ãã¼ã³ãã¾ãã
$ git clone https://github.com/abdelaz9z/NFC-Reader.git android-NFC-Reader
Cloning into 'android-NFC-Reader'...
remote: Enumerating objects: 96, done.
remote: Counting objects: 100% (96/96), done.
remote: Compressing objects: 100% (74/74), done.
remote: Total 96 (delta 6), reused 88 (delta 4), pack-reused 0 (from 0)
Receiving objects: 100% (96/96), 154.34 KiB | 3.35 MiB/s, done.
Resolving deltas: 100% (6/6), done.
次ã«ãAndroid Studio ã§èªã¿è¾¼ã¿ã¾ããååã®è¨äºã§ã¯ New Project ãé¸ã³ã¾ããããããã§ã¯ãOpen ãé¸ã³ã¾ããClone Repository ã¨ãããºããªã®ãããªãã®ãããã¾ãããä»å㯠Open ã®æ¹ã使ãã¾ãã
Welcome to Android Studio
ããã¨ããã£ã¬ã¯ããªã鏿ãããã¤ã¢ãã°ãåºãã®ã§ãã¯ãã¼ã³ãããã£ã¬ã¯ããªã鏿ãã¦ãOK ãã¯ãªãã¯ãã¾ããèªã¿è¾¼ã¿ã«æéããããå ´åãããã¾ãããå¾
ã¦ã°ãã¡ããã¨è¡¨ç¤ºããã¾ãã
Open File or Project
Android Studio ãèµ·åããããã¯ã°ã©ã¦ã³ãã§ãã«ããªã©ãå§ã¾ãã¾ããæéããããã¾ãããå¾
ã¡ã¾ããããããæ
å ±ãè¦åãåºã¾ããããã£ããã¯ç¡è¦ãã¾ãããGradle ãã¢ããã°ã¬ã¼ããã¾ããï¼ãã¨ããããã«æ¨å¥¨ããã¾ãããã¾ãã¯ããã®ã¾ã¾ï¼éçºãããæ¹ã®ç°å¢ã夿´ããªãï¼ã«ãã¦ãããæ¹ãããã§ãããã¾ãåããªãå ´åã«ãã¢ããã°ã¬ã¼ãããã£ã¦ã¿ããã¨ããããæ¹ã§ããã¨æãã¾ãã
ãã°ããããã¨ããã«ããå®äºãã¾ãã
NFCãªã¼ãã¼ã¢ããªãã¨ãã¥ã¬ã¼ã¿ã§èµ·åãã
æ©éãã¨ãã¥ã¬ã¼ã¿ã§èµ·åãã¦ã¿ã¾ããã¨ãã¥ã¬ã¼ã¿ã鏿ããã¦ãããã¨ã確èªãã¦ãâ·ãã¿ã³ãæ¼ãã¦èµ·åãã¾ããç¡äºã«èµ·åã§ãã¾ããã
ã¨ãã¥ã¬ã¼ã¿ã§èµ·åã§ãã
NFCãªã¼ãã¼ã¢ããªã宿©ã§èµ·åãã
ã¨ãã¥ã¬ã¼ã¿ã§ã¯ãNFCã¿ã°ãèªã¾ãããã¨ãåºæ¥ãªããããªã®ã§ãæ©é宿©ã§åããã¦ããã¾ããååãHelloWorldã¢ããªã§ãã£ãæ¹æ³ã¨åãããã«æºåãã¦ãAndroid Studio ã®æ¹ããã¨ãã¥ã¬ã¼ã¿ãã宿©ã«åãæ¿ãã¦ãâ·ãã¿ã³ãæ¼ãã¦èµ·åãã¾ãã
宿©ã§NFCãªã¼ãã¼ãèµ·åã§ãã
ãã¾ãèµ·åã§ãããããªã®ã§ã使ããªããªã£ãã»ãã³ã¤ã¬ãã³ã® nanacoã«ã¼ããã¹ããã®è£é¢ã«ãã¿ãã¨æ¥è§¦ããã¾ãããã¾ãèªã¿è¾¼ãã¾ããï¼
nanacoã«ã¼ããèªã¿è¾¼ã¾ããçµæ
NFCãªã¼ãã¼ã®ã½ã¼ã¹ãè§£æãã
NFCãªã¼ãã¼ã®ã½ã¼ã¹ã³ã¼ãã確èªãã¦ããã¾ããã¾ãã¯ãæ§æãè¦ã¦ããã¾ãã
NFCãªã¼ãã¼ã®ã½ã¼ã¹ã³ã¼ãã®æ§æ
Android Studio ã§ãã«ããå®è¡ãããã¨ã§ãããã¤ãã®ã½ã¼ã¹ã追å ã夿´ããã¦ããããã§ãã
$ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: .idea/compiler.xml
modified: .idea/gradle.xml
modified: .idea/misc.xml
Untracked files:
(use "git add <file>..." to include in what will be committed)
.idea/deploymentTargetSelector.xml
.idea/runConfigurations.xml
.idea/vcs.xml
treeã³ãã³ãã®çµæã§ãã.git ã app/build ãªã©ã¯é表示ã«ãã¦ãã¾ããapp/src/main/java/com/casecode/nfcreader ããã¡ã¤ã³ã®ã½ã¼ã¹ã³ã¼ãã§ãã
$ tree -a
.
|-- .git (â
é表示ã¨ãã)
|-- .gitignore
|-- .gradle
| |-- 8.2
| | |-- checksums
| | | |-- checksums.lock
| | | |-- md5-checksums.bin
| | | `-- sha1-checksums.bin
| | |-- dependencies-accessors
| | | |-- dependencies-accessors.lock
| | | `-- gc.properties
| | |-- executionHistory
| | | |-- executionHistory.bin
| | | `-- executionHistory.lock
| | |-- fileChanges
| | | `-- last-build.bin
| | |-- fileHashes
| | | |-- fileHashes.bin
| | | |-- fileHashes.lock
| | | `-- resourceHashesCache.bin
| | |-- gc.properties
| | `-- vcsMetadata
| |-- buildOutputCleanup
| | |-- buildOutputCleanup.lock
| | |-- cache.properties
| | `-- outputFiles.bin
| |-- config.properties
| |-- file-system.probe
| |-- kotlin
| | |-- errors
| | `-- sessions
| `-- vcs-1
| `-- gc.properties
|-- .idea
| |-- .gitignore
| |-- .name
| |-- android-NFC-Reader.iml
| |-- appInsightsSettings.xml
| |-- caches
| | `-- deviceStreaming.xml
| |-- compiler.xml
| |-- deploymentTargetDropDown.xml
| |-- deploymentTargetSelector.xml
| |-- gradle.xml
| |-- kotlinc.xml
| |-- migrations.xml
| |-- misc.xml
| |-- runConfigurations.xml
| |-- vcs.xml
| `-- workspace.xml
|-- README.md
|-- Screenshot_20240220_094138.png
|-- Screenshot_20240220_094158.png
|-- app
| |-- .gitignore
| |-- build (â
GitHubã½ã¼ã¹ã«å«ã¾ããªãã®ã§å²æãã)
| |-- build.gradle.kts
| |-- proguard-rules.pro
| `-- src
| |-- androidTest
| | `-- java
| | `-- com
| | `-- casecode
| | `-- nfcreader
| | `-- ExampleInstrumentedTest.kt
| |-- main
| | |-- AndroidManifest.xml
| | |-- ic_launcher-playstore.png
| | |-- java
| | | `-- com
| | | `-- casecode
| | | `-- nfcreader
| | | |-- Coroutines.kt
| | | |-- NFCManager.kt
| | | |-- NFCStatus.kt
| | | |-- ui
| | | | |-- MainActivity.kt
| | | | `-- MainFragment.kt
| | | `-- viewmodel
| | | `-- MainViewModel.kt
| | `-- res
| | |-- drawable
| | | |-- baseline_nfc_24.xml
| | | |-- ic_launcher_background.xml
| | | `-- ic_launcher_foreground.xml
| | |-- layout
| | | |-- activity_main.xml
| | | `-- fragment_main.xml
| | |-- mipmap-anydpi-v26
| | | |-- ic_launcher.xml
| | | `-- ic_launcher_round.xml
| | |-- mipmap-hdpi
| | | |-- ic_launcher.webp
| | | `-- ic_launcher_round.webp
| | |-- mipmap-mdpi
| | | |-- ic_launcher.webp
| | | `-- ic_launcher_round.webp
| | |-- mipmap-xhdpi
| | | |-- ic_launcher.webp
| | | `-- ic_launcher_round.webp
| | |-- mipmap-xxhdpi
| | | |-- ic_launcher.webp
| | | `-- ic_launcher_round.webp
| | |-- mipmap-xxxhdpi
| | | |-- ic_launcher.webp
| | | `-- ic_launcher_round.webp
| | |-- values
| | | |-- colors.xml
| | | |-- ic_launcher_background.xml
| | | |-- strings.xml
| | | `-- themes.xml
| | |-- values-night
| | | `-- themes.xml
| | `-- xml
| | |-- backup_rules.xml
| | `-- data_extraction_rules.xml
| `-- test
| `-- java
| `-- com
| `-- casecode
| `-- nfcreader
| `-- ExampleUnitTest.kt
|-- build.gradle.kts
|-- gradle
| `-- wrapper
| |-- gradle-wrapper.jar
| `-- gradle-wrapper.properties
|-- gradle.properties
|-- gradlew
|-- gradlew.bat
|-- local.properties (â
GitHubã½ã¼ã¹ã«å«ã¾ãã¦ãªãã£ãâãã«ãã§æ°ãã追å ããã)
`-- settings.gradle.kts
458 directories, 889 files
NFCãªã¼ãã¼ã®ã½ã¼ã¹ã³ã¼ã
AndroidManifest.xml
ã¾ããAndroidManifest.xml ãè¦ã¦ã¿ã¾ãããã®ãã¡ã¤ã«ã¯ãAndroidã¢ããªã®æ§æãã使ç¨ãããã¼ãã¦ã§ã¢ã®ãã¼ããã·ã§ã³ãªã©ã®æ
å ±ãæ¸ããã¦ãã¾ãã
ããã¨è¦ãã¨ããã¯ãNFC ã許å¯ããã¦ãããããã§ããã以å¤ã¯ç¹å¥ãªè¨è¿°ã¯ç¡ãããã§ããactivityè¦ç´ ãè¦ãã¨ãintent-filterè¦ç´ ã«ãandroid.intent.action.MAIN ã¨ããã®ã§ã.ui.MainActivity ãã¨ã³ããªãã¤ã³ãã§ãããã¨ãåããã¾ããã¾ããandroid.intent.category.LAUNCHER ã¨ããã®ã§ãã¹ãã¼ããã©ã³ã®ãã¼ã ç»é¢ã«ã¢ããªã®ã¢ã¤ã³ã³ã表示ããã¾ãããã®ãããã¯ãHelloWorldã¢ããªã¨åãã§ããã
xml version="1.0" encoding="utf-8"
<manifest xmlnsandroid="http://schemas.android.com/apk/res/android"
xmlnstools="http://schemas.android.com/tools">
<uses-permission androidname="android.permission.NFC" />
<uses-feature
androidname="android.hardware.nfc"
androidrequired="true" />
<application
androidallowBackup="true"
androiddataExtractionRules="@xml/data_extraction_rules"
androidfullBackupContent="@xml/backup_rules"
androidicon="@mipmap/ic_launcher"
androidlabel="@string/app_name"
androidroundIcon="@mipmap/ic_launcher_round"
androidsupportsRtl="true"
androidtheme="@style/Theme.NFCReader"
toolstargetApi="31">
<activity
androidname=".ui.MainActivity"
androidexported="true">
<intent-filter>
<action androidname="android.intent.action.MAIN" />
<category androidname="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
ui/MainActivity.kt
ã¨ã³ããªãã¤ã³ãã® ui/MainActivity.kt ãè¦ã¾ããå
¨ã½ã¼ã¹ãè²¼ãã¨é·ãã®ã§ãä¸é¨ã¨ãã¡ã½ããã ããè²¼ãã¾ããç¹ã«ãNFCã¿ã°ãèªã¿åºãã¨ãããä¸å¿ã«è¦ã¦ããã¾ãã
onCreate() ã¯ãã¢ããªãèµ·åãã¦æåã«å¼ã³åºããã¾ããããã§ã¯ã¡ã¤ã³é¢æ°ã®ãããªæãã§ã主ã«åæåå¦çãè¡ã£ã¦ãã¾ããR.layout.activity_main ã¯ãRã¯ã©ã¹ã§ç®¡çããã¦ãããªã½ã¼ã¹IDã§ããKotlin ã§ã¯ ? ã¨ããã®ãå¤ãåºã¦ãã¾ãããããã¯ãnull許容åï¼null ã代å
¥ãããã¨ã許å¯ããï¼ã¨ããæå³ã§ãbinder 㯠null許容åã®å¤æ°ã¨ãããã¨ã§ããbinder?.viewModel ã¯ãæ¬æ¥ã¯ null ãã©ããã確èªãã ifæãå
¥ãã¨ããããbinder ã null ãããããªããã¨ãèæ
®ããæ¸ãæ¹ã§ããbinder?.viewModel ã«ãMainViewModelã¯ã©ã¹ã®ãªãã¸ã§ã¯ããå
¥ã£ã¦ãã¨ããæãã§ãã
onCheckedChanged() ã¯ãNFC ã® ON/OFF ã®ãã¿ã³ã®å¦çã¨ãã¦ãON ã«ãªãã¨ãNFC ãæå¹ã«ãã¦ãOFF ã«ãã㨠NFC ãç¡å¹ã«ããå¦çãè¡ã£ã¦ãã¾ããonTagDiscovered() ã¯ãNFCã¿ã°ãæ¤åºããã¨ãã«å¼ã°ããå¦çã§ããããã§ãMainViewModelã¯ã©ã¹ã® readTagã¡ã½ãããå¼ã³åºãã¦ãã¾ããlaunchMainFragment() ã¯ãäºéèµ·åã®é²æ¢ãªã©ã®å¦çããã¦ãã¾ãã
package com.casecode.nfcreader.ui
class MainActivity : AppCompatActivity(), CompoundButton.OnCheckedChangeListener,
NfcAdapter.ReaderCallback {
private var binder: ActivityBinder? = null
private val viewModel: MainViewModel by viewModels<MainViewModel>()
override fun onCreate(savedInstanceState: Bundle?) { ... }
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
binder = DataBindingUtil.setContentView(this@MainActivity, R.layout.activity_main)
binder?.viewModel = viewModel
binder?.lifecycleOwner = this@MainActivity
... }
override fun onTagDiscovered(tag: Tag?) {
binder?.viewModel?.readTag(tag)
}
private fun launchMainFragment() { ... }
}
viewmodel/MainViewModel.kt
readTagã¡ã½ãããæã¤ãMainViewModelã¯ã©ã¹ã§ããreadTagã¡ã½ãããè¦ã¦ããã¾ãã
stringBuilder夿°ã«æååã追å ãã¦ãã£ã¦ã¾ããããããä¸ã§å®æ©ã§ç¢ºèªã§ããå
容ã ã¨æãã¾ããid!! ã® !! ã¯ãnullé許容åã¸å¼·å¶ãã£ã¹ããããã®ã§ãid 㯠ByteArray? ãªã®ã§ãnull許容åã§å®£è¨ããã¦ã¾ãããå¼·å¶ãã£ã¹ããããã®ã§ãid ã null ã®å ´åã¯ä¾å¤ãçºçãã¾ãã
id ã 3ãã¿ã¼ã³ï¼16é²ã10é²ã10é²ãéé ã§è¡¨ç¤ºï¼ï¼ã§è¡¨ç¤ºããå¾ãTechnologies ã¨ãããã¨ã§ãTypeAãTypeBãTypeFï¼FeliCaï¼ã表示ãã¦ãã¾ãããã®å¾ã¯ãMifareClassic ã ã£ãå ´åã®è©³ç´°è¡¨ç¤ºã¨ãMifareUltralight ã ã£ãå ´åã®è©³ç´°è¡¨ç¤ºãè¡ã£ã¦ãã¾ãã
class MainViewModel(application: Application) : AndroidViewModel(application) {
fun readTag(tag: Tag?) {
Coroutines.default(this@MainViewModel) {
Log.d(TAG, "readTag(${tag} ${tag?.techList})")
postNFCStatus(NFCStatus.Process)
val stringBuilder: StringBuilder = StringBuilder()
val id: ByteArray? = tag?.id
stringBuilder.append("Tag ID (hex): ${getHex(id!!)} \n")
stringBuilder.append("Tag ID (dec): ${getDec(id)} \n")
stringBuilder.append("Tag ID (reversed): ${getReversed(id)} \n")
stringBuilder.append("Technologies: ")
tag.techList.forEach { tech ->
stringBuilder.append(tech.substring(prefix.length))
stringBuilder.append(", ")
}
stringBuilder.delete(stringBuilder.length - 2, stringBuilder.length)
tag.techList.forEach { tech ->
if (tech.equals(MifareClassic::class.java.name)) {
stringBuilder.append('\n')
val mifareTag: MifareClassic = MifareClassic.get(tag)
val type: String =
when (mifareTag.type) {
MifareClassic.TYPE_CLASSIC -> "Classic"
MifareClassic.TYPE_PLUS -> "Plus"
MifareClassic.TYPE_PRO -> "Pro"
else -> "Unknown"
}
stringBuilder.append("Mifare Classic type: $type \n")
stringBuilder.append("Mifare size: ${mifareTag.size} bytes \n")
stringBuilder.append("Mifare sectors: ${mifareTag.sectorCount} \n")
stringBuilder.append("Mifare blocks: ${mifareTag.blockCount}")
}
if (tech.equals(MifareUltralight::class.java.name)) {
stringBuilder.append('\n');
val mifareUlTag: MifareUltralight = MifareUltralight.get(tag);
val type: String =
when (mifareUlTag.type) {
MifareUltralight.TYPE_ULTRALIGHT -> "Ultralight"
MifareUltralight.TYPE_ULTRALIGHT_C -> "Ultralight C"
else -> "Unkown"
}
stringBuilder.append("Mifare Ultralight type: ");
stringBuilder.append(type)
}
}
Log.d(TAG, "Datum: $stringBuilder")
Log.d(ContentValues.TAG, "dumpTagData Return \n $stringBuilder")
postNFCStatus(NFCStatus.Read)
liveTag.emit("${getDateTimeNow()} \n $stringBuilder")
}
}
å¥ã® NFCã¿ã°ãèªã¿è¾¼ã¾ãã¦ã¿ãã®ã以ä¸ã§ããTypeA ã§ãMifareClassic ã® NFCã¿ã°ã®ããã§ããNdefFormatable ã¨ããã¾ããNDEF ã¨ã¯ãNFC Data Exchange Format ã®ç¥ã§ãç°¡åã«è¨ãã¨ãã©ã¼ãããã®ãã¨ï¼ãã¼ã¿ãã©ã®ããã«æ ¼ç´ãããã®ãã©ã¼ãããã®ãã¨ï¼ã§ããNDEF ã®ä¸èº«ãè§£æãã¦ãããæ©è½ã¯ç¡ãããã§ãã
NFCã¿ã°ï¼MifareClassicï¼
è¦ããã¨ããã¯ç¢ºèªã§ããã®ã§ãã½ã¼ã¹ã³ã¼ãã®è§£æã¯ä»¥ä¸ã¨ãããã¨æãã¾ãã
ãããã«
ä»åã¯ãNFCãªã¼ãã¼ã® Androidã¢ããªãåããã¦ã¿ã¾ããããã¾ãåãã¾ããããNDEF ã®å
容ãè§£æãã¦è¡¨ç¤ºãã¦ãããæ©è½ãããã°ããã£ã¨è¯ãã£ãã§ããæ¬¡ã¯ãã©ã¤ã¿ã¼ã®æ©è½ãæã¤ Androidã¢ããªãæ¢ãã¦è¦ããã¨æãã¾ãã
æå¾ã«ãªãã¾ããããã¨ã³ã¸ãã¢ã°ã«ã¼ãã®ã©ã³ãã³ã°ã«åå ä¸ã§ãã
æ°æ¥½ã«ãããã¨ãããããé¡ããããã¾ãð
ä»åã¯ä»¥ä¸ã§ãï¼
æå¾ã¾ã§ãèªã¿ããã ãããããã¨ããããã¾ããã