/*
* Copyright (c) 2013, Will Szumski
* Copyright (c) 2013, Doug Szumski
*
* This file is part of Cyclismo.
*
* Cyclismo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cyclismo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cyclismo. If not, see <http://www.gnu.org/licenses/>.
*/
package org.cowboycoders.cyclismo.fragments;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.util.Log;
import org.cowboycoders.cyclismo.Constants;
import org.cowboycoders.cyclismo.CourseSetupActivity;
import org.cowboycoders.cyclismo.R;
import org.cowboycoders.cyclismo.content.Bike;
import org.cowboycoders.cyclismo.content.CyclismoProviderUtils;
import org.cowboycoders.cyclismo.content.MyTracksCourseProviderUtils;
import org.cowboycoders.cyclismo.content.MyTracksProviderUtils;
import org.cowboycoders.cyclismo.content.Track;
import org.cowboycoders.cyclismo.content.User;
import org.cowboycoders.cyclismo.settings.AbstractSettingsFragment;
import org.cowboycoders.cyclismo.turbo.TurboService;
import org.cowboycoders.cyclismo.util.PreferenceEntry;
import org.cowboycoders.cyclismo.util.PreferencesUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class CourseSetupFragment extends AbstractSettingsFragment {
private final static String TAG = CourseSetupFragment.class.getSimpleName();
public static final int PICK_COURSE_REQUEST = 1567;
private OnSharedPreferenceChangeListener sharedListener;
private SharedPreferences sharedPreferences;
private UpdateSummaryCaller updateCourseModeSummaryCaller;
private UpdateSummaryCaller updateConstantCoursePowerCaller;
private UpdateSummaryCaller updateTrackIdSummaryCaller;
private UpdateSummaryCaller updateBikeSummaryCaller;
private UpdateSummaryCaller updateTurboSummaryCaller;
private List<CourseSetupObserver> observers = new ArrayList<>();
private ListPreference courseModeListPreference;
private Preference constantCoursePowerPreference;
private Preference courseTrackIdPreference;
private Preference bikeSelectPreference;
private Preference turboSelectPreference;
private PreferenceEntry courseModes;
private TurboService turboService;
public CourseSetupFragment addObserver(CourseSetupObserver observer) {
observers.add(observer);
return this;
}
private String setupCourseModeSelector(Context context, String turboSelectValue) {
Log.i(TAG, "updating available course modes");
courseModeListPreference = (ListPreference) findPreference(getString(R.string.course_mode));
// Since the course modes are a function of the turbo we set them here rather than in the xml
courseModes = turboService.getCourseModesForTurbo(turboSelectValue);
courseModeListPreference.setEntries(courseModes.getEntries());
courseModeListPreference.setEntryValues(courseModes.getEntryValues());
String courseModeValue = PreferencesUtils.getString(context, R.string.course_mode, null);
if (!Arrays.asList(courseModes.getEntryValues()).contains(courseModeValue)) {
Log.d(TAG, "course mode: " + courseModeValue + " not available from turbo: " +
turboSelectValue + ", resetting to default.");
courseModeValue = getString(R.string.settings_courses_mode_simulation_value);
courseModeListPreference.setValue(courseModeValue);
}
PreferencesUtils.SettingsSelectionSummarizer courseModeSummarizer =
new PreferencesUtils.SettingsSelectionSummarizer() {
@Override
public String summarize(Object value) {
return summarizeCourseModeSelection(value);
}
};
updateCourseModeSummaryCaller = new UpdateSummaryCaller(
courseModeListPreference,
courseModes.getEntries(),
courseModes.getEntryValues(),
R.string.settings_courses_mode_summary,
courseModeSummarizer);
return courseModeValue;
}
private Integer setupConstantCoursePowerSelector(Context context) {
Log.i(TAG, "updating constant course power");
constantCoursePowerPreference = findPreference(
getString(R.string.constant_course_power));
Integer constantCoursePowerValue = PreferencesUtils.getInt(
context, R.string.constant_course_power, 0);
PreferencesUtils.SettingsSelectionSummarizer constantCoursePowerSummarizer =
new PreferencesUtils.SettingsSelectionSummarizer() {
@Override
public String summarize(Object value) {
return summarizeConstantCoursePowerSelection(value);
}
};
updateConstantCoursePowerCaller = new UpdateSummaryCaller(
constantCoursePowerPreference,
null,
null,
R.string.settings_constant_course_power_summary,
constantCoursePowerSummarizer);
// Grey out the power settings when not in constant power mode. Ideally this would actually
// remove the setting.
courseModeListPreference.setOnPreferenceChangeListener(
new
Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
configureConstantPowerModePreferences(newValue.toString());
return true;
}
});
return constantCoursePowerValue;
}
private String setupTurboSelector(Context context) {
turboSelectPreference = findPreference(getString(R.string.turbotrainer_selected));
PreferencesUtils.SettingsSelectionSummarizer turboTrainerSummarizer =
new PreferencesUtils.SettingsSelectionSummarizer() {
@Override
public String summarize(Object value) {
return summarizeTurboSelection(value);
}
};
updateTurboSummaryCaller = new UpdateSummaryCaller(
turboSelectPreference,
getResources().getStringArray(R.array.turbotrainer_options),
getResources().getStringArray(R.array.turbotrainer_values),
R.string.settings_turbotrainer_select_summary,
turboTrainerSummarizer);
return PreferencesUtils.getString(context, R.string.turbotrainer_selected, null);
}
private Long setupTrackSelector(Context context) {
courseTrackIdPreference = findPreference(getString(R.string.course_track_id));
courseTrackIdPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
startActivityForResult(preference.getIntent(), PICK_COURSE_REQUEST);
return true;
}
});
PreferencesUtils.SettingsSelectionSummarizer summarizer =
new PreferencesUtils.SettingsSelectionSummarizer() {
@Override
public String summarize(Object value) {
return summarizeTrackIdSelection(value);
}
};
this.updateTrackIdSummaryCaller = new UpdateSummaryCaller(
courseTrackIdPreference, null, null, R.string.settings_courses_route_summary, summarizer);
return PreferencesUtils.getLong(context, R.string.course_track_id);
}
private Long setupBikeSelector(Context context) {
bikeSelectPreference = findPreference(getString(R.string.course_bike_id));
bikeSelectPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
startActivityForResult(preference.getIntent(), R.string.bike_select_request_code);
return true;
}
});
PreferencesUtils.SettingsSelectionSummarizer bikeSelectSummarizer =
new PreferencesUtils.SettingsSelectionSummarizer() {
@Override
public String summarize(Object value) {
return summarizeBikeSelection(value);
}
};
this.updateBikeSummaryCaller = new UpdateSummaryCaller(
bikeSelectPreference, null, null, R.string.settings_courses_bike_select_summary,
bikeSelectSummarizer);
return PreferencesUtils.getLong(context, R.string.settings_select_bike_current_selection_key);
}
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "fragment onCreate");
super.onCreate(savedInstanceState);
final Context context = this.getActivity();
if (context instanceof CourseSetupActivity) {
turboService = ((CourseSetupActivity) context).getTurboService();
} else {
throw new IllegalArgumentException("Invalid context");
}
addPreferencesFromResource(R.xml.course_settings);
sharedPreferences = getActivity().
getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
// Setup menu preferences
Long courseTrackIdValue = setupTrackSelector(context);
Long bikeValue = setupBikeSelector(context);
String turboValue = setupTurboSelector(context);
String courseModeValue = setupCourseModeSelector(context, turboValue);
Integer constantCoursePowerValue = setupConstantCoursePowerSelector(context);
// Initial configuration of settings UI
updateTurbo(turboValue);
updateBike(bikeValue);
updateCourseTrackId(courseTrackIdValue);
updateCourseMode(courseModeValue);
updateConstantCoursePower(constantCoursePowerValue);
// Listener for updating UI when selections change
sharedListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferencesIn, String key) {
if (key.equals(PreferencesUtils.getKey(getActivity(), R.string.course_mode))) {
String newValue = sharedPreferences.getString(key, null);
updateCourseMode(newValue);
} else if (key.equals(PreferencesUtils.getKey(getActivity(), R.string.course_track_id))) {
Long newValue = sharedPreferences.getLong(key, -1l);
updateCourseTrackId(newValue);
} else if (key.equals(PreferencesUtils.getKey(getActivity(), R.string.constant_course_power))) {
Integer newValue = sharedPreferences.getInt(key, 0);
updateConstantCoursePower(newValue);
} else if (key.equals(PreferencesUtils.getKey(getActivity(), R.string
.settings_select_bike_current_selection_key))) {
Long newValue = sharedPreferences.getLong(key, -1l);
updateBike(newValue);
} else if (key.equals(PreferencesUtils.getKey(getActivity(), R.string
.turbotrainer_selected))) {
String newValue = sharedPreferences.getString(key, null);
updateTurbo(newValue);
setupCourseModeSelector(context, newValue);
}
}
};
// This is required to initialse the settings. The whole class could use some refactoring at
// some stage to support adding new settings in a more transparent and extensible way.
for (CourseSetupObserver observer : observers) {
observer.onCourseModeUpdate(courseModeValue);
observer.onTrackIdUpdate(courseTrackIdValue);
updateBike(bikeValue);
observer.onTurboUpdate(turboValue);
observer.onConstantCoursePowerUpdate(constantCoursePowerValue);
}
}
@Override
public void onStart() {
super.onStart();
this.sharedPreferences.registerOnSharedPreferenceChangeListener(sharedListener);
}
@Override
public void onDestroy() {
super.onStop();
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedListener);
}
protected String summarizeTrackIdSelection(Object value) {
Long trackId = (Long) value;
Log.i(TAG, "Selected track id: " + trackId);
String noneSelected = this.getString(R.string.item_not_selected);
if (trackId == -1) {
return noneSelected;
}
Track track = new MyTracksCourseProviderUtils(this.getActivity().getContentResolver())
.getTrack(trackId);
if (track == null) {
Log.w(TAG, "track invalid : shared preference mismatch");
PreferencesUtils.setLong(this.getActivity(), R.string.course_track_id, -1L);
return noneSelected;
}
return track.getName();
}
private static User getUser(Context context) {
final CyclismoProviderUtils providerUtils = MyTracksProviderUtils.Factory.getCyclimso(context);
final long currentUserId = PreferencesUtils.getLong(context, R.string
.settings_select_user_current_selection_key);
User user = providerUtils.getUser(currentUserId);
return user;
}
private void updateBike(final Long newValue) {
Log.i(TAG, "new bike selected with id: " + newValue);
this.updateBikeSummaryCaller.updateSummary(newValue);
for (final CourseSetupObserver observer : observers) {
new AsyncTask<Object, Integer, Bike>() {
@Override
protected Bike doInBackground(Object... params) {
long bikeId = getCurrentBikeId();
if (bikeId == -1L) {
observer.onBikeUpdate(null);
return null;
}
Bike bike = loadBike(bikeId);
observer.onBikeUpdate(bike);
return bike;
}
}.execute(new Object());
}
}
private long getCurrentBikeId() {
User currentUser = getUser(CourseSetupFragment.this.getActivity());
long bikeId = currentUser.getCurrentlySelectedBike();
return bikeId;
}
private Bike loadBike(long id) {
return MyTracksProviderUtils.Factory.getCyclimso(this.getActivity()).getBike(id);
}
protected String summarizeBikeSelection(Object value) {
String noneSelected = this.getString(R.string.item_not_selected);
// the bikeid coming is from the shared preference / get current users bike
Long bikeId = getCurrentBikeId();
if (bikeId == -1L) {
return noneSelected;
}
Bike bike = loadBike(bikeId);
if (bike == null) {
Log.w(TAG, "bike invalid : shared preference mismatch");
PreferencesUtils.setLong(this.getActivity(), R.string
.settings_select_bike_current_selection_key, -1L);
return noneSelected;
}
return bike.getName();
}
protected String summarizeCourseModeSelection(Object value) {
String courseMode = (String) value;
if (courseMode == null) {
Log.w(TAG, "course mode invalid");
PreferencesUtils.setLong(this.getActivity(), R.string.course_mode, -1L);
return this.getString(R.string.item_not_selected);
}
return courseMode;
}
protected String summarizeConstantCoursePowerSelection(Object value) {
Integer power = (Integer) value;
if (power == null) {
Log.w(TAG, "constant course power invalid");
PreferencesUtils.setInt(this.getActivity(), R.string.constant_course_power, 0);
return this.getString(R.string.item_not_selected);
}
return power.toString() + "W";
}
protected String summarizeTurboSelection(Object value) {
String turbo = (String) value;
if (turbo == null) {
Log.w(TAG, "turbo invalid");
PreferencesUtils.setLong(this.getActivity(), R.string.turbotrainer_selected, -1L);
return this.getString(R.string.item_not_selected);
}
return turbo;
}
private void updateCourseTrackId(Long newValue) {
Log.i(TAG, "new track selected with id: " + newValue);
updateTrackIdSummaryCaller.updateSummary(newValue);
for (CourseSetupObserver observer : observers) {
observer.onTrackIdUpdate(newValue);
}
}
private void updateCourseMode(String newValue) {
Log.i(TAG, "course mode: " + newValue);
updateCourseModeSummaryCaller.updateSummary(newValue);
for (CourseSetupObserver observer : observers) {
observer.onCourseModeUpdate(newValue);
}
// TODO: Re-enable this if we have mode which doesn't need a track
//if (!newValue.equals(getString(R.string.settings_courses_mode_simulation_value))) {
// courseTrackIdPreference.setEnabled(false);
//}
}
private void updateConstantCoursePower(Integer newValue) {
Log.i(TAG, "constant course power: " + newValue);
updateConstantCoursePowerCaller.updateSummary(newValue);
for (CourseSetupObserver observer : observers) {
observer.onConstantCoursePowerUpdate(newValue);
}
configureConstantPowerModePreferences(null);
}
/**
* If constant power mode is selected, then show the settings for it. Otherwise hide them.
*
* @param newMode is the current selection in the trainer mode menu. If null is passed,
* the selection is retrieved from the mode list preference.
*/
private void configureConstantPowerModePreferences(String newMode) {
String constantPowerMode = getString(R.string.settings_courses_mode_constant_power_value);
newMode = (newMode != null) ? newMode : courseModeListPreference.getValue().toString();
if (newMode.equals(constantPowerMode)) {
constantCoursePowerPreference.setEnabled(true);
} else {
constantCoursePowerPreference.setEnabled(false);
}
}
private void updateTurbo(final String newValue) {
Log.i(TAG, "new turbo selected with id: " + newValue);
updateTurboSummaryCaller.updateSummary(newValue);
for (CourseSetupObserver observer : observers) {
observer.onTurboUpdate(newValue);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PICK_COURSE_REQUEST) {
if (resultCode == Activity.RESULT_OK || resultCode == Activity.RESULT_CANCELED) {
if (data != null) {
Long trackId = data.getLongExtra(getString(R.string.course_track_id), -1L);
updateCourseTrackId(trackId);
} else {
Log.d(TAG, "onActivityResult : data was null");
updateCourseTrackId(-1L);
}
}
}
super.onActivityResult(requestCode, resultCode, data);
}
public interface CourseSetupObserver {
void onTrackIdUpdate(Long trackId);
void onBikeUpdate(Bike bike);
void onCourseModeUpdate(String modeString);
void onConstantCoursePowerUpdate(Integer power);
void onTurboUpdate(String turbo);
}
}