Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"java.configuration.updateBuildConfiguration": "interactive"
}
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ To start using Tapcard, follow these simple steps:

### Download

- **[APK Download]:** (https://drive.google.com/file/d/13TxF7Qsc1Du18hsoJyhIMYkEgU5ODG2Y/view?usp=drivesdk).
- **GitHub**:(https://github.com/Damigrace1/TapCardApp)
- **[APK Download]:** (https://drive.google.com/file/d/13TxF7Qsc1Du18hsoJyhIMYkEgU5ODG2Y/view?usp=drivesdk)
- **[GitHub]**:(https://github.com/Damigrace1/TapCardApp)
- **[Figma Design]**:(https://www.figma.com/design/IQcbrxkevs9FzEweXbOH3v/TEAM-KIMIKO-(TELEX)---NFC-PROJECTS?node-id=1203-5993&t=2YiFAagW3O2boMqf-0)

### Usage

Expand Down Expand Up @@ -71,7 +72,7 @@ To start using Tapcard, follow these simple steps:

1. Damilare Ogunwehin - Team Lead
2. David Ohimai Ohiosimuan
3. Ezekiel Chukwudumebi
3. Ezeukwu Chukwudumebi
4. Olaleye Nathaniel Oluwatosin
5. Tosin Ezekiel

Expand Down
11 changes: 11 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.NFC" />
<!-- Added this -->
<uses-feature android:name="android.hardware.nfc" android:required="true" />
<!-- Added this -->
<!-- <service android:name=".HceService" android:exported="true"
android:permission="android.permission.BIND_NFC_SERVICE">
<intent-filter>
<action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
</intent-filter>
<meta-data android:name="android.nfc.cardemulation.host_apdu_service"
android:resource="@xml/apduservice"/>
</service> -->
<application
android:label="tapcard"
android:name="${applicationName}"
Expand Down
14 changes: 14 additions & 0 deletions android/app/src/main/kotlin/com/example/tapcard/HceService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import android.nfc.cardemulation.HostApduService
import android.os.Bundle

class HceService : HostApduService() {
override fun processCommandApdu(commandApdu: ByteArray?, extras: Bundle?): ByteArray {
// Implement your HCE logic here
// This is where you'd handle the NFC commands and respond with your business card data
return ByteArray(0)
}

override fun onDeactivated(reason: Int) {
// Handle deactivation
}
}
8 changes: 8 additions & 0 deletions android/app/src/main/res/apduservice.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/service_name"
android:requireDeviceUnlock="false">
<aid-group android:description="@string/aid_description"
android:category="other">
<aid-filter android:name="F0010203040506"/>
</aid-group>
</host-apdu-service>
171 changes: 114 additions & 57 deletions lib/controllers/home_controller.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
Expand All @@ -10,7 +12,6 @@ import 'package:tapcard/views/customize/cardcustom.dart';
import 'package:localstore/localstore.dart';
import 'package:nfc_manager/nfc_manager.dart';


import 'package:tapcard/views/edit_card.dart';
import 'package:tapcard/views/widgets/dialogs/received_card.dart';
import 'package:tapcard/views/widgets/dialogs/scan_incoming_card.dart';
Expand All @@ -27,11 +28,10 @@ class HomeController extends GetxController {
List myCards = [];

getMyCards() async {
myCards = await LocalStorageService.instance.getMyCards()??[];
myCards = await LocalStorageService.instance.getMyCards() ?? [];
update();
}


@override
void onInit() async {
super.onInit();
Expand All @@ -55,9 +55,8 @@ class HomeController extends GetxController {
update();
}


void showEditCardDialog(BuildContext context, bm.BusinessCardModel cardModel) {

void showEditCardDialog(
BuildContext context, bm.BusinessCardModel cardModel) {
Get.dialog(AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
Expand All @@ -79,7 +78,9 @@ class HomeController extends GetxController {
title: Text('Change Details'),
onTap: () {
Navigator.pop(context);
Get.to(() => EditCard(cardModel: cardModel,));
Get.to(() => EditCard(
cardModel: cardModel,
));
},
),
ListTile(
Expand All @@ -104,111 +105,138 @@ class HomeController extends GetxController {
),
title: Text('Delete'),
onTap: () {
LocalStorageService.instance.deleteMyCard(cardModel.id.toString());
LocalStorageService.instance
.deleteMyCard(cardModel.id.toString());
HomeController.it.getMyCards();
Navigator.pop(context);
},
),
SizedBox(
width: double.infinity,
child: CustomButton(
text: 'Close',
filled: false,
onPressed: () {
Navigator.pop(context);
}),
// child: CustomButton(
// text: 'Close',
// filled: false,
// onPressed: () {
// Navigator.pop(context);
// }),
)
text: 'Close',
filled: false,
onPressed: () {
Navigator.pop(context);
},
),
),
],
),
));

}

var businessCards = <BusinessCardModel>[].obs;

// void fetchCardsFromDatabase() async {
// final items = await _db.collection('businessCards').get();
// if (items != null) {
// businessCards.value =
// items.values.map((item) => BusinessCardModel.fromJson(item)).toList();
// }
// }
// Future<void> shareBusinessCard(bm.BusinessCardModel card) async {
// try {
// bool isAvailable = await NfcManager.instance.isAvailable();
// if (!isAvailable) {
// Get.snackbar('Error', 'NFC is not available on this device');
// return;
// }
// showDialog(
// context: Get.context!,
// builder: (context) => const StartSharing(),
// );
// NfcManager.instance.startSession(onDiscovered: (NfcTag tag) async {
// try {
// Ndef? ndef = Ndef.from(tag);
// if (ndef == null) {
// Get.snackbar('Device Error', 'Tag is not NDEF compatible');
// return;
// }

// NdefMessage message = NdefMessage([
// NdefRecord.createText(jsonEncode(card.toMap())),
// ]);

// await ndef.write(message);
// Navigator.pop(Get.context!);
// // Get.snackbar('Write Success ' , 'Card data written successfully');

// void addBusinessCard(BusinessCardModel card) async {
// final id = _db.collection('businessCards').doc().id;
// await _db.collection('businessCards').doc(id).set(card.toJson());
// businessCards.add(card);
// showDialog(
// context: Get.context!,
// builder: (context) => const SharingComplete(),
// );
// NfcManager.instance.stopSession();
// } catch (e) {
// print('Error writing to NFC tag: $e');
// NfcManager.instance.stopSession(errorMessage: e.toString());
// }
// });
// } catch (e) {
// print('Error starting NFC session: $e');
// }
// }

// Added this here
Future<void> shareBusinessCard(bm.BusinessCardModel card) async {
try {
bool isAvailable = await NfcManager.instance.isAvailable();
if (!isAvailable) {

Get.snackbar('Error','NFC is not available on this device');
Get.snackbar('Error', 'NFC is not available on this device');
return;
}
showDialog(
context: Get.context!,
builder: (context) => const StartSharing(),
);
NfcManager.instance.startSession(onDiscovered: (NfcTag tag) async {
print('Discovered NFC tag');
try {
Ndef? ndef = Ndef.from(tag);
if (ndef == null) {
Get.snackbar('Device Error', 'Tag is not NDEF compatible');
return;
if (ndef != null) {
// NDEF compatible device
NdefMessage message = NdefMessage([
NdefRecord.createText(jsonEncode(card.toMap())),
]);
await ndef.write(message);
} else {
// Non-NDEF device, try Android Beam or HCE
if (Platform.isAndroid) {
await shareViaAndroidBeam(card);
} else {
// For iOS or other platforms, you might need a different approach
Get.snackbar('Error', 'Unsupported device for non-NDEF sharing');
}
}

NdefMessage message = NdefMessage([
NdefRecord.createText(jsonEncode(card.toMap())),
]);

await ndef.write(message);
Navigator.pop(Get.context!);
// Get.snackbar('Write Success ' , 'Card data written successfully');

showDialog(
context: Get.context!,
builder: (context) => const SharingComplete(),
);
NfcManager.instance.stopSession();
} catch (e) {
print('Error writing to NFC tag: $e');
NfcManager.instance.stopSession(errorMessage: e.toString());
print('Error sharing card: $e');
Get.snackbar('Error', 'Failed to share card: $e');
} finally {
NfcManager.instance.stopSession();
}
});
} catch (e) {
print('Error starting NFC session: $e');
Get.snackbar('Error', 'Failed to start NFC session: $e');
}
}

Future<bm.BusinessCardModel?> readBusinessCard() async {
try {
bool isAvailable = await NfcManager.instance.isAvailable();
if (!isAvailable) {

Get.snackbar('Sorry', 'NFC is not available on this device');
return null;
}
showDialog(context: Get.context!, builder: (context)=>
ScanIncomingCard());
NfcManager.instance.startSession(
onDiscovered: (NfcTag tag) async
{
showDialog(
context: Get.context!, builder: (context) => ScanIncomingCard());
NfcManager.instance.startSession(onDiscovered: (NfcTag tag) async {
print('Discovered NFC tag');
try {
Ndef? ndef = Ndef.from(tag);

if (ndef == null) {
Navigator.pop(Get.context!);
Get.snackbar('Device Error', 'Tag is not NDEF compatible');
return ;
return;
}

NdefMessage? message = await ndef.read();
Expand All @@ -231,9 +259,10 @@ class HomeController extends GetxController {
Navigator.pop(Get.context!);
showDialog(
context: Get.context!,
builder: (context) => ReceivedCard(businessCardModel: card,),
builder: (context) => ReceivedCard(
businessCardModel: card,
),
);

} catch (e) {
print('Error parsing business card data: $e');
}
Expand All @@ -253,5 +282,33 @@ class HomeController extends GetxController {
return null;
}

Future<void> shareViaAndroidBeam(bm.BusinessCardModel card) async {
try {
NfcManager.instance.startSession(onDiscovered: (NfcTag tag) async {
var ndef = Ndef.from(tag);
if (ndef == null) {
Get.snackbar('Error', 'Tag is not NDEF compatible');
return;
}

NdefMessage message = NdefMessage([
NdefRecord.createMime(
'application/vnd.com.example.businesscard',
Uint8List.fromList(jsonEncode(card.toMap()).codeUnits),
),
]);

try {
ndef.isWritable;
Get.snackbar('Success', 'Card shared successfully');
} catch (e) {
Get.snackbar('Error', 'Failed to share card: $e');
} finally {
NfcManager.instance.stopSession();
}
});
} catch (e) {
Get.snackbar('Error', 'Failed to start NFC session: $e');
}
}
}
1 change: 1 addition & 0 deletions lib/utils/const.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const Color kpurple = Color(0xff8E60DD);
const Color kpurple1 = Color(0xff623F9D);
const Color kgrey4 = Color(0xffD0D5DD);
const Color kgrey5 = Color(0xff9B9B9B);
const Color kgrey6 = Color(0xffB7B7B7);
const Color kwhite = Color(0xffffffff);
const Color kblack = Color(0xff000000);
const Color kgrey3 = Color(0xff49454F);
4 changes: 3 additions & 1 deletion lib/views/home/home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@ class _HomeScreenState extends State<HomeScreen>
],
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: FloatingActionButton(onPressed: () {
floatingActionButton: FloatingActionButton(
backgroundColor: kgrey6.withOpacity(1),
onPressed: () {

HomeController.it.readBusinessCard();
},child: Column(
Expand Down
Loading