Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ android {
defaultConfig {
// Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "de.wger.flutter"
minSdkVersion flutter.minSdkVersion
minSdkVersion 26
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
Expand Down
25 changes: 25 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,20 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />

<!-- Health Connect permissions for weight sync -->
<uses-permission android:name="android.permission.health.READ_WEIGHT"/>
<uses-permission android:name="android.permission.health.READ_HEALTH_DATA_HISTORY"/>

<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
<!-- Health Connect -->
<package android:name="com.google.android.apps.healthdata" />
<intent>
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
</intent>
</queries>

<supports-screens
Expand Down Expand Up @@ -48,8 +57,24 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

<!-- Health Connect permissions rationale -->
<intent-filter>
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
</intent-filter>
</activity>

<!-- Required for Health Connect on Google Play -->
<activity-alias
android:name="ViewPermissionUsageActivity"
android:exported="true"
android:targetActivity=".MainActivity"
android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
<intent-filter>
<action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
<category android:name="android.intent.category.HEALTH_PERMISSIONS" />
</intent-filter>
</activity-alias>

<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
Expand Down
4 changes: 2 additions & 2 deletions android/app/src/main/kotlin/de/wger/flutter/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package de.wger.flutter

import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.android.FlutterFragmentActivity

class MainActivity: FlutterActivity() {
class MainActivity: FlutterFragmentActivity() {
}
2 changes: 2 additions & 0 deletions ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
<true/>
<key>NSCameraUsageDescription</key>
<string>Workout photos</string>
<key>NSHealthShareUsageDescription</key>
<string>wger uses your health data to automatically sync weight measurements from your smart scale</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
Expand Down
6 changes: 6 additions & 0 deletions ios/Runner/Runner.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,11 @@
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.healthkit</key>
<true/>
<key>com.apple.developer.healthkit.access</key>
<array>
<string>health-records</string>
</array>
</dict>
</plist>
14 changes: 3 additions & 11 deletions lib/database/exercises/exercise_database.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import 'dart:io';

import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:drift_flutter/drift_flutter.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:wger/database/exercises/type_converters.dart';
import 'package:wger/models/exercises/category.dart';
import 'package:wger/models/exercises/equipment.dart';
Expand Down Expand Up @@ -110,10 +106,6 @@ class ExerciseDatabase extends _$ExerciseDatabase {
}
}

LazyDatabase _openConnection() {
return LazyDatabase(() async {
final dbFolder = await getApplicationCacheDirectory();
final file = File(p.join(dbFolder.path, 'exercises.sqlite'));
return NativeDatabase.createInBackground(file);
});
QueryExecutor _openConnection() {
return driftDatabase(name: 'exercises');
}
14 changes: 3 additions & 11 deletions lib/database/ingredients/ingredients_database.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import 'dart:io';

import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:drift_flutter/drift_flutter.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';

part 'ingredients_database.g.dart';

Expand Down Expand Up @@ -63,10 +59,6 @@ class IngredientDatabase extends _$IngredientDatabase {
}
}

LazyDatabase _openConnection() {
return LazyDatabase(() async {
final dbFolder = await getApplicationCacheDirectory();
final file = File(p.join(dbFolder.path, 'ingredients.sqlite'));
return NativeDatabase.createInBackground(file);
});
QueryExecutor _openConnection() {
return driftDatabase(name: 'ingredients');
}
26 changes: 26 additions & 0 deletions lib/helpers/shared_preferences.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,30 @@ class PreferenceHelper {
);
}
}

// Health sync
static const _healthSyncEnabledKey = 'healthSyncEnabled';
static const _lastHealthSyncTimestampKey = 'lastHealthSyncTimestamp';

Future<void> setHealthSyncEnabled(bool value) async {
await PreferenceHelper.asyncPref.setBool(_healthSyncEnabledKey, value);
}

Future<bool> getHealthSyncEnabled() async {
final value = await PreferenceHelper.asyncPref.getBool(_healthSyncEnabledKey);
return value ?? false;
}

Future<void> setLastHealthSyncTimestamp(String value) async {
await PreferenceHelper.asyncPref.setString(_lastHealthSyncTimestampKey, value);
}

Future<String?> getLastHealthSyncTimestamp() async {
return PreferenceHelper.asyncPref.getString(_lastHealthSyncTimestampKey);
}

Future<void> clearHealthSyncPreferences() async {
await PreferenceHelper.asyncPref.remove(_healthSyncEnabledKey);
await PreferenceHelper.asyncPref.remove(_lastHealthSyncTimestampKey);
}
}
23 changes: 22 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1180,5 +1180,26 @@
}
},
"searchLanguageAll": "All languages",
"@searchLanguageAll": {}
"@searchLanguageAll": {},
"healthSync": "Health sync",
"@healthSync": {
"description": "Title for the health platform sync setting"
},
"healthSyncDescription": "Import weight from Apple Health or Health Connect",
"@healthSyncDescription": {
"description": "Subtitle for the health platform sync setting"
},
"healthSyncSuccess": "Synced {count} weight entries from Health",
"@healthSyncSuccess": {
"description": "Snackbar message after successful health sync",
"placeholders": {
"count": {
"type": "int"
}
}
},
"health": "Health",
"@health": {
"description": "Section header for health-related settings"
}
}
8 changes: 8 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,14 @@ void main() async {

// Catch errors that happen outside of the Flutter framework (e.g., in async operations)
PlatformDispatcher.instance.onError = (error, stack) {
// Skip the StackFrame assertion error from the stack_trace package.
// This is a known Flutter framework issue where async gap markers in stack
// traces cause an assertion failure in StackFrame.fromStackTraceLine.
if (error is AssertionError && error.toString().contains('asynchronous gap')) {
logger.warning('Suppressed StackFrame assertion error (known Flutter issue)');
return true;
}

logger.severe('Error caught by PlatformDispatcher.instance.onError: $error');
logger.severe('Stack trace: $stack');

Expand Down
2 changes: 1 addition & 1 deletion lib/models/body_weight/weight_entry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class WeightEntry {
}
}

WeightEntry copyWith({int? id, int? weight, DateTime? date}) => WeightEntry(
WeightEntry copyWith({int? id, num? weight, DateTime? date}) => WeightEntry(
id: id,
weight: weight ?? this.weight,
date: date ?? this.date,
Expand Down
11 changes: 6 additions & 5 deletions lib/models/body_weight/weight_entry.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion lib/providers/body_weight.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:wger/core/exceptions/http_exception.dart';
import 'package:wger/helpers/consts.dart';
import 'package:wger/helpers/date.dart';
import 'package:wger/models/body_weight/weight_entry.dart';
import 'package:wger/providers/base_provider.dart';

Expand Down Expand Up @@ -58,7 +59,9 @@ class BodyWeightProvider with ChangeNotifier {

WeightEntry? findByDate(DateTime date) {
try {
return _entries.firstWhere((plan) => plan.date == date);
return _entries.firstWhere(
(entry) => entry.date.isSameDayAs(date),
);
} on StateError {
return null;
}
Expand Down
Loading