forked from upstream-mirrors/OpenTracks
Implemented basic HealthConnect exporter
Fixes #1542 Co-authored-by: Manuel <manuel@mmarca.tech>
This commit is contained in:
@@ -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'
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
+93
@@ -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);
|
||||
}
|
||||
}
|
||||
+185
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">我想您應該會對這個路線感興趣。
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user