Implemented basic HealthConnect exporter

Fixes #1542

Co-authored-by: Manuel <manuel@mmarca.tech>
This commit is contained in:
Dennis Guse
2026-07-03 08:24:42 +02:00
46 changed files with 571 additions and 50 deletions
+1
View File
@@ -156,6 +156,7 @@ dependencies {
implementation 'androidx.mediarouter:mediarouter:1.8.1'
implementation 'androidx.core:core-location-altitude:1.0.0'
implementation 'androidx.work:work-runtime:2.11.2'
implementation 'androidx.health.connect:connect-client:1.1.0'
androidTestImplementation 'androidx.test:core:1.7.0'
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
+29 -2
View File
@@ -69,6 +69,9 @@ limitations under the License.
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"
android:minSdkVersion="33" />
<uses-permission android:name="android.permission.health.WRITE_EXERCISE" />
<uses-permission android:name="android.permission.health.WRITE_EXERCISE_ROUTE" />
<application
android:name=".Startup"
android:allowBackup="true"
@@ -126,9 +129,32 @@ limitations under the License.
<activity android:name=".AboutActivity" />
<!-- Permissions -->
<!-- until Android 13 -->
<activity
android:name=".HelpActivity"
android:label="@string/menu_help" />
android:name=".HealthConnectPermissionsRationaleActivity"
android:exported="true">
<intent-filter>
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
</intent-filter>
</activity>
<!-- for Android 14 -->
<activity-alias
android:name="ViewPermissionUsageActivity"
android:exported="true"
android:permission="android.permission.START_VIEW_PERMISSION_USAGE"
android:targetActivity=".HealthConnectPermissionsRationaleActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
<category android:name="android.intent.category.HEALTH_PERMISSIONS" />
</intent-filter>
</activity-alias>
<activity android:name=".settings.HealthConnect$Settings" />
<activity android:name=".settings.HealthConnect$ConfigurePermissions" />
<activity android:name=".settings.HealthConnect$ExportAll" />
<activity android:name=".HelpActivity" />
<activity android:name=".io.file.importer.DirectoryChooserActivity$ImportDirectoryChooserActivity" />
@@ -410,5 +436,6 @@ limitations under the License.
<intent>
<action android:name="android.intent.action.TTS_SERVICE" />
</intent>
<package android:name="com.google.android.apps.healthdata" />
</queries>
</manifest>
@@ -0,0 +1,28 @@
package de.dennisguse.opentracks;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import de.dennisguse.opentracks.databinding.ActivityHealthConnectPermissionsRationaleBinding;
public class HealthConnectPermissionsRationaleActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityHealthConnectPermissionsRationaleBinding viewBinding = ActivityHealthConnectPermissionsRationaleBinding.inflate(getLayoutInflater());
setSupportActionBar(viewBinding.healthConnectActivityToolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(viewBinding.getRoot());
}
@Override
public boolean onSupportNavigateUp() {
getOnBackPressedDispatcher().onBackPressed();
return true;
}
}
@@ -18,6 +18,7 @@ import de.dennisguse.opentracks.data.models.TrackBuilder;
import de.dennisguse.opentracks.databinding.TrackStoppedBinding;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
import de.dennisguse.opentracks.io.file.exporter.ExportUtils;
import de.dennisguse.opentracks.io.healthconnect.exporter.HealthConnectUtils;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragment;
@@ -93,6 +94,7 @@ public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements
viewBinding.finishButton.setOnClickListener(v -> {
storeTrackMetaData(contentProviderUtils, track);
ExportUtils.postWorkoutExport(this, trackId);
HealthConnectUtils.postExportTrack(this, trackId);
finish();
});
@@ -36,7 +36,7 @@ public class ExportUtils {
private static final String TAG = ExportUtils.class.getSimpleName();
public static void postWorkoutExport(AppCompatActivity context, Track.Id trackId) {
if (PreferencesUtils.shouldInstantExportAfterWorkout()) {
if (PreferencesUtils.shouldInstantExportToFile()) {
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat();
DocumentFile directory = IntentUtils.toDocumentFile(context, PreferencesUtils.getDefaultExportDirectoryUri());
@@ -0,0 +1,93 @@
package de.dennisguse.opentracks.io.healthconnect.exporter;
import android.content.Context;
import androidx.health.connect.client.HealthConnectClient;
import androidx.health.connect.client.PermissionController;
import androidx.health.connect.client.permission.HealthPermission;
import androidx.health.connect.client.records.ExerciseSessionRecord;
import androidx.work.Data;
import androidx.work.ExistingWorkPolicy;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
import androidx.work.WorkRequest;
import java.util.Set;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.settings.PreferencesUtils;
import kotlin.coroutines.Continuation;
import kotlin.jvm.JvmClassMappingKt;
import kotlin.jvm.functions.Function2;
import kotlinx.coroutines.BuildersKt;
/**
* Exports a single track as an ExerciseSession to Health Connect.
* Uses the Track.uuid as clientRecordId to prevent duplicates on re-export.
*/
public class HealthConnectUtils {
private static final String TAG = HealthConnectUtils.class.getSimpleName();
static final String WRITE_EXERCISE_PERMISSION = HealthPermission.getWritePermission(JvmClassMappingKt.getKotlinClass(ExerciseSessionRecord.class));
static final String WRITE_EXERCISE_ROUTE_PERMISSION = HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE;
//TODO Should be package private
public static final Set<String> PERMISSIONS = Set.of(WRITE_EXERCISE_PERMISSION, WRITE_EXERCISE_ROUTE_PERMISSION);
private HealthConnectUtils() {
}
public static boolean isAvailable(Context context) {
return HealthConnectClient.getSdkStatus(context) == HealthConnectClient.SDK_AVAILABLE;
}
public static Set<String> getGrantedPermissions(Context context) {
try {
HealthConnectClient client = HealthConnectClient.getOrCreate(context);
PermissionController controller = client.getPermissionController();
return (Set<String>) BuildersKt.runBlocking(
kotlin.coroutines.EmptyCoroutineContext.INSTANCE,
(Function2) (scope, cont) -> controller.getGrantedPermissions((Continuation) cont));
} catch (InterruptedException e) {
return Set.of();
}
}
static boolean hasRequiredPermissions(Context context) {
return getGrantedPermissions(context)
.containsAll(PERMISSIONS);
}
public static void postExportTrack(Context context, Track.Id trackId) {
if (!PreferencesUtils.shouldInstantExportToHealthConnect()) {
return;
}
WorkRequest request = new OneTimeWorkRequest.Builder(HealthConnectWorker.class)
.addTag(HealthConnectWorker.WORK_TAG)
.setInputData(new Data.Builder()
.putLong(HealthConnectWorker.TRACK_ID_KEY, trackId.id())
.build())
.build();
WorkManager.getInstance(context).enqueue(request);
}
public static void postExportAll(Context context) {
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(HealthConnectWorker.class)
.addTag(HealthConnectWorker.WORK_TAG)
.build();
WorkManager.getInstance(context).enqueueUniqueWork(
HealthConnectWorker.WORK_TAG,
ExistingWorkPolicy.REPLACE,
request
);
}
public static void cancelHealthConnectExport(Context context) {
WorkManager workManager = WorkManager.getInstance(context);
workManager.cancelAllWorkByTag(HealthConnectWorker.WORK_TAG);
}
}
@@ -0,0 +1,185 @@
package de.dennisguse.opentracks.io.healthconnect.exporter;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.health.connect.client.HealthConnectClient;
import androidx.health.connect.client.records.ExerciseRoute;
import androidx.health.connect.client.records.ExerciseSessionRecord;
import androidx.health.connect.client.records.metadata.Device;
import androidx.health.connect.client.records.metadata.Metadata;
import androidx.health.connect.client.units.Length;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.TrackPointIterator;
import de.dennisguse.opentracks.data.models.ActivityType;
import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint;
import kotlin.coroutines.Continuation;
import kotlin.jvm.functions.Function2;
import kotlinx.coroutines.BuildersKt;
/**
* Exports a single track as an ExerciseSession to Health Connect.
* Uses the Track.uuid as clientRecordId to prevent duplicates on re-export.
*/
//TODO add reasons for failures (if shown in the UI: must be localized)
//TODO Export sensor data (permissions needed)
//TODO Show progress: like ExportActivity? Or use a notification (aka handle it as a background process)
public class HealthConnectWorker extends Worker {
private static final String TAG = HealthConnectWorker.class.getSimpleName();
static final String WORK_TAG = "HealthConnectSync";
static final String TRACK_ID_KEY = "trackId";
public HealthConnectWorker(Context context, WorkerParameters params) {
super(context, params);
}
@NonNull
@Override
public Result doWork() {
if (!HealthConnectUtils.isAvailable(getApplicationContext())) {
Log.w(TAG, "Health Connect SDK not available");
return Result.failure();
}
//TODO If not all permissions are granted, export the data that was granted.
if (!HealthConnectUtils.hasRequiredPermissions(getApplicationContext())) {
Log.w(TAG, "Health Connect required permissions not granted");
return Result.failure();
}
Track.Id trackId = new Track.Id(getInputData().getLong(TRACK_ID_KEY, -1L));
if (trackId.id() != -1L) {
return exportTrack(trackId);
}
return exportAll();
}
private Result exportTrack(Track.Id trackId) {
try {
export(getApplicationContext(), trackId);
return Result.success();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return Result.retry();
} catch (Exception e) {
Log.e(TAG, "Health Connect export failed for track " + trackId, e);
return Result.failure();
}
}
private Result exportAll() {
List<Track> allTracks = new ContentProviderUtils(getApplicationContext())
.getTracks();
for (Track track : allTracks) {
if (isStopped()) {
Log.i(TAG, "Initial Health Connect sync stopped by WorkManager");
return Result.success();
}
try {
export(getApplicationContext(), track.id());
} catch (InterruptedException e) {
return Result.retry();
} catch (Exception e) {
Log.e(TAG, "Initial Health Connect sync failed for track " + track.id().id(), e);
return Result.failure();
}
}
return Result.success();
}
private static ExportResult export(Context context, Track.Id trackId) throws InterruptedException {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
Track track = contentProviderUtils.getTrack(trackId);
if (track == null) {
throw new RuntimeException("Track not found: " + trackId);
}
ExerciseRoute exerciseRoute = createExerciseRoute(contentProviderUtils, trackId);
HealthConnectClient client = HealthConnectClient.getOrCreate(context);
ExerciseSessionRecord record = new ExerciseSessionRecord(
track.startTime().toInstant(),
track.startTime().getOffset(),
track.stopTime().toInstant(),
track.stopTime().getOffset(),
Metadata.activelyRecorded(
new Device(Device.TYPE_PHONE, Build.MANUFACTURER, Build.MODEL),
track.uuid().toString()
),
toExerciseType(track.activityType()),
track.name(),
track.description(),
Collections.emptyList(),
Collections.emptyList(),
exerciseRoute);
List<ExerciseSessionRecord> records = Collections.singletonList(record);
Object ignored = BuildersKt.runBlocking(
kotlin.coroutines.EmptyCoroutineContext.INSTANCE,
(Function2) (scope, cont) -> client.insertRecords(records, (Continuation) cont));
Log.i(TAG, "Exported track to Health Connect: " + track.name());
return new ExportResult(track.name());
}
private static ExerciseRoute createExerciseRoute(ContentProviderUtils contentProviderUtils, Track.Id trackId) {
List<ExerciseRoute.Location> route = new ArrayList<>();
try (TrackPointIterator iterator = contentProviderUtils.getTrackPointIterator(trackId, null)) {
while (iterator.hasNext()) {
TrackPoint trackPoint = iterator.next();
if (trackPoint.type() != TrackPoint.Type.TRACKPOINT) continue;
Position position = trackPoint.position();
route.add(new ExerciseRoute.Location(
trackPoint.getTime(),
position.latitude(),
position.longitude(),
position.horizontalAccuracy() != null ? Length.meters(position.horizontalAccuracy().toM()) : null,
position.verticalAccuracy() != null ? Length.meters(position.verticalAccuracy().toM()) : null,
position.altitude() != null ? Length.meters(position.altitude().toM()) : null)
);
}
}
return new ExerciseRoute(route);
}
private static int toExerciseType(ActivityType activityType) {
return switch (activityType) {
case RUNNING, STREET_RUNNING, TRAIL_RUNNING, TRACK_RUNNING -> ExerciseSessionRecord.EXERCISE_TYPE_RUNNING;
case BIKING, CYCLING, ROAD_BIKING, MOUNTAIN_BIKING, TRACK_CYCLING -> ExerciseSessionRecord.EXERCISE_TYPE_BIKING;
case WALKING, SPEED_WALKING -> ExerciseSessionRecord.EXERCISE_TYPE_WALKING;
case HIKING, OFF_TRAIL_HIKING, TRAIL_HIKING -> ExerciseSessionRecord.EXERCISE_TYPE_HIKING;
case CROSS_COUNTRY_SKIING, SKIING -> ExerciseSessionRecord.EXERCISE_TYPE_SKIING;
case SNOW_BOARDING -> ExerciseSessionRecord.EXERCISE_TYPE_SNOWBOARDING;
case KAYAKING -> ExerciseSessionRecord.EXERCISE_TYPE_PADDLING;
case ROWING -> ExerciseSessionRecord.EXERCISE_TYPE_ROWING_MACHINE;
case SWIMMING -> ExerciseSessionRecord.EXERCISE_TYPE_SWIMMING_POOL;
case SWIMMING_OPEN -> ExerciseSessionRecord.EXERCISE_TYPE_SWIMMING_OPEN_WATER;
default -> ExerciseSessionRecord.EXERCISE_TYPE_OTHER_WORKOUT;
};
}
public record ExportResult(String trackName) {
}
}
@@ -0,0 +1,54 @@
package de.dennisguse.opentracks.settings;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.health.connect.client.HealthConnectClient;
import androidx.health.connect.client.PermissionController;
import de.dennisguse.opentracks.io.healthconnect.exporter.HealthConnectUtils;
public class HealthConnect {
public static class Settings extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startActivity(new Intent(HealthConnectClient.getHealthConnectSettingsAction()));
finish();
}
}
public static class ConfigurePermissions extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!HealthConnectUtils.isAvailable(this)) {
finish();
}
registerForActivityResult(
PermissionController.createRequestPermissionResultContract(),
grantedPermissions -> {
// TODO User feedback via Toast.makeText()?
finish();
}
).launch(HealthConnectUtils.PERMISSIONS);
}
}
public static class ExportAll extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
HealthConnectUtils.postExportAll(this);
finish();
}
}
}
@@ -14,11 +14,11 @@ import java.util.Map;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.io.file.TrackFilenameGenerator;
import de.dennisguse.opentracks.io.healthconnect.exporter.HealthConnectUtils;
import de.dennisguse.opentracks.util.IntentUtils;
public class ImportExportSettingsFragment extends PreferenceFragmentCompat {
private static final String TAG = ImportExportSettingsFragment.class.getSimpleName();
public class ImportExportSettingsFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
@@ -27,6 +27,11 @@ public class ImportExportSettingsFragment extends PreferenceFragmentCompat {
setExportTrackFileFormatOptions();
setExportDirectorySummary();
setFilenameTemplate();
findPreference("healthConnectPermissions").setSummaryProvider(preference -> {
//TODO This should be localized and prefixes trimmed.
return String.join(",", HealthConnectUtils.getGrantedPermissions(getActivity()));
});
}
@Override
@@ -40,9 +45,21 @@ public class ImportExportSettingsFragment extends PreferenceFragmentCompat {
super.onResume();
setExportDirectorySummary();
findPreference(getString(R.string.post_workout_export_enabled_key)).setEnabled(PreferencesUtils.isDefaultExportDirectoryUri());
Preference instantExportEnabledPreference = findPreference(getString(R.string.post_workout_export_enabled_key));
instantExportEnabledPreference.setEnabled(PreferencesUtils.isDefaultExportDirectoryUri());
setHealthConnectPreference();
}
private void setHealthConnectPreference() {
boolean healthConnectAvailable = HealthConnectUtils.isAvailable(requireContext());
findPreference("healthConnectCategory")
.setEnabled(healthConnectAvailable);
if (!healthConnectAvailable) {
findPreference(getString(R.string.post_workout_export_health_connect_enabled_key))
.setSummary(R.string.health_connect_not_available);
return;
}
}
private void setExportTrackFileFormatOptions() {
@@ -71,6 +71,7 @@ public class PreferencesUtils {
private static SharedPreferences sharedPreferences;
private static Resources resources;
private static final long LONG_UNSET = Long.MIN_VALUE;
/**
* Must be called during application startup.
@@ -168,24 +169,36 @@ public class PreferencesUtils {
}
}
private static long getLong(int keyId, long defaultValue) {
try {
return sharedPreferences.getLong(getKey(keyId), defaultValue);
} catch (ClassCastException e) {
// Ignore.
}
try {
String stringValue = sharedPreferences.getString(getKey(keyId), null);
return Long.parseLong(stringValue);
} catch (NumberFormatException e) {
return defaultValue;
}
}
public static String getString(int keyId, String defaultValue) {
return sharedPreferences.getString(getKey(keyId), defaultValue);
}
@VisibleForTesting
public static void setString(int keyId, String value) {
static void setString(int keyId, String value) {
Editor editor = sharedPreferences.edit();
editor.putString(getKey(keyId), value);
editor.apply();
}
@VisibleForTesting
public static void setString(int keyId, int valueId) {
static void setString(int keyId, int valueId) {
setString(keyId, resources.getString(valueId));
}
@VisibleForTesting
public static void setBoolean(int keyId, Boolean value) {
static void setBoolean(int keyId, Boolean value) {
Editor editor = sharedPreferences.edit();
editor.putBoolean(getKey(keyId), value);
editor.apply();
@@ -197,6 +210,12 @@ public class PreferencesUtils {
editor.apply();
}
private static void setLong(int keyId, long value) {
Editor editor = sharedPreferences.edit();
editor.putLong(getKey(keyId), value);
editor.apply();
}
public static boolean isPublicAPIenabled() {
return getBoolean(R.string.publicapi_enabled_key, resources.getBoolean(R.bool.publicapi_enabled_default));
}
@@ -660,12 +679,16 @@ public class PreferencesUtils {
return entries;
}
public static boolean shouldInstantExportAfterWorkout() {
public static boolean shouldInstantExportToFile() {
final boolean INSTANT_POST_WORKOUT_EXPORT_DEFAULT = resources.getBoolean(R.bool.post_workout_export_enabled_default);
return getBoolean(R.string.post_workout_export_enabled_key, INSTANT_POST_WORKOUT_EXPORT_DEFAULT) && isDefaultExportDirectoryUri();
}
public static boolean shouldInstantExportToHealthConnect() {
final boolean DEFAULT = resources.getBoolean(R.bool.post_workout_export_health_connect_enabled_default);
return getBoolean(R.string.post_workout_export_health_connect_enabled_key, DEFAULT);
}
public static TrackFilenameGenerator getTrackFileformatGenerator() {
String DEFAULT = resources.getString(R.string.export_filename_format_default);
TrackFilenameGenerator generator = new TrackFilenameGenerator(getString(R.string.export_filename_format_key, DEFAULT));
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/health_connect_activity_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:title="@string/app_name" />
</com.google.android.material.appbar.AppBarLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
style="?attr/textAppearanceHeadline6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/health_connect_permissions_rationale_heading" />
<TextView
style="?attr/textAppearanceBody1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/health_connect_permissions_rationale_summary" />
</LinearLayout>
</ScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
+1 -1
View File
@@ -456,7 +456,7 @@ limitations under the License.
<string name="settings_recording_title">التسجيل</string>
<string name="settings_public_api_enabled_title">واجهة برمجة التطبيقات العامة</string>
<string name="settings_public_api_disabled_toast">واجهة برمجة التطبيقات العامة لـ المسارات المفتوحة معطلة: يمكن تفعيلها في الإعدادات.</string>
<string name="settings_export_section">التصدير</string>
<string name="settings_export_as_file_section">التصدير</string>
<string name="instant_export_enabled_summary">تصدير المسار إلى التخزين بعد انتهاء التسجيل</string>
<string name="create_marker_error">غير قادر على إنشاء علامة. حاول مرة أخرى.</string>
<string name="sensor_state_heart_rate_avg">متوسط معدل ضربات القلب</string>
+1 -1
View File
@@ -430,7 +430,7 @@
<string name="settings_gps_summary">Intervalo de tiempo, Intervalo de distancia, Distancia máxima, Precisión</string>
<string name="settings_sensors_title">Sensores</string>
<string name="settings_sensors_summary">Sensores de ciclismo, Circunferencia de la rueda, Sensores de carrera</string>
<string name="settings_export_section">Exportar</string>
<string name="settings_export_as_file_section">Exportar</string>
<string name="activity_type_swimming">Natación</string>
<string name="activity_type_swimming_open">Natación en aguas abiertas</string>
<string name="activity_type_workout">Entrenamiento</string>
+1 -1
View File
@@ -456,7 +456,7 @@
<string name="settings_stats_units_imperial_meter">Imperiale (mi, m)</string>
<string name="settings_stats_units_metric">Metrica (km, m)</string>
<string name="settings_import_section">Impurtà</string>
<string name="settings_export_section">Espurtà</string>
<string name="settings_export_as_file_section">Espurtà</string>
<string name="settings_public_api_package_title">Nome di u pacchettu per l’API publica</string>
<string name="settings_public_api_enabled_summary_off">Solu OpenTracks pò lancià è piantà l’arregistramenti.</string>
<string name="settings_public_api_enabled_summary_on">D’altre appiecazioni installate ponu lancià o piantà l’arregistramenti.</string>
+1 -1
View File
@@ -444,7 +444,7 @@ limitations under the License.
<string name="settings_cycling_sensor">Cyklistika</string>
<string name="settings_running_sensor">Běh</string>
<string name="settings_import_section">Importovat</string>
<string name="settings_export_section">Exportovat</string>
<string name="settings_export_as_file_section">Exportovat</string>
<string name="stats_sensors_power">Napájení</string>
<string name="permission_bluetooth_failed_rejected">V nastavení systému je třeba ručně povolit aplikaci OpenTracks přístup k blízkým zařízením.</string>
<string name="value_float_mile_hour">%1$.1f mi/h</string>
+1 -1
View File
@@ -471,7 +471,7 @@ limitations under the License.
<string name="custom_layout_select_layout">Vælg layout</string>
<string name="settings_running_sensor">Kører</string>
<string name="settings_import_section">Importere</string>
<string name="settings_export_section">Eksport</string>
<string name="settings_export_as_file_section">Eksport</string>
<string name="stats_clock">Ur</string>
<string name="value_float_mile_hour_recommended">%1$.1f mi/h (anbefalet)</string>
<string name="value_float_mile_hour">%1$.1f mi/h</string>
+1 -1
View File
@@ -511,7 +511,7 @@ Meldet das GPS-Gerät ungenaue Daten (z.B. Standort, Geschwindigkeit, Höhe), ka
<string name="settings_sensor_bluetooth_service_filter_on">Zeige nur Bluetooth-Geräte, die benötigte Dienste bekanntmachen</string>
<string name="settings_public_api_enabled_summary_on">Andere installierte Anwendungen können Aufzeichnungen starten oder stoppen.</string>
<string name="settings_public_api_disabled_toast">OpenTracks öffentliche API ist deaktiviert: es kann in den Einstellungen aktiviert werden.</string>
<string name="settings_export_section">Export</string>
<string name="settings_export_as_file_section">Export</string>
<string name="settings_public_api_enabled_summary_off">Nur OpenTracks kann Aufzeichnungen starten und stoppen.</string>
<string name="settings_import_section">Import</string>
<string name="settings_api_title">Öffentliche API</string>
+1 -1
View File
@@ -503,7 +503,7 @@ limitations under the License.
<string name="permission_bluetooth_failed_rejected">Es necesario conceder manualmente a OpenTracks el acceso a los Dispositivos Cercanos en la configuración del sistema.</string>
<string name="settings_running_sensor">Correr</string>
<string name="settings_import_section">Importar</string>
<string name="settings_export_section">Exportar</string>
<string name="settings_export_as_file_section">Exportar</string>
<string name="generic_today">Hoy</string>
<string name="generic_yesterday">Ayer</string>
<string name="menu_add_custom_layout">Nuevo diseño</string>
+1 -1
View File
@@ -442,7 +442,7 @@ limitations under the License.
<string name="settings_reset_summary">Lähtesta seadistused vaikeväärtusteks</string>
<string name="settings_recording_customize_layout_title">Kohanda oma salvestuse paigutust</string>
<string name="settings_recording_customize_layout_select_columns">Andmeväljad rea kohta</string>
<string name="settings_export_section">Eksport</string>
<string name="settings_export_as_file_section">Eksport</string>
<string name="settings_announcements_totaltime_frequency">Ajavahemik</string>
<string name="settings_layout_reset">Lähtesta kohandatud paigutus</string>
<string name="settings_layout_reset_confirm_title">Kas lähtestad oma paigutused?</string>
+1 -1
View File
@@ -458,7 +458,7 @@ GPS gailuak datu okerrak salatzen baditu (adibidez, kokapena, abiadura, kota), O
<string name="settings_layout_reset_done">Diseinuaren ezarpen guztiak balio lehenetsietara itzuli dira</string>
<string name="settings_all_reset">Berrezarri</string>
<string name="settings_import_section">Inportatu</string>
<string name="settings_export_section">Esportatu</string>
<string name="settings_export_as_file_section">Esportatu</string>
<string name="stats_coordinates">Latitudea /Longitudea</string>
<string name="stats_clock">Erlojua</string>
<string name="value_float_mile_hour">%1$.1f mi/h</string>
+1 -1
View File
@@ -490,7 +490,7 @@ limitations under the License.
<string name="stats_sensors_power">Teho</string>
<string name="no_compatible_file_manager_installed">Yhteensopiva tiedostojen hallinta puuttuu</string>
<string name="settings_export_filename_title">Tiedostonimen muoto</string>
<string name="settings_export_section">Vienti</string>
<string name="settings_export_as_file_section">Vienti</string>
<string name="settings_recording_customize_layout_title">Muokkaa tallennuksen asettelua</string>
<string name="settings_sensor_bluetooth_service_filter_on">Näytä vain Bluetooth-laitteet, jotka ilmoittavat tarvittavat palvelut</string>
<string name="settings_public_api_dashboard_enabled_summary_off">Tallennettuja tietoja ei jaeta automaattisesti muiden sovellusten kanssa.</string>
+1 -1
View File
@@ -505,7 +505,7 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
<string name="settings_cycling_sensor">Cyclisme</string>
<string name="settings_running_sensor">Course à pied</string>
<string name="settings_import_section">Importer</string>
<string name="settings_export_section">Exporter</string>
<string name="settings_export_as_file_section">Exporter</string>
<string name="settings_reset_summary">Réinitialiser les paramètres aux valeurs par défaut</string>
<string name="settings_activity_defaults_category">Valeurs par défaut de l\'activité</string>
<string name="settings_sensors_summary">Capteurs de cyclisme, Circonférence de roue, Capteurs de course à pied</string>
+1 -1
View File
@@ -257,7 +257,7 @@
<string name="settings_public_api_enabled_summary_off">Ní féidir ach le OpenTracks taifeadtaí a thosú agus a stopadh.</string>
<string name="settings_public_api_dashboard_enabled_summary_off">Ní dhéanfar sonraí taifeadta a roinnt go huathoibríoch le feidhmchláir eile.</string>
<string name="settings_import_section">Iompórtáil</string>
<string name="settings_export_section">Easpórtáil</string>
<string name="settings_export_as_file_section">Easpórtáil</string>
<string name="share_track_share_file_body">Sílim go mb\'fhéidir go mbeadh suim agat sa rian seo.
\n
\n%1$s</string>
+1 -1
View File
@@ -443,7 +443,7 @@ limitations under the License.
<string name="settings_cycling_sensor">Ciclismo</string>
<string name="settings_running_sensor">Correr</string>
<string name="settings_import_section">Importar</string>
<string name="settings_export_section">Exportar</string>
<string name="settings_export_as_file_section">Exportar</string>
<string name="settings_announcements_distance_frequency">Lonxitude da volta</string>
<string name="permission_bluetooth_failed">OpenTracks require permiso para usar o Bluetooth.</string>
<string name="permission_bluetooth_failed_rejected">Tes que permitirlle a OpenTracks nos axustes do sistema que acceda a Dispositivos Próximos.</string>
+1 -1
View File
@@ -305,7 +305,7 @@ limitations under the License.
<string name="waypoint_type_water_fountain">fontana</string>
<string name="waypoint_type_waypoint">točka na ruti</string>
<string name="settings_import_section">Uvoz</string>
<string name="settings_export_section">Izvoz</string>
<string name="settings_export_as_file_section">Izvoz</string>
<string name="settings_prevent_reimport_tracks_title">Spriječi ponovni uvoz ruta</string>
<string name="menu_export_all_one_file">Izvezi sve (jedna datoteka za sve rute)</string>
<string name="menu_select_layout">Odaberi raspored</string>
+1 -1
View File
@@ -467,7 +467,7 @@ limitations under the License.
<string name="average_heart_rate">Átlagos pulzusszám</string>
<string name="settings_sensor_bluetooth_service_filter_on">Csak azokat a Bluetooth-eszközöket jelenítse meg, amelyek szükséges szolgáltatásokat kérnek</string>
<string name="settings_import_section">Importálás</string>
<string name="settings_export_section">Exportálás</string>
<string name="settings_export_as_file_section">Exportálás</string>
<string name="settings_layout_reset_confirm_title">Az elrendezések visszaállítása?</string>
<string name="settings_layout_reset_done">Az összes elrendezési beállítás visszaállt az alapértelmezett értékekre</string>
<string name="custom_layout_list_layout_removed">Az elrendezést eltávolították</string>
+1 -1
View File
@@ -448,7 +448,7 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
<string name="settings_cycling_sensor">Ciclismo</string>
<string name="settings_running_sensor">Corsa</string>
<string name="settings_import_section">Importa</string>
<string name="settings_export_section">Esporta</string>
<string name="settings_export_as_file_section">Esporta</string>
<string name="stats_coordinates">Latitudine/Longitudine</string>
<string name="field_remove_from_layout">Rimuovi</string>
<string name="field_set_primary">Primario</string>
+1 -1
View File
@@ -333,7 +333,7 @@
<string name="settings_public_api_dashboard_enabled_summary_on">ರೆಕಾರ್ಡಿಂಗ್ ಅನ್ನು ಪ್ರಾರಂಭಿಸಿದ ಅಪ್ಲಿಕೇಶನ್ ರೆಕಾರ್ಡ್ ಮಾಡಿದ ಡೇಟಾವನ್ನು ಸಹ ಪ್ರವೇಶಿಸಬಹುದು.</string>
<string name="settings_public_api_dashboard_enabled_summary_off">ರೆಕಾರ್ಡ್ ಮಾಡಲಾದ ಡೇಟಾವನ್ನು ಇತರ ಅಪ್ಲಿಕೇಶನ್‌ಗಳೊಂದಿಗೆ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಹಂಚಿಕೊಳ್ಳಲಾಗುವುದಿಲ್ಲ.</string>
<string name="settings_import_section">ಆಮದು</string>
<string name="settings_export_section">ರಫ್ತು</string>
<string name="settings_export_as_file_section">ರಫ್ತು</string>
<string name="settings_prevent_reimport_tracks_title">ಟ್ರ್ಯಾಕ್‌ಗಳ ಮರು-ಆಮದು ತಡೆಯಿರಿ</string>
<string name="share_track_share_file_body">ನೀವು ಈ ಟ್ರ್ಯಾಕ್‌ನಲ್ಲಿ ಆಸಕ್ತಿ ಹೊಂದಿರಬಹುದು ಎಂದು ನಾನು ಭಾವಿಸುತ್ತೇನೆ.
\n
+1 -1
View File
@@ -437,7 +437,7 @@ limitations under the License.
<string name="settings_cycling_sensor">Sykling</string>
<string name="settings_running_sensor">Løping</string>
<string name="settings_import_section">Import</string>
<string name="settings_export_section">Eksport</string>
<string name="settings_export_as_file_section">Eksport</string>
<string name="value_float_kilometer_hour">%1$.1f km/t</string>
<string name="value_float_kilometer_hour_recommended">%1$.1f km/t (anbefales)</string>
<string name="value_float_mile_hour">%1$.1f mi/h</string>
+1 -1
View File
@@ -465,7 +465,7 @@ Als het GPS-apparaat onnauwkeurige gegevens rapporteert (bijv. locatie, snelheid
<string name="settings_cycling_sensor">Fietsen</string>
<string name="settings_running_sensor">Hardlopen</string>
<string name="settings_import_section">Invoer</string>
<string name="settings_export_section">Uitvoer</string>
<string name="settings_export_as_file_section">Uitvoer</string>
<string name="settings_generic_sensor">Generiek</string>
<string name="export_dir_not_writable">" (geen schrijfrecht!)"</string>
<string name="value_float_kilometer_hour_recommended">%1$.1f km/u (aanbevolen)</string>
+1 -1
View File
@@ -450,7 +450,7 @@ limitations under the License.
<string name="settings_cycling_sensor">Kolarstwo</string>
<string name="settings_running_sensor">Bieganie</string>
<string name="settings_import_section">Import</string>
<string name="settings_export_section">Eksport</string>
<string name="settings_export_as_file_section">Eksport</string>
<string name="settings_generic_sensor">Domyślny</string>
<string name="description_speed_source_gps">GPS</string>
<string name="permission_bluetooth_failed">OpenTracks wymaga uprawnień do urzycia Bluetooth.</string>
+1 -1
View File
@@ -410,7 +410,7 @@
<string name="settings_cycling_sensor">Ciclismo</string>
<string name="settings_running_sensor">Correndo</string>
<string name="settings_import_section">Importar</string>
<string name="settings_export_section">Exportar</string>
<string name="settings_export_as_file_section">Exportar</string>
<string name="value_float_kilometer_hour">%1$.1f km/h</string>
<string name="value_float_kilometer_hour_recommended">%1$.1f km/h (Recomendado)</string>
<string name="stats_clock">Relógio</string>
+1 -1
View File
@@ -451,7 +451,7 @@
<string name="settings_public_api_dashboard_enabled_summary_on">Uma aplicação que iniciou uma gravação também pode acessar os dados gravados.</string>
<string name="settings_public_api_dashboard_enabled_summary_off">Os dados gravados não serão compartilhados automaticamente com outras aplicações.</string>
<string name="settings_import_section">Importar</string>
<string name="settings_export_section">Exportar</string>
<string name="settings_export_as_file_section">Exportar</string>
<string name="settings_prevent_reimport_tracks_title">Impedir a reimportação de trilhas</string>
<string name="stats_loss">Perda</string>
<string name="stats_coordinates">Latitude/Longitude</string>
+1 -1
View File
@@ -466,7 +466,7 @@ limitations under the License.
<string name="settings_public_api_enabled_summary_off">Apenas o OpenTracks pode iniciar e parar gravações.</string>
<string name="settings_public_api_dashboard_enabled_summary_off">Os dados gravados não serão compartilhados automaticamente com outras aplicações.</string>
<string name="settings_import_section">Importar</string>
<string name="settings_export_section">Exportar</string>
<string name="settings_export_as_file_section">Exportar</string>
<string name="settings_prevent_reimport_tracks_title">Impedir a reimportação de trilhas</string>
<string name="stats_loss">Perda</string>
<string name="stats_coordinates">Latitude/Longitude</string>
+1 -1
View File
@@ -498,7 +498,7 @@ limitations under the License.
<string name="current_heart_rate">Текущий пульс</string>
<string name="generic_filter">Фильтр</string>
<string name="generic_from">с</string>
<string name="settings_export_section">Экспорт</string>
<string name="settings_export_as_file_section">Экспорт</string>
<string name="settings_import_section">Импорт</string>
<string name="settings_api_title">Публичный API</string>
<string name="settings_public_api_disabled_toast">Публичный API OpenTracks отключен: он может быть включен в настройках.</string>
+1 -1
View File
@@ -381,7 +381,7 @@ limitations under the License.
<string name="settings_running_sensor">Spúšťanie</string>
<string name="settings_cycling_sensor">Cyklistika</string>
<string name="custom_layout_list_layout_removed">Rozloženie bolo odstránené</string>
<string name="settings_export_section">Exportujte</string>
<string name="settings_export_as_file_section">Exportujte</string>
<string name="settings_import_section">Importovať</string>
<string name="track_recording_notification_accuracy">Presnosť polohy: %1$s</string>
<string name="stats_clock">Hodiny</string>
+1 -1
View File
@@ -352,7 +352,7 @@ limitations under the License.
<string name="import_parser_error">Napaka analizatorja: %1$s</string>
<string name="settings_sensor_cycling_cadence">Kadenca</string>
<string name="menu_select_layout">Izberite postavitev</string>
<string name="settings_export_section">Izvoz</string>
<string name="settings_export_as_file_section">Izvoz</string>
<string name="stats_sensors_cadence">Kadenca</string>
<string name="stats_sensors_heart_rate">Srčni utrip</string>
<string name="settings_ui_title">Uporabniški vmesnik</string>
+1 -1
View File
@@ -338,7 +338,7 @@
<string name="settings_public_api_dashboard_enabled_summary_on">பதிவைத் தொடங்கிய பயன்பாடு பதிவு செய்யப்பட்ட தரவையும் அணுகலாம்.</string>
<string name="settings_public_api_dashboard_enabled_summary_off">பதிவுசெய்யப்பட்ட தரவு மற்ற பயன்பாடுகளுடன் தானாக பகிரப்படாது.</string>
<string name="settings_import_section">இறக்குமதி</string>
<string name="settings_export_section">ஏற்றுமதி</string>
<string name="settings_export_as_file_section">ஏற்றுமதி</string>
<string name="share_track_share_file_body">இந்த பாதையில் நீங்கள் ஆர்வமாக இருக்கலாம் என்று நினைக்கிறேன்.\n\n %1$s</string>
<string name="settings_prevent_reimport_tracks_title">தடங்களை மீண்டும் இறக்குமதி செய்வதைத் தடுக்கவும்</string>
<string name="share_track_subject">நான் உங்களுடன் ஒரு பாதையைப் பகிர்ந்து கொள்ள விரும்புகிறேன்</string>
+1 -1
View File
@@ -438,7 +438,7 @@ limitations under the License.
<string name="settings_public_api_dashboard_enabled_summary_off">Kaydedilen veriler diğer uygulamalarla otomatik olarak paylaşılmayacaktır.</string>
<!-- Settings import/export -->
<string name="settings_import_section">İçe Aktar</string>
<string name="settings_export_section">Dışa Aktar</string>
<string name="settings_export_as_file_section">Dışa Aktar</string>
<string name="settings_prevent_reimport_tracks_title">Parkurların yeniden içe aktarılmasını engelle</string>
<!-- Share Track -->
<string name="share_track_share_file_body">Bu parkurun ilginizi çekebileceğini düşündüm.\n\n%1$s</string>
+1 -1
View File
@@ -425,7 +425,7 @@ limitations under the License.
<string name="field_remove_from_layout">Видалити</string>
<string name="stats_clock">Годинник</string>
<string name="settings_recording_customize_layout_select_columns">Поля даних у рядку</string>
<string name="settings_export_section">Експорт</string>
<string name="settings_export_as_file_section">Експорт</string>
<string name="field_set_secondary">Вторинний</string>
<string name="settings_all_reset">Скидання</string>
<string name="generic_filter">Фільтр</string>
+1 -1
View File
@@ -455,7 +455,7 @@ limitations under the License.
<string name="settings_cycling_sensor">Đạp xe</string>
<string name="settings_running_sensor">Chạy</string>
<string name="settings_import_section">Nhập</string>
<string name="settings_export_section">Xuất</string>
<string name="settings_export_as_file_section">Xuất</string>
<string name="settings_generic_sensor">Chung chung</string>
<string name="value_float_mile_hour">%1$.1f mi/h</string>
<string name="value_float_mile_hour_recommended">%1$.1f mi/h (khuyến khích)</string>
+1 -1
View File
@@ -393,7 +393,7 @@ limitations under the License.
<string name="settings_public_api_dashboard_enabled_summary_off">記錄的資料將不會自動與其他應用程式共用。</string>
<!-- Settings import/export -->
<string name="settings_import_section">匯入</string>
<string name="settings_export_section">匯出</string>
<string name="settings_export_as_file_section">匯出</string>
<string name="settings_prevent_reimport_tracks_title">防止重新匯入蹤跡</string>
<!-- Share Track -->
<string name="share_track_share_file_body">我想您應該會對這個路線感興趣。
+1 -1
View File
@@ -539,7 +539,7 @@ limitations under the License.
<string name="settings_public_api_disabled_toast">OpenTracks 公共 API 已禁用:可以在设置中启用。</string>
<string name="settings_public_api_dashboard_enabled_title">自动数据传输</string>
<string name="settings_public_api_dashboard_enabled_summary_off">记录的数据不会自动与其他应用程序共享。</string>
<string name="settings_export_section">导出</string>
<string name="settings_export_as_file_section">导出</string>
<string name="stats_loss">下降</string>
<string name="stats_coordinates">纬度/经度</string>
<string name="track_pace_notification">配速:%1$s</string>
+3
View File
@@ -58,6 +58,9 @@
<string name="export_filename_format_key" translatable="false">instantExportFilename</string>
<string name="export_filename_format_default" translatable="false">{date}_{time}_{name}</string>
<string name="post_workout_export_health_connect_enabled_key" translatable="false">instantExportHealthConnectEnabled</string>
<bool name="post_workout_export_health_connect_enabled_default" translatable="false">false</bool>
<!-- Stats layout customization default layout -->
<string name="settings_layout_reset_key" translatable="false">settingsLayoutReset</string>
+10 -1
View File
@@ -280,6 +280,7 @@ limitations under the License.
<string name="menu_edit">Edit</string>
<string name="menu_export_all">Export all (one file per track)</string>
<string name="menu_export_all_one_file">Export all (one file for all tracks)</string>
<string name="menu_export_all_to_health_connect">Export all</string>
<string name="menu_help">Help</string>
<string name="menu_import_all">Import all</string>
<string name="menu_insert_marker">Insert marker</string>
@@ -448,7 +449,8 @@ limitations under the License.
<string name="settings_public_api_dashboard_enabled_summary_off">Recorded data will not be shared automatically with other applications.</string>
<!-- Settings import/export -->
<string name="settings_import_section">Import</string>
<string name="settings_export_section">Export</string>
<string name="settings_export_as_file_section">Export as file</string>
<string name="settings_export_to_health_connect">Export to Health Connect</string>
<string name="settings_prevent_reimport_tracks_title">Prevent re-import of tracks</string>
<!-- Share Track -->
<string name="share_track_share_file_body">I think you might be interested in this track.\n\n%1$s</string>
@@ -679,6 +681,13 @@ limitations under the License.
<string name="hold_to_stop">Hold to stop</string>
<string name="instant_export_enabled_summary">Export track to storage after recording is finished</string>
<string name="instant_export_enabled_title">Instant post-workout export</string>
<string name="health_connect_export_enabled_summary">Export activity to Health Connect after recording is finished</string>
<string name="health_connect_not_available">Health Connect is not available on this device</string>
<string name="health_connect_open_settings_title">Health Connect settings</string>
<string name="health_connect_open_settings_summary">Open the Health Connect settings</string>
<string name="health_connect_permissions_rationale_title">Health Connect permissions</string>
<string name="health_connect_permissions_rationale_heading">How OpenTracks uses Health Connect</string>
<string name="health_connect_permissions_rationale_summary">If enabled in the settings, OpenTracks writes exercise to Health Connect. OpenTracks does not read data from Health Connect.</string>
<string name="settings_default_export_uri_title">Track export directory</string>
<!-- Fields -->
<string name="field_set_primary">Primary</string>
+35 -2
View File
@@ -17,7 +17,7 @@
android:title="@string/settings_prevent_reimport_tracks_title" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/settings_export_section">
<PreferenceCategory android:title="@string/settings_export_as_file_section">
<Preference
android:title="@string/menu_export_all">
<intent
@@ -52,4 +52,37 @@
android:title="@string/settings_export_filename_title"
android:defaultValue="@string/export_filename_format_default" />
</PreferenceCategory>
</PreferenceScreen>
<PreferenceCategory
android:title="@string/settings_export_to_health_connect"
android:key="healthConnectCategory">
<Preference
android:persistent="false"
android:summary="@string/health_connect_open_settings_summary"
android:title="@string/health_connect_open_settings_title">
<intent
android:targetClass="de.dennisguse.opentracks.settings.HealthConnect$Settings"
android:targetPackage="@string/applicationId" />
</Preference>
<Preference
android:title="@string/health_connect_permissions_rationale_title"
android:key="healthConnectPermissions">
<intent
android:targetClass="de.dennisguse.opentracks.settings.HealthConnect$ConfigurePermissions"
android:targetPackage="@string/applicationId" />
</Preference>
<Preference android:title="@string/menu_export_all_to_health_connect">
<intent
android:targetClass="de.dennisguse.opentracks.settings.HealthConnect$ExportAll"
android:targetPackage="@string/applicationId" />
</Preference>
<SwitchPreferenceCompat
android:defaultValue="@bool/post_workout_export_health_connect_enabled_default"
android:key="@string/post_workout_export_health_connect_enabled_key"
android:summary="@string/health_connect_export_enabled_summary"
android:title="@string/instant_export_enabled_title" />
</PreferenceCategory>
</PreferenceScreen>