diff --git a/components/settings.js b/components/settings.js index dabad32..9373ec3 100644 --- a/components/settings.js +++ b/components/settings.js @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; import { View, Text, TouchableOpacity, Linking } from "react-native"; -import * as ExpoFelicaReader from "expo-felica-reader"; +import * as ExpoFelicaReader from "../modules/expo-felica-reader/src"; import * as Updates from "expo-updates"; import StatusbarDetect from "../StatusbarDetect"; import { AS } from "../storageControl"; diff --git a/modules/expo-felica-reader/android/build.gradle b/modules/expo-felica-reader/android/build.gradle new file mode 100644 index 0000000..888b263 --- /dev/null +++ b/modules/expo-felica-reader/android/build.gradle @@ -0,0 +1,92 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'maven-publish' + +group = 'expo.modules.felicareader' +version = '0.2.0' + +buildscript { + def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle") + if (expoModulesCorePlugin.exists()) { + apply from: expoModulesCorePlugin + applyKotlinExpoModulesCorePlugin() + } + + // Simple helper that allows the root project to override versions declared by this library. + ext.safeExtGet = { prop, fallback -> + rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback + } + + // Ensures backward compatibility + ext.getKotlinVersion = { + if (ext.has("kotlinVersion")) { + ext.kotlinVersion() + } else { + ext.safeExtGet("kotlinVersion", "1.8.10") + } + } + + repositories { + mavenCentral() + } + + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getKotlinVersion()}") + } +} + +afterEvaluate { + publishing { + publications { + release(MavenPublication) { + from components.release + } + } + repositories { + maven { + url = mavenLocal().url + } + } + } +} + +android { + compileSdkVersion safeExtGet("compileSdkVersion", 33) + + def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION + if (agpVersion.tokenize('.')[0].toInteger() < 8) { + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.majorVersion + } + } + + namespace "expo.modules.felicareader" + defaultConfig { + minSdkVersion safeExtGet("minSdkVersion", 21) + targetSdkVersion safeExtGet("targetSdkVersion", 34) + versionCode 2 + versionName "0.2.0" + } + lintOptions { + abortOnError false + } + publishing { + singleVariant("release") { + withSourcesJar() + } + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation project(':expo-modules-core') + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}" +} diff --git a/modules/expo-felica-reader/android/src/main/AndroidManifest.xml b/modules/expo-felica-reader/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..bdae66c --- /dev/null +++ b/modules/expo-felica-reader/android/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/modules/expo-felica-reader/android/src/main/java/expo/modules/felicareader/ExpoFelicaReaderModule.kt b/modules/expo-felica-reader/android/src/main/java/expo/modules/felicareader/ExpoFelicaReaderModule.kt new file mode 100644 index 0000000..781ac3e --- /dev/null +++ b/modules/expo-felica-reader/android/src/main/java/expo/modules/felicareader/ExpoFelicaReaderModule.kt @@ -0,0 +1,34 @@ +package expo.modules.felicareader + +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition +import expo.modules.kotlin.Promise +import android.nfc.NfcAdapter +import android.nfc.Tag + +class NfcReaderCallback(private val promise: Promise) : NfcAdapter.ReaderCallback { + override fun onTagDiscovered(tag: Tag?) { + val idmString = tag?.id?.joinToString("") { "%02x".format(it) } + promise.resolve(idmString) + } +} + +class ExpoFelicaReaderModule : Module() { + var nfcAdapter: NfcAdapter? = null + override fun definition() = ModuleDefinition { + Name("ExpoFelicaReader") + + AsyncFunction("scan") { promise: Promise -> + nfcAdapter?.enableReaderMode( + appContext.currentActivity, + NfcReaderCallback(promise), + NfcAdapter.FLAG_READER_NFC_F, + null + ) + } + + OnCreate { + nfcAdapter = NfcAdapter.getDefaultAdapter(appContext.reactContext) + } + } +} \ No newline at end of file diff --git a/modules/expo-felica-reader/expo-module.config.json b/modules/expo-felica-reader/expo-module.config.json new file mode 100644 index 0000000..6952ae1 --- /dev/null +++ b/modules/expo-felica-reader/expo-module.config.json @@ -0,0 +1,9 @@ +{ + "platforms": ["ios", "tvos", "android", "web"], + "ios": { + "modules": ["ExpoFelicaReaderModule"] + }, + "android": { + "modules": ["expo.modules.felicareader.ExpoFelicaReaderModule"] + } +} diff --git a/modules/expo-felica-reader/ios/ExpoFelicaReader.podspec b/modules/expo-felica-reader/ios/ExpoFelicaReader.podspec new file mode 100644 index 0000000..e19654c --- /dev/null +++ b/modules/expo-felica-reader/ios/ExpoFelicaReader.podspec @@ -0,0 +1,27 @@ +require 'json' + +package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json'))) + +Pod::Spec.new do |s| + s.name = 'ExpoFelicaReader' + s.version = package['version'] + s.summary = package['description'] + s.description = package['description'] + s.license = package['license'] + s.author = package['author'] + s.homepage = package['homepage'] + s.platform = :ios, '13.0' + s.swift_version = '5.4' + s.source = { git: 'https://github.com/7nohe/expo-felica-reader' } + s.static_framework = true + + s.dependency 'ExpoModulesCore' + + # Swift/Objective-C compatibility + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + 'SWIFT_COMPILATION_MODE' => 'wholemodule' + } + + s.source_files = "**/*.{h,m,swift}" +end \ No newline at end of file diff --git a/modules/expo-felica-reader/ios/ExpoFelicaReaderModule.swift b/modules/expo-felica-reader/ios/ExpoFelicaReaderModule.swift new file mode 100644 index 0000000..6207600 --- /dev/null +++ b/modules/expo-felica-reader/ios/ExpoFelicaReaderModule.swift @@ -0,0 +1,70 @@ +import ExpoModulesCore +import CoreNFC + +public class ExpoFelicaReaderModule: Module { + var session: NfcSession? + var semaphore: DispatchSemaphore? + public func definition() -> ModuleDefinition { + Name("ExpoFelicaReader") + + AsyncFunction("scan") { (promise: Promise) in + session?.startSession() + DispatchQueue.global(qos: .background).async { + self.semaphore?.wait() + promise.resolve(self.session?.message) + } + } + + OnCreate { + semaphore = DispatchSemaphore(value: 0) + session = NfcSession(semaphore: semaphore!) + } + } +} + +class NfcSession: NSObject, NFCTagReaderSessionDelegate { + var session: NFCTagReaderSession? + let semaphore: DispatchSemaphore + var message: String? + + init (semaphore: DispatchSemaphore) { + self.semaphore = semaphore + } + + func tagReaderSessionDidBecomeActive(_ session: NFCTagReaderSession) { + print("tagReaderSessionDidBecomeActive") + } + + func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) { + print("Error: \(error.localizedDescription)") + self.semaphore.signal() + self.session = nil + } + + func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) { + let tag = tags.first! + session.connect(to: tag) { error in + if nil != error { + session.invalidate(errorMessage: "Error!") + self.semaphore.signal() + return + } + guard case .feliCa(let feliCaTag) = tag else { + session.invalidate(errorMessage: "This is not FeliCa!") + self.semaphore.signal() + return + } + let idm = feliCaTag.currentIDm.map { String(format: "%.2hhx", $0) }.joined() + self.message = idm + session.alertMessage = "Success!" + session.invalidate() + self.semaphore.signal() + } + } + + func startSession() { + self.session = NFCTagReaderSession(pollingOption: [.iso14443, .iso15693, .iso18092], delegate: self, queue: nil) + session?.alertMessage = "Touch your FeliCa!" + session?.begin() + } +} \ No newline at end of file diff --git a/modules/expo-felica-reader/src/ExpoFelicaReaderModule.ts b/modules/expo-felica-reader/src/ExpoFelicaReaderModule.ts new file mode 100644 index 0000000..b74e342 --- /dev/null +++ b/modules/expo-felica-reader/src/ExpoFelicaReaderModule.ts @@ -0,0 +1,5 @@ +import { requireNativeModule } from 'expo-modules-core'; + +// It loads the native module object from the JSI or falls back to +// the bridge module (from NativeModulesProxy) if the remote debugger is on. +export default requireNativeModule('ExpoFelicaReader'); diff --git a/modules/expo-felica-reader/src/index.ts b/modules/expo-felica-reader/src/index.ts new file mode 100644 index 0000000..0b3a437 --- /dev/null +++ b/modules/expo-felica-reader/src/index.ts @@ -0,0 +1,5 @@ +import ExpoFelicaReaderModule from "./ExpoFelicaReaderModule"; + +export async function scan(): Promise { + return await ExpoFelicaReaderModule.scan(); +} diff --git a/package.json b/package.json index b337ffa..862f402 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "expo": "^50.0.11", "expo-dev-client": "~3.3.9", "expo-device": "~5.9.3", - "expo-felica-reader": "^0.1.0", "expo-font": "~11.10.3", "expo-location": "~16.5.5", "expo-notifications": "~0.27.6", diff --git a/yarn.lock b/yarn.lock index 55b07dc..8445880 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5246,11 +5246,6 @@ expo-eas-client@~0.11.0: resolved "https://registry.yarnpkg.com/expo-eas-client/-/expo-eas-client-0.11.2.tgz#2e8d6f347dcea38b58c2b6a21db7c632383fb1ba" integrity sha512-SY7rVFxb4ut/OMTgR7A39Jg+8+hXwQNRpZd+RBpB+B5XV2STj/pWXHnGFhBayEF4umI4SxrOvisY90rlPWVO9Q== -expo-felica-reader@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/expo-felica-reader/-/expo-felica-reader-0.1.0.tgz#968a9bb93d1f040f8e8dfbc899fba0214327c8b0" - integrity sha512-uDv5/eeaCpMOJ3R3supaE2m7VJZGaDE6C3AdWf3Y1pgVzteI3GUah5+doFpadf6CSjytznix5U1pTLbeSHeuUw== - expo-file-system@~16.0.0, expo-file-system@~16.0.8: version "16.0.8" resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-16.0.8.tgz#13c79a8e06e42a8e76e9297df6920597a011d989"