Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fitbit integration 💪🏼 #705

Draft
wants to merge 32 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ce941d5
feat: fitbit credentials base for study model
ibrahimozkn Dec 9, 2024
ef46183
feat: fitbit questionnaire form
ibrahimozkn Dec 11, 2024
da5b18d
fix: add fitbit question json type to question model
ibrahimozkn Dec 11, 2024
5fd3fcc
fix: form control for fitbit question
ibrahimozkn Dec 11, 2024
1afcb22
fix: fitbit question form
ibrahimozkn Dec 11, 2024
63ad0e7
feat: check if credentials exists when adding fitbit question
ibrahimozkn Dec 16, 2024
5905008
chore: melos fix
ibrahimozkn Dec 16, 2024
b7bba79
chore: Apply static analysis changes
ibrahimozkn Dec 16, 2024
91915a1
feat: sleep and step data
ibrahimozkn Dec 17, 2024
a8628d0
feat: migration file
ibrahimozkn Dec 17, 2024
61200e7
ui: fitbit credentials tab, instructions text
ibrahimozkn Dec 18, 2024
408b5ee
chore: Apply static analysis changes
ibrahimozkn Dec 18, 2024
38552a3
chore: translation
ibrahimozkn Dec 18, 2024
ba86d36
Merge remote-tracking branch 'origin/feat/fitbit' into feat/fitbit
ibrahimozkn Dec 18, 2024
c9491b2
chore: melos fix
ibrahimozkn Dec 18, 2024
f46a544
chore: add fitbitter package
ibrahimozkn Jan 4, 2025
e297a36
feat: add FitbitHandler
ibrahimozkn Jan 4, 2025
7908a89
fix: passing taskId to fitbit_question_widget.dart
ibrahimozkn Jan 4, 2025
9176ec4
chore: rename fitbit handler
ibrahimozkn Jan 8, 2025
e7585ab
feat: sync Fitbit data on button press
ibrahimozkn Jan 11, 2025
c3b9251
fix: update Fitbit integration and callback URLs
ibrahimozkn Jan 11, 2025
2de99c5
feat(fitbit): update sleep data model and sleep fetch
ibrahimozkn Jan 12, 2025
e75fa28
fix: proper onDone handling for fitbit_question_widget.dart
ibrahimozkn Jan 12, 2025
f99765d
feat: add sleep data fetching
ibrahimozkn Jan 16, 2025
e50a481
feat: add validation for Fitbit credentials in forms
ibrahimozkn Jan 16, 2025
a909949
feat(questionnaire): add validation for Fitbit question type
ibrahimozkn Jan 16, 2025
7cfe81e
feat: update Fitbit data handling and syncing process
ibrahimozkn Jan 24, 2025
a038751
chore: melos fix
ibrahimozkn Jan 24, 2025
6a9258c
refactor(fitbit): sleep data fetching logic
ibrahimozkn Jan 24, 2025
2374eef
feat(fitbit): refactor _findLatestDataEntry function in fitbit_handle…
ibrahimozkn Jan 24, 2025
99134a7
feat: Add Fitbit credential deletion on study removal
ibrahimozkn Jan 24, 2025
854f687
chore: melos fix
ibrahimozkn Jan 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions app/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
</intent>
</queries>


<application
android:label="StudyU"
android:name="${applicationName}"
Expand All @@ -52,6 +53,16 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Begin flutter_web_auth -->
<activity android:name="com.linusu.flutter_web_auth.CallbackActivity" android:exported="true">
<intent-filter android:label="flutter_web_auth">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="example" />
</intent-filter>
</activity>
<!-- End flutter_web_auth -->
<!-- Begin flutter_local_notifications -->
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
Expand All @@ -68,6 +79,7 @@
<meta-data
android:name="flutterEmbedding"
android:value="2" />

</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
Expand Down
63 changes: 34 additions & 29 deletions app/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ PODS:
- Flutter
- flutter_timezone (0.0.1):
- Flutter
- flutter_web_auth (0.6.0):
- Flutter
- just_audio (0.0.1):
- Flutter
- package_info_plus (0.4.5):
Expand All @@ -25,16 +27,15 @@ PODS:
- Flutter
- record_darwin (1.0.0):
- Flutter
- FlutterMacOS
- Sentry/HybridSDK (8.36.0)
- sentry_flutter (8.9.0):
- Sentry/HybridSDK (8.42.0)
- sentry_flutter (8.12.0):
- Flutter
- FlutterMacOS
- Sentry/HybridSDK (= 8.36.0)
- Sentry/HybridSDK (= 8.42.0)
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- sqflite (0.0.3):
- sqflite_darwin (0.0.4):
- Flutter
- FlutterMacOS
- url_launcher_ios (0.0.1):
Expand All @@ -57,14 +58,15 @@ DEPENDENCIES:
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- flutter_timezone (from `.symlinks/plugins/flutter_timezone/ios`)
- flutter_web_auth (from `.symlinks/plugins/flutter_web_auth/ios`)
- just_audio (from `.symlinks/plugins/just_audio/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- record_darwin (from `.symlinks/plugins/record_darwin/ios`)
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
Expand All @@ -91,6 +93,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
flutter_timezone:
:path: ".symlinks/plugins/flutter_timezone/ios"
flutter_web_auth:
:path: ".symlinks/plugins/flutter_web_auth/ios"
just_audio:
:path: ".symlinks/plugins/just_audio/ios"
package_info_plus:
Expand All @@ -105,8 +109,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/sentry_flutter/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sqflite:
:path: ".symlinks/plugins/sqflite/darwin"
sqflite_darwin:
:path: ".symlinks/plugins/sqflite_darwin/darwin"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
video_player_avfoundation:
Expand All @@ -117,28 +121,29 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"

SPEC CHECKSUMS:
app_links: e7a6750a915a9e161c58d91bc610e8cd1d4d0ad0
audio_session: 088d2483ebd1dc43f51d253d4a1c517d9a2e7207
camera_avfoundation: dd002b0330f4981e1bbcb46ae9b62829237459a4
app_links: 3da4c36b46cac3bf24eb897f1a6ce80bda109874
audio_session: f08db0697111ac84ba46191b55488c0563bb29c6
camera_avfoundation: 04b44aeb14070126c6529e5ab82cc7c9fca107cf
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_file_dialog: 4c014a45b105709a27391e266c277d7e588e9299
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
flutter_timezone: ffb07bdad3c6276af8dada0f11978d8a1f8a20bb
just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
record_darwin: df0a677188e5fed18472550298e675f19ddaffbe
Sentry: f8374b5415bc38dfb5645941b3ae31230fbeae57
sentry_flutter: 0eb93e5279eb41e2392212afe1ccd2fecb4f8cbe
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
webview_flutter_wkwebview: 0982481e3d9c78fd5c6f62a002fcd24fc791f1e4
flutter_file_dialog: ca8d7fbd1772d4f0c2777b4ab20a7787ef4e7dd8
flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
flutter_timezone: ee50ce7786b5fde27e2fe5375bbc8c9661ffc13f
flutter_web_auth: e5618c2e8a6822d86a0dc49f08d00a7ba25cfafd
just_audio: 6c031bb61297cf218b4462be616638e81c058e97
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
record_darwin: fb1f375f1d9603714f55b8708a903bbb91ffdb0a
Sentry: 38ed8bf38eab5812787274bf591e528074c19e02
sentry_flutter: a72ca0eb6e78335db7c4ddcddd1b9f6c8ed5b764
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b
wakelock_plus: 04623e3f525556020ebd4034310f20fe7fda8b49
webview_flutter_wkwebview: 44d4dee7d7056d5ad185d25b38404436d56c547c

PODFILE CHECKSUM: 666752a84cc2c75e2476eadd0afa8f31ee5056b0

COCOAPODS: 1.15.2
COCOAPODS: 1.16.2
2 changes: 2 additions & 0 deletions app/lib/constants.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const String playStoreUrl =
'https://play.google.com/store/apps/details?id=health.studyu.app';
const String appstoreUrl = 'https://itunes.apple.com/app/id1571991198';
const String fitbitRedirectUrl = 'example://fitbit/auth';
const String fitbitCallbackScheme = 'example';
3 changes: 3 additions & 0 deletions app/lib/screens/app_onboarding/preview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:studyu_app/models/app_state.dart';
import 'package:studyu_app/routes.dart';
import 'package:studyu_app/util/fitbit_handler.dart';
import 'package:studyu_core/core.dart';
import 'package:studyu_flutter_common/studyu_flutter_common.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
Expand All @@ -13,6 +14,7 @@ class Preview {
final AppLanguage appLanguage;
String? selectedRoute;
String? extra;

bool hasRoute() => selectedRoute != null && selectedRoute!.isNotEmpty;
Study? study;
String? selectedStudyObjectId;
Expand Down Expand Up @@ -75,6 +77,7 @@ class Preview {
],
);
subject.delete();
FitbitHandler.deleteFitbitCredentials(subject.studyId);
deleteActiveStudyReference();
selectedStudyObjectId = '';
}
Expand Down
4 changes: 4 additions & 0 deletions app/lib/screens/study/dashboard/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:provider/provider.dart';
import 'package:studyu_app/models/app_state.dart';
import 'package:studyu_app/routes.dart';
import 'package:studyu_app/util/app_analytics.dart';
import 'package:studyu_app/util/fitbit_handler.dart';
import 'package:studyu_app/util/localization.dart';
import 'package:studyu_app/util/schedule_notifications.dart';
import 'package:studyu_core/core.dart';
Expand Down Expand Up @@ -188,6 +189,7 @@ class OptOutAlertDialog extends StatelessWidget {
onPressed: () async {
await subject!.softDelete();
await deleteActiveStudyReference();
await FitbitHandler.deleteFitbitCredentials(subject!.studyId);
if (context.mounted) await cancelNotifications(context);
if (context.mounted) {
Navigator.pushNamedAndRemoveUntil(
Expand Down Expand Up @@ -223,6 +225,8 @@ class DeleteAlertDialog extends StatelessWidget {
try {
await subject!.delete(); // hard-delete
await deleteLocalData();
await FitbitHandler.deleteFitbitCredentials(subject!.studyId);

if (context.mounted) await cancelNotifications(context);
if (context.mounted) {
Navigator.pushNamedAndRemoveUntil(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class _QuestionnaireTaskWidgetState extends State<QuestionnaireTaskWidget> {
key: formKey,
child: QuestionnaireWidget(
widget.task.questions.questions,
taskId: widget.task.id,
header: widget.task.header,
footer: widget.task.footer,
onChange: _responseValidator,
Expand Down
1 change: 1 addition & 0 deletions app/lib/util/cache.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';

Expand Down
Loading
Loading