diff --git a/android/app/build.gradle b/android/app/build.gradle
index 38a3b8dc..04754268 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -54,7 +54,7 @@ android {
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 21
- targetSdkVersion 33
+ targetSdkVersion 34
versionCode System.getenv('APPCENTER_BUILD_ID')?.toInteger() ?: 404
versionName flutterVersionName
}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 323f9cd0..f0ae7ec7 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -29,6 +29,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/flutter_i18n/de.json b/assets/flutter_i18n/de.json
index a03b4a0c..c01a8577 100644
--- a/assets/flutter_i18n/de.json
+++ b/assets/flutter_i18n/de.json
@@ -417,5 +417,41 @@
"save_locally": "Lokal speichern",
"save_locally_description": "Stellen Sie sicher, dass Sie Ihre Schlüssel lokal speichern und dann können Sie loslegen.",
"axs_storage_permission_use_case": "Die AXS Wallet benötigt Zugriff auf Ihren Speicher für Funktionen wie RWA. Ihre Speicherdaten werden nicht gespeichert oder mit Dritten geteilt.",
- "local_backup": "Lokale Sicherung"
+ "local_backup": "Lokale Sicherung",
+ "no_description": "Keine Beschreibung",
+ "bluetooth_not_supported": "Bluetooth wird auf diesem Gerät nicht unterstützt.",
+ "blueberry_ring_hooks": "Blaubeere Ring Haken",
+ "nearby_blueberry_rings": "In der Nähe von Blaubeer-Ringen",
+ "unable_to_continue_bluetooth_is_turned_off": "Kann nicht fortsetzen, Bluetooth ist ausgeschaltet!",
+ "scanning": "Scannen ...",
+ "blueberry_ring_inactive_alert_title": "Zeit für einen Spaziergang!",
+ "blueberry_ring_inactive_alert_text": "Sie waren eine Weile inaktiv. Ein kurzer Spaziergang könnte wirklich belebend sein!",
+ "blueberry_ring_sleep_alert_title": "Sie haben in letzter Zeit weniger Tiefschlaf bekommen als üblich. Versuchen Sie, sich heute Abend 30 Minuten früher zu entspannen, um besser auszuruhen.",
+ "blueberry_ring_sleep_alert_text": "Sie haben in letzter Zeit weniger Tiefschlaf bekommen als üblich. Versuchen Sie, sich heute Abend 30 Minuten früher zu entspannen, um besser auszuruhen.",
+ "blueberry_ring_heart_rate_alert_title": "Ihre Herzfrequenz war heute ungewöhnlich hoch. Wenn Sie sich unwohl fühlen, könnte es gut sein, einen Arzt aufzusuchen.",
+ "blueberry_ring_heart_rate_alert_text": "Ihre Herzfrequenz war heute ungewöhnlich hoch. Wenn Sie sich unwohl fühlen, könnte es gut sein, einen Arzt aufzusuchen.",
+ "blueberry_ring_battery_alert_title": "Der Akku ist fast leer!",
+ "blueberry_ring_battery_alert_text": "Der Akku Ihres Blueberry Rings ist fast leer. Bitte laden Sie ihn bald auf, um Datenlücken zu vermeiden.",
+ "activity_reminder": "Aktivitätserinnerung",
+ "sleep_insight": "Schlaf Einblick",
+ "heart_alert": "Herzalarm",
+ "low_battery": "Niedriger Akkustand",
+ "blueberry_background_notifications_requirements_title": "Dieser Service erfordert:",
+ "blueberry_background_notifications_requirements_text_1": "1. Bluetooth soll EINGESCHALTET sein",
+ "blueberry_background_notifications_requirements_text_2": "2. Blueberry soll erreichbar sein",
+ "auto_sync_started": "Blueberry Ring Autosynchronisation gestartet 🏁",
+ "no_rings_selected_notification_title": "Sieht so aus, als hätten Sie keine Blueberry Rings ausgewählt. ℹ️",
+ "no_rings_selected_notification_text": "Bitte gehen Sie zur Blueberry Ring DApp, um Ringe auszuwählen.",
+ "auto_sync_successful_notification_title": "Erfolgreiche Auto-Claim von Blueberry Ring ✅",
+ "auto_sync_successful_notification_text": "Ihre Ringdaten wurden erfolgreich mit der AXS-Wallet synchronisiert.",
+ "already_synced_notification_title": "Hoppla, bereits synchronisiert ℹ️",
+ "already_synced_notification_text": "Die AXS-Wallet hat versucht, Ihre Ringdaten zu synchronisieren, aber es sieht so aus, als hätten Sie die Ringdaten heute bereits synchronisiert.",
+ "auto_sync_failed": "Blueberry Ring Auto-Sync fehlgeschlagen ❌",
+ "no_rings_owned_notification": "Sieht so aus, als ob diese Wallet keine Blueberry Rings besitzt. ℹ️",
+ "syncing_data_from_ring": "Synchronisieren von Daten aus dem Blueberry Ring #{0}. ⛏️",
+ "already_synced_ring": "Blaubeere Ring #{0}: Bereits synchronisiert. ℹ️",
+ "data_synced_successfully_ring": "Blaubeere Ring #{0}: Daten erfolgreich synchronisiert. ✅",
+ "data_syncing_failed": "Blaubeere Ring #{0}: Daten-Synchronisationsprozess fehlgeschlagen. ❌",
+ "blueberry_hooks": "Blaubeerhaken",
+ "message": "Nachricht"
}
\ No newline at end of file
diff --git a/assets/flutter_i18n/en.json b/assets/flutter_i18n/en.json
index 9346e225..4a862b33 100644
--- a/assets/flutter_i18n/en.json
+++ b/assets/flutter_i18n/en.json
@@ -412,5 +412,41 @@
"dapp_removal_dialog_text": "Deleting this dApp will remove it from your Home Screen.",
"information": "Information",
"ok": "Ok",
- "local_backup": "Local backup"
+ "local_backup": "Local backup",
+ "no_description": "No description",
+ "bluetooth_not_supported": "Bluetooth is not supported on this device.",
+ "blueberry_ring_hooks": "Blueberry Ring hooks",
+ "nearby_blueberry_rings": "Nearby Blueberry rings",
+ "unable_to_continue_bluetooth_is_turned_off": "Unable to continue Bluetooth is turned off!",
+ "scanning": "Scanning ...",
+ "blueberry_ring_inactive_alert_title": "Time to go for a walk!",
+ "blueberry_ring_inactive_alert_text": "You've been inactive for a while. A short walk could be really invigorating!",
+ "blueberry_ring_sleep_alert_title": "You've been getting less deep sleep than usual. Try unwinding 30 minutes earlier tonight for better rest.",
+ "blueberry_ring_sleep_alert_text": "You've been getting less deep sleep than usual. Try unwinding 30 minutes earlier tonight for better rest.",
+ "blueberry_ring_heart_rate_alert_title": "Your heart rate has been unusually high today. If you feel unwell, it might be good to check in with a doctor.",
+ "blueberry_ring_heart_rate_alert_text": "Your heart rate has been unusually high today. If you feel unwell, it might be good to check in with a doctor.",
+ "blueberry_ring_battery_alert_title": "Battery is low!",
+ "blueberry_ring_battery_alert_text": "Your Blueberry Ring's battery is low. Please charge soon to avoid data gaps.",
+ "activity_reminder": "Activity reminder",
+ "sleep_insight": "Sleep insight",
+ "heart_alert": "Heart alert",
+ "low_battery": "Low battery",
+ "blueberry_background_notifications_requirements_title": "This service requires: ",
+ "blueberry_background_notifications_requirements_text_1": "1. Bluetooth to be ON",
+ "blueberry_background_notifications_requirements_text_2": "2. Blueberry to be reachable",
+ "auto_sync_started": "Blueberry Ring auto sync started 🏁",
+ "no_rings_selected_notification_title": "Looks like you haven't selected any Blueberry Rings. ℹ️",
+ "no_rings_selected_notification_text": "Please head over to Blueberry Ring DApp for selecting rings.",
+ "auto_sync_successful_notification_title": "Blueberry Ring aut-claim successful ✅",
+ "auto_sync_successful_notification_text": "AXS wallet has been successfully synced your ring data",
+ "already_synced_notification_title": "Oops, Already synced ℹ️",
+ "already_synced_notification_text": "AXS wallet tried to sync your ring data, But looks like you have synced the ring data today.",
+ "auto_sync_failed": "Blueberry Ring aut-sync failed ❌",
+ "no_rings_owned_notification": "Looks like this wallet doesn't own any Blueberry Rings. ℹ️",
+ "syncing_data_from_ring": "Syncing data from Blueberry Ring #{0}. ⛏️",
+ "already_synced_ring": "Blueberry Ring #{0}: Already synced. ℹ️",
+ "data_synced_successfully_ring": "Blueberry Ring #{0}: Data synced successfully. ✅",
+ "data_syncing_failed": "Blueberry Ring #{0}: Data syncing process failed. ❌",
+ "blueberry_hooks": "Blueberry hooks",
+ "message": "Message"
}
\ No newline at end of file
diff --git a/assets/flutter_i18n/es.json b/assets/flutter_i18n/es.json
index 64d48fff..215ffc05 100644
--- a/assets/flutter_i18n/es.json
+++ b/assets/flutter_i18n/es.json
@@ -417,5 +417,41 @@
"save_locally": "Guardar localmente",
"save_locally_description": "Asegúrate de guardar tus claves localmente y luego estás listo para continuar.",
"axs_storage_permission_use_case": "La billetera AXS necesita acceso a tu almacenamiento para funciones como RWA. Tus datos de almacenamiento no se guardan ni se comparten con terceros.",
- "local_backup": "Copia de seguridad local"
+ "local_backup": "Copia de seguridad local",
+ "no_description": "Sin descripción",
+ "bluetooth_not_supported": "Bluetooth no es compatible con este dispositivo.",
+ "blueberry_ring_hooks": "Blueberry Ring se engancha",
+ "nearby_blueberry_rings": "Anillos de Arándano Cercanos",
+ "unable_to_continue_bluetooth_is_turned_off": "No se puede continuar, ¡el Bluetooth está apagado!",
+ "scanning": "Escaneando ...",
+ "blueberry_ring_inactive_alert_title": "¡Hora de ir a dar un paseo!",
+ "blueberry_ring_inactive_alert_text": "Has estado inactivo durante un tiempo. ¡Un corto paseo podría ser realmente vigorizante!",
+ "blueberry_ring_sleep_alert_title": "Has estado obteniendo menos sueño profundo de lo habitual. Intenta relajarte 30 minutos antes esta noche para un mejor descanso.",
+ "blueberry_ring_sleep_alert_text": "Has estado obteniendo menos sueño profundo de lo habitual. Intenta relajarte 30 minutos antes esta noche para descansar mejor.",
+ "blueberry_ring_heart_rate_alert_title": "Tu ritmo cardíaco ha estado inusualmente alto hoy. Si te sientes mal, podría ser bueno consultar con un médico.",
+ "blueberry_ring_heart_rate_alert_text": "Tu ritmo cardíaco ha estado inusualmente alto hoy. Si te sientes mal, podría ser bueno consultar con un médico.",
+ "blueberry_ring_battery_alert_title": "¡La batería está baja!",
+ "blueberry_ring_battery_alert_text": "La batería de tu Anillo Blueberry está baja. Por favor, cárgala pronto para evitar interrupciones en los datos.",
+ "activity_reminder": "Recordatorio de actividad",
+ "sleep_insight": "Perspectiva del sueño",
+ "heart_alert": "Alerta de corazón",
+ "low_battery": "Batería baja",
+ "blueberry_background_notifications_requirements_title": "Este servicio requiere:",
+ "blueberry_background_notifications_requirements_text_1": "1. Bluetooth debe estar ENCENDIDO",
+ "blueberry_background_notifications_requirements_text_2": "2. Blueberry para ser accesible",
+ "auto_sync_started": "El auto sincronización de Blueberry Ring ha comenzado 🏁",
+ "no_rings_selected_notification_title": "Parece que no has seleccionado ningún Anillo de Arándano. ℹ️",
+ "no_rings_selected_notification_text": "Por favor, dirígete a la DApp de Blueberry Ring para seleccionar anillos.",
+ "auto_sync_successful_notification_title": "El auto-reclamo de Blueberry Ring ha sido exitoso ✅",
+ "auto_sync_successful_notification_text": "La cartera AXS ha sincronizado con éxito tus datos de anillo.",
+ "already_synced_notification_title": "Ups, ya sincronizado ℹ️",
+ "already_synced_notification_text": "La cartera AXS intentó sincronizar tus datos de anillo, pero parece que ya has sincronizado los datos del anillo hoy.",
+ "auto_sync_failed": "Falló la sincronización automática de Blueberry Ring ❌",
+ "no_rings_owned_notification": "Parece que esta billetera no posee ningún Blueberry Rings. ℹ️",
+ "syncing_data_from_ring": "Sincronizando datos desde Blueberry Ring #{0}. ⛏️",
+ "already_synced_ring": "Anillo de Arándano #{0}: Ya sincronizado. ℹ️",
+ "data_synced_successfully_ring": "Anillo de Arándano #{0}: Datos sincronizados con éxito. ✅",
+ "data_syncing_failed": "Anillo de Arándano #{0}: El proceso de sincronización de datos ha fallado. ❌",
+ "blueberry_hooks": "Ganchos de Arándano",
+ "message": "Mensaje"
}
\ No newline at end of file
diff --git a/assets/flutter_i18n/fr.json b/assets/flutter_i18n/fr.json
index 6f7a6d6c..4a6f6bd6 100644
--- a/assets/flutter_i18n/fr.json
+++ b/assets/flutter_i18n/fr.json
@@ -417,5 +417,41 @@
"save_locally": "Enregistrez localement",
"save_locally_description": "Assurez-vous de sauvegarder vos clés localement et ensuite vous êtes prêt à partir.",
"axs_storage_permission_use_case": "Le portefeuille AXS a besoin d'accéder à votre stockage pour des fonctionnalités telles que RWA. Vos données de stockage ne sont pas sauvegardées ni partagées avec des tiers.",
- "local_backup": "Sauvegarde locale"
+ "local_backup": "Sauvegarde locale",
+ "no_description": "Aucune description",
+ "bluetooth_not_supported": "Le Bluetooth n'est pas pris en charge sur cet appareil.",
+ "blueberry_ring_hooks": "Les Crochets de l'Anneau de Myrtille",
+ "nearby_blueberry_rings": "Anneaux de myrtille à proximité",
+ "unable_to_continue_bluetooth_is_turned_off": "Impossible de continuer, le Bluetooth est éteint!",
+ "scanning": "Analyse en cours...",
+ "blueberry_ring_inactive_alert_title": "Il est temps d'aller se promener!",
+ "blueberry_ring_inactive_alert_text": "Vous avez été inactif pendant un moment. Une petite promenade pourrait être vraiment revigorante!",
+ "blueberry_ring_sleep_alert_title": "Vous avez eu moins de sommeil profond que d'habitude. Essayez de vous détendre 30 minutes plus tôt ce soir pour un meilleur repos.",
+ "blueberry_ring_sleep_alert_text": "Vous avez eu moins de sommeil profond que d'habitude. Essayez de vous détendre 30 minutes plus tôt ce soir pour un meilleur repos.",
+ "blueberry_ring_heart_rate_alert_title": "Votre rythme cardiaque a été inhabituellement élevé aujourd'hui. Si vous ne vous sentez pas bien, il pourrait être bon de consulter un médecin.",
+ "blueberry_ring_heart_rate_alert_text": "Votre rythme cardiaque a été inhabituellement élevé aujourd'hui. Si vous ne vous sentez pas bien, il pourrait être bon de consulter un médecin.",
+ "blueberry_ring_battery_alert_title": "La batterie est faible!",
+ "blueberry_ring_battery_alert_text": "La batterie de votre Blueberry Ring est faible. Veuillez la charger bientôt pour éviter les lacunes de données.",
+ "activity_reminder": "Rappel d'activité",
+ "sleep_insight": "Aperçu du sommeil",
+ "heart_alert": "Alerte de coeur",
+ "low_battery": "Batterie faible",
+ "blueberry_background_notifications_requirements_title": "Ce service nécessite:",
+ "blueberry_background_notifications_requirements_text_1": "1. Bluetooth doit être activé",
+ "blueberry_background_notifications_requirements_text_2": "2. Blueberry doit être accessible",
+ "auto_sync_started": "Le démarrage automatique de la synchronisation de Blueberry Ring a commencé 🏁",
+ "no_rings_selected_notification_title": "On dirait que vous n'avez sélectionné aucun Anneaux de Myrtille. ℹ️",
+ "no_rings_selected_notification_text": "Veuillez vous rendre sur Blueberry Ring DApp pour sélectionner des anneaux.",
+ "auto_sync_successful_notification_title": "La revendication automatique de Blueberry Ring a réussi ✅",
+ "auto_sync_successful_notification_text": "Le portefeuille AXS a synchronisé avec succès vos données de ring.",
+ "already_synced_notification_title": "Oups, déjà synchronisé ℹ️",
+ "already_synced_notification_text": "Le portefeuille AXS a essayé de synchroniser vos données de ring, mais il semble que vous ayez déjà synchronisé les données de ring aujourd'hui.",
+ "auto_sync_failed": "L'auto-synchronisation de Blueberry Ring a échoué ❌",
+ "no_rings_owned_notification": "On dirait que ce portefeuille ne possède pas de Blueberry Rings. ℹ️",
+ "syncing_data_from_ring": "Synchronisation des données à partir de Blueberry Ring #{0}. ⛏️",
+ "already_synced_ring": "Anneau de Myrtille #{0} : Déjà synchronisé. ℹ️",
+ "data_synced_successfully_ring": "Bague de Myrtille #{0} : Données synchronisées avec succès. ✅",
+ "data_syncing_failed": "Bague de Myrtille #{0} : Le processus de synchronisation des données a échoué. ❌",
+ "blueberry_hooks": "Crochets de Myrtille",
+ "message": "Message"
}
\ No newline at end of file
diff --git a/assets/flutter_i18n/id.json b/assets/flutter_i18n/id.json
index bfcf324c..2d49ae80 100644
--- a/assets/flutter_i18n/id.json
+++ b/assets/flutter_i18n/id.json
@@ -417,5 +417,41 @@
"save_locally": "Simpan secara lokal",
"save_locally_description": "Pastikan untuk menyimpan kunci Anda secara lokal dan kemudian Anda siap untuk melanjutkan.",
"axs_storage_permission_use_case": "Dompet AXS membutuhkan akses ke penyimpanan Anda untuk fitur seperti RWA. Data penyimpanan Anda tidak disimpan atau dibagikan dengan pihak ketiga mana pun.",
- "local_backup": "Cadangan lokal"
+ "local_backup": "Cadangan lokal",
+ "no_description": "Tidak ada deskripsi",
+ "bluetooth_not_supported": "Bluetooth tidak didukung pada perangkat ini.",
+ "blueberry_ring_hooks": "Blueberry Ring mengaitkan",
+ "nearby_blueberry_rings": "Cincin Blueberry terdekat",
+ "unable_to_continue_bluetooth_is_turned_off": "Tidak dapat melanjutkan, Bluetooth dimatikan!",
+ "scanning": "Memindai ...",
+ "blueberry_ring_inactive_alert_title": "Waktunya untuk pergi berjalan-jalan!",
+ "blueberry_ring_inactive_alert_text": "Anda telah tidak aktif untuk beberapa waktu. Sebuah jalan kaki singkat bisa sangat menyegarkan!",
+ "blueberry_ring_sleep_alert_title": "Anda telah mendapatkan tidur nyenyak yang lebih sedikit dari biasanya. Coba santai 30 menit lebih awal malam ini untuk istirahat yang lebih baik.",
+ "blueberry_ring_sleep_alert_text": "Anda telah mendapatkan tidur nyenyak yang lebih sedikit dari biasanya. Coba santai 30 menit lebih awal malam ini untuk istirahat yang lebih baik.",
+ "blueberry_ring_heart_rate_alert_title": "Denyut jantung Anda hari ini tidak biasanya tinggi. Jika Anda merasa tidak sehat, mungkin baik untuk berkonsultasi dengan dokter.",
+ "blueberry_ring_heart_rate_alert_text": "Detak jantung Anda hari ini tidak biasanya tinggi. Jika Anda merasa tidak sehat, mungkin baik untuk berkonsultasi dengan dokter.",
+ "blueberry_ring_battery_alert_title": "Baterai sedang lemah!",
+ "blueberry_ring_battery_alert_text": "Baterai Cincin Blueberry Anda rendah. Silakan isi ulang segera untuk menghindari kekosongan data.",
+ "activity_reminder": "Pengingat aktivitas",
+ "sleep_insight": "Wawasan tidur",
+ "heart_alert": "Peringatan jantung",
+ "low_battery": "Baterai rendah",
+ "blueberry_background_notifications_requirements_title": "Layanan ini memerlukan:",
+ "blueberry_background_notifications_requirements_text_1": "1. Bluetooth harus dalam keadaan ON",
+ "blueberry_background_notifications_requirements_text_2": "2. Blueberry harus dapat dijangkau",
+ "auto_sync_started": "Sinkronisasi otomatis Blueberry Ring telah dimulai 🏁",
+ "no_rings_selected_notification_title": "Sepertinya Anda belum memilih Blueberry Rings apa pun. ℹ️",
+ "no_rings_selected_notification_text": "Silakan menuju ke Blueberry Ring DApp untuk memilih cincin.",
+ "auto_sync_successful_notification_title": "Berhasil klaim otomatis Blueberry Ring ✅",
+ "auto_sync_successful_notification_text": "Dompet AXS telah berhasil menyinkronkan data cincin Anda.",
+ "already_synced_notification_title": "Ups, Sudah disinkronkan ℹ️",
+ "already_synced_notification_text": "Dompet AXS mencoba untuk menyinkronkan data ring Anda, Tetapi tampaknya Anda telah menyinkronkan data ring hari ini.",
+ "auto_sync_failed": "Gagal sinkronisasi otomatis Blueberry Ring ❌",
+ "no_rings_owned_notification": "Sepertinya dompet ini tidak memiliki Blueberry Rings. ℹ️",
+ "syncing_data_from_ring": "Menyinkronkan data dari Blueberry Ring #{0}. ⛏️",
+ "already_synced_ring": "Cincin Blueberry #{0}: Sudah disinkronkan. ℹ️",
+ "data_synced_successfully_ring": "Cincin Blueberry #{0}: Data berhasil disinkronkan. ✅",
+ "data_syncing_failed": "Cincin Blueberry #{0}: Proses sinkronisasi data gagal. ❌",
+ "blueberry_hooks": "Kait Blueberry",
+ "message": "Pesan"
}
\ No newline at end of file
diff --git a/assets/flutter_i18n/it.json b/assets/flutter_i18n/it.json
index 55d88b5a..0c3cfbe8 100644
--- a/assets/flutter_i18n/it.json
+++ b/assets/flutter_i18n/it.json
@@ -417,5 +417,41 @@
"save_locally": "Salva localmente",
"save_locally_description": "Assicurati di salvare le tue chiavi localmente e poi sei pronto per andare.",
"axs_storage_permission_use_case": "Il portafoglio AXS necessita l'accesso al tuo spazio di archiviazione per funzioni come RWA. I tuoi dati di archiviazione non vengono salvati o condivisi con terze parti.",
- "local_backup": "Backup locale"
+ "local_backup": "Backup locale",
+ "no_description": "Nessuna descrizione",
+ "bluetooth_not_supported": "Il Bluetooth non è supportato su questo dispositivo.",
+ "blueberry_ring_hooks": "Ganci dell'Anello di Mirtilli",
+ "nearby_blueberry_rings": "Anelli di mirtilli nelle vicinanze",
+ "unable_to_continue_bluetooth_is_turned_off": "Impossibile continuare, il Bluetooth è spento!",
+ "scanning": "Scansione in corso...",
+ "blueberry_ring_inactive_alert_title": "È ora di fare una passeggiata!",
+ "blueberry_ring_inactive_alert_text": "Sei stato inattivo per un po'. Una breve passeggiata potrebbe essere davvero rinvigorente!",
+ "blueberry_ring_sleep_alert_title": "Hai avuto meno sonno profondo del solito. Prova a rilassarti 30 minuti prima stasera per un riposo migliore.",
+ "blueberry_ring_sleep_alert_text": "Hai avuto meno sonno profondo del solito. Prova a rilassarti 30 minuti prima stasera per un riposo migliore.",
+ "blueberry_ring_heart_rate_alert_title": "La tua frequenza cardiaca è stata insolitamente alta oggi. Se ti senti male, potrebbe essere utile consultare un medico.",
+ "blueberry_ring_heart_rate_alert_text": "La tua frequenza cardiaca è stata insolitamente alta oggi. Se ti senti male, potrebbe essere utile consultare un medico.",
+ "blueberry_ring_battery_alert_title": "La batteria è scarica!",
+ "blueberry_ring_battery_alert_text": "La batteria del tuo Blueberry Ring è scarica. Si prega di ricaricare presto per evitare interruzioni dei dati.",
+ "activity_reminder": "Promemoria di attività",
+ "sleep_insight": "Intuizione del sonno",
+ "heart_alert": "Allerta cuore",
+ "low_battery": "Batteria scarica",
+ "blueberry_background_notifications_requirements_title": "Questo servizio richiede:",
+ "blueberry_background_notifications_requirements_text_1": "1. Bluetooth da attivare",
+ "blueberry_background_notifications_requirements_text_2": "2. Blueberry deve essere raggiungibile",
+ "auto_sync_started": "L'avvio della sincronizzazione automatica di Blueberry Ring è iniziato 🏁",
+ "no_rings_selected_notification_title": "Sembra che non hai selezionato nessun Blueberry Rings. ℹ️",
+ "no_rings_selected_notification_text": "Si prega di recarsi alla DApp Blueberry Ring per selezionare gli anelli.",
+ "auto_sync_successful_notification_title": "Il reclamo automatico di Blueberry Ring è stato eseguito con successo ✅",
+ "auto_sync_successful_notification_text": "Il tuo portafoglio AXS ha sincronizzato con successo i tuoi dati dell'anello.",
+ "already_synced_notification_title": "Ops, già sincronizzato ℹ️",
+ "already_synced_notification_text": "Il portafoglio AXS ha cercato di sincronizzare i tuoi dati del ring, ma sembra che tu abbia già sincronizzato i dati del ring oggi.",
+ "auto_sync_failed": "Blueberry Ring auto-sync non riuscita ❌",
+ "no_rings_owned_notification": "Sembra che questo portafoglio non possieda nessun Blueberry Rings. ℹ️",
+ "syncing_data_from_ring": "Sincronizzazione dei dati da Blueberry Ring #{0}. ⛏️",
+ "already_synced_ring": "Anello di Mirtilli #{0}: Già sincronizzato. ℹ️",
+ "data_synced_successfully_ring": "Anello di Mirtilli #{0}: Dati sincronizzati con successo. ✅",
+ "data_syncing_failed": "Anello di Mirtilli #{0}: Il processo di sincronizzazione dei dati è fallito. ❌",
+ "blueberry_hooks": "Ganci Blueberry",
+ "message": "Messaggio"
}
\ No newline at end of file
diff --git a/assets/flutter_i18n/ja.json b/assets/flutter_i18n/ja.json
index f9694cf6..59f634b0 100644
--- a/assets/flutter_i18n/ja.json
+++ b/assets/flutter_i18n/ja.json
@@ -417,5 +417,41 @@
"save_locally": "ローカルに保存する",
"save_locally_description": "あなたのキーをローカルに保存してから、準備が整ったら始めてください。",
"axs_storage_permission_use_case": "AXSウォレットは、RWAのような機能のためにあなたのストレージへのアクセスが必要です。あなたのストレージデータは保存されず、また第三者と共有されません。",
- "local_backup": "ローカルバックアップ"
+ "local_backup": "ローカルバックアップ",
+ "no_description": "説明なし",
+ "bluetooth_not_supported": "このデバイスではBluetoothはサポートされていません。",
+ "blueberry_ring_hooks": "ブルーベリーリングフック",
+ "nearby_blueberry_rings": "近くのブルーベリーリング",
+ "unable_to_continue_bluetooth_is_turned_off": "Bluetoothがオフになっているため、続行できません!",
+ "scanning": "スキャン中...",
+ "blueberry_ring_inactive_alert_title": "散歩に行く時間です!",
+ "blueberry_ring_inactive_alert_text": "あなたはしばらく活動していませんでした。短い散歩は本当に元気づけることができますよ!",
+ "blueberry_ring_sleep_alert_title": "あなたは通常より深い眠りを得ていないようです。今夜は30分早くリラックスして、より良い休息を取るように試みてください。",
+ "blueberry_ring_sleep_alert_text": "あなたは通常より深い眠りを得ていないようです。今夜は30分早くリラックスして、より良い休息を取るように試みてください。",
+ "blueberry_ring_heart_rate_alert_title": "あなたの心拍数は今日、普段よりも高いです。体調が悪い場合、医者に診てもらうと良いかもしれません。",
+ "blueberry_ring_heart_rate_alert_text": "あなたの心拍数は今日異常に高いです。体調が悪い場合、医者に診てもらうと良いかもしれません。",
+ "blueberry_ring_battery_alert_title": "バッテリーが少ないです!",
+ "blueberry_ring_battery_alert_text": "あなたのブルーベリーリングのバッテリーが低くなっています。データの欠損を避けるために、早めに充電してください。",
+ "activity_reminder": "活動リマインダー",
+ "sleep_insight": "睡眠の洞察",
+ "heart_alert": "ハートアラート",
+ "low_battery": "バッテリー残量が少ない",
+ "blueberry_background_notifications_requirements_title": "このサービスには以下が必要です:",
+ "blueberry_background_notifications_requirements_text_1": "1. ブルートゥースをONにする",
+ "blueberry_background_notifications_requirements_text_2": "2. ブルーベリーが到達可能になること",
+ "auto_sync_started": "ブルーベリーリングの自動同期が開始されました 🏁",
+ "no_rings_selected_notification_title": "あなたはまだブルーベリーリングを選んでいないようです。ℹ️",
+ "no_rings_selected_notification_text": "リングの選択のために、ブルーベリーリングDAppに移動してください。",
+ "auto_sync_successful_notification_title": "ブルーベリーリングの自動請求が成功しました✅",
+ "auto_sync_successful_notification_text": "AXSウォレットはあなたのリングデータを正常に同期しました。",
+ "already_synced_notification_title": "おっと、すでに同期済みℹ️",
+ "already_synced_notification_text": "AXSウォレットはあなたのリングデータを同期しようと試みましたが、今日すでにリングデータを同期しているようです。",
+ "auto_sync_failed": "ブルーベリーリングの自動同期が失敗しました❌",
+ "no_rings_owned_notification": "このウォレットはブルーベリーリングを一つも所有していないようです。ℹ️",
+ "syncing_data_from_ring": "ブルーベリーリング #{0} からのデータ同期中。⛏️",
+ "already_synced_ring": "ブルーベリーリング #{0}: すでに同期済みです。ℹ️",
+ "data_synced_successfully_ring": "ブルーベリーリング #{0}: データは正常に同期されました。✅",
+ "data_syncing_failed": "ブルーベリーリング #{0}: データ同期処理が失敗しました。❌",
+ "blueberry_hooks": "ブルーベリーフック",
+ "message": "メッセージ"
}
\ No newline at end of file
diff --git a/assets/flutter_i18n/ko.json b/assets/flutter_i18n/ko.json
index 08159005..b94e64c0 100644
--- a/assets/flutter_i18n/ko.json
+++ b/assets/flutter_i18n/ko.json
@@ -417,5 +417,41 @@
"save_locally": "로컬에 저장",
"save_locally_description": "당신의 키를 로컬에 저장하십시오, 그런 다음 시작하면 됩니다.",
"axs_storage_permission_use_case": "AXS 지갑은 RWA와 같은 기능을 위해 당신의 저장 공간에 접근할 필요가 있습니다. 당신의 저장 데이터는 어떠한 제3자와도 저장되거나 공유되지 않습니다.",
- "local_backup": "로컬 백업"
+ "local_backup": "로컬 백업",
+ "no_description": "설명 없음",
+ "bluetooth_not_supported": "이 장치는 블루투스를 지원하지 않습니다.",
+ "blueberry_ring_hooks": "블루베리 링 훅",
+ "nearby_blueberry_rings": "근처의 블루베리 반지",
+ "unable_to_continue_bluetooth_is_turned_off": "Bluetooth가 꺼져 있어 계속할 수 없습니다!",
+ "scanning": "스캔 중 ...",
+ "blueberry_ring_inactive_alert_title": "산책하러 가봐야 할 시간이에요!",
+ "blueberry_ring_inactive_alert_text": "당신은 잠시동안 비활동적이었습니다. 짧은 산책은 정말로 활력을 불어넣을 수 있습니다!",
+ "blueberry_ring_sleep_alert_title": "보통보다 깊은 수면을 덜 취하고 있습니다. 오늘 밤에는 30분 일찍 푹 쉬기 위해 풀어보세요.",
+ "blueberry_ring_sleep_alert_text": "보통보다 깊은 수면을 적게 취하고 있습니다. 오늘 밤에는 30분 더 일찍 푹 쉬어 보세요.",
+ "blueberry_ring_heart_rate_alert_title": "오늘 당신의 심박수가 평소보다 높게 측정되었습니다. 몸 상태가 좋지 않다면, 의사에게 상담하는 것이 좋을 수 있습니다.",
+ "blueberry_ring_heart_rate_alert_text": "오늘 당신의 심박수가 평소보다 높게 측정되었습니다. 몸 상태가 좋지 않다면, 의사에게 상담하는 것이 좋을 수 있습니다.",
+ "blueberry_ring_battery_alert_title": "배터리가 부족합니다!",
+ "blueberry_ring_battery_alert_text": "당신의 블루베리 링의 배터리가 부족합니다. 데이터 손실을 피하기 위해 곧 충전해 주세요.",
+ "activity_reminder": "활동 알림",
+ "sleep_insight": "수면 통찰력",
+ "heart_alert": "하트 알림",
+ "low_battery": "낮은 배터리",
+ "blueberry_background_notifications_requirements_title": "이 서비스는 다음을 필요로 합니다:",
+ "blueberry_background_notifications_requirements_text_1": "블루투스를 켜주세요.",
+ "blueberry_background_notifications_requirements_text_2": "2. 블루베리가 접근 가능해야 합니다",
+ "auto_sync_started": "블루베리 링 자동 동기화 시작됨 🏁",
+ "no_rings_selected_notification_title": "보아하니 당신은 어떠한 블루베리 링도 선택하지 않은 것 같습니다. ℹ️",
+ "no_rings_selected_notification_text": "다음의 반지를 선택하기 위해 Blueberry Ring DApp으로 이동해 주세요.",
+ "auto_sync_successful_notification_title": "블루베리 링 자동-클레임 성공 ✅",
+ "auto_sync_successful_notification_text": "다음의 영어 텍스트를 한국어로 번역하면: AXS 지갑이 성공적으로 당신의 링 데이터를 동기화했습니다.",
+ "already_synced_notification_title": "이런, 이미 동기화되었습니다 ℹ️",
+ "already_synced_notification_text": "AXS 지갑이 귀하의 링 데이터를 동기화하려고 시도했지만, 오늘 이미 링 데이터를 동기화한 것 같습니다.",
+ "auto_sync_failed": "블루베리 링 자동 동기화 실패 ❌",
+ "no_rings_owned_notification": "이 지갑은 블루베리 링을 소유하고 있지 않은 것 같습니다. ℹ️",
+ "syncing_data_from_ring": "블루베리 링 #{0}에서 데이터 동기화 중. ⛏️",
+ "already_synced_ring": "블루베리 링 #{0}: 이미 동기화되었습니다. ℹ️",
+ "data_synced_successfully_ring": "블루베리 링 #{0}: 데이터 동기화 성공적으로 완료. ✅",
+ "data_syncing_failed": "블루베리 링 #{0}: 데이터 동기화 과정이 실패했습니다. ❌",
+ "blueberry_hooks": "블루베리 훅스",
+ "message": "메시지"
}
\ No newline at end of file
diff --git a/assets/flutter_i18n/nl.json b/assets/flutter_i18n/nl.json
index 1b92693e..ee0b0e12 100644
--- a/assets/flutter_i18n/nl.json
+++ b/assets/flutter_i18n/nl.json
@@ -417,5 +417,41 @@
"save_locally": "Lokaal opslaan",
"save_locally_description": "Zorg ervoor dat je je sleutels lokaal opslaat en dan ben je klaar om te gaan.",
"axs_storage_permission_use_case": "De AXS Wallet heeft toegang tot uw opslag nodig voor functies zoals RWA. Uw opslaggegevens worden niet opgeslagen of gedeeld met derden.",
- "local_backup": "Lokale back-up"
+ "local_backup": "Lokale back-up",
+ "no_description": "Geen beschrijving",
+ "bluetooth_not_supported": "Bluetooth wordt niet ondersteund op dit apparaat.",
+ "blueberry_ring_hooks": "Blueberry Ring haken",
+ "nearby_blueberry_rings": "In de buurt Blauwe bessenringen",
+ "unable_to_continue_bluetooth_is_turned_off": "Kan niet doorgaan, Bluetooth is uitgeschakeld!",
+ "scanning": "Scannen ...",
+ "blueberry_ring_inactive_alert_title": "Tijd om te gaan wandelen!",
+ "blueberry_ring_inactive_alert_text": "Je bent een tijdje inactief geweest. Een korte wandeling kan echt verkwikkend zijn!",
+ "blueberry_ring_sleep_alert_title": "Je hebt de laatste tijd minder diepe slaap gekregen dan normaal. Probeer vanavond 30 minuten eerder te ontspannen voor een betere rust.",
+ "blueberry_ring_sleep_alert_text": "Je hebt de laatste tijd minder diepe slaap gekregen dan normaal. Probeer vanavond 30 minuten eerder te ontspannen voor een betere rust.",
+ "blueberry_ring_heart_rate_alert_title": "Uw hartslag is vandaag ongewoon hoog geweest. Als u zich niet goed voelt, kan het goed zijn om een arts te raadplegen.",
+ "blueberry_ring_heart_rate_alert_text": "Uw hartslag is vandaag ongewoon hoog geweest. Als u zich niet goed voelt, kan het goed zijn om een arts te raadplegen.",
+ "blueberry_ring_battery_alert_title": "De batterij is bijna leeg!",
+ "blueberry_ring_battery_alert_text": "De batterij van je Blueberry Ring is bijna leeg. Laad deze alstublieft snel op om datagaten te voorkomen.",
+ "activity_reminder": "Activiteit herinnering",
+ "sleep_insight": "Slaapinzicht",
+ "heart_alert": "Hart alarm",
+ "low_battery": "Lage batterij",
+ "blueberry_background_notifications_requirements_title": "Deze service vereist:",
+ "blueberry_background_notifications_requirements_text_1": "1. Bluetooth moet AAN zijn",
+ "blueberry_background_notifications_requirements_text_2": "2. Blueberry moet bereikbaar zijn",
+ "auto_sync_started": "Blueberry Ring auto sync gestart 🏁",
+ "no_rings_selected_notification_title": "Het lijkt erop dat je nog geen Blueberry Rings hebt geselecteerd. ℹ️",
+ "no_rings_selected_notification_text": "Ga alstublieft naar Blueberry Ring DApp om ringen te selecteren.",
+ "auto_sync_successful_notification_title": "Blueberry Ring aut-claim succesvol ✅",
+ "auto_sync_successful_notification_text": "Uw AXS-portemonnee heeft met succes uw ringgegevens gesynchroniseerd.",
+ "already_synced_notification_title": "Oeps, al gesynchroniseerd ℹ️",
+ "already_synced_notification_text": "De AXS-portemonnee probeerde je ringgegevens te synchroniseren, maar het lijkt erop dat je de ringgegevens vandaag al hebt gesynchroniseerd.",
+ "auto_sync_failed": "Blueberry Ring auto-sync mislukt ❌",
+ "no_rings_owned_notification": "Het lijkt erop dat deze portemonnee geen Blueberry Rings bezit. ℹ️",
+ "syncing_data_from_ring": "Gegevens synchroniseren van Blueberry Ring #{0}. ⛏️",
+ "already_synced_ring": "Blueberry Ring #{0}: Al gesynchroniseerd. ℹ️",
+ "data_synced_successfully_ring": "Blueberry Ring #{0}: Gegevens succesvol gesynchroniseerd. ✅",
+ "data_syncing_failed": "Blueberry Ring #{0}: Gegevens synchronisatieproces mislukt. ❌",
+ "blueberry_hooks": "Blueberry haken",
+ "message": "Bericht"
}
\ No newline at end of file
diff --git a/assets/flutter_i18n/pt.json b/assets/flutter_i18n/pt.json
index ecf4d054..d7cebf1b 100644
--- a/assets/flutter_i18n/pt.json
+++ b/assets/flutter_i18n/pt.json
@@ -417,5 +417,41 @@
"save_locally": "Guarde localmente",
"save_locally_description": "Certifique-se de salvar suas chaves localmente e então você estará pronto para prosseguir.",
"axs_storage_permission_use_case": "A carteira AXS precisa de acesso ao seu armazenamento para recursos como RWA. Seus dados de armazenamento não são salvos ou compartilhados com terceiros.",
- "local_backup": "Backup local"
+ "local_backup": "Backup local",
+ "no_description": "Sem descrição",
+ "bluetooth_not_supported": "O Bluetooth não é suportado neste dispositivo.",
+ "blueberry_ring_hooks": "Blueberry Ring se conecta",
+ "nearby_blueberry_rings": "Anéis de Mirtilo Próximos",
+ "unable_to_continue_bluetooth_is_turned_off": "Não é possível continuar, o Bluetooth está desligado!",
+ "scanning": "Analisando ...",
+ "blueberry_ring_inactive_alert_title": "Hora de ir para uma caminhada!",
+ "blueberry_ring_inactive_alert_text": "Você esteve inativo por um tempo. Uma curta caminhada poderia ser realmente revigorante!",
+ "blueberry_ring_sleep_alert_title": "Você tem tido menos sono profundo do que o habitual. Tente relaxar 30 minutos mais cedo esta noite para um descanso melhor.",
+ "blueberry_ring_sleep_alert_text": "Você tem tido menos sono profundo do que o habitual. Tente relaxar 30 minutos mais cedo esta noite para um descanso melhor.",
+ "blueberry_ring_heart_rate_alert_title": "A sua frequência cardíaca tem estado anormalmente alta hoje. Se você se sentir mal, pode ser bom consultar um médico.",
+ "blueberry_ring_heart_rate_alert_text": "A sua frequência cardíaca tem estado anormalmente alta hoje. Se você se sentir mal, pode ser bom consultar um médico.",
+ "blueberry_ring_battery_alert_title": "A bateria está fraca!",
+ "blueberry_ring_battery_alert_text": "A bateria do seu Anel Blueberry está baixa. Por favor, carregue em breve para evitar lacunas de dados.",
+ "activity_reminder": "Lembrete de atividade",
+ "sleep_insight": "Percepção do sono",
+ "heart_alert": "Alerta de coração",
+ "low_battery": "Bateria baixa",
+ "blueberry_background_notifications_requirements_title": "Este serviço requer:",
+ "blueberry_background_notifications_requirements_text_1": "1. Bluetooth para estar LIGADO",
+ "blueberry_background_notifications_requirements_text_2": "2. Blueberry para ser alcançável",
+ "auto_sync_started": "Início automático da sincronização do Blueberry Ring 🏁",
+ "no_rings_selected_notification_title": "Parece que você não selecionou nenhum Anel de Mirtilo. ℹ️",
+ "no_rings_selected_notification_text": "Por favor, vá para o DApp Blueberry Ring para selecionar anéis.",
+ "auto_sync_successful_notification_title": "Sucesso na auto-reivindicação do Blueberry Ring ✅",
+ "auto_sync_successful_notification_text": "A carteira AXS foi sincronizada com sucesso com os seus dados de anel.",
+ "already_synced_notification_title": "Ops, já sincronizado ℹ️",
+ "already_synced_notification_text": "A carteira AXS tentou sincronizar seus dados de anel, mas parece que você já sincronizou os dados do anel hoje.",
+ "auto_sync_failed": "Falha na sincronização automática do Blueberry Ring ❌",
+ "no_rings_owned_notification": "Parece que esta carteira não possui nenhum Blueberry Rings. ℹ️",
+ "syncing_data_from_ring": "Sincronizando dados do Anel de Mirtilo #{0}. ⛏️",
+ "already_synced_ring": "Anel de Mirtilo #{0}: Já sincronizado. ℹ️",
+ "data_synced_successfully_ring": "Anel de Mirtilo #{0}: Dados sincronizados com sucesso. ✅",
+ "data_syncing_failed": "Anel de Mirtilo #{0}: Processo de sincronização de dados falhou. ❌",
+ "blueberry_hooks": "Ganchos de Mirtilo",
+ "message": "Mensagem"
}
\ No newline at end of file
diff --git a/assets/flutter_i18n/ro.json b/assets/flutter_i18n/ro.json
index 677df4c4..4bde8ddb 100644
--- a/assets/flutter_i18n/ro.json
+++ b/assets/flutter_i18n/ro.json
@@ -417,5 +417,41 @@
"save_locally": "Salvează local",
"save_locally_description": "Asigurați-vă că salvați cheile local și apoi sunteți gata de plecare.",
"axs_storage_permission_use_case": "Portofelul AXS are nevoie de acces la spațiul tău de stocare pentru funcții precum RWA. Datele tale de stocare nu sunt salvate sau partajate cu terțe părți.",
- "local_backup": "Copie de rezervă locală"
+ "local_backup": "Copie de rezervă locală",
+ "no_description": "Fără descriere",
+ "bluetooth_not_supported": "Bluetooth nu este suportat pe acest dispozitiv.",
+ "blueberry_ring_hooks": "Cârligele Blueberry Ring",
+ "nearby_blueberry_rings": "Inele de afine din apropiere",
+ "unable_to_continue_bluetooth_is_turned_off": "Imposibil de continuat, Bluetooth este oprit!",
+ "scanning": "Scanare...",
+ "blueberry_ring_inactive_alert_title": "Timpul să mergem la plimbare!",
+ "blueberry_ring_inactive_alert_text": "Ai fost inactiv pentru o perioadă. O scurtă plimbare ar putea fi cu adevărat revigorantă!",
+ "blueberry_ring_sleep_alert_title": "Ai avut un somn mai puțin profund decât de obicei. Încearcă să te relaxezi cu 30 de minute mai devreme în seara asta pentru un odihnă mai bună.",
+ "blueberry_ring_sleep_alert_text": "Ai avut un somn mai puțin profund decât de obicei. Încearcă să te relaxezi cu 30 de minute mai devreme în seara asta pentru un odihnă mai bună.",
+ "blueberry_ring_heart_rate_alert_title": "Ritmul tău cardiac a fost neobișnuit de mare astăzi. Dacă te simți rău, ar fi bine să consulți un medic.",
+ "blueberry_ring_heart_rate_alert_text": "Ritmul cardiac a fost neobișnuit de ridicat astăzi. Dacă te simți rău, ar fi bine să consulți un medic.",
+ "blueberry_ring_battery_alert_title": "Bateria este descărcată!",
+ "blueberry_ring_battery_alert_text": "Bateria inelului tău Blueberry este descărcată. Te rugăm să o încarci în curând pentru a evita lacunele de date.",
+ "activity_reminder": "Reamintire de activitate",
+ "sleep_insight": "Intuiție despre somn",
+ "heart_alert": "Alertă de inimă",
+ "low_battery": "Baterie scăzută",
+ "blueberry_background_notifications_requirements_title": "Acest serviciu necesită:",
+ "blueberry_background_notifications_requirements_text_1": "1. Bluetooth să fie PORNIT",
+ "blueberry_background_notifications_requirements_text_2": "2. Blueberry să fie accesibil",
+ "auto_sync_started": "Sincronizarea automată a Blueberry Ring a început 🏁",
+ "no_rings_selected_notification_title": "Se pare că nu ai selectat niciun Inel de Afine. ℹ️",
+ "no_rings_selected_notification_text": "Vă rugăm să mergeți la Blueberry Ring DApp pentru a selecta inelele.",
+ "auto_sync_successful_notification_title": "Succes la auto-revendicarea Blueberry Ring ✅",
+ "auto_sync_successful_notification_text": "Portofelul AXS a sincronizat cu succes datele inelului tău.",
+ "already_synced_notification_title": "Ups, deja sincronizat ℹ️",
+ "already_synced_notification_text": "Portofelul AXS a încercat să sincronizeze datele tale de inel, dar se pare că ai sincronizat deja datele de inel astăzi.",
+ "auto_sync_failed": "Eșec la sincronizarea automată a Blueberry Ring ❌",
+ "no_rings_owned_notification": "Se pare că acest portofel nu deține niciun Blueberry Rings. ℹ️",
+ "syncing_data_from_ring": "Sincronizare date de la Blueberry Ring #{0}. ⛏️",
+ "already_synced_ring": "Inelul Blueberry #{0}: Deja sincronizat. ℹ️",
+ "data_synced_successfully_ring": "Inelul Blueberry #{0}: Datele au fost sincronizate cu succes. ✅",
+ "data_syncing_failed": "Inelul Blueberry #{0}: Procesul de sincronizare a datelor a eșuat. ❌",
+ "blueberry_hooks": "Cârlige de afine",
+ "message": "Mesaj"
}
\ No newline at end of file
diff --git a/assets/flutter_i18n/ru.json b/assets/flutter_i18n/ru.json
index 63997a0c..60060a13 100644
--- a/assets/flutter_i18n/ru.json
+++ b/assets/flutter_i18n/ru.json
@@ -417,5 +417,41 @@
"save_locally": "Сохранить локально",
"save_locally_description": "Убедитесь, что вы сохранили свои ключи локально, и тогда вы готовы к работе.",
"axs_storage_permission_use_case": "Кошелек AXS требует доступа к вашему хранилищу для функций, таких как RWA. Ваши данные хранения не сохраняются и не передаются третьим сторонам.",
- "local_backup": "Локальное резервное копирование"
+ "local_backup": "Локальное резервное копирование",
+ "no_description": "Нет описания",
+ "bluetooth_not_supported": "Bluetooth не поддерживается на этом устройстве.",
+ "blueberry_ring_hooks": "Крючки Blueberry Ring",
+ "nearby_blueberry_rings": "Ближайшие кольца Блуберри",
+ "unable_to_continue_bluetooth_is_turned_off": "Невозможно продолжить, Bluetooth выключен!",
+ "scanning": "Сканирование...",
+ "blueberry_ring_inactive_alert_title": "Пора идти на прогулку!",
+ "blueberry_ring_inactive_alert_text": "Вы были неактивны некоторое время. Короткая прогулка могла бы быть действительно бодрящей!",
+ "blueberry_ring_sleep_alert_title": "Вы спите меньше глубокого сна, чем обычно. Попробуйте расслабиться на 30 минут раньше сегодня вечером для лучшего отдыха.",
+ "blueberry_ring_sleep_alert_text": "Вы спите меньше глубокого сна, чем обычно. Попробуйте расслабиться на 30 минут раньше сегодня вечером для лучшего отдыха.",
+ "blueberry_ring_heart_rate_alert_title": "Ваш пульс сегодня необычайно высок. Если вы чувствуете себя нездорово, может быть хорошей идеей обратиться к врачу.",
+ "blueberry_ring_heart_rate_alert_text": "Ваш пульс сегодня необычайно высокий. Если вы чувствуете себя плохо, было бы неплохо обратиться к врачу.",
+ "blueberry_ring_battery_alert_title": "Заряд батареи низкий!",
+ "blueberry_ring_battery_alert_text": "Заряд батареи вашего Blueberry Ring низкий. Пожалуйста, зарядите его в ближайшее время, чтобы избежать пропусков в данных.",
+ "activity_reminder": "Напоминание о активности",
+ "sleep_insight": "Инсайт во время сна",
+ "heart_alert": "Оповещение сердца",
+ "low_battery": "Низкий уровень заряда",
+ "blueberry_background_notifications_requirements_title": "Этот сервис требует:",
+ "blueberry_background_notifications_requirements_text_1": "1. Bluetooth должен быть включен",
+ "blueberry_background_notifications_requirements_text_2": "2. Blueberry должен быть доступен",
+ "auto_sync_started": "Автосинхронизация Blueberry Ring началась 🏁",
+ "no_rings_selected_notification_title": "Похоже, вы не выбрали ни одного Blueberry Rings. ℹ️",
+ "no_rings_selected_notification_text": "Пожалуйста, перейдите в Blueberry Ring DApp для выбора колец.",
+ "auto_sync_successful_notification_title": "Успешное авто-выполнение Blueberry Ring ✅",
+ "auto_sync_successful_notification_text": "Кошелек AXS успешно синхронизировал ваши данные кольца.",
+ "already_synced_notification_title": "Упс, уже синхронизировано ℹ️",
+ "already_synced_notification_text": "Кошелек AXS попытался синхронизировать ваши данные кольца, но похоже, что вы уже синхронизировали данные кольца сегодня.",
+ "auto_sync_failed": "Синхронизация Blueberry Ring не удалась ❌",
+ "no_rings_owned_notification": "Похоже, что в этом кошельке нет Blueberry Rings. ℹ️",
+ "syncing_data_from_ring": "Синхронизация данных с Blueberry Ring #{0}. ⛏️",
+ "already_synced_ring": "Черничное кольцо #{0}: Уже синхронизировано. ℹ️",
+ "data_synced_successfully_ring": "Кольцо Blueberry #{0}: Данные успешно синхронизированы. ✅",
+ "data_syncing_failed": "Черничное кольцо #{0}: Процесс синхронизации данных не удался. ❌",
+ "blueberry_hooks": "Крючки для черники",
+ "message": "Сообщение"
}
\ No newline at end of file
diff --git a/assets/flutter_i18n/tr.json b/assets/flutter_i18n/tr.json
index bc965e61..2ac40c4b 100644
--- a/assets/flutter_i18n/tr.json
+++ b/assets/flutter_i18n/tr.json
@@ -417,5 +417,41 @@
"save_locally": "Yerel olarak kaydet",
"save_locally_description": "Anahtarlarınızı yerel olarak kaydettiğinizden emin olun ve sonra işlem yapmaya hazırsınız.",
"axs_storage_permission_use_case": "AXS Cüzdanı, RWA gibi özellikler için depolama alanınıza erişim gerektirir. Depolama verileriniz hiçbir üçüncü taraf ile kaydedilmez veya paylaşılmaz.",
- "local_backup": "Yerel yedekleme"
+ "local_backup": "Yerel yedekleme",
+ "no_description": "Açıklama yok",
+ "bluetooth_not_supported": "Bu cihazda Bluetooth desteklenmiyor.",
+ "blueberry_ring_hooks": "Mavi Yüzük kanca",
+ "nearby_blueberry_rings": "Yakındaki Yaban Mersini halkaları",
+ "unable_to_continue_bluetooth_is_turned_off": "Devam edilemiyor, Bluetooth kapalı!",
+ "scanning": "Tarama ...",
+ "blueberry_ring_inactive_alert_title": "Yürüyüşe çıkma zamanı!",
+ "blueberry_ring_inactive_alert_text": "Bir süre boyunca hareketsiz kaldınız. Kısa bir yürüyüş gerçekten canlandırıcı olabilir!",
+ "blueberry_ring_sleep_alert_title": "Daha önceki dönemlere kıyasla daha az derin uyku uyuyorsunuz. Daha iyi dinlenmek için bu gece 30 dakika daha erken rahatlamayı deneyin.",
+ "blueberry_ring_sleep_alert_text": "Daha önceki dönemlere kıyasla daha az derin uyku uyuyorsunuz. Daha iyi dinlenmek için bu gece 30 dakika daha erken rahatlamayı deneyin.",
+ "blueberry_ring_heart_rate_alert_title": "Bugün kalp atış hızınız olağanın üzerinde yüksek olmuş. Kendinizi iyi hissetmiyorsanız, bir doktora başvurmanız iyi olabilir.",
+ "blueberry_ring_heart_rate_alert_text": "Bugün kalp atış hızınız olağanın üzerinde yüksek olmuş. Kendinizi iyi hissetmiyorsanız, bir doktora başvurmanız iyi olabilir.",
+ "blueberry_ring_battery_alert_title": "Pil düşük!",
+ "blueberry_ring_battery_alert_text": "Mavi Yüzük'ünüzün pil seviyesi düşük. Veri boşluklarını önlemek için lütfen yakında şarj edin.",
+ "activity_reminder": "Aktivite hatırlatıcısı",
+ "sleep_insight": "Uyku içgörüsü",
+ "heart_alert": "Kalp alarmı",
+ "low_battery": "Düşük pil",
+ "blueberry_background_notifications_requirements_title": "Bu hizmet şunları gerektirir:",
+ "blueberry_background_notifications_requirements_text_1": "Bluetooth'un AÇIK olması",
+ "blueberry_background_notifications_requirements_text_2": "2. Blueberry'nin ulaşılabilir olması",
+ "auto_sync_started": "Yabanmersini Halkası otomatik senkronizasyon başladı 🏁",
+ "no_rings_selected_notification_title": "Görünüşe göre herhangi bir Blueberry Rings seçmemişsiniz. ℹ️",
+ "no_rings_selected_notification_text": "Lütfen yüzük seçmek için Blueberry Ring DApp'e gidin.",
+ "auto_sync_successful_notification_title": "Blueberry Ring otomatik talep başarılı ✅",
+ "auto_sync_successful_notification_text": "AXS cüzdanınız başarıyla halka verilerinizi senkronize etti.",
+ "already_synced_notification_title": "Hata, Zaten senkronize edildi ℹ️",
+ "already_synced_notification_text": "AXS cüzdanınız yüzük verilerinizi senkronize etmeye çalıştı, ancak görünüşe göre yüzük verilerinizi bugün senkronize etmişsiniz.",
+ "auto_sync_failed": "Blueberry Ring otomatik senkronizasyon başarısız oldu ❌",
+ "no_rings_owned_notification": "Görünüşe göre bu cüzdanın hiç Mavi Yüzük'ü yok. ℹ️",
+ "syncing_data_from_ring": "Blueberry Ring #{0} 'dan veri senkronizasyonu yapılıyor. ⛏️",
+ "already_synced_ring": "Yaban Mersini Halkası #{0}: Zaten senkronize edildi. ℹ️",
+ "data_synced_successfully_ring": "Yaban Mersini Halkası #{0}: Veri başarıyla senkronize edildi. ✅",
+ "data_syncing_failed": "Yaban Mersini Halkası #{0}: Veri senkronizasyon işlemi başarısız oldu. ❌",
+ "blueberry_hooks": "Yaban Mersini Çengelleri",
+ "message": "Mesaj"
}
\ No newline at end of file
diff --git a/assets/flutter_i18n/vi.json b/assets/flutter_i18n/vi.json
index 7a03cf27..08224dc2 100644
--- a/assets/flutter_i18n/vi.json
+++ b/assets/flutter_i18n/vi.json
@@ -417,5 +417,41 @@
"save_locally": "Lưu trữ cục bộ",
"save_locally_description": "Hãy chắc chắn lưu lại khóa của bạn ở cục bộ và sau đó bạn đã sẵn sàng để tiếp tục.",
"axs_storage_permission_use_case": "Ví AXS cần quyền truy cập vào bộ nhớ của bạn để sử dụng các tính năng như RWA. Dữ liệu lưu trữ của bạn không được lưu lại hoặc chia sẻ với bất kỳ bên thứ ba nào.",
- "local_backup": "Sao lưu cục bộ"
+ "local_backup": "Sao lưu cục bộ",
+ "no_description": "Không mô tả",
+ "bluetooth_not_supported": "Bluetooth không được hỗ trợ trên thiết bị này.",
+ "blueberry_ring_hooks": "Móc Blueberry Ring",
+ "nearby_blueberry_rings": "Vòng Blueberry gần đây",
+ "unable_to_continue_bluetooth_is_turned_off": "Không thể tiếp tục vì Bluetooth đã bị tắt!",
+ "scanning": "Đang quét...",
+ "blueberry_ring_inactive_alert_title": "Đã đến lúc đi dạo!",
+ "blueberry_ring_inactive_alert_text": "Bạn đã không hoạt động trong một thời gian. Một cuộc dạo chơi ngắn có thể thực sự làm tăng sức sống!",
+ "blueberry_ring_sleep_alert_title": "Bạn đã có ít giấc ngủ sâu hơn so với bình thường. Hãy thử thư giãn 30 phút sớm hơn tối nay để có giấc ngủ tốt hơn.",
+ "blueberry_ring_sleep_alert_text": "Bạn đã ngủ không sâu như thường lệ. Hãy thử thư giãn 30 phút sớm hơn tối nay để có giấc ngủ tốt hơn.",
+ "blueberry_ring_heart_rate_alert_title": "Tốc độ nhịp tim của bạn hôm nay bất thường cao. Nếu bạn cảm thấy không khỏe, có thể nên đi kiểm tra với bác sĩ.",
+ "blueberry_ring_heart_rate_alert_text": "Tốc độ nhịp tim của bạn hôm nay bất thường cao. Nếu bạn cảm thấy không khỏe, có thể nên đi kiểm tra với bác sĩ.",
+ "blueberry_ring_battery_alert_title": "Pin đang yếu!",
+ "blueberry_ring_battery_alert_text": "Pin của Chiếc Nhẫn Blueberry của bạn đang yếu. Vui lòng sạc sớm để tránh mất dữ liệu.",
+ "activity_reminder": "Nhắc nhở hoạt động",
+ "sleep_insight": "Sự hiểu biết về giấc ngủ",
+ "heart_alert": "Cảnh báo tim",
+ "low_battery": "Pin yếu",
+ "blueberry_background_notifications_requirements_title": "Dịch vụ này yêu cầu:",
+ "blueberry_background_notifications_requirements_text_1": "1. Bluetooth cần được BẬT",
+ "blueberry_background_notifications_requirements_text_2": "2. Blueberry cần có thể tiếp cận được",
+ "auto_sync_started": "Bắt đầu tự động đồng bộ Blueberry Ring 🏁",
+ "no_rings_selected_notification_title": "Có vẻ như bạn chưa chọn bất kỳ Nhẫn Blueberry nào. ℹ️",
+ "no_rings_selected_notification_text": "Vui lòng đến ứng dụng phi tập trung Blueberry Ring để chọn nhẫn.",
+ "auto_sync_successful_notification_title": "Thành công trong việc tự động yêu cầu Blueberry Ring ✅",
+ "auto_sync_successful_notification_text": "Ví AXS đã đồng bộ thành công dữ liệu vòng của bạn",
+ "already_synced_notification_title": "Ồ, đã được đồng bộ hóa ℹ️",
+ "already_synced_notification_text": "Ví AXS đã cố gắng đồng bộ dữ liệu vòng của bạn, Nhưng có vẻ như bạn đã đồng bộ dữ liệu vòng hôm nay.",
+ "auto_sync_failed": "Vòng Blueberry tự đồng bộ thất bại ❌",
+ "no_rings_owned_notification": "Dường như ví này không sở hữu bất kỳ Blueberry Rings nào. ℹ️",
+ "syncing_data_from_ring": "Đồng bộ hóa dữ liệu từ Blueberry Ring #{0}. ⛏️",
+ "already_synced_ring": "Vòng Blueberry #{0}: Đã được đồng bộ. ℹ️",
+ "data_synced_successfully_ring": "Vòng Blueberry #{0}: Dữ liệu đã được đồng bộ thành công. ✅",
+ "data_syncing_failed": "Vòng Blueberry #{0}: Quá trình đồng bộ hóa dữ liệu thất bại. ❌",
+ "blueberry_hooks": "Móc Blueberry",
+ "message": "Tin nhắn"
}
\ No newline at end of file
diff --git a/assets/flutter_i18n/zh_CN.json b/assets/flutter_i18n/zh_CN.json
index 9c00f609..76df5729 100644
--- a/assets/flutter_i18n/zh_CN.json
+++ b/assets/flutter_i18n/zh_CN.json
@@ -417,5 +417,41 @@
"save_locally": "在本地保存",
"save_locally_description": "确保将您的密钥本地保存,然后就可以开始了。",
"axs_storage_permission_use_case": "AXS钱包需要访问您的存储空间以使用如RWA等功能。您的存储数据不会被保存或与任何第三方共享。",
- "local_backup": "本地备份"
+ "local_backup": "本地备份",
+ "no_description": "无描述",
+ "bluetooth_not_supported": "这个设备不支持蓝牙。",
+ "blueberry_ring_hooks": "蓝莓环钩",
+ "nearby_blueberry_rings": "附近的蓝莓环",
+ "unable_to_continue_bluetooth_is_turned_off": "无法继续,蓝牙已关闭!",
+ "scanning": "正在扫描...",
+ "blueberry_ring_inactive_alert_title": "是时候去散步了!",
+ "blueberry_ring_inactive_alert_text": "您已经有一段时间没有活动了。短暂的散步可能会让你精神焕发!",
+ "blueberry_ring_sleep_alert_title": "你最近的深度睡眠时间比平时少。尝试在今晚提前30分钟放松,以获得更好的休息。",
+ "blueberry_ring_sleep_alert_text": "你最近的深度睡眠时间比平时少。尝试在今晚提前30分钟放松,以获得更好的休息。",
+ "blueberry_ring_heart_rate_alert_title": "你今天的心率异常升高。如果你感觉不适,可能需要去看医生。",
+ "blueberry_ring_heart_rate_alert_text": "你今天的心率异常升高。如果你感到不适,最好去看医生。",
+ "blueberry_ring_battery_alert_title": "电池电量低!",
+ "blueberry_ring_battery_alert_text": "您的Blueberry Ring电池电量低。请尽快充电以避免数据断裂。",
+ "activity_reminder": "活动提醒",
+ "sleep_insight": "睡眠洞察",
+ "heart_alert": "心脏警报",
+ "low_battery": "电池电量低",
+ "blueberry_background_notifications_requirements_title": "此服务需要:",
+ "blueberry_background_notifications_requirements_text_1": "1. 需要打开蓝牙",
+ "blueberry_background_notifications_requirements_text_2": "2. 蓝莓需要能够被联系到",
+ "auto_sync_started": "蓝莓环自动同步开始🏁",
+ "no_rings_selected_notification_title": "看起来你还没有选择任何蓝莓环。ℹ️",
+ "no_rings_selected_notification_text": "请前往Blueberry Ring DApp选择戒指。",
+ "auto_sync_successful_notification_title": "蓝莓环自动领取成功 ✅",
+ "auto_sync_successful_notification_text": "AXS钱包已成功同步您的环数据。",
+ "already_synced_notification_title": "哎呀,已经同步ℹ️",
+ "already_synced_notification_text": "AXS钱包试图同步您的环数据,但看起来您今天已经同步了环数据。",
+ "auto_sync_failed": "蓝莓环自动同步失败 ❌",
+ "no_rings_owned_notification": "看起来这个钱包并未拥有任何蓝莓环币。ℹ️",
+ "syncing_data_from_ring": "从Blueberry Ring #{0}同步数据。⛏️",
+ "already_synced_ring": "蓝莓环 # {0}:已经同步。ℹ️",
+ "data_synced_successfully_ring": "蓝莓环 # {0}:数据同步成功。✅",
+ "data_syncing_failed": "蓝莓环#{0}:数据同步过程失败。❌",
+ "blueberry_hooks": "蓝莓钩子",
+ "message": "消息"
}
\ No newline at end of file
diff --git a/assets/flutter_i18n/zh_TW.json b/assets/flutter_i18n/zh_TW.json
index b78e8c40..295eb9bd 100644
--- a/assets/flutter_i18n/zh_TW.json
+++ b/assets/flutter_i18n/zh_TW.json
@@ -417,5 +417,41 @@
"save_locally": "在本地保存",
"save_locally_description": "確保將您的密鑰本地保存,然後就可以開始了。",
"axs_storage_permission_use_case": "AXS錢包需要訪問您的存储空間以使用如RWA等功能。您的存儲數據不會被保存或與任何第三方共享。",
- "local_backup": "本地備份"
+ "local_backup": "本地備份",
+ "no_description": "無描述",
+ "bluetooth_not_supported": "此設備不支援藍牙。",
+ "blueberry_ring_hooks": "藍莓環鉤",
+ "nearby_blueberry_rings": "附近的藍莓環",
+ "unable_to_continue_bluetooth_is_turned_off": "無法繼續,藍牙已關閉!",
+ "scanning": "正在掃描...",
+ "blueberry_ring_inactive_alert_title": "該去散步了!",
+ "blueberry_ring_inactive_alert_text": "您已經有一段時間沒有活動了。短暫的散步可能真的很振奮人心!",
+ "blueberry_ring_sleep_alert_title": "您最近的深度睡眠時間比平常少。試著今晚提早30分鐘放鬆心情,以獲得更好的休息。",
+ "blueberry_ring_sleep_alert_text": "您最近的深度睡眠時間比平常少。試著今晚提早30分鐘放鬆心情,以獲得更好的休息。",
+ "blueberry_ring_heart_rate_alert_title": "你今天的心率異常地高。如果你感到不適,可能需要去看醫生。",
+ "blueberry_ring_heart_rate_alert_text": "你今天的心率異常地高。如果你感到不適,可能需要去看醫生。",
+ "blueberry_ring_battery_alert_title": "電池電量低!",
+ "blueberry_ring_battery_alert_text": "您的Blueberry Ring電池電量低。請儘快充電以避免數據出現間隙。",
+ "activity_reminder": "活動提醒",
+ "sleep_insight": "睡眠洞察",
+ "heart_alert": "心臟警報",
+ "low_battery": "低電量",
+ "blueberry_background_notifications_requirements_title": "此服務需要:",
+ "blueberry_background_notifications_requirements_text_1": "1. 開啟藍牙",
+ "blueberry_background_notifications_requirements_text_2": "2. 藍莓需能被聯繫到",
+ "auto_sync_started": "藍莓環自動同步已開始 🏁",
+ "no_rings_selected_notification_title": "看起來您還沒有選擇任何藍莓環。ℹ️",
+ "no_rings_selected_notification_text": "請前往Blueberry Ring DApp選擇戒指。",
+ "auto_sync_successful_notification_title": "藍莓環自動索取成功 ✅",
+ "auto_sync_successful_notification_text": "AXS 錢包已成功同步您的環數據",
+ "already_synced_notification_title": "哎呀,已經同步了ℹ️",
+ "already_synced_notification_text": "AXS錢包嘗試同步您的環形數據,但看起來您今天已經同步了環形數據。",
+ "auto_sync_failed": "藍莓環自動同步失敗❌",
+ "no_rings_owned_notification": "看起來這個錢包並未擁有任何藍莓環。ℹ️",
+ "syncing_data_from_ring": "從Blueberry Ring #{0}同步數據。⛏️",
+ "already_synced_ring": "藍莓環#{0}:已經同步。ℹ️",
+ "data_synced_successfully_ring": "藍莓環#{0}:數據同步成功。✅",
+ "data_syncing_failed": "藍莓環#{0}:數據同步過程失敗。❌",
+ "blueberry_hooks": "藍莓鉤子",
+ "message": "訊息"
}
\ No newline at end of file
diff --git a/assets/js/bluetooth/bluetooth.js b/assets/js/bluetooth/bluetooth.js
new file mode 100644
index 00000000..6af8e4f5
--- /dev/null
+++ b/assets/js/bluetooth/bluetooth.js
@@ -0,0 +1,415 @@
+class BluetoothCharacteristicProperties {
+ constructor(
+ broadcast,
+ read,
+ writeWithoutResponse,
+ write,
+ notify,
+ indicate,
+ authenticatedSignedWrites,
+ reliableWrite,
+ writableAuxiliaries
+ ) {
+ this.broadcast = broadcast;
+ this.read = read;
+ this.writeWithoutResponse = writeWithoutResponse;
+ this.write = write;
+ this.notify = notify;
+ this.indicate = indicate;
+ this.authenticatedSignedWrites = authenticatedSignedWrites;
+ this.reliableWrite = reliableWrite;
+ this.writableAuxiliaries = writableAuxiliaries;
+ }
+}
+
+class BluetoothDevice extends EventTarget {
+ constructor(id, name, gatt, watchingAdvertisements) {
+ super();
+ this.id = id;
+ this.name = name;
+ this.gatt = gatt;
+ this.watchingAdvertisements = watchingAdvertisements;
+ }
+
+ async watchAdvertisements() {
+ console.log("BluetoothRemoteGATTServer:watchAdvertisements ");
+ const response = await window.axs?.callHandlerWrapper(
+ "BluetoothDevice.watchAdvertisements"
+ );
+ console.log(
+ "BluetoothRemoteGATTServer:watchAdvertisements ",
+ JSON.stringify(response, null, 2)
+ );
+ return response;
+ }
+
+ async forget() {
+ console.log("BluetoothRemoteGATTServer:forget ");
+ const response = await window.axs?.callHandlerWrapper("BluetoothDevice.forget");
+ console.log(
+ "BluetoothRemoteGATTServer:forget ",
+ JSON.stringify(response, null, 2)
+ );
+ return response;
+ }
+}
+
+class BluetoothRemoteGATTServer extends EventTarget {
+ constructor(device, connected) {
+ super();
+ this.device = device;
+ this.connected = connected;
+ }
+
+ async connect() {
+ console.log("BluetoothRemoteGATTServer:connect ");
+ const response = await window.axs?.callHandlerWrapper(
+ "BluetoothRemoteGATTServer.connect",
+ {}
+ );
+ console.log(
+ "BluetoothRemoteGATTServer:connect ",
+ JSON.stringify(response, null, 2)
+ );
+ this.device = response.device;
+ this.connected = response.connected;
+ return this;
+ }
+
+ async disconnect() {
+ console.log("BluetoothRemoteGATTServer:disconnect ");
+ await window.axs?.callHandlerWrapper("BluetoothRemoteGATTServer.disconnect", {});
+ }
+
+ async getPrimaryService(service) {
+ console.log("BluetoothRemoteGATTServer:getPrimaryService ", service);
+ const data = { service: service };
+ const response = await window.axs?.callHandlerWrapper(
+ "BluetoothRemoteGATTServer.getPrimaryService",
+ data
+ );
+ const respService = new BluetoothRemoteGATTService(
+ response.device,
+ response.uuid,
+ response.isPrimary
+ );
+ navigator.bluetooth.serviceArray.push(respService);
+ console.log(
+ "BluetoothRemoteGATTServer:getPrimaryService ",
+ JSON.stringify(response, null, 2)
+ );
+ return respService;
+ }
+
+ async getPrimaryServices(service) {
+ console.log("BluetoothRemoteGATTServer:getPrimaryServices ", service);
+ const data = { service: service };
+ const response = await window.axs?.callHandlerWrapper(
+ "BluetoothRemoteGATTServer.getPrimaryServices",
+ data
+ );
+ return response;
+ }
+}
+
+class BluetoothRemoteGATTCharacteristic extends EventTarget {
+ constructor(service, uuid, value, properties) {
+ super();
+ this.service = service;
+ this.uuid = uuid;
+ this.value = value;
+ this.properties = properties;
+ }
+
+ async getDescriptor(descriptor) {
+ console.log("BluetoothRemoteGATTCharacteristic:getDescriptor ", descriptor);
+ const data = {
+ this: this.uuid,
+ serviceUUID: this.service.uuid,
+ descriptor: descriptor,
+ };
+ const response = await window.axs?.callHandlerWrapper(
+ "BluetoothRemoteGATTCharacteristic.getDescriptor",
+ data
+ );
+ return response;
+ }
+
+ async getDescriptors(descriptor) {
+ console.log(
+ "BluetoothRemoteGATTCharacteristic:getDescriptors ",
+ descriptor
+ );
+ const data = {
+ this: this.uuid,
+ serviceUUID: this.service.uuid,
+ descriptor: descriptor,
+ };
+
+ const response = await window.axs?.callHandlerWrapper(
+ "BluetoothRemoteGATTCharacteristic.getDescriptors",
+ data
+ );
+ return response;
+ }
+
+ async readValue() {
+ console.log("BluetoothRemoteGATTCharacteristic:readValue");
+ const data = { this: this.uuid, serviceUUID: this.service.uuid };
+ const response = await window.axs?.callHandlerWrapper(
+ "BluetoothRemoteGATTCharacteristic.readValue",
+ data
+ );
+
+ console.log(
+ "BluetoothRemoteGATTCharacteristic:readValue",
+ JSON.stringify(response, null, 2)
+ );
+
+ const bytes = new Uint8Array(response);
+ console.log("Bytes : ", bytes);
+ const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
+ selectedCharacteristic.value = dv;
+
+ return dv;
+ }
+
+ async writeValue(value) {
+ console.log("BluetoothRemoteGATTCharacteristic:writeValue", value);
+ const data = {
+ this: this.uuid,
+ serviceUUID: this.service.uuid,
+ value: value,
+ };
+
+ const response = await window.axs?.callHandlerWrapper(
+ "BluetoothRemoteGATTCharacteristic.writeValue",
+ data
+ );
+
+ console.log(
+ "BluetoothRemoteGATTCharacteristic:writeValue",
+ JSON.stringify(response, null, 2)
+ );
+
+ return {};
+ }
+
+ async writeValueWithResponse(value) {
+ console.log(
+ "BluetoothRemoteGATTCharacteristic:writeValueWithResponse",
+ value
+ );
+ const data = {
+ this: this.uuid,
+ serviceUUID: this.service.uuid,
+ value: value,
+ };
+
+ await window.axs?.callHandlerWrapper(
+ "BluetoothRemoteGATTCharacteristic.writeValueWithResponse",
+ data
+ );
+
+ return {};
+ }
+
+ async writeValueWithoutResponse(value) {
+ console.log(
+ "BluetoothRemoteGATTCharacteristic:writeValueWithoutResponse",
+ value
+ );
+ const data = {
+ this: this.uuid,
+ serviceUUID: this.service.uuid,
+ value: value,
+ };
+
+ await window.axs?.callHandlerWrapper(
+ "BluetoothRemoteGATTCharacteristic.writeValueWithoutResponse",
+ data
+ );
+
+ return {};
+ }
+
+ async startNotifications() {
+ console.log("BluetoothRemoteGATTCharacteristic:startNotifications");
+ const data = { this: this.uuid, serviceUUID: this.service.uuid };
+
+ await window.axs?.callHandlerWrapper(
+ "BluetoothRemoteGATTCharacteristic.startNotifications",
+ data
+ );
+ return this;
+ }
+
+ async stopNotifications() {
+ console.log("BluetoothRemoteGATTCharacteristic:stopNotifications");
+ const data = { this: this.uuid, serviceUUID: this.service.uuid };
+
+ await window.axs?.callHandlerWrapper(
+ "BluetoothRemoteGATTCharacteristic.stopNotifications",
+ data
+ );
+ return this;
+ }
+
+ addEventListener(type, listener, useCapture = false) {
+ // Custom addEventListener implementation to handle specific types
+ super.addEventListener(type, listener, useCapture);
+ }
+}
+
+class BluetoothRemoteGATTService extends EventTarget {
+ constructor(device, uuid, isPrimary) {
+ super();
+ this.device = device;
+ this.uuid = uuid;
+ this.isPrimary = isPrimary;
+ }
+
+ async getCharacteristic(characteristic) {
+ console.log(
+ "BluetoothRemoteGATTService:getCharacteristic ",
+ characteristic
+ );
+ let data = { this: this.uuid, characteristic: characteristic };
+ const response = await window.axs?.callHandlerWrapper(
+ "BluetoothRemoteGATTService.getCharacteristic",
+ data
+ );
+ console.log(
+ "BluetoothRemoteGATTService:getCharacteristic ",
+ JSON.stringify(response, null, 2)
+ );
+ const characteristicInstance = new BluetoothRemoteGATTCharacteristic(
+ this,
+ response.uuid,
+ response.value,
+ undefined
+ );
+ navigator.bluetooth.characteristicArray.push(characteristicInstance);
+ return characteristicInstance;
+ }
+
+ async getCharacteristics(characteristic) {
+ console.log(
+ "BluetoothRemoteGATTService:getCharacteristics ",
+ characteristic
+ );
+ const response = await window.axs?.callHandlerWrapper(
+ "BluetoothRemoteGATTService.getCharacteristics",
+ { this: "$uuid", characteristic: characteristic }
+ );
+ return response;
+ }
+
+ async getIncludedService(service) {
+ console.log("BluetoothRemoteGATTService:getIncludedService ", service);
+ const response = await window.axs?.callHandlerWrapper(
+ "BluetoothRemoteGATTService.getIncludedService",
+ { this: "$uuid", service: service }
+ );
+ return response;
+ }
+
+ async getIncludedServices(service) {
+ console.log("BluetoothRemoteGATTService:getIncludedServices ", service);
+ const response = await window.axs?.callHandlerWrapper(
+ "BluetoothRemoteGATTService.getIncludedServices",
+ { this: "$uuid", service: service }
+ );
+ return response;
+ }
+
+ addEventListener(type, listener, useCapture = false) {
+ // Custom addEventListener implementation to handle specific types
+ super.addEventListener(type, listener, useCapture);
+ }
+}
+
+class AXSBluetooth {
+ constructor() {
+ this.serviceArray = [];
+ this.characteristicArray = [];
+ this.bluetoothDevice = {};
+ }
+
+ async requestDevice(options) {
+ console.log("AXSBluetooth:requestDevice ", options);
+ const response = await window.axs?.callHandlerWrapper("requestDevice", options);
+
+ const gatt = new BluetoothRemoteGATTServer(
+ response.gatt.device,
+ response.gatt.connected
+ );
+
+ const device = new BluetoothDevice(
+ response.id,
+ response.name,
+ gatt,
+ response.watchingAdvertisements
+ );
+
+ return device;
+ }
+
+ dispatchCharacteristicEvent(characteristicUUID, eventName) {
+ console.log(
+ "AXSBluetooth:dispatchCharacteristicEvent ",
+ characteristicUUID,
+ " ",
+ eventName
+ );
+ let selectedCharacteristic =
+ this.getCharacteristicByUUID(characteristicUUID);
+ if (selectedCharacteristic != undefined) {
+ selectedCharacteristic.dispatchEvent(new Event(eventName));
+ console.log(
+ "AXSBluetooth:dispatchCharacteristicEvent:eventName ",
+ eventName
+ );
+ }
+ }
+
+ updateCharacteristicValue(characteristicUUID, value) {
+ console.log(
+ "AXSBluetooth:updateCharacteristicValue ",
+ characteristicUUID,
+ " ",
+ value
+ );
+ const bytes = new Uint8Array(value);
+ console.log("Bytes : ", bytes);
+ console.log("Bytes type: ", typeof bytes);
+ const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
+ let selectedCharacteristic =
+ this.getCharacteristicByUUID(characteristicUUID);
+
+ selectedCharacteristic.value = dv;
+ console.log(
+ "Selected characteristic : ",
+ JSON.stringify(selectedCharacteristic, null, 2)
+ );
+ this.dispatchCharacteristicEvent(
+ characteristicUUID,
+ "characteristicvaluechanged"
+ );
+ }
+
+ getServiceByUUID(serviceUUID) {
+ console.log("AXSBluetooth:getServiceByUUID ", serviceUUID);
+ return this.serviceArray.find((service) => service.uuid === serviceUUID);
+ }
+
+ getCharacteristicByUUID(characteristicUUID) {
+ console.log("AXSBluetooth:getCharacteristicByUUID ", characteristicUUID);
+ return this.characteristicArray.find(
+ (characteristic) => characteristic.uuid === characteristicUUID
+ );
+ }
+}
+
+navigator.bluetooth = new AXSBluetooth();
+console.log(JSON.stringify(navigator.bluetooth, null, 2));
diff --git a/assets/svg/blueberryring.svg b/assets/svg/blueberryring.svg
new file mode 100644
index 00000000..653826ec
--- /dev/null
+++ b/assets/svg/blueberryring.svg
@@ -0,0 +1,114 @@
+
+
diff --git a/ios/Podfile b/ios/Podfile
index ec26a17f..14e1fd1e 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -87,8 +87,8 @@ post_install do |installer|
## dart: PermissionGroup.sensors
# 'PERMISSION_SENSORS=1',
- ## dart: PermissionGroup.bluetooth
- # 'PERMISSION_BLUETOOTH=1',
+ # dart: PermissionGroup.bluetooth
+ 'PERMISSION_BLUETOOTH=1',
## dart: PermissionGroup.appTrackingTransparency
# 'PERMISSION_APP_TRACKING_TRANSPARENCY=1',
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 30e132e8..9c2bab40 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -1,156 +1,157 @@
-
- BGTaskSchedulerPermittedIdentifiers
-
- com.transistorsoft.fetch
- com.mxc.axswallet.periodicalTasks
- com.mxc.axswallet.dappHooksTasks
- com.mxc.axswallet.minerAutoClaimTask
-
- CADisableMinimumFrameDurationOnPhone
-
- CFBundleDevelopmentRegion
- $(DEVELOPMENT_LANGUAGE)
- CFBundleDisplayName
- AXS
- CFBundleDocumentTypes
-
-
- CFBundleTypeName
- FlSharedLink
- LSHandlerRank
- Default
- LSItemContentTypes
-
- public.file-url
- public.image
- public.text
- public.url
- public.data
-
-
-
- CFBundleExecutable
- $(EXECUTABLE_NAME)
- CFBundleIdentifier
- $(PRODUCT_BUNDLE_IDENTIFIER)
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleLocalizations
-
- en
- zh_TW
- zh_CN
- zh_HK
- ko
- ja
- vi
- ru
- tr
- de
- es
- pt
- tl
- id
- it
- fr
-
- CFBundleName
- axs
- CFBundlePackageType
- APPL
- CFBundleShortVersionString
- $(FLUTTER_BUILD_NAME)
- CFBundleSignature
- ????
- CFBundleVersion
- $(FLUTTER_BUILD_NUMBER)
- LSApplicationQueriesSchemes
-
- tg
- weixin
- wechat
- googlegmail
- x-dispatch
- readdle-spark
- airmail
- ms-outlook
- ymail
- fastmail
- superhuman
- protonmail
-
- LSRequiresIPhoneOS
-
- LSSupportsOpeningDocumentsInPlace
- No
- NSAppTransportSecurity
+
+ BGTaskSchedulerPermittedIdentifiers
+
+ com.transistorsoft.fetch
+ com.mxc.axswallet.notificationsTask
+ com.mxc.axswallet.wifiHooksTask
+ com.mxc.axswallet.minerAutoClaimTask
+ com.mxc.axswallet.blueberryAutoSyncTask
+
+ CADisableMinimumFrameDurationOnPhone
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ AXS
+ CFBundleDocumentTypes
+
- NSAllowsArbitraryLoads
-
+ CFBundleTypeName
+ FlSharedLink
+ LSHandlerRank
+ Default
+ LSItemContentTypes
+
+ public.file-url
+ public.image
+ public.text
+ public.url
+ public.data
+
- NSAppleMusicUsageDescription
- Would you allow AXS Wallet to use the Media Library?
- NSBluetoothPeripheralUsageDescription
- Would you allow AXS Wallet to use the Bluetooth?
- NSCalendarsUsageDescription
- Would you allow AXS Wallet to use the Calendar?
- NSCameraUsageDescription
- AXS Wallet needs access to your camera when the app is in use for services such as RWA minting. Image data is not saved or shared with any third parties.
- NSFaceIDUsageDescription
- AXS Wallet needs access to your Biometric data when the app is in use for authentication only. Your Biometric data is not saved or shared with any third parties.
- NSLocationAlwaysAndWhenInUseUsageDescription
- AXS Wallet needs access to your location for features such as Wi-Fi hooks while the app is in use or running in the background. Your location data is not saved or shared with any third parties.
-
- NSLocationAlwaysUsageDescription
- AXS Wallet requires access to your location at all times for services such as Wi-Fi hooks. Your location data is not saved or shared with any third parties.
-
- NSLocationUsageDescription
- AXS Wallet needs access to your location to provide services such as Wi-Fi hooks. Your location data is not saved or shared with any third parties.
-
- NSLocationWhenInUseUsageDescription
- AXS Wallet needs access to your location when the app is in use for services such as Wi-Fi hooks. Your location data is not saved or shared with any third parties.
- NSMotionUsageDescription
- Would you allow AXS Wallet to use the Motion?
- NSPhotoLibraryAddUsageDescription
- AXS Wallet needs access to your photos to save images such as RWA. Your photos are not saved or shared with any third parties.
- NSPhotoLibraryUsageDescription
- AXS Wallet needs access to your photos when the app is in use for services such as RWA minting. Your photos are not saved or shared with any third parties.
- NSSpeechRecognitionUsageDescription
- Would you allow AXS Wallet to use the Speech Recognition?
- PermissionGroupNotification
- Would you allow AXS Wallet to use the Notification?
- UIApplicationSupportsIndirectInputEvents
-
- UIBackgroundModes
-
- fetch
- location
- processing
- remote-notification
-
- UILaunchStoryboardName
- LaunchScreen
- UIMainStoryboardFile
- Main
- UISupportedInterfaceOrientations
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
- UISupportedInterfaceOrientations~ipad
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationPortraitUpsideDown
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
- UIViewControllerBasedStatusBarAppearance
-
- UISupportsDocumentBrowser
+
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleLocalizations
+
+ en
+ zh_TW
+ zh_CN
+ zh_HK
+ ko
+ ja
+ vi
+ ru
+ tr
+ de
+ es
+ pt
+ tl
+ id
+ it
+ fr
+
+ CFBundleName
+ axs
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSApplicationQueriesSchemes
+
+ tg
+ weixin
+ wechat
+ googlegmail
+ x-dispatch
+ readdle-spark
+ airmail
+ ms-outlook
+ ymail
+ fastmail
+ superhuman
+ protonmail
+
+ LSRequiresIPhoneOS
+
+ LSSupportsOpeningDocumentsInPlace
+ No
+ NSAppTransportSecurity
+
+ NSAllowsArbitraryLoads
+ NSAppleMusicUsageDescription
+ Would you allow AXS Wallet to use the Media Library?
+ NSBluetoothPeripheralUsageDescription
+ Would you allow AXS Wallet to use the Bluetooth?
+ NSCalendarsUsageDescription
+ Would you allow AXS Wallet to use the Calendar?
+ NSCameraUsageDescription
+ AXS Wallet needs access to your camera when the app is in use for services such as RWA minting. Image data is not saved or shared with any third parties.
+ NSFaceIDUsageDescription
+ AXS Wallet needs access to your Biometric data when the app is in use for authentication only. Your Biometric data is not saved or shared with any third parties.
+ NSLocationAlwaysAndWhenInUseUsageDescription
+ AXS Wallet needs access to your location for features such as Wi-Fi hooks while the app is in use or running in the background. Your location data is not saved or shared with any third parties.
+ NSLocationAlwaysUsageDescription
+ AXS Wallet requires access to your location at all times for services such as Wi-Fi hooks. Your location data is not saved or shared with any third parties.
+ NSLocationUsageDescription
+ AXS Wallet needs access to your location to provide services such as Wi-Fi hooks. Your location data is not saved or shared with any third parties.
+ NSLocationWhenInUseUsageDescription
+ AXS Wallet needs access to your location when the app is in use for services such as Wi-Fi hooks. Your location data is not saved or shared with any third parties.
+ NSMotionUsageDescription
+ Would you allow AXS Wallet to use the Motion?
+ NSPhotoLibraryAddUsageDescription
+ AXS Wallet needs access to your photos to save images such as RWA. Your photos are not saved or shared with any third parties.
+ NSPhotoLibraryUsageDescription
+ AXS Wallet needs access to your photos when the app is in use for services such as RWA minting. Your photos are not saved or shared with any third parties.
+ NSSpeechRecognitionUsageDescription
+ Would you allow AXS Wallet to use the Speech Recognition?
+ PermissionGroupNotification
+ Would you allow AXS Wallet to use the Notification?
+ NSBluetoothAlwaysUsageDescription
+ AXS Wallet needs access to your Bluetooth when the app is in use or running in the background for services such as BluberryRing. Blutooth data is not saved or shared with any third parties.
+ UIApplicationSupportsIndirectInputEvents
+
+ UIBackgroundModes
+
+ fetch
+ location
+ processing
+ remote-notification
+ bluetooth-central
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+ UISupportsDocumentBrowser
+
+
diff --git a/lib/common/bottom_sheets/blueberry_rings_bottom_sheet.dart b/lib/common/bottom_sheets/blueberry_rings_bottom_sheet.dart
new file mode 100644
index 00000000..0f4edd62
--- /dev/null
+++ b/lib/common/bottom_sheets/blueberry_rings_bottom_sheet.dart
@@ -0,0 +1,51 @@
+import 'package:datadashwallet/features/dapps/subfeatures/open_dapp/widgets/widgets.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_blue_plus/flutter_blue_plus.dart';
+import 'package:flutter_i18n/flutter_i18n.dart';
+import 'package:mxc_ui/mxc_ui.dart';
+
+Future showBlueberryRingsBottomSheet(
+ BuildContext context,
+) {
+ return showModalBottomSheet(
+ context: context,
+ useRootNavigator: true,
+ isScrollControlled: true,
+ isDismissible: false,
+ useSafeArea: true,
+ backgroundColor: Colors.transparent,
+ builder: (BuildContext context) => StatefulBuilder(
+ builder: (BuildContext context, setState) {
+ return Container(
+ padding:
+ const EdgeInsets.only(left: 16, right: 16, top: 0, bottom: 44),
+ decoration: BoxDecoration(
+ color: ColorsTheme.of(context).screenBackground,
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(20),
+ topRight: Radius.circular(20),
+ ),
+ ),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ MxcAppBarEvenly.title(
+ titleText:
+ FlutterI18n.translate(context, 'nearby_blueberry_rings'),
+ action: Container(
+ alignment: Alignment.centerRight,
+ child: InkWell(
+ child: const Icon(Icons.close),
+ onTap: () => Navigator.of(context).pop(null),
+ ),
+ ),
+ ),
+ const BlueberryDeviceInfo(),
+ const SizedBox(height: 10),
+ ],
+ ),
+ );
+ },
+ ),
+ );
+}
diff --git a/lib/common/bottom_sheets/bottom_sheets.dart b/lib/common/bottom_sheets/bottom_sheets.dart
new file mode 100644
index 00000000..642f3154
--- /dev/null
+++ b/lib/common/bottom_sheets/bottom_sheets.dart
@@ -0,0 +1 @@
+export 'blueberry_rings_bottom_sheet.dart';
diff --git a/lib/common/common.dart b/lib/common/common.dart
index 2ed174bb..a336f1ee 100644
--- a/lib/common/common.dart
+++ b/lib/common/common.dart
@@ -10,3 +10,4 @@ export 'biometric.dart';
export 'components/components.dart';
export 'dialogs/dialogs.dart';
export 'assets.dart';
+export 'bottom_sheets/bottom_sheets.dart';
diff --git a/lib/common/components/list/single_line_info_item.dart b/lib/common/components/list/single_line_info_item.dart
index 9b2b7759..0b9f6b29 100644
--- a/lib/common/components/list/single_line_info_item.dart
+++ b/lib/common/components/list/single_line_info_item.dart
@@ -49,7 +49,7 @@ class SingleLineInfoItem extends HookConsumerWidget {
value,
style: FontTheme.of(context).body1.primary(),
softWrap: true,
- textAlign: TextAlign.end,
+ textAlign: TextAlign.start,
),
),
if (valueActionIcon != null)
diff --git a/lib/core/src/background_process/axs_background_fetch.dart b/lib/core/src/background_process/axs_background_fetch.dart
index f0adb4ff..e4dc9428 100644
--- a/lib/core/src/background_process/axs_background_fetch.dart
+++ b/lib/core/src/background_process/axs_background_fetch.dart
@@ -22,12 +22,14 @@ class AXSBackgroundFetch {
}
static void handleCallBackDispatcher(String taskId) async {
- if (taskId == BackgroundExecutionConfig.axsPeriodicalTask) {
+ if (taskId == BackgroundExecutionConfig.notificationsTask) {
NotificationsService.notificationsCallbackDispatcher(taskId);
- } else if (taskId == BackgroundExecutionConfig.dappHookTasks) {
+ } else if (taskId == BackgroundExecutionConfig.wifiHooksTask) {
DAppHooksService.dappHooksServiceCallBackDispatcherForeground(taskId);
} else if (taskId == BackgroundExecutionConfig.minerAutoClaimTask) {
DAppHooksService.autoClaimServiceCallBackDispatcherForeground(taskId);
+ } else if (taskId == BackgroundExecutionConfig.blueberryAutoSyncTask) {
+ DAppHooksService.blueberryAutoSyncServiceCallBackDispatcherForeground(taskId);
} else {
bgFetch.BackgroundFetch.finish(taskId);
}
@@ -40,7 +42,7 @@ class AXSBackgroundFetch {
static bool turnOffAll(
DAppHooksModel dAppHooksData, PeriodicalCallData periodicalCallData) {
- return !dAppHooksData.enabled &&
+ return !dAppHooksData.wifiHooks.enabled &&
!periodicalCallData.serviceEnabled &&
!dAppHooksData.minerHooks.enabled;
}
@@ -77,7 +79,7 @@ class AXSBackgroundFetch {
requiresCharging: false,
requiresStorageNotLow: false,
requiresDeviceIdle: false,
- requiredNetworkType: bgFetch.NetworkType.ANY),
+ requiredNetworkType: bgFetch.NetworkType.ANY,),
handleCallBackDispatcher);
// Android Only
final backgroundFetchState =
diff --git a/lib/core/src/background_process/dapp_hooks_service.dart b/lib/core/src/background_process/dapp_hooks_service.dart
index eb335b97..c2670c05 100644
--- a/lib/core/src/background_process/dapp_hooks_service.dart
+++ b/lib/core/src/background_process/dapp_hooks_service.dart
@@ -26,20 +26,17 @@ class DAppHooksService {
final isLoggedIn = authUseCase.loggedIn;
final account = accountUseCase.account.value;
- final serviceEnabled = dappHooksData.enabled;
final wifiHooksEnabled = dappHooksData.wifiHooks.enabled;
// Make sure user is logged in
- if (isLoggedIn && MXCChains.isMXCChains(chainId) && serviceEnabled) {
+ if (isLoggedIn && MXCChains.isMXCChains(chainId) && wifiHooksEnabled) {
await AXSNotification()
.setupFlutterNotifications(shouldInitFirebase: false);
- await contextLessTranslationUseCase.setupTranslator();
- if (wifiHooksEnabled) {
- await dAppHooksUseCase.sendWifiInfo(
- account!,
- );
- }
+ await dAppHooksUseCase.sendWifiInfo(
+ account!,
+ );
+
BackgroundFetch.finish(taskId);
} else {
// terminate background fetch
@@ -79,7 +76,6 @@ class DAppHooksService {
if (isLoggedIn && MXCChains.isMXCChains(chainId) && minerHooksEnabled) {
await AXSNotification()
.setupFlutterNotifications(shouldInitFirebase: false);
- await contextLessTranslationUseCase.setupTranslator();
await dAppHooksUseCase.executeMinerAutoClaim(
account: account!,
@@ -94,4 +90,48 @@ class DAppHooksService {
BackgroundFetch.finish(taskId);
}
}
+
+ static void blueberryAutoSyncServiceCallBackDispatcherForeground(
+ String taskId) async {
+ try {
+ await loadProviders();
+
+ final container = ProviderContainer();
+ final authUseCase = container.read(authUseCaseProvider);
+ final chainConfigurationUseCase =
+ container.read(chainConfigurationUseCaseProvider);
+ final accountUseCase = container.read(accountUseCaseProvider);
+ final dAppHooksUseCase = container.read(dAppHooksUseCaseProvider);
+ final contextLessTranslationUseCase =
+ container.read(contextLessTranslationUseCaseProvider);
+
+ final selectedNetwork =
+ chainConfigurationUseCase.getCurrentNetworkWithoutRefresh();
+ DAppHooksModel dappHooksData = dAppHooksUseCase.dappHooksData.value;
+ final chainId = selectedNetwork.chainId;
+
+ final isLoggedIn = authUseCase.loggedIn;
+ final account = accountUseCase.account.value;
+ // final serviceEnabled = dappHooksData.enabled;
+ final autoSyncEnabled = dappHooksData.blueberryRingHooks.enabled;
+ final ringHooksTime = dappHooksData.blueberryRingHooks.time;
+ final selectedRings = dappHooksData.blueberryRingHooks.selectedRings;
+ // Make sure user is logged in
+ if (isLoggedIn && MXCChains.isMXCChains(chainId) && autoSyncEnabled) {
+ await AXSNotification()
+ .setupFlutterNotifications(shouldInitFirebase: false);
+
+ await dAppHooksUseCase.syncBlueberryRingSync(
+ account: account!,
+ selectedRingsListId: selectedRings,
+ ringAutoSyncTime: ringHooksTime);
+ BackgroundFetch.finish(taskId);
+ } else {
+ // terminate background fetch
+ BackgroundFetch.stop(taskId);
+ }
+ } catch (e) {
+ BackgroundFetch.finish(taskId);
+ }
+ }
}
diff --git a/lib/core/src/background_process/notifications_service.dart b/lib/core/src/background_process/notifications_service.dart
index 1e6f387c..cf4eabb9 100644
--- a/lib/core/src/background_process/notifications_service.dart
+++ b/lib/core/src/background_process/notifications_service.dart
@@ -16,6 +16,8 @@ class NotificationsService {
final accountUseCase = container.read(accountUseCaseProvider);
final backgroundFetchConfigUseCase =
container.read(backgroundFetchConfigUseCaseProvider);
+ final blueberryRingBackgroundNotificationsUseCase =
+ container.read(blueberryRingBackgroundNotificationsUseCaseProvider);
final contextLessTranslationUseCase =
container.read(contextLessTranslationUseCaseProvider);
@@ -39,11 +41,16 @@ class NotificationsService {
periodicalCallData.expectedEpochOccurrenceEnabled;
final serviceEnabled = periodicalCallData.serviceEnabled;
+ final activityReminderEnabled =
+ periodicalCallData.activityReminderEnabled;
+ final sleepInsightEnabled = periodicalCallData.sleepInsightEnabled;
+ final heartAlertEnabled = periodicalCallData.heartAlertEnabled;
+ final lowBatteryEnabled = periodicalCallData.lowBatteryEnabled;
+
// Make sure user is logged in
if (isLoggedIn && MXCChains.isMXCChains(chainId) && serviceEnabled) {
await AXSNotification()
.setupFlutterNotifications(shouldInitFirebase: false);
- await contextLessTranslationUseCase.setupTranslator();
if (lowBalanceLimitEnabled) {
await backgroundFetchConfigUseCase.checkLowBalance(
@@ -64,6 +71,23 @@ class NotificationsService {
chainId);
}
+ if (activityReminderEnabled) {
+ await blueberryRingBackgroundNotificationsUseCase
+ .checkActivityReminder();
+ }
+
+ if (sleepInsightEnabled) {
+ await blueberryRingBackgroundNotificationsUseCase.checkSleepInsight();
+ }
+
+ if (heartAlertEnabled) {
+ await blueberryRingBackgroundNotificationsUseCase.checkHeartAlert();
+ }
+
+ if (lowBatteryEnabled) {
+ await blueberryRingBackgroundNotificationsUseCase.checkLowBattery();
+ }
+
backgroundFetchConfigUseCase.updateItem(periodicalCallData);
BackgroundFetch.finish(taskId);
} else {
diff --git a/lib/core/src/providers/providers_use_cases.dart b/lib/core/src/providers/providers_use_cases.dart
index 2071c11f..8f1ad66c 100644
--- a/lib/core/src/providers/providers_use_cases.dart
+++ b/lib/core/src/providers/providers_use_cases.dart
@@ -146,6 +146,7 @@ final Provider dAppHooksUseCaseProvider = Provider(
),
ref.watch(errorUseCaseProvider),
ref.watch(contextLessTranslationUseCaseProvider),
+ ref.watch(blueberryRingBackgroundSyncUseCase),
),
);
@@ -259,3 +260,42 @@ final Provider ipfsUseCaseProvider = Provider(
ref.watch(chainConfigurationUseCaseProvider),
),
);
+
+final Provider bluetoothUseCaseProvider = Provider(
+ (ref) => BluetoothUseCase(
+ ref.watch(web3RepositoryProvider),
+ ref.watch(chainConfigurationUseCaseProvider),
+ ref.watch(authUseCaseProvider),
+ ),
+);
+
+final Provider blueberryRingUseCaseProvider = Provider(
+ (ref) => BlueberryRingUseCase(
+ ref.watch(web3RepositoryProvider),
+ ref.watch(chainConfigurationUseCaseProvider),
+ ref.watch(bluetoothUseCaseProvider),
+ ),
+);
+
+final Provider
+ blueberryRingBackgroundNotificationsUseCaseProvider = Provider(
+ (ref) => BlueberryRingBackgroundNotificationsUseCase(
+ ref.watch(web3RepositoryProvider),
+ ref.watch(chainConfigurationUseCaseProvider),
+ ref.watch(bluetoothUseCaseProvider),
+ ref.watch(blueberryRingUseCaseProvider),
+ ref.watch(contextLessTranslationUseCaseProvider),
+ ),
+);
+
+final Provider
+ blueberryRingBackgroundSyncUseCase = Provider(
+ (ref) => BlueberryRingBackgroundSyncUseCase(
+ ref.watch(web3RepositoryProvider),
+ ref.watch(chainConfigurationUseCaseProvider),
+ ref.watch(bluetoothUseCaseProvider),
+ ref.watch(blueberryRingUseCaseProvider),
+ ref.watch(accountUseCaseProvider),
+ ref.watch(contextLessTranslationUseCaseProvider),
+ ),
+);
diff --git a/lib/features/common/app/contextless_translation_use_case.dart b/lib/features/common/app/contextless_translation_use_case.dart
index 5793260a..3dab3385 100644
--- a/lib/features/common/app/contextless_translation_use_case.dart
+++ b/lib/features/common/app/contextless_translation_use_case.dart
@@ -8,16 +8,22 @@ import 'package:datadashwallet/features/settings/settings.dart';
class ContextLessTranslationUseCase extends ReactiveUseCase {
ContextLessTranslationUseCase(
this._languageUseCase,
- );
+ ) {
+ setupTranslator();
+ }
final LanguageUseCase _languageUseCase;
+ bool initialized = false;
static Map? _currentTranslations;
Future setupTranslator() async {
- final Locale currentLocale =
- _languageUseCase.currentLocale.value?.toLocale() ?? window.locale;
- await loadTranslations(currentLocale);
+ if (!initialized) {
+ final Locale currentLocale =
+ _languageUseCase.currentLocale.value?.toLocale() ?? window.locale;
+ await loadTranslations(currentLocale);
+ initialized = true;
+ }
}
Future loadTranslations(Locale locale) async {
diff --git a/lib/features/common/common.dart b/lib/features/common/common.dart
index 7bb4b28a..9b35be46 100644
--- a/lib/features/common/common.dart
+++ b/lib/features/common/common.dart
@@ -3,4 +3,5 @@ export 'account/account_use_case.dart';
export 'app_nav_bar/app_nav_bar.dart';
export 'app/app.dart';
export 'contract/contract.dart';
+export 'packages/packages.dart';
diff --git a/lib/features/common/contract/miner_use_case.dart b/lib/features/common/contract/miner_use_case.dart
index 6bf51273..12ea9274 100644
--- a/lib/features/common/contract/miner_use_case.dart
+++ b/lib/features/common/contract/miner_use_case.dart
@@ -14,18 +14,19 @@ class MinerUseCase extends ReactiveUseCase {
String cTranslate(String key) =>
_contextLessTranslationUseCase.translate(key);
- Future claimMinersReward(
- {required List selectedMinerListId,
- required Account account,
- required void Function(
- String title,
- String? text,
- )
- showNotification,
- required String Function(
- String key,
- )
- translate}) async {
+ Future claimMinersReward({
+ required List selectedMinerListId,
+ required Account account,
+ required void Function(
+ String title,
+ String? text,
+ )
+ showNotification,
+ required String Function(
+ String key,
+ )
+ translate,
+ }) async {
return await _repository.minerRepository.claimMinersReward(
selectedMinerListId: selectedMinerListId,
account: account,
@@ -33,7 +34,9 @@ class MinerUseCase extends ReactiveUseCase {
translate: translate);
}
- Future getAddressMiners(String address) async =>
+ Future getAddressMiners(
+ String address,
+ ) async =>
await _repository.minerRepository.getAddressMiners(address);
Future> helperGetClaimRewards(
diff --git a/lib/features/common/contract/token_contract_use_case.dart b/lib/features/common/contract/token_contract_use_case.dart
index 1839a9b7..283723ca 100644
--- a/lib/features/common/contract/token_contract_use_case.dart
+++ b/lib/features/common/contract/token_contract_use_case.dart
@@ -269,6 +269,17 @@ class TokenContractUseCase extends ReactiveUseCase {
.getTokenTransferData(tokenHash, toAddress, amount);
}
+ String signPersonalMessage({required String privateKey, required String message}) {
+ return _repository.tokenContract
+ .signPersonalMessage(privateKey: privateKey, message: message);
+ }
+
+
+ String signMessage({required String privateKey, required String message}) {
+ return _repository.tokenContract
+ .signMessage(privateKey: privateKey, message: message);
+ }
+
String signTypedMessage({required String privateKey, required String data}) {
return _repository.tokenContract
.signTypedMessage(privateKey: privateKey, data: data);
diff --git a/lib/features/common/packages/bluetooth/blue_plus/blue_plus.dart b/lib/features/common/packages/bluetooth/blue_plus/blue_plus.dart
new file mode 100644
index 00000000..3c6ef267
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blue_plus/blue_plus.dart
@@ -0,0 +1,2 @@
+export 'blue_plus_bluetooth_utils.dart';
+export 'bluetooth_use_case.dart';
diff --git a/lib/features/common/packages/bluetooth/blue_plus/blue_plus_bluetooth_utils.dart b/lib/features/common/packages/bluetooth/blue_plus/blue_plus_bluetooth_utils.dart
new file mode 100644
index 00000000..8f0c3ed2
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blue_plus/blue_plus_bluetooth_utils.dart
@@ -0,0 +1,70 @@
+import 'package:flutter_blue_plus/flutter_blue_plus.dart';
+
+class BluePlusBluetoothUtils {
+ static Future getPrimaryService(
+ ScanResult selectedScanResult,
+ Guid serviceUUID,
+ ) async {
+ final services = await selectedScanResult.device.discoverServices();
+ final service = _getPrimaryServiceWithUUID(services, serviceUUID);
+ if (service != null) {
+ return service;
+ } else {
+ throw "Service with ${serviceUUID.str} UUID not found";
+ }
+ }
+
+ static BluetoothCharacteristic getCharacteristicWithService(
+ BluetoothService service,
+ Guid characteristicUUID,
+ ) {
+ final characteristics = service.characteristics;
+ final characteristic =
+ _getCharacteristicWithUUID(characteristics, characteristicUUID);
+ if (characteristic != null) {
+ return characteristic;
+ } else {
+ throw "Characteristic with ${characteristicUUID.str} UUID not found";
+ }
+ }
+
+ static Future getCharacteristicWithServiceUUID(
+ ScanResult selectedScanResult,
+ Guid serviceUUID,
+ Guid characteristicUUID,
+ ) async {
+ final service = await getPrimaryService(selectedScanResult, serviceUUID);
+ final characteristics = service.characteristics;
+ final characteristic =
+ _getCharacteristicWithUUID(characteristics, characteristicUUID);
+ if (characteristic != null) {
+ return characteristic;
+ } else {
+ throw "Characteristic with ${characteristicUUID.str} UUID not found";
+ }
+ }
+
+// Return blue plus bluetooth device
+ static BluetoothService? _getPrimaryServiceWithUUID(
+ List services, Guid targetServiceUUID) {
+ BluetoothService? primaryService;
+ for (BluetoothService service in services) {
+ if (service.uuid == targetServiceUUID && service.isPrimary) {
+ primaryService = service;
+ }
+ }
+ return primaryService;
+ }
+
+ static BluetoothCharacteristic? _getCharacteristicWithUUID(
+ List characteristics,
+ Guid targetCharacteristicUUID) {
+ BluetoothCharacteristic? targetCharacteristic;
+ for (BluetoothCharacteristic characteristic in characteristics) {
+ if (characteristic.characteristicUuid == targetCharacteristicUUID) {
+ targetCharacteristic = characteristic;
+ }
+ }
+ return targetCharacteristic;
+ }
+}
diff --git a/lib/features/common/packages/bluetooth/blue_plus/bluetooth_use_case.dart b/lib/features/common/packages/bluetooth/blue_plus/bluetooth_use_case.dart
new file mode 100644
index 00000000..43d44b2b
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blue_plus/bluetooth_use_case.dart
@@ -0,0 +1,230 @@
+import 'dart:async';
+import 'dart:developer';
+import 'dart:io';
+
+import 'package:app_settings/app_settings.dart';
+import 'package:flutter_blue_plus/flutter_blue_plus.dart';
+import 'package:mxc_logic/mxc_logic.dart';
+
+import 'package:datadashwallet/core/core.dart';
+import 'package:datadashwallet/features/settings/subfeatures/chain_configuration/domain/chain_configuration_use_case.dart';
+
+class BluetoothTimeoutError extends Error {
+ static const String message = 'unable_to_continue_bluetooth_is_turned_off';
+
+ BluetoothTimeoutError();
+
+ @override
+ String toString() {
+ return "TimeoutError: $message";
+ }
+}
+
+class BluetoothUseCase extends ReactiveUseCase {
+ BluetoothUseCase(
+ this._repository,
+ this._chainConfigurationUseCase,
+ this._authUseCase,
+ ) {
+ initBluetoothUseCase();
+ }
+
+ final Web3Repository _repository;
+ final ChainConfigurationUseCase _chainConfigurationUseCase;
+ final AuthUseCase _authUseCase;
+
+ StreamSubscription>? scannerListener;
+ StreamSubscription? stateListener;
+ StreamSubscription? isScanningStateListener;
+
+ late final ValueStream isScanning = reactive(false);
+ late final ValueStream bluetoothStatus =
+ reactive(BluetoothAdapterState.off);
+ late final ValueStream> scanResults = reactive([]);
+
+ void initBluetoothUseCase() {
+ initStateListener();
+ bluetoothStatus.listen((state) {
+ if (state == BluetoothAdapterState.on) {
+ // usually start scanning, connecting, etc
+ isScanningListener();
+ initScannerListener();
+ } else if (state == BluetoothAdapterState.unauthorized ||
+ state == BluetoothAdapterState.unavailable ||
+ state == BluetoothAdapterState.off ||
+ state == BluetoothAdapterState.unknown) {
+ // show an error to the user, etc
+ cancelIsScanningListener();
+ _cancelScannerListen();
+ }
+ });
+ }
+
+ Future checkBluetoothSupport() async {
+ final isSupported = await isBluetoothSupported();
+ if (!isSupported) {
+ // TODO: show error snack bar
+ }
+ }
+
+ Future isBluetoothSupported() async {
+ // first, check if bluetooth is supported by your hardware
+ // Note: The platform is initialized on the first call to any FlutterBluePlus method.
+ final res = await FlutterBluePlus.isSupported;
+ return res;
+ }
+
+ void initStateListener() {
+ // handle bluetooth on & off
+ // note: for iOS the initial state is typically BluetoothAdapterState.unknown
+ // note: if you have permissions issues you will get stuck at BluetoothAdapterState.unauthorized
+ stateListener =
+ FlutterBluePlus.adapterState.listen((BluetoothAdapterState state) {
+ update(bluetoothStatus, state);
+ });
+ }
+
+ void initScannerListener() {
+ // listen to scan results
+ // Note: `onScanResults` only returns live scan results, i.e. during scanning. Use
+ // `scanResults` if you want live scan results *or* the results from a previous scan.
+ scannerListener = FlutterBluePlus.onScanResults.listen(
+ (results) {
+ inspect(results);
+ if (results.isNotEmpty) {
+ update(scanResults, results);
+ }
+ },
+ onError: (e) => print(e),
+ );
+ }
+
+ void isScanningListener() async {
+ isScanningStateListener = FlutterBluePlus.isScanning.listen((event) {
+ update(isScanning, event);
+ });
+ }
+
+ Future turnOnBluetoothAndProceed() async {
+ // Try to turn on
+ await turnOnBluetooth();
+
+ // Wait till IT's turned on
+ await bluetoothStatus
+ .firstWhere((event) => event == BluetoothAdapterState.on)
+ .timeout(const Duration(seconds: 5),
+ onTimeout: () => throw BluetoothTimeoutError());
+ }
+
+ Future checkBluetoothTurnedOn(
+ Future Function() turnOnBluetoothFunction) async {
+ final isBluetoothOn = await isBluetoothTurnedOn();
+
+ if (!isBluetoothOn) {
+ await turnOnBluetoothFunction();
+ }
+ }
+
+ Future turnOnBluetooth() async {
+ await checkBluetoothTurnedOn(() async {
+ // turn on bluetooth ourself if we can
+ // for iOS, the user controls bluetooth enable/disable
+ if (Platform.isAndroid) {
+ await FlutterBluePlus.turnOn();
+ } else {
+ // IOS
+ await AppSettings.openAppSettings(type: AppSettingsType.bluetooth);
+ }
+ });
+ }
+
+ Future isBluetoothTurnedOn() async {
+ if (bluetoothStatus.value == BluetoothAdapterState.on) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ Future connectionHandler(BluetoothDevice device) async {
+ int attempts = 0;
+ const maxAttempts = 4;
+ while (attempts < maxAttempts) {
+ try {
+ print('Attempt ${attempts + 1} to connect to device...');
+ await device.connect();
+ print('Connected to device successfully.');
+ break; // Exit the loop if the connection is successful
+ } catch (e) {
+ if (e is FlutterBluePlusException && e.function == 'connect') {
+ attempts++;
+ print('Failed to connect. Attempt $attempts of $maxAttempts.');
+ if (attempts >= maxAttempts) {
+ print('Max attempts reached. Could not connect to device.');
+ break;
+ }
+ } else {
+ print('Unexpected error: $e');
+ break; // Exit on unexpected errors
+ }
+ }
+ }
+ }
+
+ void startScanning({
+ List? withServices,
+ List? withRemoteIds,
+ List? withNames,
+ List? withKeywords,
+ List? withMsd,
+ List? withServiceData,
+ Duration? timeout,
+ Duration? removeIfGone,
+ bool? continuousUpdates,
+ int? continuousDivisor,
+ bool? oneByOne,
+ AndroidScanMode? androidScanMode,
+ bool? androidUsesFineLocation,
+ }) async {
+ // Start scanning w/ timeout
+ // Optional: use `stopScan()` as an alternative to timeout
+ if (isScanning.value == true) {
+ return;
+ }
+
+ await FlutterBluePlus.startScan(
+ withServices: withServices ?? [],
+ withNames: withNames ?? [],
+ withMsd: withMsd ?? [],
+ withRemoteIds: withRemoteIds ?? [],
+ withServiceData: withServiceData ?? [],
+ withKeywords: withKeywords ?? [],
+ timeout: timeout,
+ removeIfGone: removeIfGone,
+ continuousUpdates: continuousUpdates ?? false,
+ continuousDivisor: continuousDivisor ?? 1,
+ oneByOne: oneByOne ?? false,
+ androidScanMode: androidScanMode ?? AndroidScanMode.lowLatency,
+ androidUsesFineLocation: androidUsesFineLocation ?? false,
+ );
+ }
+
+ void _cancelScannerListen() {
+ // cleanup: cancel subscription when scanning stops
+ if (scannerListener != null) {
+ FlutterBluePlus.cancelWhenScanComplete(scannerListener!);
+ }
+ }
+
+ void cancelStateListener() {
+ stateListener?.cancel();
+ }
+
+ void cancelIsScanningListener() {
+ isScanningStateListener?.cancel();
+ }
+
+ void stopScanner() {
+ FlutterBluePlus.stopScan();
+ }
+}
diff --git a/lib/features/common/packages/bluetooth/blueberry_ring/blueberry_ring.dart b/lib/features/common/packages/bluetooth/blueberry_ring/blueberry_ring.dart
new file mode 100644
index 00000000..c630dd8f
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blueberry_ring/blueberry_ring.dart
@@ -0,0 +1,3 @@
+export 'utils/utils.dart';
+export 'entities/entities.dart';
+export 'domain/domain.dart';
diff --git a/lib/features/common/packages/bluetooth/blueberry_ring/domain/blueberry_repository.dart b/lib/features/common/packages/bluetooth/blueberry_ring/domain/blueberry_repository.dart
new file mode 100644
index 00000000..e69de29b
diff --git a/lib/features/common/packages/bluetooth/blueberry_ring/domain/blueberry_ring_background_notifications_use_case.dart b/lib/features/common/packages/bluetooth/blueberry_ring/domain/blueberry_ring_background_notifications_use_case.dart
new file mode 100644
index 00000000..efd5154a
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blueberry_ring/domain/blueberry_ring_background_notifications_use_case.dart
@@ -0,0 +1,118 @@
+import 'dart:async';
+import 'package:datadashwallet/features/common/common.dart';
+import 'package:mxc_logic/mxc_logic.dart';
+
+import 'package:datadashwallet/core/core.dart';
+import 'package:datadashwallet/features/settings/subfeatures/chain_configuration/domain/chain_configuration_use_case.dart';
+
+import '../../../../../../app/logger.dart';
+
+class BlueberryRingBackgroundNotificationsUseCase extends ReactiveUseCase {
+ BlueberryRingBackgroundNotificationsUseCase(
+ this._repository,
+ this._chainConfigurationUseCase,
+ this._bluetoothUseCase,
+ this._blueberryRingUseCase,
+ this._contextLessTranslationUseCase);
+
+ final Web3Repository _repository;
+ final ChainConfigurationUseCase _chainConfigurationUseCase;
+ final BluetoothUseCase _bluetoothUseCase;
+ final BlueberryRingUseCase _blueberryRingUseCase;
+ final ContextLessTranslationUseCase _contextLessTranslationUseCase;
+
+ // Context less translation, This should be only used for BG functions
+ String cTranslate(String key) =>
+ _contextLessTranslationUseCase.translate(key);
+
+ Future checkActivityReminder() async {
+ final data = await _blueberryRingUseCase.readSteps();
+ print('checkActivityReminder:data ${data.map((e) => e.toJson()).toList()}');
+ // Get spteps data from cache and compare
+ // If steps is below a certain number then show a
+ // Below 5000
+ final latestData = data.first;
+ final lastDate = DateTime.fromMillisecondsSinceEpoch(
+ latestData.date * 1000,
+ );
+ final now = DateTime.now();
+ final isToday = now.year == lastDate.year &&
+ now.month == lastDate.month &&
+ now.day == lastDate.day;
+
+ if (isToday && latestData.step < 5000) {
+ AXSNotification().showNotification(
+ cTranslate('activity_reminder'),
+ cTranslate('blueberry_ring_inactive_alert_text'),
+ );
+ }
+ }
+
+ Future checkSleepInsight() async {
+ final data = await _blueberryRingUseCase.readSleep();
+ print('checkSleepInsight:data ${data.map((e) => e.toJson()).toList()}');
+ // If sleeps is below standard level
+ // loop throug all and get average
+ final now = DateTime.now();
+
+ final todaysData = data.where((element) {
+ final date = DateTime.fromMillisecondsSinceEpoch(
+ element.date * 1000,
+ );
+ final isToday = now.year == date.year &&
+ now.month == date.month &&
+ now.day == date.day;
+ return isToday;
+ });
+
+ print(
+ 'checkSleepInsight:todaysData ${todaysData.map((e) => e.toJson()).toList()}');
+ if (todaysData.isEmpty) {
+ return;
+ }
+
+ final isNormal = BlueberryRingDataAnalyzer.isSleepQualityNormal(
+ todaysData.map((e) => e.value).toList());
+
+ if (!isNormal) {
+ AXSNotification().showNotification(
+ cTranslate('sleep_insight'),
+ cTranslate('blueberry_ring_sleep_alert_text'),
+ );
+ }
+ }
+
+ Future checkHeartAlert() async {
+ final data = await _blueberryRingUseCase.readHeartRate();
+ print('checkHeartAlert:data ${data.map((e) => e.toJson()).toList()}');
+ // If below standard but between person to person different
+ final latestData = data.first;
+ final lastDate = DateTime.fromMillisecondsSinceEpoch(
+ latestData.date * 1000,
+ );
+ final now = DateTime.now();
+ final isToday = now.year == lastDate.year &&
+ now.month == lastDate.month &&
+ now.day == lastDate.day;
+
+ if (isToday && latestData.value >= 100) {
+ AXSNotification().showNotification(
+ cTranslate('heart_alert'),
+ cTranslate('blueberry_ring_heart_rate_alert_text'),
+ );
+ }
+ }
+
+ Future checkLowBattery() async {
+ final data = await _blueberryRingUseCase.readLevel();
+ print('checkLowBattery:data $data');
+ // What si the low battery level
+ // Is 10 OK
+ if (data < 20) {
+ AXSNotification().showNotification(
+ cTranslate('low_battery'),
+ cTranslate('blueberry_ring_battery_alert_text'),
+ );
+ }
+ }
+}
diff --git a/lib/features/common/packages/bluetooth/blueberry_ring/domain/blueberry_ring_background_sync_use_case.dart b/lib/features/common/packages/bluetooth/blueberry_ring/domain/blueberry_ring_background_sync_use_case.dart
new file mode 100644
index 00000000..b12c42ce
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blueberry_ring/domain/blueberry_ring_background_sync_use_case.dart
@@ -0,0 +1,144 @@
+import 'dart:async';
+import 'dart:convert';
+import 'package:datadashwallet/features/common/common.dart';
+import 'package:mxc_logic/mxc_logic.dart';
+
+import 'package:datadashwallet/core/core.dart';
+import 'package:datadashwallet/features/settings/subfeatures/chain_configuration/domain/chain_configuration_use_case.dart';
+
+import '../../../../../../app/logger.dart';
+
+class BlueberryRingBackgroundSyncUseCase extends ReactiveUseCase {
+ BlueberryRingBackgroundSyncUseCase(
+ this._repository,
+ this._chainConfigurationUseCase,
+ this._bluetoothUseCase,
+ this._blueberryRingUseCase,
+ this._accountUserCase,
+ this._contextLessTranslationUseCase);
+
+ final Web3Repository _repository;
+ final ChainConfigurationUseCase _chainConfigurationUseCase;
+ final BluetoothUseCase _bluetoothUseCase;
+ final BlueberryRingUseCase _blueberryRingUseCase;
+ final AccountUseCase _accountUserCase;
+ final ContextLessTranslationUseCase _contextLessTranslationUseCase;
+
+
+ // Context less translation, This should be only used for BG functions
+ String cTranslate(String key) =>
+ _contextLessTranslationUseCase.translate(key);
+
+
+ Future syncRings({
+ required List selectedRingsListId,
+ required Account account,
+ required void Function(String title, String? text) showNotification,
+ required String Function(
+ String key,
+ )
+ translate,
+ }) async {
+ // Get miner from cache
+ // for (String ring in selectedRings) {
+
+ // }
+ // Get rings list
+ // Get the data from contract
+// async function arrayFilterDate(array: T[], date?: number) {
+// if (!date)
+// return array
+// return array.filter((item: any) => item.date > (date || 0)) as T[]
+// }
+// arrayFilterDate(arr, detail.steps.at(-1)?.date)
+// List arrayFilterDate(List array, {int? date}) {
+// if (date == null) return array;
+
+// return array.where((item) {
+// var itemDate = (item as dynamic).date; // Use 'dynamic' to access the 'date' property
+// return itemDate > (date ?? 0);
+// }).toList();
+// }
+
+ // showNotification(
+ // translate('no_token_to_claim_miner')
+ // .replaceFirst('{0}', miner.mep1004TokenId!),
+ // null,
+ // );
+ // no_rings_owned_notification
+ // syncing_data_from_ring
+ // already_synced_ring
+ // data_synced_successfully_ring
+ // data_syncing_failed
+ // final memo = await fetchRingData();
+
+ // final postClaimRequest = PostClaimRequestModel(
+ // sncode: ring.sncode,
+ // sender: account.address,
+ // );
+ // final postClaimResponse = await _repository.blueberryRingRepository.postClaim(
+ // postClaimRequest,
+ // );
+
+ // final txSig = await _repository.blueberryRingRepository.sendSyncTransaction(account.privateKey, ring, postClaimResponse, memo);
+
+ // // showNotification(
+ // // translate('no_token_to_claim_miner')
+ // // .replaceFirst('{0}', miner.mep1004TokenId!),
+ // // null,
+ // // );
+
+ return true;
+ }
+
+ Future syncRing(BlueberryRingMiner ring) async{}
+
+
+ Future fetchRingData() async {
+ collectLog('fetchRingData');
+
+ final sleep = await _blueberryRingUseCase.readSleep();
+ final bloodOxygens = await _blueberryRingUseCase.readBloodOxygens();
+ final steps = await _blueberryRingUseCase.readSteps();
+ final heartRate = await _blueberryRingUseCase.readHeartRate();
+
+ final data = {
+ 'sleep': sleep.map((e) => e.toJson()).toList(),
+ 'steps': steps.map((e) => e.toJson()).toList(),
+ 'heartRate': heartRate.map((e) => e.toJson()).toList(),
+ 'bloodOxygens': bloodOxygens.map((e) => e.toJson()).toList(),
+ };
+
+
+ final content = json.encode(data);
+
+ collectLog('fetchRingData:content : $content');
+
+ final mep3355 = {
+ 'format': 'MEP-3355',
+ 'version': '1.0.0',
+ 'metadata': {
+ 'data_source': 'BlueberryRingV1',
+ 'data_collection_method': 'bluetooth',
+ 'preprocessing': 'weighted average of data',
+ },
+ 'data': [
+ {
+ 'type': 'sensor',
+ // 'content': await compress(content),
+ 'compression': 'brotli',
+ },
+ ],
+ };
+
+ collectLog('fetchRingData:content : $mep3355');
+
+ final returndataMap = {
+ 'json': mep3355,
+ 'data': data,
+ };
+ final returnDataJson = json.encode(returndataMap);
+ return returnDataJson;
+ }
+
+}
diff --git a/lib/features/common/packages/bluetooth/blueberry_ring/domain/blueberry_ring_use_case.dart b/lib/features/common/packages/bluetooth/blueberry_ring/domain/blueberry_ring_use_case.dart
new file mode 100644
index 00000000..3d1b34f8
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blueberry_ring/domain/blueberry_ring_use_case.dart
@@ -0,0 +1,296 @@
+import 'dart:async';
+import 'dart:typed_data';
+
+import 'package:datadashwallet/app/logger.dart';
+import 'package:datadashwallet/common/common.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_blue_plus/flutter_blue_plus.dart';
+import 'package:mxc_logic/mxc_logic.dart';
+
+import 'package:datadashwallet/core/core.dart';
+import 'package:datadashwallet/features/settings/subfeatures/chain_configuration/domain/chain_configuration_use_case.dart';
+
+import '../../bluetooth.dart';
+
+final bluetoothServiceUUID =
+ Guid.fromString('0000fff0-0000-1000-8000-00805f9b34fb');
+final bluetoothCharacteristicUUID =
+ Guid.fromString('0000fff6-0000-1000-8000-00805f9b34fb');
+final bluetoothCharacteristicNotificationUUID =
+ Guid.fromString('0000fff7-0000-1000-8000-00805f9b34fb');
+
+class BlueberryRingUseCase extends ReactiveUseCase {
+ BlueberryRingUseCase(this._repository, this._chainConfigurationUseCase,
+ this._bluetoothUseCase) {
+ // initBlueberryRingUseCase();
+ }
+
+ final Web3Repository _repository;
+ final ChainConfigurationUseCase _chainConfigurationUseCase;
+ final BluetoothUseCase _bluetoothUseCase;
+
+ late final ValueStream selectedBlueberryRing = reactive(null);
+ late final ValueStream blueberryRingCharacteristic =
+ reactive(null);
+ late final ValueStream
+ blueberryRingNotificationsCharacteristic = reactive(null);
+
+ late final ValueStream bluetoothStatus =
+ reactive(BluetoothAdapterState.off);
+
+ void initBlueberryRingSelectedActions() {
+ selectedBlueberryRing.listen((event) {
+ if (event != null) {
+ // Blueberry ring is selected
+ }
+ });
+ }
+
+ Future getBlueberryRingBackground() async {
+ _bluetoothUseCase.startScanning(
+ withServices: [bluetoothServiceUUID],
+ // withNames: ['Mi Smart Band 4'],
+ withNames: ['2301'],
+ withKeywords: ['2301'],
+ );
+
+ await Future.delayed(const Duration(seconds: 4), () async {
+ final scanResults = _bluetoothUseCase.scanResults.value;
+ if (scanResults.isNotEmpty) {
+ // only one scan results
+ final scanResult = scanResults.first;
+ update(selectedBlueberryRing, scanResult);
+ } else {
+ throw 'Error: Unable to locate blueberry ring';
+ }
+ });
+
+ _bluetoothUseCase.stopScanner();
+ }
+
+ // Sets the selectedBlueberryRing
+ Future getBlueberryRingsNearby(BuildContext context) async {
+ _bluetoothUseCase.startScanning(
+ withServices: [bluetoothServiceUUID],
+ );
+
+ await Future.delayed(const Duration(seconds: 3), () async {
+ final scanResults = _bluetoothUseCase.scanResults.value;
+ if (scanResults.length > 1 || scanResults.isEmpty) {
+ // We need to let the user to choose If two or more devices of rings are available and even If empty maybe let the user to wait
+ final scanResult = await showBlueberryRingsBottomSheet(
+ context,
+ );
+ if (scanResult != null) {
+ update(selectedBlueberryRing, scanResult);
+ }
+ } else {
+ // only one scan results
+ final scanResult = scanResults.first;
+ update(selectedBlueberryRing, scanResult);
+ }
+ });
+
+ _bluetoothUseCase.stopScanner();
+ }
+
+ Future connectToBlueberryRing() async {
+ await _bluetoothUseCase
+ .connectionHandler(selectedBlueberryRing.value!.device);
+ }
+
+ Future getBlueberryRingBluetoothService() async {
+ return await _getBlueberryRingPrimaryService(bluetoothServiceUUID);
+ }
+
+ /// This function will check the blueberry ring, connection
+ Future checkEstablishment(Future Function() func) async {
+ collectLog('checkEstablishment');
+
+ final isBlueberryRingAvailable = selectedBlueberryRing.hasValue;
+ collectLog(
+ 'checkEstablishment:isBlueberryRingAvailable $isBlueberryRingAvailable');
+
+ if (!isBlueberryRingAvailable) {
+ await getBlueberryRingBackground();
+ }
+
+ bool isBlueberryRingConnected =
+ selectedBlueberryRing.value?.device.isConnected ?? false;
+ collectLog(
+ 'checkEstablishment:isBlueberryRingConnected $isBlueberryRingConnected');
+
+ if (!isBlueberryRingConnected) {
+ await _bluetoothUseCase
+ .connectionHandler(selectedBlueberryRing.value!.device);
+ isBlueberryRingConnected =
+ selectedBlueberryRing.value?.device.isConnected ?? false;
+ if (!isBlueberryRingConnected) {
+ throw 'Error: Unable to connect to the bluetooth device';
+ }
+ }
+
+ final isBlueberryRingCharacteristicAvailable =
+ blueberryRingCharacteristic.hasValue;
+ collectLog(
+ 'checkEstablishment:isBlueberryRingCharacteristicAvailable $isBlueberryRingCharacteristicAvailable');
+ if (!isBlueberryRingCharacteristicAvailable) {
+ await getBlueberryRingCharacteristic();
+ }
+
+ final isBlueberryRingNotificationsCharacteristicAvailable =
+ blueberryRingNotificationsCharacteristic.hasValue;
+ collectLog(
+ 'checkEstablishment:isBlueberryRingNotificationsCharacteristicAvailable $isBlueberryRingNotificationsCharacteristicAvailable');
+ if (!isBlueberryRingNotificationsCharacteristicAvailable) {
+ await getBlueberryRingNotificationsCharacteristic();
+ }
+
+ final isNotifying =
+ blueberryRingNotificationsCharacteristic.value!.isNotifying;
+ collectLog(
+ 'checkEstablishment:isBlueberryRingNotificationsCharacteristicNotifiying $isNotifying');
+ if (!isNotifying) {
+ blueberryRingNotificationsCharacteristic.value!.setNotifyValue(true);
+ }
+
+ return await func();
+ }
+
+ Future readData(Uint8List Function() getCommandFunc, String dataName,
+ T Function(Uint8List) resolveData, bool isFrag) async {
+ return checkEstablishment(
+ () async {
+ final command = getCommandFunc();
+ collectLog('read$dataName:command $command');
+
+ // Prepare to listen for the response before writing
+ final Stream> stream =
+ blueberryRingNotificationsCharacteristic.value!.lastValueStream;
+
+ // Create a completer to handle the response
+ final Completer> completer = Completer>();
+
+ Timer? timer;
+ List data = [];
+
+ // Subscribe to the stream and filter for the specific command
+ final StreamSubscription> subscription =
+ stream.listen((element) {
+ if (element.isNotEmpty && element.first == command.first) {
+ timer?.cancel();
+ collectLog('read$dataName:element $element');
+ data.addAll(element);
+
+ if (!completer.isCompleted && !isFrag) {
+ completer.complete(data);
+ } else if (!completer.isCompleted &&
+ isFrag &&
+ element.last == 255) {
+ completer.complete(data);
+ } else {
+ timer = Timer(
+ const Duration(milliseconds: 5000),
+ () => completer.complete(data),
+ );
+ }
+ }
+ });
+
+ blueberryRingCharacteristic.value?.write(command);
+
+ // Wait for the expected value to be received
+ final List value = await completer.future;
+
+ // Cancel the subscription to avoid memory leaks
+ await subscription.cancel();
+
+ collectLog('read$dataName:value $value');
+ return resolveData(Uint8List.fromList(value));
+ },
+ );
+ }
+
+ Future readLevel() async => readData(BlueberryCommands.readLevel,
+ 'Level', BlueberryResolves.readLevel, BlueberryMethods.readLevel.frag);
+
+ Future readVersion() async => readData(
+ BlueberryCommands.readVersion,
+ 'Version',
+ BlueberryResolves.readVersion,
+ BlueberryMethods.readVersion.frag);
+
+ Future readTime() async => readData(
+ BlueberryCommands.readTime,
+ 'Time',
+ BlueberryResolves.readTime,
+ BlueberryMethods.readTime.frag);
+
+ Future> readSleep() async =>
+ readData>(BlueberryCommands.readSleep, 'Sleep',
+ BlueberryResolves.readSleep, BlueberryMethods.readSleep.frag);
+
+ Future> readBloodOxygens() async =>
+ readData>(
+ BlueberryCommands.readBloodOxygens,
+ 'BloodOxygens',
+ BlueberryResolves.readBloodOxygens,
+ BlueberryMethods.readBloodOxygens.frag);
+
+ Future> readSteps() async => readData>(
+ BlueberryCommands.readSteps,
+ 'Steps',
+ BlueberryResolves.readSteps,
+ BlueberryMethods.readSteps.frag);
+
+ Future> readHeartRate() async =>
+ readData>(
+ BlueberryCommands.readHeartRates,
+ 'HeartRate',
+ BlueberryResolves.readHeartRates,
+ BlueberryMethods.readHeartRates.frag);
+
+ Future getBlueberryRingCharacteristic() async {
+ final service = await getBlueberryRingBluetoothService();
+ final resp = await _getBlueberryRingCharacteristic(
+ service, bluetoothCharacteristicUUID);
+ update(blueberryRingCharacteristic, resp);
+ return resp;
+ }
+
+ Future
+ getBlueberryRingNotificationsCharacteristic() async {
+ final service = await getBlueberryRingBluetoothService();
+ final resp = await _getBlueberryRingCharacteristic(
+ service, bluetoothCharacteristicNotificationUUID);
+ update(blueberryRingNotificationsCharacteristic, resp);
+ return resp;
+ }
+
+ // Future startBlueberryRingCharacteristicNotifications() async {
+ // final characteristicNotifications =
+ // await getBlueberryRingCharacteristicNotifications();
+ // await characteristicNotifications.setNotifyValue(true);
+ // characteristicNotifications.lastValueStream.listen((event) {});
+ // final value = characteristicNotifications.read();
+ // }
+
+ Future _getBlueberryRingPrimaryService(
+ Guid serviceUUID,
+ ) async {
+ return await BluePlusBluetoothUtils.getPrimaryService(
+ selectedBlueberryRing.value!,
+ serviceUUID,
+ );
+ }
+
+ Future _getBlueberryRingCharacteristic(
+ BluetoothService service,
+ Guid characteristicUUID,
+ ) async {
+ return BluePlusBluetoothUtils.getCharacteristicWithService(
+ service,
+ characteristicUUID,
+ );
+ }
+}
diff --git a/lib/features/common/packages/bluetooth/blueberry_ring/domain/domain.dart b/lib/features/common/packages/bluetooth/blueberry_ring/domain/domain.dart
new file mode 100644
index 00000000..01bb0256
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blueberry_ring/domain/domain.dart
@@ -0,0 +1,4 @@
+export 'blueberry_repository.dart';
+export 'blueberry_ring_use_case.dart';
+export 'blueberry_ring_background_notifications_use_case.dart';
+export 'blueberry_ring_background_sync_use_case.dart';
diff --git a/lib/features/common/packages/bluetooth/blueberry_ring/entities/blood_oxygens_data.dart b/lib/features/common/packages/bluetooth/blueberry_ring/entities/blood_oxygens_data.dart
new file mode 100644
index 00000000..f429ea3c
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blueberry_ring/entities/blood_oxygens_data.dart
@@ -0,0 +1,61 @@
+import 'dart:convert';
+
+class BloodOxygensData {
+ int id;
+ int value;
+ int date;
+ BloodOxygensData({
+ required this.id,
+ required this.value,
+ required this.date,
+ });
+
+ BloodOxygensData copyWith({
+ int? id,
+ int? value,
+ int? date,
+ }) {
+ return BloodOxygensData(
+ id: id ?? this.id,
+ value: value ?? this.value,
+ date: date ?? this.date,
+ );
+ }
+
+ Map toMap() {
+ return {
+ 'id': id,
+ 'value': value,
+ 'date': date,
+ };
+ }
+
+ factory BloodOxygensData.fromMap(Map map) {
+ return BloodOxygensData(
+ id: map['id']?.toInt() ?? 0,
+ value: map['value']?.toInt() ?? 0,
+ date: map['date']?.toInt() ?? 0,
+ );
+ }
+
+ String toJson() => json.encode(toMap());
+
+ factory BloodOxygensData.fromJson(String source) =>
+ BloodOxygensData.fromMap(json.decode(source));
+
+ @override
+ String toString() => 'BloodOxygensData(id: $id, value: $value, date: $date)';
+
+ @override
+ bool operator ==(Object other) {
+ if (identical(this, other)) return true;
+
+ return other is BloodOxygensData &&
+ other.id == id &&
+ other.value == value &&
+ other.date == date;
+ }
+
+ @override
+ int get hashCode => id.hashCode ^ value.hashCode ^ date.hashCode;
+}
diff --git a/lib/features/common/packages/bluetooth/blueberry_ring/entities/entities.dart b/lib/features/common/packages/bluetooth/blueberry_ring/entities/entities.dart
new file mode 100644
index 00000000..ceb6ea69
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blueberry_ring/entities/entities.dart
@@ -0,0 +1,5 @@
+export 'sleep_data.dart';
+export 'periodic_sleep_data.dart';
+export 'steps_data.dart';
+export 'heart_rate_data.dart';
+export 'blood_oxygens_data.dart';
diff --git a/lib/features/common/packages/bluetooth/blueberry_ring/entities/heart_rate_data.dart b/lib/features/common/packages/bluetooth/blueberry_ring/entities/heart_rate_data.dart
new file mode 100644
index 00000000..e7ac01df
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blueberry_ring/entities/heart_rate_data.dart
@@ -0,0 +1,61 @@
+import 'dart:convert';
+
+class HeartRateData {
+ int id;
+ int value;
+ int date;
+ HeartRateData({
+ required this.id,
+ required this.value,
+ required this.date,
+ });
+
+ HeartRateData copyWith({
+ int? id,
+ int? value,
+ int? date,
+ }) {
+ return HeartRateData(
+ id: id ?? this.id,
+ value: value ?? this.value,
+ date: date ?? this.date,
+ );
+ }
+
+ Map toMap() {
+ return {
+ 'id': id,
+ 'value': value,
+ 'date': date,
+ };
+ }
+
+ factory HeartRateData.fromMap(Map map) {
+ return HeartRateData(
+ id: map['id']?.toInt() ?? 0,
+ value: map['value']?.toInt() ?? 0,
+ date: map['date']?.toInt() ?? 0,
+ );
+ }
+
+ String toJson() => json.encode(toMap());
+
+ factory HeartRateData.fromJson(String source) =>
+ HeartRateData.fromMap(json.decode(source));
+
+ @override
+ String toString() => 'HeartRateData(id: $id, value: $value, date: $date)';
+
+ @override
+ bool operator ==(Object other) {
+ if (identical(this, other)) return true;
+
+ return other is HeartRateData &&
+ other.id == id &&
+ other.value == value &&
+ other.date == date;
+ }
+
+ @override
+ int get hashCode => id.hashCode ^ value.hashCode ^ date.hashCode;
+}
diff --git a/lib/features/common/packages/bluetooth/blueberry_ring/entities/periodic_sleep_data.dart b/lib/features/common/packages/bluetooth/blueberry_ring/entities/periodic_sleep_data.dart
new file mode 100644
index 00000000..bb408757
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blueberry_ring/entities/periodic_sleep_data.dart
@@ -0,0 +1,54 @@
+import 'dart:convert';
+
+class PeriodicSleepData {
+ int date;
+ int value;
+ PeriodicSleepData({
+ required this.date,
+ required this.value,
+ });
+
+ PeriodicSleepData copyWith({
+ int? date,
+ int? value,
+ }) {
+ return PeriodicSleepData(
+ date: date ?? this.date,
+ value: value ?? this.value,
+ );
+ }
+
+ Map toMap() {
+ return {
+ 'date': date,
+ 'value': value,
+ };
+ }
+
+ factory PeriodicSleepData.fromMap(Map map) {
+ return PeriodicSleepData(
+ date: map['date']?.toInt() ?? 0,
+ value: map['value']?.toInt() ?? 0,
+ );
+ }
+
+ String toJson() => json.encode(toMap());
+
+ factory PeriodicSleepData.fromJson(String source) =>
+ PeriodicSleepData.fromMap(json.decode(source));
+
+ @override
+ String toString() => 'PeriodicSleepData(date: $date, value: $value)';
+
+ @override
+ bool operator ==(Object other) {
+ if (identical(this, other)) return true;
+
+ return other is PeriodicSleepData &&
+ other.date == date &&
+ other.value == value;
+ }
+
+ @override
+ int get hashCode => date.hashCode ^ value.hashCode;
+}
diff --git a/lib/features/common/packages/bluetooth/blueberry_ring/entities/sleep_data.dart b/lib/features/common/packages/bluetooth/blueberry_ring/entities/sleep_data.dart
new file mode 100644
index 00000000..c0b8a384
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blueberry_ring/entities/sleep_data.dart
@@ -0,0 +1,74 @@
+import 'dart:convert';
+
+import 'package:flutter/foundation.dart';
+
+class SleepData {
+ int id;
+ List sleeps;
+ int length;
+ int date;
+ SleepData({
+ required this.id,
+ required this.sleeps,
+ required this.length,
+ required this.date,
+ });
+
+ SleepData copyWith({
+ int? id,
+ List? sleeps,
+ int? length,
+ int? date,
+ }) {
+ return SleepData(
+ id: id ?? this.id,
+ sleeps: sleeps ?? this.sleeps,
+ length: length ?? this.length,
+ date: date ?? this.date,
+ );
+ }
+
+ Map toMap() {
+ return {
+ 'id': id,
+ 'sleeps': sleeps,
+ 'length': length,
+ 'date': date,
+ };
+ }
+
+ factory SleepData.fromMap(Map map) {
+ return SleepData(
+ id: map['id']?.toInt() ?? 0,
+ sleeps: List.from(map['sleeps']),
+ length: map['length']?.toInt() ?? 0,
+ date: map['date']?.toInt() ?? 0,
+ );
+ }
+
+ String toJson() => json.encode(toMap());
+
+ factory SleepData.fromJson(String source) =>
+ SleepData.fromMap(json.decode(source));
+
+ @override
+ String toString() {
+ return 'SleepData(id: $id, sleeps: $sleeps, length: $length, date: $date)';
+ }
+
+ @override
+ bool operator ==(Object other) {
+ if (identical(this, other)) return true;
+
+ return other is SleepData &&
+ other.id == id &&
+ listEquals(other.sleeps, sleeps) &&
+ other.length == length &&
+ other.date == date;
+ }
+
+ @override
+ int get hashCode {
+ return id.hashCode ^ sleeps.hashCode ^ length.hashCode ^ date.hashCode;
+ }
+}
diff --git a/lib/features/common/packages/bluetooth/blueberry_ring/entities/steps_data.dart b/lib/features/common/packages/bluetooth/blueberry_ring/entities/steps_data.dart
new file mode 100644
index 00000000..acdd72b1
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blueberry_ring/entities/steps_data.dart
@@ -0,0 +1,83 @@
+import 'dart:convert';
+
+class StepsData {
+ int id;
+ int step;
+ int kcal;
+ int km;
+ int date;
+ StepsData({
+ required this.id,
+ required this.step,
+ required this.kcal,
+ required this.km,
+ required this.date,
+ });
+
+ StepsData copyWith({
+ int? id,
+ int? step,
+ int? kcal,
+ int? km,
+ int? date,
+ }) {
+ return StepsData(
+ id: id ?? this.id,
+ step: step ?? this.step,
+ kcal: kcal ?? this.kcal,
+ km: km ?? this.km,
+ date: date ?? this.date,
+ );
+ }
+
+ Map toMap() {
+ return {
+ 'id': id,
+ 'step': step,
+ 'kcal': kcal,
+ 'km': km,
+ 'date': date,
+ };
+ }
+
+ factory StepsData.fromMap(Map map) {
+ return StepsData(
+ id: map['id']?.toInt() ?? 0,
+ step: map['step']?.toInt() ?? 0,
+ kcal: map['kcal']?.toInt() ?? 0,
+ km: map['km']?.toInt() ?? 0,
+ date: map['date']?.toInt() ?? 0,
+ );
+ }
+
+ String toJson() => json.encode(toMap());
+
+ factory StepsData.fromJson(String source) =>
+ StepsData.fromMap(json.decode(source));
+
+ @override
+ String toString() {
+ return 'StepsData(id: $id, step: $step, kcal: $kcal, km: $km, date: $date)';
+ }
+
+ @override
+ bool operator ==(Object other) {
+ if (identical(this, other)) return true;
+
+ return other is StepsData &&
+ other.id == id &&
+ other.step == step &&
+ other.kcal == kcal &&
+ other.km == km &&
+ other.date == date;
+ }
+
+ @override
+ int get hashCode {
+ return id.hashCode ^
+ step.hashCode ^
+ kcal.hashCode ^
+ km.hashCode ^
+ date.hashCode;
+ }
+}
diff --git a/lib/features/common/packages/bluetooth/blueberry_ring/utils/blueberry_commands_utils.dart b/lib/features/common/packages/bluetooth/blueberry_ring/utils/blueberry_commands_utils.dart
new file mode 100644
index 00000000..79c5a1a2
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blueberry_ring/utils/blueberry_commands_utils.dart
@@ -0,0 +1,46 @@
+import 'dart:typed_data';
+
+class Method {
+ final int uid;
+ final List? arg;
+ final bool frag;
+
+ Method({required this.uid, this.arg, this.frag = false});
+}
+
+class BlueberryCommandsUtils {
+ static List radix16bcd(List array, {bool no0x = false}) {
+ return array.map((v) {
+ String s = v.toRadixString(16);
+ String b = '0x${s.length == 1 ? '0$s' : s}';
+ return no0x ? bcd(b) : b;
+ }).toList();
+ }
+
+ static String bcd(String str) {
+ return str.replaceAll('0x', '');
+ }
+
+ static List> splitArrayByLength(List arr, int length) {
+ List> result = [];
+ for (int i = 0; i < arr.length; i += length) {
+ int end = (i + length < arr.length) ? i + length : arr.length;
+ result.add(arr.sublist(i, end));
+ }
+ return result;
+ }
+
+ static Uint8List parseMethodBytes(Method method,
+ [List args = const []]) {
+ List command = [method.uid, ...List.filled(15, 0x00)];
+ int ai = 0;
+ for (final index in method.arg ?? []) {
+ command[index] = args.length > ai ? args[ai] : command[index];
+ ai++;
+ }
+ int last = command.length - 1;
+ int crc = command.sublist(0, last - 1).reduce((t, c) => t + c);
+ command[last] = crc & 0xFF;
+ return Uint8List.fromList(command);
+ }
+}
diff --git a/lib/features/common/packages/bluetooth/blueberry_ring/utils/blueberry_configs.dart b/lib/features/common/packages/bluetooth/blueberry_ring/utils/blueberry_configs.dart
new file mode 100644
index 00000000..4757063b
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blueberry_ring/utils/blueberry_configs.dart
@@ -0,0 +1,165 @@
+import 'dart:typed_data';
+
+// import 'package:dayjs/dayjs.dart';
+import 'package:collection/collection.dart';
+import 'package:datadashwallet/features/common/common.dart';
+
+typedef GetType = String Function();
+
+class GetMappings {
+ static const int last = 0;
+ static const int pos = 1;
+ static const int next = 2;
+}
+
+class BlueberryMethods {
+ static final readLevel = Method(uid: 0x13, arg: [1]);
+ static final readVersion = Method(uid: 0x27);
+ static final readTime = Method(uid: 0x41);
+ static final readSteps = Method(uid: 0x52, arg: [1], frag: true);
+ static final readSleep = Method(uid: 0x53, arg: [1, 2, 3], frag: true);
+ static final readHeartRates = Method(uid: 0x55, arg: [1, 2, 3], frag: true);
+ static final readBloodOxygens = Method(uid: 0x66, arg: [1, 2, 3], frag: true);
+ static final writeTime = Method(uid: 0x01, arg: [1, 2, 3, 4, 5, 6]);
+ static final writeRestore = Method(uid: 0x12, arg: []);
+}
+
+class BlueberryCommands {
+ static Uint8List readLevel() =>
+ BlueberryCommandsUtils.parseMethodBytes(BlueberryMethods.readLevel);
+ static Uint8List readVersion() =>
+ BlueberryCommandsUtils.parseMethodBytes(BlueberryMethods.readVersion);
+ static Uint8List readTime() =>
+ BlueberryCommandsUtils.parseMethodBytes(BlueberryMethods.readTime);
+
+ static Uint8List readSteps() {
+ const mapping = GetMappings.last;
+ return BlueberryCommandsUtils.parseMethodBytes(
+ BlueberryMethods.readSteps, [mapping]);
+ }
+
+ static Uint8List readSleep() {
+ const mapping = GetMappings.last;
+ return BlueberryCommandsUtils.parseMethodBytes(
+ BlueberryMethods.readSleep, [mapping]);
+ }
+
+ static Uint8List readHeartRates() {
+ const mapping = GetMappings.last;
+ return BlueberryCommandsUtils.parseMethodBytes(
+ BlueberryMethods.readHeartRates, [mapping]);
+ }
+
+ static Uint8List readBloodOxygens() {
+ const mapping = GetMappings.last;
+ return BlueberryCommandsUtils.parseMethodBytes(
+ BlueberryMethods.readBloodOxygens, [mapping]);
+ }
+}
+
+class BlueberryResolves {
+ static int readLevel(Uint8List data) => data[1];
+
+ static String readVersion(Uint8List data) {
+ final values = BlueberryCommandsUtils.radix16bcd(data, no0x: true)
+ .map(int.parse)
+ .toList();
+ return '${values[1]}.${values[2]}${values[3]}${values[4]}';
+ }
+
+ static Uint8List readTime(Uint8List data) => data;
+
+ static List readSleep(Uint8List data) {
+ final sleepDataList = BlueberryCommandsUtils.splitArrayByLength(
+ BlueberryCommandsUtils.radix16bcd(data), 130)
+ .map((e) => e.map(int.parse).toList())
+ .where((item) => item[1] != 0xFF)
+ .map((item) {
+ final int id = parseInt([item[1], item[2]]);
+ final int date =
+ parseDate([item[3], item[4], item[5], item[6], item[7], item[8]]);
+ final int length = item[9];
+ final List sleeps = item.sublist(10, item.length);
+ return SleepData(id: id, sleeps: sleeps, length: length, date: date);
+ }).toList();
+
+ final periodicSleepData = sleepDataList
+ .expand((item) => item.sleeps
+ .mapIndexed(
+ (e, index) {
+ final int date = item.date + (index * 60);
+ return PeriodicSleepData(date: date, value: e);
+ },
+ )
+ .where((e) => e.value != 0)
+ .toList())
+ .toList()
+ ..sort((a, b) => a.date.compareTo(b.date));
+
+ return periodicSleepData;
+ }
+
+ static List readSteps(Uint8List data) {
+ return BlueberryCommandsUtils.splitArrayByLength(
+ BlueberryCommandsUtils.radix16bcd(data), 25)
+ .map((item) => item.map(int.parse).toList())
+ .toList()
+ .where((item) => item[1] != 0xFF)
+ .map((item) {
+ final int id = parseInt([item[1], item[2]]);
+ final int date =
+ parseDate([item[3], item[4], item[5], item[6], item[7], item[8]]);
+ final int step = parseInt([item[9], item[10]]);
+ final int kcal = parseInt([item[11], item[12]]);
+ final int km = parseInt([item[13], item[14]]);
+ return StepsData(id: id, step: step, kcal: kcal, km: km, date: date);
+ }).toList()
+ ..sort((a, b) => a.date.compareTo(b.date));
+ }
+
+ static List readHeartRates(Uint8List data) {
+ return BlueberryCommandsUtils.splitArrayByLength(
+ BlueberryCommandsUtils.radix16bcd(data), 10)
+ .map((item) => item.map(int.parse).toList())
+ .toList()
+ .where((item) => item[1] != 0xFF)
+ .map((item) {
+ final int id = parseInt([item[1], item[2]]);
+ final int date =
+ parseDate([item[3], item[4], item[5], item[6], item[7], item[8]]);
+ final int value = item[9];
+ return HeartRateData(id: id, value: value, date: date);
+ })
+ .where((e) => e.value != 0)
+ .toList()
+ ..sort((a, b) => a.date.compareTo(b.date));
+ }
+
+ static List readBloodOxygens(Uint8List data) {
+ return BlueberryCommandsUtils.splitArrayByLength(
+ BlueberryCommandsUtils.radix16bcd(data), 10)
+ .map((item) => item.map(int.parse).toList())
+ .where((item) => item[1] != 0xFF)
+ .map((item) {
+ final int id = parseInt([item[1], item[2]]);
+ final int date =
+ parseDate([item[3], item[4], item[5], item[6], item[7], item[8]]);
+ final int value = item[9];
+ return BloodOxygensData(id: id, value: value, date: date);
+ }).toList()
+ ..sort((a, b) => a.date.compareTo(b.date));
+ }
+}
+
+int parseDate(List data) {
+ // 200x24-0x06-0x05 0x06:0x39:0x02
+ final parts = BlueberryCommandsUtils.radix16bcd(data, no0x: true);
+ final ymd = '20${parts[0]}-${parts[1]}-${parts[2]}';
+ final hms = '${parts[3]}:${parts[4]}:${parts[5]}';
+ final date = DateTime.parse('$ymd $hms');
+ return date.millisecondsSinceEpoch ~/ 1000;
+}
+
+int parseInt(List data) {
+ return int.parse(data.reversed.map((e) => e.toString()).join(''));
+}
diff --git a/lib/features/common/packages/bluetooth/blueberry_ring/utils/blueberry_ring_data_analyzer.dart b/lib/features/common/packages/bluetooth/blueberry_ring/utils/blueberry_ring_data_analyzer.dart
new file mode 100644
index 00000000..349b5d74
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blueberry_ring/utils/blueberry_ring_data_analyzer.dart
@@ -0,0 +1,38 @@
+// Define the weights for each sleep stage
+final Map weights = {
+ 1: 1.0, // Deep sleep
+ 2: 0.5, // Light sleep
+ 3: 0.5, // REM sleep
+ 4: 0.1, // Awake
+ 5: 0.1, // Awake
+};
+// Define the threshold for normal sleep quality
+const double threshold = 0.5;
+
+class BlueberryRingDataAnalyzer {
+ static double calculateSleepQuality(List sleepData) {
+ double totalWeight = 0.0;
+ double weightedSum = 0.0;
+
+ for (int stage in sleepData) {
+ double weight = weights[stage] ?? 0.0;
+ weightedSum += weight;
+ totalWeight += 1.0;
+ }
+
+ return weightedSum / totalWeight;
+ }
+
+ static bool isSleepQualityBelowNormal(List sleepData) {
+ double sleepQuality = calculateSleepQuality(sleepData);
+ return sleepQuality < threshold;
+ }
+
+ static bool isSleepQualityNormal(List sleepData) {
+ if (isSleepQualityBelowNormal(sleepData)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/lib/features/common/packages/bluetooth/blueberry_ring/utils/blueberry_ring_data_groupings.dart b/lib/features/common/packages/bluetooth/blueberry_ring/utils/blueberry_ring_data_groupings.dart
new file mode 100644
index 00000000..24c870e9
--- /dev/null
+++ b/lib/features/common/packages/bluetooth/blueberry_ring/utils/blueberry_ring_data_groupings.dart
@@ -0,0 +1,130 @@
+import 'package:intl/intl.dart';
+
+class Grouping {
+ List