Files
OpenTracks/src/main/java/de/dennisguse/opentracks/settings/SettingsActivity.java
T
2021-04-09 18:34:23 +02:00

305 lines
14 KiB
Java

package de.dennisguse.opentracks.settings;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.DialogFragment;
import androidx.preference.EditTextPreference;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import java.util.Locale;
import de.dennisguse.opentracks.AbstractActivity;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.services.TrackRecordingService;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.services.TrackRecordingServiceStatus;
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeCyclingCadenceAndSpeedPreference;
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeCyclingPowerPreference;
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeHeartRatePreference;
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeSensorPreference;
import de.dennisguse.opentracks.util.ActivityUtils;
import de.dennisguse.opentracks.util.BluetoothUtils;
import de.dennisguse.opentracks.util.HackUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.StringUtils;
public class SettingsActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, ResetDialogPreference.ResetCallback {
private static final String TAG = SettingsActivity.class.getSimpleName();
public static final String EXTRAS_CHECK_EXPORT_DIRECTORY = "Check Export Directory";
private PrefsFragment prefsFragment;
private boolean checkExportDirectory = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (intent != null && intent.hasExtra(EXTRAS_CHECK_EXPORT_DIRECTORY)) {
checkExportDirectory = true;
}
onReset();
}
@Override
protected View getRootView() {
return getLayoutInflater().inflate(R.layout.settings, null);
}
@Override
protected void setupActionBarBack(Toolbar toolbar) {
super.setupActionBarBack(toolbar);
toolbar.setTitle(R.string.menu_settings);
}
@Override
protected void onResume() {
if (checkExportDirectory) {
checkExportDirectory = false;
new AlertDialog.Builder(this)
.setIcon(R.drawable.ic_logo_24dp)
.setTitle(R.string.app_name)
.setMessage(R.string.export_error_post_workout)
.setNeutralButton(R.string.generic_ok, null)
.create()
.show();
}
super.onResume();
}
@Override
public void onChooseActivityTypeDone(String iconValue) {
prefsFragment.setDefaultActivity(iconValue);
}
@Override
public void onReset() {
prefsFragment = new PrefsFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.settings_fragment, prefsFragment).commit();
}
public static class PrefsFragment extends PreferenceFragmentCompat implements TrackRecordingServiceStatus.Listener {
private SharedPreferences sharedPreferences;
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
if (PreferencesUtils.isKey(getActivity(), R.string.stats_units_key, key)) {
getActivity().runOnUiThread(this::updateUnits);
}
if (PreferencesUtils.isKey(getActivity(), R.string.night_mode_key, key)) {
getActivity().runOnUiThread(() -> ActivityUtils.applyNightMode(sharedPreferences, getContext()));
}
};
private TrackRecordingServiceConnection trackRecordingServiceConnection;
private boolean isRecording;
private final Runnable bindServiceCallback = new Runnable() {
@Override
public void run() {
TrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
if (service != null) {
service.addListener(PrefsFragment.this);
}
}
};
// Used to forward update from ChooseActivityTypeDialogFragment; TODO Could be replaced with LiveData.
private ActivityTypePreference.ActivityPreferenceDialog activityPreferenceDialog;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
sharedPreferences = PreferencesUtils.getSharedPreferences(getContext());
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindServiceCallback);
try {
setPreferencesFromResource(R.xml.settings, rootKey);
} catch (ClassCastException e) {
// Some sharedPreference is broken: delete all and reset them; it is just a last resort...
Toast.makeText(getContext(), R.string.settings_error_initial_values_restored, Toast.LENGTH_LONG).show();
PreferencesUtils.resetPreferences(getContext(), true);
setPreferencesFromResource(R.xml.settings, rootKey);
}
setExportTrackFileFormatOptions();
setWheelCircumferenceInputFilter();
Preference instantExportDirectoryPreference = findPreference(getString(R.string.settings_default_export_directory_key));
instantExportDirectoryPreference.setSummaryProvider(preference -> {
DocumentFile directory = PreferencesUtils.getDefaultExportDirectoryUri(sharedPreferences, getContext());
//Use same value for not set as Androidx ListPreference and EditTextPreference
return directory != null ? directory.getName() : getString(R.string.not_set);
});
}
@Override
public void onResume() {
super.onResume();
trackRecordingServiceConnection.bind(getContext());
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
updateUnits();
updatePrefsDependOnRecording();
updateBluetooth();
updatePostWorkoutExport();
}
private void updatePostWorkoutExport() {
Preference instantExportEnabledPreference = findPreference(getString(R.string.post_workout_export_enabled_key));
instantExportEnabledPreference.setEnabled(PreferencesUtils.isDefaultExportDirectoryUri(sharedPreferences, getContext()));
}
@Override
public void onPause() {
super.onPause();
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
}
@Override
public void onDisplayPreferenceDialog(Preference preference) {
DialogFragment dialogFragment = null;
if (preference instanceof ResetDialogPreference) {
dialogFragment = ResetDialogPreference.ResetPreferenceDialog.newInstance(preference.getKey());
} else if (preference instanceof ActivityTypePreference) {
activityPreferenceDialog = ActivityTypePreference.ActivityPreferenceDialog.newInstance(preference.getKey());
dialogFragment = activityPreferenceDialog;
} else if (preference instanceof BluetoothLeHeartRatePreference) {
dialogFragment = BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(preference.getKey(), BluetoothUtils.HEART_RATE_SUPPORTING_DEVICES);
} else if (preference instanceof BluetoothLeCyclingCadenceAndSpeedPreference) {
dialogFragment = BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(preference.getKey(), BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID);
} else if (preference instanceof BluetoothLeCyclingPowerPreference) {
dialogFragment = BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(preference.getKey(), BluetoothUtils.CYCLING_POWER_UUID);
}
if (dialogFragment != null) {
dialogFragment.setTargetFragment(this, 0);
dialogFragment.show(getParentFragmentManager(), getClass().getSimpleName());
return;
}
super.onDisplayPreferenceDialog(preference);
}
@Override
public void onDestroy() {
super.onDestroy();
trackRecordingServiceConnection.unbind(getContext());
sharedPreferences = null;
}
public void setDefaultActivity(String iconValue) {
if (activityPreferenceDialog != null) {
activityPreferenceDialog.updateUI(iconValue);
}
}
private void updatePrefsDependOnRecording() {
Preference resetPreference = findPreference(getString(R.string.settings_reset_key));
Preference importPreference = findPreference(getString(R.string.settings_import));
Preference exportPreference = findPreference(getString(R.string.settings_export));
resetPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : "");
importPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : "");
exportPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : "");
resetPreference.setEnabled(!isRecording);
importPreference.setEnabled(!isRecording);
exportPreference.setEnabled(!isRecording);
}
private void updateBluetooth() {
// Disable Bluetooth preference if device does not have Bluetooth
BluetoothLeSensorPreference bluetoothPreference = findPreference(getString(R.string.settings_sensor_bluetooth_heart_rate_key));
bluetoothPreference.setVisible(BluetoothUtils.hasBluetooth(getContext()));
}
private void updateUnits() {
boolean metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getActivity());
ListPreference voiceFrequency = findPreference(getString(R.string.voice_frequency_key));
voiceFrequency.setEntries(StringUtils.getFrequencyOptions(getActivity(), metricUnits));
ListPreference minRecordingInterval = findPreference(getString(R.string.min_recording_interval_key));
minRecordingInterval.setEntries(PreferenceHelper.getMinRecordingIntervalEntries(getActivity()));
ListPreference recordingDistanceInterval = findPreference(getString(R.string.recording_distance_interval_key));
recordingDistanceInterval.setEntries(PreferenceHelper.getRecordingDistanceIntervalEntries(getActivity(), metricUnits));
ListPreference maxRecordingDistance = findPreference(getString(R.string.max_recording_distance_key));
maxRecordingDistance.setEntries(PreferenceHelper.getMaxRecordingDistanceEntries(getActivity(), metricUnits));
ListPreference recordingGpsAccuracy = findPreference(getString(R.string.recording_gps_accuracy_key));
recordingGpsAccuracy.setEntries(PreferenceHelper.getRecordingGpsAccuracyEntries(getActivity(), metricUnits));
ListPreference statsRatePreferences = findPreference(getString(R.string.stats_rate_key));
String[] entries = getResources().getStringArray(metricUnits ? R.array.stats_rate_metric_options : R.array.stats_rate_imperial_options);
statsRatePreferences.setEntries(entries);
HackUtils.invalidatePreference(statsRatePreferences);
}
private void setExportTrackFileFormatOptions() {
final TrackFileFormat[] trackFileFormats = {TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES, TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA, TrackFileFormat.GPX};
String[] entries = new String[trackFileFormats.length];
String[] entryValues = new String[trackFileFormats.length];
for (int i = 0; i < entries.length; i++) {
TrackFileFormat trackFileFormat = trackFileFormats[i];
String trackFileFormatUpperCase = trackFileFormat.getExtension().toUpperCase(Locale.US); //ASCII upper case
int photoMessageId = trackFileFormat.includesPhotos() ? R.string.export_with_photos : R.string.export_without_photos;
entries[i] = String.format("%s (%s)", trackFileFormatUpperCase, getString(photoMessageId));
entryValues[i] = trackFileFormat.name();
}
ListPreference listPreference = findPreference(getString(R.string.export_trackfileformat_key));
listPreference.setEntries(entries);
listPreference.setEntryValues(entryValues);
}
private void setWheelCircumferenceInputFilter() {
EditTextPreference wheelPreference = findPreference(getString(R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_key));
wheelPreference.setOnPreferenceChangeListener((preference, newValue) -> {
if (newValue instanceof String) {
try {
int newValueInt = Integer.parseInt((String) newValue);
return newValueInt > 500 && newValueInt < 4000;
} catch (NumberFormatException e) {
Log.w(TAG, "Entered string is no number.");
}
}
return false;
});
}
@Override
public void onTrackRecordingId(Track.Id trackId) {
if (trackId != null && !isRecording && isAdded()) {
isRecording = true;
updatePrefsDependOnRecording();
}
}
}
}