|
...
|
...
|
@@ -100,6 +100,7 @@ class TodayController extends GetMaterialController { |
|
|
|
final showHealthDataAuthCardStatus = 1.obs;
|
|
|
|
final stressSubtitle = ''.obs;
|
|
|
|
bool _isFirstLoad = true;
|
|
|
|
bool _isHandlingHealthAuthorizationTap = false;
|
|
|
|
|
|
|
|
// ── HRV 表盘引导 Banner ───────────────────
|
|
|
|
final showHrvAdBanner = false.obs;
|
|
...
|
...
|
@@ -133,6 +134,8 @@ class TodayController extends GetMaterialController { |
|
|
|
final v2RealtimeStress = Rxn<V2RealtimeStressData>();
|
|
|
|
final v2ActivityTarget = Rxn<V2ActivityTarget>();
|
|
|
|
|
|
|
|
int _currentStatus = 0;
|
|
|
|
|
|
|
|
@override
|
|
|
|
void onReady() {
|
|
|
|
super.onReady();
|
|
...
|
...
|
@@ -160,9 +163,11 @@ class TodayController extends GetMaterialController { |
|
|
|
|
|
|
|
unawaited(loadDataForDate(today));
|
|
|
|
|
|
|
|
// _checkHealthAuthStatus();
|
|
|
|
|
|
|
|
checkAddFriendVisible();
|
|
|
|
checkHrvAdBannerVisible();
|
|
|
|
checkHealthDataAuthCardVisible();
|
|
|
|
checkHealthDataAuthCardVisible(shouldCaculateAndUpload: true);
|
|
|
|
checkNotificationAuthorizationBannerVisible();
|
|
|
|
checkNotificationCardVisible();
|
|
|
|
if (!Get.find<UserStateService>().isVip) {
|
|
...
|
...
|
@@ -290,40 +295,66 @@ class TodayController extends GetMaterialController { |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> requestHealthAuthorization(
|
|
|
|
Future<void> _checkHealthAuthStatus(
|
|
|
|
{bool isPullToRefresh = false,
|
|
|
|
bool shouldCaculateAndUpload = false}) async {
|
|
|
|
final result = await _hostApi.checkHealthAppAuthorization();
|
|
|
|
_currentStatus = result.status;
|
|
|
|
_refreshHealthDataAuthCardStatus(status: _currentStatus);
|
|
|
|
if (_currentStatus != 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!shouldCaculateAndUpload) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_performCaculateAndUpload(isPullToRefresh: isPullToRefresh);
|
|
|
|
}
|
|
|
|
|
|
|
|
_performCaculateAndUpload({
|
|
|
|
bool isPullToRefresh = false,
|
|
|
|
}) {
|
|
|
|
_performHealthDataUpload();
|
|
|
|
_calculateHealthDataWithoutLoading(
|
|
|
|
uploaded: () async {
|
|
|
|
_refreshHealthDataAuthCardStatus(status: 1);
|
|
|
|
},
|
|
|
|
isPullToRefresh: isPullToRefresh,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> requestHealthAuthorization(
|
|
|
|
{bool isFromNoPermissionPage = false}) async {
|
|
|
|
if (GetPlatform.isIOS) {
|
|
|
|
try {
|
|
|
|
final result = await _hostApi.checkHealthAppAuthorization();
|
|
|
|
AppLogger.d("checkHealthAppAuthorization : $result");
|
|
|
|
if (result.status == 0) {
|
|
|
|
await _checkHealthAuthStatus();
|
|
|
|
AppLogger.d("checkHealthAppAuthorization : $_currentStatus");
|
|
|
|
if (_currentStatus == 0) {
|
|
|
|
bool success = await _hostApi.requestHealthClientAuthorization();
|
|
|
|
AppLogger.d("requestHealthClientAuthorization : $success");
|
|
|
|
if (success) {
|
|
|
|
_currentStatus = 1;
|
|
|
|
_refreshHealthDataAuthCardStatus(status: 1);
|
|
|
|
// await LoadingService.instance.run(() async {
|
|
|
|
_performHealthDataUpload();
|
|
|
|
_startCoreCaculate(refreshAfterComplete: true);
|
|
|
|
_performCaculateAndUpload();
|
|
|
|
// });
|
|
|
|
// result.status = 1;
|
|
|
|
// AppToast.show('刷新完成');
|
|
|
|
if (isFromNoPermissionPage) {
|
|
|
|
Get.back();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
// Get.to(NoHealthDataPage(
|
|
|
|
// onRefresh: _performDataUpload,
|
|
|
|
// ));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
} else if (result.status == 1) {
|
|
|
|
// await LoadingService.instance.run(() async {
|
|
|
|
_performHealthDataUpload();
|
|
|
|
_startCoreCaculate(refreshAfterComplete: true);
|
|
|
|
// });
|
|
|
|
return false;
|
|
|
|
} else if (_currentStatus == 1) {
|
|
|
|
_refreshHealthDataAuthCardStatus(status: 1);
|
|
|
|
if (isFromNoPermissionPage) {
|
|
|
|
Get.back();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// 打开苹果健康
|
|
|
|
_platformHostApi.nativeHandleUrl('x-apple-health://');
|
|
...
|
...
|
@@ -331,34 +362,54 @@ class TodayController extends GetMaterialController { |
|
|
|
AppToast.show(e.toString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> onAuthorizeTap() async {
|
|
|
|
if (_isHandlingHealthAuthorizationTap) {
|
|
|
|
AppLogger.d('TodayController.onAuthorizeTap ignored: handling');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_isHandlingHealthAuthorizationTap = true;
|
|
|
|
ta.track('click_doublefeel_today_status', properties: {'ita': '授权访问健康数据'});
|
|
|
|
// if (showHealthDataAuthCardStatus.value == 1) {
|
|
|
|
// return;
|
|
|
|
// } else
|
|
|
|
if (showHealthDataAuthCardStatus.value == 0) {
|
|
|
|
await requestHealthAuthorization(isFromNoPermissionPage: true);
|
|
|
|
checkHealthDataAuthCardVisible();
|
|
|
|
} else {
|
|
|
|
await Get.to(NoHealthDataPage(
|
|
|
|
onRefresh: () {
|
|
|
|
refreshTab();
|
|
|
|
Future.delayed(const Duration(milliseconds: 800), () {
|
|
|
|
AppToast.show(l10n.refreshComplete);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
onHelp: () {
|
|
|
|
Get.toNamed(Routes.HELP);
|
|
|
|
},
|
|
|
|
));
|
|
|
|
checkHealthDataAuthCardVisible();
|
|
|
|
try {
|
|
|
|
if (_currentStatus == 1 && hrvChartData.isNotEmpty) {
|
|
|
|
return;
|
|
|
|
} else if (_currentStatus == 0) {
|
|
|
|
final success = await requestHealthAuthorization();
|
|
|
|
if (success) {
|
|
|
|
_currentStatus = 1;
|
|
|
|
_refreshHealthDataAuthCardStatus(status: 1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
await Get.to(NoHealthDataPage(
|
|
|
|
onRefresh: () {
|
|
|
|
refreshTab();
|
|
|
|
Future.delayed(const Duration(milliseconds: 800), () {
|
|
|
|
AppToast.show(l10n.refreshComplete);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
onHelp: () {
|
|
|
|
Get.toNamed(Routes.HELP);
|
|
|
|
},
|
|
|
|
));
|
|
|
|
checkHealthDataAuthCardVisible();
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
_isHandlingHealthAuthorizationTap = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_refreshHealthDataAuthCardStatus({required int status}) {
|
|
|
|
if (status != 1) {
|
|
|
|
showHealthDataAuthCardStatus.value = status;
|
|
|
|
}
|
|
|
|
showHealthDataAuthCardStatus.value = hrvChartData.isEmpty ? -1 : status;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> checkHealthDataAuthCardVisible(
|
|
|
|
{bool isPullToRefresh = false}) async {
|
|
|
|
{bool isPullToRefresh = false,
|
|
|
|
bool shouldCaculateAndUpload = false}) async {
|
|
|
|
final firstTime = DateUtils.dateOnly(
|
|
|
|
DateTime(lastSelectableDay.value.year, lastSelectableDay.value.month - 6,
|
|
|
|
lastSelectableDay.value.day),
|
|
...
|
...
|
@@ -368,27 +419,13 @@ class TodayController extends GetMaterialController { |
|
|
|
firstSelectableDay.value = firstTime;
|
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
final result = await _hostApi.checkHealthAppAuthorization();
|
|
|
|
ta.setSuperProperties(
|
|
|
|
{'health_data_permission': result.status.clamp(0, 1)});
|
|
|
|
if (result.status == 1) {
|
|
|
|
_calculateHealthDataWithoutLoading(
|
|
|
|
uploaded: () async {
|
|
|
|
if (DateUtils.isSameDay(
|
|
|
|
selectedDate.value, lastSelectableDay.value) &&
|
|
|
|
hrvChartData.isEmpty) {
|
|
|
|
showHealthDataAuthCardStatus.value = -1;
|
|
|
|
} else {
|
|
|
|
showHealthDataAuthCardStatus.value = result.status;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
await _checkHealthAuthStatus(
|
|
|
|
isPullToRefresh: isPullToRefresh,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
unawaited(loadDataForDate(selectedDate.value,
|
|
|
|
isPullToRefresh: isPullToRefresh));
|
|
|
|
showHealthDataAuthCardStatus.value = result.status;
|
|
|
|
}
|
|
|
|
shouldCaculateAndUpload: shouldCaculateAndUpload);
|
|
|
|
ta.setSuperProperties(
|
|
|
|
{'health_data_permission': _currentStatus.clamp(0, 1)});
|
|
|
|
unawaited(loadDataForDate(selectedDate.value,
|
|
|
|
isPullToRefresh: isPullToRefresh));
|
|
|
|
} catch (error, stackTrace) {
|
|
|
|
AppLogger.e(
|
|
|
|
'TodayController.checkHealthDataAuthCardVisible failed',
|
|
...
|
...
|
@@ -415,11 +452,13 @@ class TodayController extends GetMaterialController { |
|
|
|
}
|
|
|
|
case AppFailure():
|
|
|
|
}
|
|
|
|
showHealthDataAuthCardStatus.value = !hasUploaded ? -1 : result.status;
|
|
|
|
_refreshHealthDataAuthCardStatus(status: result.status);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _calculateHealthDataWithoutLoading(
|
|
|
|
{Future<void> Function()? uploaded, bool isPullToRefresh = false}) {
|
|
|
|
{Future<void> Function()? uploaded,
|
|
|
|
bool isPullToRefresh = false,
|
|
|
|
bool refreshAfterComplete = true}) {
|
|
|
|
unawaited(
|
|
|
|
(() async {
|
|
|
|
try {
|
|
...
|
...
|
@@ -427,6 +466,9 @@ class TodayController extends GetMaterialController { |
|
|
|
await loadDataForDate(selectedDate.value,
|
|
|
|
isPullToRefresh: isPullToRefresh);
|
|
|
|
await uploaded?.call();
|
|
|
|
if (refreshAfterComplete) {
|
|
|
|
await loadDataForDate(selectedDate.value);
|
|
|
|
}
|
|
|
|
} catch (error, stackTrace) {
|
|
|
|
AppLogger.e('Apple Health calculate failed', error, stackTrace);
|
|
|
|
}
|
|
...
|
...
|
@@ -749,7 +791,7 @@ class TodayController extends GetMaterialController { |
|
|
|
changeDate(today);
|
|
|
|
checkAddFriendVisible();
|
|
|
|
checkHrvAdBannerVisible();
|
|
|
|
checkHealthDataAuthCardVisible();
|
|
|
|
checkHealthDataAuthCardVisible(shouldCaculateAndUpload: true);
|
|
|
|
checkNotificationAuthorizationBannerVisible();
|
|
|
|
_performHealthDataUpload();
|
|
|
|
// _startCoreCaculate();
|
|
...
|
...
|
@@ -789,7 +831,7 @@ class TodayController extends GetMaterialController { |
|
|
|
}
|
|
|
|
|
|
|
|
// 未决定 → 弹出系统授权弹窗
|
|
|
|
final result = await _platformHostApi.requestNotificationAuth();
|
|
|
|
await _platformHostApi.requestNotificationAuth();
|
|
|
|
// AppLogger.d(result.isGranted
|
|
|
|
// ? 'Notification authorization granted'
|
|
|
|
// : 'Notification authorization skipped');
|
...
|
...
|
|