/*
* SettingsActivity.java
* Copyright (C) 2015 Nicholas Killewald
*
* This file is distributed under the terms of the BSD license.
* The source package should have a LICENSE file at the toplevel.
*/
package net.exclaimindustries.geohashdroid.activities;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.backup.BackupManager;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.widget.Toast;
import com.commonsware.cwac.wakeful.WakefulIntentService;
import net.exclaimindustries.geohashdroid.R;
import net.exclaimindustries.geohashdroid.services.AlarmService;
import net.exclaimindustries.geohashdroid.services.WikiService;
import net.exclaimindustries.geohashdroid.util.GHDConstants;
import net.exclaimindustries.geohashdroid.util.HashBuilder;
import net.exclaimindustries.tools.QueueService;
import java.util.List;
/**
* <p>
* So, the actual Android class is already called "{@link PreferenceActivity}",
* it turns out. So let's call this one <code>PreferencesScreen</code>, because
* it got really confusing to call it <code>PreferencesActivity</code> like it
* used to be.
* </p>
*
* <p>
* Note that this doesn't inherit from BaseGHDThemeActivity. It has to
* implement all of that itself.
* </p>
*/
public class PreferencesScreen extends PreferenceActivity {
private boolean mStartedInNight = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
mStartedInNight = prefs.getBoolean(GHDConstants.PREF_NIGHT_MODE, false);
// We have to do this BEFORE any layouts are set up.
if(mStartedInNight)
setTheme(R.style.Theme_GeohashDroidDark);
else
setTheme(R.style.Theme_GeohashDroid);
super.onCreate(savedInstanceState);
}
@Override
protected void onResume() {
super.onResume();
// If the nightiness has changed since we paused, do a recreate.
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if(prefs.getBoolean(GHDConstants.PREF_NIGHT_MODE, false) != mStartedInNight)
recreate();
}
/**
* This largely comes from Android Studio's default Setting Activity wizard
* thingamajig. It conveniently updates preferences with summaries.
*/
private static Preference.OnPreferenceChangeListener mSummaryUpdater = new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
// The basic stringy version of the value.
String stringValue = newValue.toString();
if(preference instanceof ListPreference) {
// For list preferences, look up the correct display value in
// the preference's 'entries' list.
ListPreference listPreference = (ListPreference) preference;
int index = listPreference.findIndexOfValue(stringValue);
// Set the summary to reflect the new value.
preference.setSummary(
index >= 0
? listPreference.getEntries()[index]
: null);
} else {
// Eh, just use the string value. That's simple enough.
preference.setSummary(stringValue);
}
return true;
}
};
/**
* Also from Android Studio, this attaches a preference to the summary
* updater.
*/
private static void bindPreferenceSummaryToValue(Preference preference) {
// Set the listener to watch for value changes.
preference.setOnPreferenceChangeListener(mSummaryUpdater);
// Trigger the listener immediately with the preference's current value.
mSummaryUpdater.onPreferenceChange(preference,
PreferenceManager
.getDefaultSharedPreferences(preference.getContext())
.getString(preference.getKey(), ""));
}
@Override
public void onBuildHeaders(List<Header> target) {
// Let's Honeycomb these preferences right up. Headers!
loadHeadersFromResource(R.xml.pref_headers, target);
}
@Override
protected boolean isValidFragment(String fragmentName) {
return(fragmentName.equals(MapPreferenceFragment.class.getName())
|| fragmentName.equals(WikiPreferenceFragment.class.getName())
|| fragmentName.equals(OtherPreferenceFragment.class.getName()));
}
/**
* These are your garden-variety map preferences, assuming your garden is on
* the map somewhere.
*/
public static class MapPreferenceFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref_map);
bindPreferenceSummaryToValue(findPreference(GHDConstants.PREF_DIST_UNITS));
bindPreferenceSummaryToValue(findPreference(GHDConstants.PREF_COORD_UNITS));
bindPreferenceSummaryToValue(findPreference(GHDConstants.PREF_STARTUP_BEHAVIOR));
bindPreferenceSummaryToValue(findPreference(GHDConstants.PREF_KNOWN_NOTIFICATION));
// The known locations manager is just another Activity.
findPreference("_knownLocations").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
Intent i = new Intent(getActivity(), KnownLocationsPicker.class);
startActivity(i);
return true;
}
});
}
@Override
public void onStop() {
BackupManager bm = new BackupManager(getActivity());
bm.dataChanged();
super.onStop();
}
}
/**
* These are the preferences you'll be seeing way too often if you keep
* getting your wiki password wrong.
*/
public static class WikiPreferenceFragment extends PreferenceFragment {
/**
* This keeps track of whether or not the wiki username and/or password
* have changed. If so, we need to ask WikiService to resume itself, as
* the user might've come here to resolve a bad login error.
*/
private boolean mHasChanged = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref_wiki);
// Unfortunately, we can't use the otherwise-common binding method
// for username and password, owing to the extra boolean we need to
// track. Worse, since we don't want to update the summary for
// password (for obvious reasons), we can't even share the same
// object between the two preferences. Well, we CAN, but that won't
// really buy us much in terms of efficiency.
Preference usernamePref = findPreference(GHDConstants.PREF_WIKI_USER);
usernamePref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
preference.setSummary(newValue.toString());
mHasChanged = true;
return true;
}
});
usernamePref.setSummary(PreferenceManager.getDefaultSharedPreferences(getActivity()).getString(GHDConstants.PREF_WIKI_USER, ""));
findPreference(GHDConstants.PREF_WIKI_PASS).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
mHasChanged = true;
return true;
}
});
}
@Override
public void onStop() {
// If something changed, tell WikiService to kick back in. Don't
// worry; if WikiService isn't paused, this won't do anything, and
// if it's stopped for any other reason, it'll stop again when this
// comes in.
if(mHasChanged) {
mHasChanged = false;
Intent i = new Intent(getActivity(), WikiService.class);
i.putExtra(QueueService.COMMAND_EXTRA, QueueService.COMMAND_RESUME);
getActivity().startService(i);
}
BackupManager bm = new BackupManager(getActivity());
bm.dataChanged();
super.onStop();
}
}
/**
* These preferences are outcasts, and nobody likes them.
*/
public static class OtherPreferenceFragment extends PreferenceFragment {
private static final String WIPE_DIALOG = "wipeDialog";
private static final String RESET_BUGGING_ME_DIALOG = "resetBuggingMe";
/**
* This is the {@link DialogFragment} that shows up when the user wants
* to wipe the stock cache, just to make really really sure the user
* really wants to do so.
*/
public static class WipeCacheDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity()).setMessage(R.string.pref_stockwipe_dialog_text)
.setTitle(R.string.pref_stockwipe_title)
.setPositiveButton(R.string.dialog_stockwipe_yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Well, you heard the orders!
dismiss();
if(HashBuilder.deleteCache(getActivity())) {
Toast.makeText(
getActivity(),
R.string.toast_stockwipe_success,
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(
getActivity(),
R.string.toast_stockwipe_failure,
Toast.LENGTH_SHORT).show();
}
}
})
.setNegativeButton(R.string.dialog_stockwipe_no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dismiss();
}
})
.create();
}
}
public static class ResetBuggingMeDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity()).setMessage(R.string.pref_reset_butting_me_dialog_text)
.setTitle(R.string.pref_reset_bugging_me_title)
.setPositiveButton(R.string.dialog_reset_bugging_me_yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Well, you heard the orders!
dismiss();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
SharedPreferences.Editor editor = prefs.edit();
// This list will grow and grow as I keep adding
// in new prompts until I get sick of it and
// come up with a more efficient way to do this.
editor.putBoolean(GHDConstants.PREF_STOP_BUGGING_ME_PREFETCH_WARNING, false);
editor.apply();
Toast.makeText(
getActivity(),
R.string.toast_reset_bugging_me_success,
Toast.LENGTH_SHORT).show();
}
})
.setNegativeButton(R.string.dialog_reset_bugging_me_no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dismiss();
}
})
.create();
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref_other);
bindPreferenceSummaryToValue(findPreference(GHDConstants.PREF_STOCK_CACHE_SIZE));
// The stock alarm preference needs to enable/disable the alarm as
// need be.
findPreference(GHDConstants.PREF_STOCK_ALARM).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference,
Object newValue) {
if(newValue instanceof Boolean) {
Boolean set = (Boolean) newValue;
Intent i = new Intent(getActivity(), AlarmService.class);
if(set) {
// ON! Start the service!
i.setAction(AlarmService.STOCK_ALARM_ON);
} else {
// OFF! Stop the service and cancel all alarms!
i.setAction(AlarmService.STOCK_ALARM_OFF);
}
WakefulIntentService.sendWakefulWork(getActivity(), i);
}
return true;
}
});
// Cache wiping is more a button than a preference, per se.
findPreference("_stockWipe").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
DialogFragment frag = new WipeCacheDialogFragment();
frag.show(getFragmentManager(), WIPE_DIALOG);
return true;
}
});
// As is the reminder unremindening.
findPreference("_resetBuggingMe").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
DialogFragment frag = new ResetBuggingMeDialogFragment();
frag.show(getFragmentManager(), RESET_BUGGING_ME_DIALOG);
return true;
}
});
}
@Override
public void onStop() {
BackupManager bm = new BackupManager(getActivity());
bm.dataChanged();
super.onStop();
}
}
}