Showing
9 changed files
with
73 additions
and
37 deletions
| @@ -163,7 +163,7 @@ class TodayTabBody extends StatelessWidget { | @@ -163,7 +163,7 @@ class TodayTabBody extends StatelessWidget { | ||
| 163 | if (controller.isFriend || showInteractionEntry) | 163 | if (controller.isFriend || showInteractionEntry) |
| 164 | const Positioned( | 164 | const Positioned( |
| 165 | right: 16, | 165 | right: 16, |
| 166 | - bottom: 92, | 166 | + bottom: 160, |
| 167 | child: FriendInteractionFloatingEntry(), | 167 | child: FriendInteractionFloatingEntry(), |
| 168 | ), | 168 | ), |
| 169 | ], | 169 | ], |
| @@ -15,7 +15,7 @@ class TodayHrvNumberCard extends StatelessWidget { | @@ -15,7 +15,7 @@ class TodayHrvNumberCard extends StatelessWidget { | ||
| 15 | @override | 15 | @override |
| 16 | Widget build(BuildContext context) { | 16 | Widget build(BuildContext context) { |
| 17 | return Container( | 17 | return Container( |
| 18 | - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), | 18 | + padding: const EdgeInsets.symmetric(vertical: 16), |
| 19 | decoration: BoxDecoration( | 19 | decoration: BoxDecoration( |
| 20 | color: Colors.white, | 20 | color: Colors.white, |
| 21 | borderRadius: BorderRadius.circular(16), | 21 | borderRadius: BorderRadius.circular(16), |
| @@ -87,7 +87,7 @@ class _NumberItem extends StatelessWidget { | @@ -87,7 +87,7 @@ class _NumberItem extends StatelessWidget { | ||
| 87 | @override | 87 | @override |
| 88 | Widget build(BuildContext context) { | 88 | Widget build(BuildContext context) { |
| 89 | return Padding( | 89 | return Padding( |
| 90 | - padding: const EdgeInsets.symmetric(horizontal: 12), | 90 | + padding: const EdgeInsets.only(left: 20, right: 2), |
| 91 | child: Column( | 91 | child: Column( |
| 92 | crossAxisAlignment: CrossAxisAlignment.start, | 92 | crossAxisAlignment: CrossAxisAlignment.start, |
| 93 | children: [ | 93 | children: [ |
| @@ -105,11 +105,8 @@ class _NumberItem extends StatelessWidget { | @@ -105,11 +105,8 @@ class _NumberItem extends StatelessWidget { | ||
| 105 | ), | 105 | ), |
| 106 | ), | 106 | ), |
| 107 | ), | 107 | ), |
| 108 | - if (interactionEnabled != null) | ||
| 109 | - FriendInteractionButton(enabled: interactionEnabled!), | ||
| 110 | ], | 108 | ], |
| 111 | ), | 109 | ), |
| 112 | - const SizedBox(height: 6), | ||
| 113 | Row( | 110 | Row( |
| 114 | crossAxisAlignment: CrossAxisAlignment.end, | 111 | crossAxisAlignment: CrossAxisAlignment.end, |
| 115 | children: [ | 112 | children: [ |
| @@ -124,7 +121,7 @@ class _NumberItem extends StatelessWidget { | @@ -124,7 +121,7 @@ class _NumberItem extends StatelessWidget { | ||
| 124 | ), | 121 | ), |
| 125 | const SizedBox(width: 4), | 122 | const SizedBox(width: 4), |
| 126 | Padding( | 123 | Padding( |
| 127 | - padding: const EdgeInsets.only(bottom: 3), | 124 | + padding: const EdgeInsets.only(bottom: 0), |
| 128 | child: Text( | 125 | child: Text( |
| 129 | unit, | 126 | unit, |
| 130 | style: TextStyle( | 127 | style: TextStyle( |
| @@ -134,6 +131,18 @@ class _NumberItem extends StatelessWidget { | @@ -134,6 +131,18 @@ class _NumberItem extends StatelessWidget { | ||
| 134 | ), | 131 | ), |
| 135 | ), | 132 | ), |
| 136 | ), | 133 | ), |
| 134 | + if (interactionEnabled != null) ...[ | ||
| 135 | + const SizedBox(width: 8), | ||
| 136 | + FriendInteractionButton( | ||
| 137 | + enabled: interactionEnabled!, | ||
| 138 | + padding: const EdgeInsets.only( | ||
| 139 | + left: 8, | ||
| 140 | + right: 20, | ||
| 141 | + top: 8, | ||
| 142 | + bottom: 0, | ||
| 143 | + ), | ||
| 144 | + ), | ||
| 145 | + ], | ||
| 137 | ], | 146 | ], |
| 138 | ), | 147 | ), |
| 139 | ], | 148 | ], |
| @@ -458,7 +458,7 @@ class _TodaySummaryCard extends StatelessWidget { | @@ -458,7 +458,7 @@ class _TodaySummaryCard extends StatelessWidget { | ||
| 458 | 458 | ||
| 459 | return Container( | 459 | return Container( |
| 460 | margin: const EdgeInsets.only(bottom: 8), | 460 | margin: const EdgeInsets.only(bottom: 8), |
| 461 | - padding: const EdgeInsets.fromLTRB(20, 18, 20, 15), | 461 | + padding: const EdgeInsets.fromLTRB(20, 10, 10, 15), |
| 462 | decoration: BoxDecoration( | 462 | decoration: BoxDecoration( |
| 463 | color: Colors.white, | 463 | color: Colors.white, |
| 464 | borderRadius: BorderRadius.circular(16), | 464 | borderRadius: BorderRadius.circular(16), |
| @@ -477,28 +477,20 @@ class _TodaySummaryCard extends StatelessWidget { | @@ -477,28 +477,20 @@ class _TodaySummaryCard extends StatelessWidget { | ||
| 477 | fontWeight: FontWeight.w600, | 477 | fontWeight: FontWeight.w600, |
| 478 | ), | 478 | ), |
| 479 | ), | 479 | ), |
| 480 | - if (interactionEnabled != null) ...[ | ||
| 481 | - const SizedBox(width: 8), | ||
| 482 | - FriendInteractionButton(enabled: interactionEnabled!), | ||
| 483 | - ], | ||
| 484 | - const Spacer(), | ||
| 485 | - Text( | ||
| 486 | - actionText, | ||
| 487 | - style: TextStyle( | ||
| 488 | - color: colors.textSecondary, | ||
| 489 | - fontSize: 12, | ||
| 490 | - fontWeight: FontWeight.w400, | ||
| 491 | - ), | ||
| 492 | - ), | 480 | + SizedBox(width: 4), |
| 493 | Image.asset( | 481 | Image.asset( |
| 494 | 'assets/images/common/ic_more_gray.png', | 482 | 'assets/images/common/ic_more_gray.png', |
| 495 | width: 16, | 483 | width: 16, |
| 496 | height: 16, | 484 | height: 16, |
| 497 | color: colors.textTertiary, | 485 | color: colors.textTertiary, |
| 498 | ), | 486 | ), |
| 487 | + const Spacer(), | ||
| 488 | + if (interactionEnabled != null) ...[ | ||
| 489 | + FriendInteractionButton(enabled: interactionEnabled!), | ||
| 490 | + ], | ||
| 499 | ], | 491 | ], |
| 500 | ), | 492 | ), |
| 501 | - const SizedBox(height: 16), | 493 | + const SizedBox(height: 8), |
| 502 | Row( | 494 | Row( |
| 503 | crossAxisAlignment: CrossAxisAlignment.start, | 495 | crossAxisAlignment: CrossAxisAlignment.start, |
| 504 | children: [ | 496 | children: [ |
| @@ -6,11 +6,18 @@ class FriendInteractionButton extends StatelessWidget { | @@ -6,11 +6,18 @@ class FriendInteractionButton extends StatelessWidget { | ||
| 6 | const FriendInteractionButton({ | 6 | const FriendInteractionButton({ |
| 7 | super.key, | 7 | super.key, |
| 8 | required this.enabled, | 8 | required this.enabled, |
| 9 | - this.size = 24, | 9 | + this.size = 24, |
| 10 | + this.padding = const EdgeInsets.only( | ||
| 11 | + left: 20, | ||
| 12 | + right: 10, | ||
| 13 | + top: 8, | ||
| 14 | + bottom: 8, | ||
| 15 | + ), | ||
| 10 | }); | 16 | }); |
| 11 | 17 | ||
| 12 | final bool enabled; | 18 | final bool enabled; |
| 13 | final double size; | 19 | final double size; |
| 20 | + final EdgeInsets padding; | ||
| 14 | 21 | ||
| 15 | @override | 22 | @override |
| 16 | Widget build(BuildContext context) => Opacity( | 23 | Widget build(BuildContext context) => Opacity( |
| @@ -18,18 +25,12 @@ class FriendInteractionButton extends StatelessWidget { | @@ -18,18 +25,12 @@ class FriendInteractionButton extends StatelessWidget { | ||
| 18 | child: GestureDetector( | 25 | child: GestureDetector( |
| 19 | behavior: HitTestBehavior.opaque, | 26 | behavior: HitTestBehavior.opaque, |
| 20 | onTap: enabled ? _openInteraction : null, | 27 | onTap: enabled ? _openInteraction : null, |
| 21 | - child: SizedBox( | ||
| 22 | - width: size, | ||
| 23 | - height: size, | ||
| 24 | - child: DecoratedBox( | ||
| 25 | - decoration: const BoxDecoration( | ||
| 26 | - color: Color(0xFF845EEE), | ||
| 27 | - shape: BoxShape.circle, | ||
| 28 | - ), | ||
| 29 | - child: Padding( | ||
| 30 | - padding: EdgeInsets.all(size / 8), | ||
| 31 | - child: Image.asset('assets/images/interact/icon_stick.png'), | ||
| 32 | - ), | 28 | + child: Padding( |
| 29 | + padding: padding, | ||
| 30 | + child: SizedBox( | ||
| 31 | + width: size, | ||
| 32 | + height: size, | ||
| 33 | + child: Image.asset('assets/images/interact/icon_stick.png'), | ||
| 33 | ), | 34 | ), |
| 34 | ), | 35 | ), |
| 35 | ), | 36 | ), |
| @@ -2136,6 +2136,11 @@ double _integerDouble(num value) => value.toInt().toDouble(); | @@ -2136,6 +2136,11 @@ double _integerDouble(num value) => value.toInt().toDouble(); | ||
| 2136 | int _unixSecondsNow() => DateTime.now().millisecondsSinceEpoch ~/ 1000; | 2136 | int _unixSecondsNow() => DateTime.now().millisecondsSinceEpoch ~/ 1000; |
| 2137 | 2137 | ||
| 2138 | class HealthRawStressLocalStore { | 2138 | class HealthRawStressLocalStore { |
| 2139 | + // Version 12 deliberately re-runs the date-key migration. Some shipped | ||
| 2140 | + // databases were stamped with version 11 before this column was present. | ||
| 2141 | + // SQLite consequently skips the old (< 9) migration for those databases. | ||
| 2142 | + static const _databaseVersion = 12; | ||
| 2143 | + | ||
| 2139 | HealthRawStressLocalStore({ | 2144 | HealthRawStressLocalStore({ |
| 2140 | Directory? rootDirectory, | 2145 | Directory? rootDirectory, |
| 2141 | DatabaseFactory? databaseFactory, | 2146 | DatabaseFactory? databaseFactory, |
| @@ -2625,7 +2630,7 @@ class HealthRawStressLocalStore { | @@ -2625,7 +2630,7 @@ class HealthRawStressLocalStore { | ||
| 2625 | final db = await factory.openDatabase( | 2630 | final db = await factory.openDatabase( |
| 2626 | path, | 2631 | path, |
| 2627 | options: OpenDatabaseOptions( | 2632 | options: OpenDatabaseOptions( |
| 2628 | - version: 11, | 2633 | + version: _databaseVersion, |
| 2629 | onCreate: (db, version) async { | 2634 | onCreate: (db, version) async { |
| 2630 | await _createTables(db); | 2635 | await _createTables(db); |
| 2631 | }, | 2636 | }, |
| @@ -2661,9 +2666,17 @@ class HealthRawStressLocalStore { | @@ -2661,9 +2666,17 @@ class HealthRawStressLocalStore { | ||
| 2661 | if (oldVersion < 11) { | 2666 | if (oldVersion < 11) { |
| 2662 | await _createIndexes(db); | 2667 | await _createIndexes(db); |
| 2663 | } | 2668 | } |
| 2669 | + if (oldVersion < _databaseVersion) { | ||
| 2670 | + await _repairDateKeySchema(db); | ||
| 2671 | + } | ||
| 2664 | }, | 2672 | }, |
| 2665 | ), | 2673 | ), |
| 2666 | ); | 2674 | ); |
| 2675 | + // Do not rely exclusively on user_version here. Historical releases may | ||
| 2676 | + // have recorded the target version even though their schema migration did | ||
| 2677 | + // not complete. This is idempotent, so it also protects a database whose | ||
| 2678 | + // migration was interrupted before the app was terminated. | ||
| 2679 | + await _repairDateKeySchema(db); | ||
| 2667 | _opened[userId] = db; | 2680 | _opened[userId] = db; |
| 2668 | return db; | 2681 | return db; |
| 2669 | } | 2682 | } |
| @@ -2842,6 +2855,10 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | @@ -2842,6 +2855,10 @@ CREATE TABLE IF NOT EXISTS $sleepResultsTable ( | ||
| 2842 | } | 2855 | } |
| 2843 | } | 2856 | } |
| 2844 | 2857 | ||
| 2858 | + Future<void> _repairDateKeySchema(DatabaseExecutor db) async { | ||
| 2859 | + await _addDateKeyColumns(db); | ||
| 2860 | + } | ||
| 2861 | + | ||
| 2845 | Future<void> _addPushSendTimeColumns(DatabaseExecutor db) async { | 2862 | Future<void> _addPushSendTimeColumns(DatabaseExecutor db) async { |
| 2846 | for (final table in const [ | 2863 | for (final table in const [ |
| 2847 | hrvResultsTable, | 2864 | hrvResultsTable, |
| @@ -10,6 +10,10 @@ import 'huawei_health_data_type.dart'; | @@ -10,6 +10,10 @@ import 'huawei_health_data_type.dart'; | ||
| 10 | import 'ohos_health_raw_data_sync_service.dart'; | 10 | import 'ohos_health_raw_data_sync_service.dart'; |
| 11 | 11 | ||
| 12 | class OhosHealthRawDataSqliteStore implements OhosHealthRawDataLocalStore { | 12 | class OhosHealthRawDataSqliteStore implements OhosHealthRawDataLocalStore { |
| 13 | + // Re-run the date-key repair for databases that were marked as v2 before | ||
| 14 | + // their dynamic raw-data tables completed the v2 migration. | ||
| 15 | + static const _databaseVersion = 3; | ||
| 16 | + | ||
| 13 | OhosHealthRawDataSqliteStore({ | 17 | OhosHealthRawDataSqliteStore({ |
| 14 | required int Function()? userIdProvider, | 18 | required int Function()? userIdProvider, |
| 15 | Directory? rootDirectory, | 19 | Directory? rootDirectory, |
| @@ -417,15 +421,22 @@ class OhosHealthRawDataSqliteStore implements OhosHealthRawDataLocalStore { | @@ -417,15 +421,22 @@ class OhosHealthRawDataSqliteStore implements OhosHealthRawDataLocalStore { | ||
| 417 | final db = await factory.openDatabase( | 421 | final db = await factory.openDatabase( |
| 418 | path, | 422 | path, |
| 419 | options: OpenDatabaseOptions( | 423 | options: OpenDatabaseOptions( |
| 420 | - version: 2, | 424 | + version: _databaseVersion, |
| 421 | onCreate: (db, version) => _createTables(db), | 425 | onCreate: (db, version) => _createTables(db), |
| 422 | onUpgrade: (db, oldVersion, newVersion) async { | 426 | onUpgrade: (db, oldVersion, newVersion) async { |
| 423 | if (oldVersion < 2) { | 427 | if (oldVersion < 2) { |
| 424 | await _addDateKeyColumns(db); | 428 | await _addDateKeyColumns(db); |
| 425 | } | 429 | } |
| 430 | + if (oldVersion < _databaseVersion) { | ||
| 431 | + await _repairDateKeySchema(db); | ||
| 432 | + } | ||
| 426 | }, | 433 | }, |
| 427 | ), | 434 | ), |
| 428 | ); | 435 | ); |
| 436 | + // Dynamic raw-data tables can be created while a prior app version is | ||
| 437 | + // running. Do not trust user_version alone: make their date-key schema | ||
| 438 | + // valid before any read or write uses the cached database handle. | ||
| 439 | + await _repairDateKeySchema(db); | ||
| 429 | _opened[userId] = db; | 440 | _opened[userId] = db; |
| 430 | return db; | 441 | return db; |
| 431 | } | 442 | } |
| @@ -703,6 +714,10 @@ CREATE TABLE IF NOT EXISTS $table ( | @@ -703,6 +714,10 @@ CREATE TABLE IF NOT EXISTS $table ( | ||
| 703 | } | 714 | } |
| 704 | } | 715 | } |
| 705 | 716 | ||
| 717 | + Future<void> _repairDateKeySchema(DatabaseExecutor db) async { | ||
| 718 | + await _addDateKeyColumns(db); | ||
| 719 | + } | ||
| 720 | + | ||
| 706 | Future<void> _backfillDateKey(DatabaseExecutor db, String table) async { | 721 | Future<void> _backfillDateKey(DatabaseExecutor db, String table) async { |
| 707 | final columns = await db.rawQuery('PRAGMA table_info($table)'); | 722 | final columns = await db.rawQuery('PRAGMA table_info($table)'); |
| 708 | final columnNames = | 723 | final columnNames = |
| @@ -138,3 +138,5 @@ flutter: | @@ -138,3 +138,5 @@ flutter: | ||
| 138 | - assets/images/user_onboarding/oh/ | 138 | - assets/images/user_onboarding/oh/ |
| 139 | - assets/images/premium/ | 139 | - assets/images/premium/ |
| 140 | - assets/images/premium/zh/ | 140 | - assets/images/premium/zh/ |
| 141 | + - assets/images/interact/ | ||
| 142 | + - assets/images/interact/animations/ |
-
Please register or login to post a comment