From 94dd0bc74038c532ad55245b0bbadfd4d9687765 Mon Sep 17 00:00:00 2001 From: Damigrace1 Date: Wed, 31 Jul 2024 21:26:26 +0100 Subject: [PATCH 1/3] updated README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5fc00b5..19283da 100644 --- a/README.md +++ b/README.md @@ -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 From cbdb4432cf2caf7e63cbb729952c604815d896b6 Mon Sep 17 00:00:00 2001 From: Damigrace1 Date: Thu, 1 Aug 2024 12:56:27 +0100 Subject: [PATCH 2/3] Corrected dumebi name --- README.md | 2 +- lib/utils/const.dart | 1 + lib/views/home/home.dart | 4 +- lib/views/home/home_tabs/mycontacts.dart | 65 +------------------- lib/views/widgets/dialogs/received_card.dart | 4 +- 5 files changed, 10 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 19283da..553789b 100644 --- a/README.md +++ b/README.md @@ -72,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 diff --git a/lib/utils/const.dart b/lib/utils/const.dart index 936b44b..874280f 100644 --- a/lib/utils/const.dart +++ b/lib/utils/const.dart @@ -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); diff --git a/lib/views/home/home.dart b/lib/views/home/home.dart index 237b580..dda8471 100644 --- a/lib/views/home/home.dart +++ b/lib/views/home/home.dart @@ -135,7 +135,9 @@ class _HomeScreenState extends State ], ), floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, - floatingActionButton: FloatingActionButton(onPressed: () { + floatingActionButton: FloatingActionButton( + backgroundColor: kgrey6.withOpacity(1), + onPressed: () { HomeController.it.readBusinessCard(); },child: Column( diff --git a/lib/views/home/home_tabs/mycontacts.dart b/lib/views/home/home_tabs/mycontacts.dart index 161baa0..04922a0 100644 --- a/lib/views/home/home_tabs/mycontacts.dart +++ b/lib/views/home/home_tabs/mycontacts.dart @@ -33,73 +33,12 @@ class Contact { } class ContactController extends GetxController { - // var contacts = [ - // Contact( - // id: '1', - // name: 'Isaac', - // role: 'UI/UX Designer @ HNG', - // date: 'July 28, 2024', - // email: 'isaac@example.com', - // phone: '+234 805 456 321', - // website: 'www.isaac.com', - // company: 'GreenBolt', - // ), - // Contact( - // id: '2', - // name: 'Yetunde', - // role: 'UI/UX Designer @ HNG', - // date: 'July 28, 2024', - // email: 'yetunde@example.com', - // phone: '+234 805 456 322', - // website: 'www.yetunde.com', - // company: 'TechCorp', - // ), - // Contact( - // id: '3', - // name: 'Jethro', - // role: 'UI/UX Designer @ HNG', - // date: 'July 28, 2024', - // email: 'jethro@example.com', - // phone: '+234 805 456 323', - // website: 'www.jethro.com', - // company: 'DesignHub', - // ), - // Contact( - // id: '4', - // name: 'Ifeoluwa', - // role: 'UI/UX Designer @ HNG', - // date: 'June 20, 2024', - // email: 'ifeoluwa@example.com', - // phone: '+234 805 456 324', - // website: 'www.ifeoluwa.com', - // company: 'Creatives Inc.', - // ), - // Contact( - // id: '5', - // name: 'Favor', - // role: 'UI/UX Designer @ HNG', - // date: 'June 20, 2024', - // email: 'favor@example.com', - // phone: '+234 805 456 325', - // website: 'www.favor.com', - // company: 'InnovateX', - // ), - // Contact( - // id: '6', - // name: 'Eskor', - // role: 'UI/UX Designer @ HNG', - // date: 'June 20, 2024', - // email: 'eskor@example.com', - // phone: '+234 805 456 326', - // website: 'www.eskor.com', - // company: 'Creative Minds', - // ), - // ].obs; + var contacts = [].obs; void getContacts() async { final List localContactsDocs = - await LocalStorageService.instance.getOthersCards()??[]; + await LocalStorageService.instance.getMyCards()??[]; List cardModels = localContactsDocs.map((lC) => BusinessCardModel.fromMap(lC)).toList(); contacts.value = cardModels diff --git a/lib/views/widgets/dialogs/received_card.dart b/lib/views/widgets/dialogs/received_card.dart index 67212f5..109421e 100644 --- a/lib/views/widgets/dialogs/received_card.dart +++ b/lib/views/widgets/dialogs/received_card.dart @@ -12,6 +12,8 @@ import 'package:tapcard/views/home/home_tabs/mycontacts.dart'; import 'package:tapcard/views/widgets/business_card.dart'; import 'package:tapcard/views/widgets/dialogs/contact_saved.dart'; +import '../../../utils/themes.dart'; + class ReceivedCard extends StatelessWidget { const ReceivedCard({Key? key, required this.businessCardModel}) : super(key: key); final BusinessCardModel businessCardModel; @@ -23,7 +25,7 @@ final BusinessCardModel businessCardModel; child: Container( width: MediaQuery.of(context).size.width, decoration: BoxDecoration( - color: Colors.white, + color: currentTheme.scaffoldBackgroundColor, borderRadius: BorderRadius.circular(10), ), child: Padding( From 750bd693da3e2aa395bfa882fec67da4d35ddee3 Mon Sep 17 00:00:00 2001 From: David Ohimai Date: Sat, 3 Aug 2024 10:07:50 +0100 Subject: [PATCH 3/3] fix/sharing for non-ndef device --- .vscode/settings.json | 3 + android/app/src/main/AndroidManifest.xml | 11 ++ .../kotlin/com/example/tapcard/HceService.kt | 14 ++ android/app/src/main/res/apduservice.xml | 8 + lib/controllers/home_controller.dart | 171 ++++++++++++------ pubspec.lock | 56 ++++++ pubspec.yaml | 2 + 7 files changed, 208 insertions(+), 57 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 android/app/src/main/kotlin/com/example/tapcard/HceService.kt create mode 100644 android/app/src/main/res/apduservice.xml diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c5f3f6b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive" +} \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a45d440..b2e482f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/lib/controllers/home_controller.dart b/lib/controllers/home_controller.dart index 40511c2..32cae92 100644 --- a/lib/controllers/home_controller.dart +++ b/lib/controllers/home_controller.dart @@ -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'; @@ -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'; @@ -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(); @@ -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), @@ -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( @@ -104,7 +105,8 @@ 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); }, @@ -112,46 +114,68 @@ class HomeController extends GetxController { 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 = [].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 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 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( @@ -159,33 +183,39 @@ class HomeController extends GetxController { 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'); } } @@ -193,22 +223,20 @@ class HomeController extends GetxController { 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(); @@ -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'); } @@ -253,5 +282,33 @@ class HomeController extends GetxController { return null; } + Future 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'); + } + } } diff --git a/pubspec.lock b/pubspec.lock index ebf82c2..9e71195 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -310,6 +310,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" + flutter_nfc_kit: + dependency: "direct main" + description: + name: flutter_nfc_kit + sha256: fe0b86f4883e4a0ebeb6d1fbbc5a86123b95969a4d225bc5a760acda5e3f42e8 + url: "https://pub.dev" + source: hosted + version: "3.5.2" flutter_screenutil: dependency: "direct main" description: @@ -608,6 +616,54 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb" + url: "https://pub.dev" + source: hosted + version: "11.3.1" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: b29a799ca03be9f999aa6c39f7de5209482d638e6f857f6b93b0875c618b7e54 + url: "https://pub.dev" + source: hosted + version: "12.0.7" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: e6f6d73b12438ef13e648c4ae56bd106ec60d17e90a59c4545db6781229082a0 + url: "https://pub.dev" + source: hosted + version: "9.4.5" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: "6cac773d389e045a8d4f85418d07ad58ef9e42a56e063629ce14c4c26344de24" + url: "https://pub.dev" + source: hosted + version: "0.1.2" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: "48d4fcf201a1dad93ee869ab0d4101d084f49136ec82a8a06ed9cfeacab9fd20" + url: "https://pub.dev" + source: hosted + version: "4.2.1" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + url: "https://pub.dev" + source: hosted + version: "0.2.1" platform: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d17cca2..a0b7984 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -54,6 +54,8 @@ dependencies: json_serializable: ^6.7.1 js: ^0.7.1 dotted_border: ^2.1.0 + permission_handler: ^11.3.1 + flutter_nfc_kit: ^3.5.2 dev_dependencies: