Skip to content

Commit

Permalink
feat: NFC support for canteen cards to query the balance (#48)
Browse files Browse the repository at this point in the history
* feat: NFC support for canteen cards to query the balance

* fix tests

* fixes

* revert changes

* wip

* fixes
  • Loading branch information
robingenz authored Jul 29, 2024
1 parent 7df5892 commit 42b82c1
Show file tree
Hide file tree
Showing 32 changed files with 557 additions and 10 deletions.
1 change: 0 additions & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
save-exact=true

@capawesome-team:registry=https://npm.pkg.github.com
1 change: 1 addition & 0 deletions android/app/capacitor.build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dependencies {
implementation project(':capacitor-splash-screen')
implementation project(':capacitor-status-bar')
implementation project(':capawesome-team-capacitor-file-opener')
implementation project(':capawesome-team-capacitor-nfc')

}

Expand Down
4 changes: 2 additions & 2 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
</provider>
</application>

<!-- Permissions -->

<uses-permission android:name="android.permission.INTERNET" />
<!-- To get access to the NFC hardware. -->
<uses-permission android:name="android.permission.NFC" />
</manifest>
4 changes: 4 additions & 0 deletions android/app/src/main/assets/capacitor.plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,9 @@
{
"pkg": "@capawesome-team/capacitor-file-opener",
"classpath": "io.capawesome.capacitorjs.plugins.fileopener.FileOpenerPlugin"
},
{
"pkg": "@capawesome-team/capacitor-nfc",
"classpath": "io.capawesome.capacitorjs.plugins.nfc.NfcPlugin"
}
]
3 changes: 3 additions & 0 deletions android/capacitor.settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacit

include ':capawesome-team-capacitor-file-opener'
project(':capawesome-team-capacitor-file-opener').projectDir = new File('../node_modules/@capawesome-team/capacitor-file-opener/android')

include ':capawesome-team-capacitor-nfc'
project(':capawesome-team-capacitor-nfc').projectDir = new File('../node_modules/@capawesome-team/capacitor-nfc/android')
4 changes: 4 additions & 0 deletions ios/App/App.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
7C07B6D22B91FB6C00652B63 /* App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = "<group>"; };
AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -73,6 +74,7 @@
504EC3061FED79650016851F /* App */ = {
isa = PBXGroup;
children = (
7C07B6D22B91FB6C00652B63 /* App.entitlements */,
50379B222058CBB4000EE86E /* capacitor.config.json */,
504EC3071FED79650016851F /* AppDelegate.swift */,
504EC30B1FED79650016851F /* Main.storyboard */,
Expand Down Expand Up @@ -345,6 +347,7 @@
baseConfigurationReference = FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
Expand All @@ -369,6 +372,7 @@
baseConfigurationReference = AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
Expand Down
10 changes: 10 additions & 0 deletions ios/App/App/App.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
<string>TAG</string>
</array>
</dict>
</plist>
10 changes: 8 additions & 2 deletions ios/App/App/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<key>CFBundleShortVersionString</key>
<string>5.0.0</string>
<key>CFBundleVersion</key>
<key>CFBundleVersion</key>
<string>5.0.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
Expand All @@ -45,5 +45,11 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
<key>NFCReaderUsageDescription</key>
<string>The app enables the reading and writing of various NFC tags.</string>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
<string>D2760000850100</string>
</array>
</dict>
</plist>
1 change: 1 addition & 0 deletions ios/App/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def capacitor_pods
pod 'CapacitorSplashScreen', :path => '../../node_modules/@capacitor/splash-screen'
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
pod 'CapawesomeTeamCapacitorFileOpener', :path => '../../node_modules/@capawesome-team/capacitor-file-opener'
pod 'CapawesomeTeamCapacitorNfc', :path => '../../node_modules/@capawesome-team/capacitor-nfc'
pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'
end

Expand Down
10 changes: 8 additions & 2 deletions ios/App/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ PODS:
- Capacitor
- CapawesomeTeamCapacitorFileOpener (5.0.4):
- Capacitor
- CapawesomeTeamCapacitorNfc (5.1.0-dev.7d38c01.1709239536):
- Capacitor
- CordovaPlugins (5.6.0):
- CapacitorCordova

Expand All @@ -35,6 +37,7 @@ DEPENDENCIES:
- "CapacitorSplashScreen (from `../../node_modules/@capacitor/splash-screen`)"
- "CapacitorStatusBar (from `../../node_modules/@capacitor/status-bar`)"
- "CapawesomeTeamCapacitorFileOpener (from `../../node_modules/@capawesome-team/capacitor-file-opener`)"
- "CapawesomeTeamCapacitorNfc (from `../../node_modules/@capawesome-team/capacitor-nfc`)"
- CordovaPlugins (from `../capacitor-cordova-ios-plugins`)

EXTERNAL SOURCES:
Expand All @@ -60,6 +63,8 @@ EXTERNAL SOURCES:
:path: "../../node_modules/@capacitor/status-bar"
CapawesomeTeamCapacitorFileOpener:
:path: "../../node_modules/@capawesome-team/capacitor-file-opener"
CapawesomeTeamCapacitorNfc:
:path: "../../node_modules/@capawesome-team/capacitor-nfc"
CordovaPlugins:
:path: "../capacitor-cordova-ios-plugins"

Expand All @@ -75,8 +80,9 @@ SPEC CHECKSUMS:
CapacitorSplashScreen: dd3de3f3644710fa2a697cfb91ec262eece4d242
CapacitorStatusBar: f390fbb49b82ffb754ea4b3cf71dc8b048baf3e7
CapawesomeTeamCapacitorFileOpener: a635d36c142c95018855c21c102da07eb88a4e1e
CapawesomeTeamCapacitorNfc: 62f7b56f556612e050d95a1be2d459c55b02eaa3
CordovaPlugins: 51a49218c1b6e2ae7d7f40efa9159eb978ddc888

PODFILE CHECKSUM: 26c4df6714124140b8e56789f2c8fd87c2fef304
PODFILE CHECKSUM: baa07d86309c338c813d17409138037a1834285f

COCOAPODS: 1.14.3
COCOAPODS: 1.15.2
16 changes: 16 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@capacitor/splash-screen": "5.0.7",
"@capacitor/status-bar": "5.0.7",
"@capawesome-team/capacitor-file-opener": "5.0.4",
"@capawesome-team/capacitor-nfc": "5.1.0-dev.7d38c01.1709239536",
"@fortawesome/angular-fontawesome": "0.14.1",
"@fortawesome/fontawesome-svg-core": "6.5.1",
"@fortawesome/free-brands-svg-icons": "6.5.1",
Expand Down
7 changes: 6 additions & 1 deletion src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { registerLocaleData } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { ErrorHandler, NgModule } from '@angular/core';
import localeDe from '@angular/common/locales/de';
import { ErrorHandler, LOCALE_ID, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';
import { CoreModule, GlobalErrorHandlerService } from '@app/core';
Expand All @@ -11,6 +13,8 @@ import { register as registerSwiper } from 'swiper/element/bundle';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

registerLocaleData(localeDe);

registerSwiper();

@NgModule({
Expand All @@ -34,6 +38,7 @@ registerSwiper();
provide: ErrorHandler,
useClass: GlobalErrorHandlerService,
},
{ provide: LOCALE_ID, useValue: 'de-DE' },
],
bootstrap: [AppComponent],
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { Injectable, NgZone } from '@angular/core';
import { Capacitor } from '@capacitor/core';
import {
ConnectOptions,
Nfc,
NfcTag,
PollingOption,
TransceiveOptions,
TransceiveResult,
} from '@capawesome-team/capacitor-nfc';
import { Observable, Subject } from 'rxjs';

@Injectable({
providedIn: 'root',
})
export class CapacitorNfcService {
private readonly scannedTagSubject = new Subject<NfcTag>();
private readonly sessionCanceledSubject = new Subject<void>();

constructor(private readonly ngZone: NgZone) {
void Nfc.removeAllListeners().then(() => {
void Nfc.addListener('nfcTagScanned', event => {
this.ngZone.run(() => {
this.scannedTagSubject.next(event.nfcTag);
});
});
void Nfc.addListener('scanSessionCanceled', () => {
this.ngZone.run(() => {
this.sessionCanceledSubject.next();
});
});
});
}

public get scannedTag$(): Observable<NfcTag> {
return this.scannedTagSubject.asObservable();
}

public get sessionCanceled$(): Observable<void> {
return this.sessionCanceledSubject.asObservable();
}

public async startScanSession(): Promise<void> {
await Nfc.startScanSession({
pollingOptions: [PollingOption.iso14443, PollingOption.iso15693],
});
}

public async stopScanSession(): Promise<void> {
await Nfc.stopScanSession();
}

public transceive(options: TransceiveOptions): Promise<TransceiveResult> {
return Nfc.transceive(options);
}

public async connect(options: ConnectOptions): Promise<void> {
const isAndroid = Capacitor.getPlatform() === 'android';
if (isAndroid) {
await Nfc.connect(options);
}
}

public async close(): Promise<void> {
const isAndroid = Capacitor.getPlatform() === 'android';
if (isAndroid) {
await Nfc.close();
}
}

public async isSupported(): Promise<boolean> {
const { isSupported } = await Nfc.isSupported();
return isSupported;
}

public async isEnabled(): Promise<boolean> {
const isAndroid = Capacitor.getPlatform() === 'android';
if (!isAndroid) {
return true;
}
const { isEnabled } = await Nfc.isEnabled();
return isEnabled;
}
}
1 change: 1 addition & 0 deletions src/app/core/services/capacitor/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './capacitor-app/capacitor-app.service';
export * from './capacitor-nfc/capacitor-nfc.service';
2 changes: 2 additions & 0 deletions src/app/core/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ export * from './capacitor';
export * from './dialog/dialog.service';
export * from './global-error-handler/global-error-handler.service';
export * from './native-http/native-http.service';
export * from './nfc';
export * from './notification/notification.service';
export * from './storage/storage.service';
export * from './timeout/timeout.service';
export * from './user/user.service';
2 changes: 2 additions & 0 deletions src/app/core/services/nfc/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './nfc-helper/nfc-helper.service';
export * from './nfc/nfc.service';
23 changes: 23 additions & 0 deletions src/app/core/services/nfc/nfc-helper/nfc-helper.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Injectable } from '@angular/core';
import { NfcUtils } from '@capawesome-team/capacitor-nfc';

@Injectable({
providedIn: 'root',
})
export class NfcHelperService {
private readonly nfcUtilsInstance = new NfcUtils();

constructor() {}

public convertHexToBytes(hex: string): number[] {
return this.nfcUtilsInstance.convertHexToBytes({ hex }).bytes;
}

public convertBytesToHex(bytes: number[]): string {
return this.nfcUtilsInstance.convertBytesToHex({ bytes }).hex;
}

public convertHexToNumber(hex: string): number {
return this.nfcUtilsInstance.convertHexToNumber({ hex }).number;
}
}
Loading

0 comments on commit 42b82c1

Please sign in to comment.