package de.danoeh.antennapodsp.preferences;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import de.danoeh.antennapodsp.AppConfig;
import de.danoeh.antennapodsp.AppPreferences;
import de.danoeh.antennapodsp.R;
import de.danoeh.antennapodsp.receiver.FeedUpdateReceiver;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Provides access to preferences set by the user in the settings screen. A
* private instance of this class must first be instantiated via
* createInstance() or otherwise every public method will throw an Exception
* when called.
*/
public class UserPreferences implements
SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = "UserPreferences";
public static final String PREF_PAUSE_ON_HEADSET_DISCONNECT = "prefPauseOnHeadsetDisconnect";
public static final String PREF_FOLLOW_QUEUE = "prefFollowQueue";
public static final String PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY = "prefDownloadMediaOnWifiOnly";
public static final String PREF_UPDATE_INTERVAL = "prefAutoUpdateIntervall";
public static final String PREF_MOBILE_UPDATE = "prefMobileUpdate";
public static final String PREF_DISPLAY_ONLY_EPISODES = "prefDisplayOnlyEpisodes";
public static final String PREF_AUTO_DELETE = "prefAutoDelete";
public static final String PREF_THEME = "prefTheme";
public static final String PREF_DATA_FOLDER = "prefDataFolder";
public static final String PREF_ENABLE_AUTODL = "prefEnableAutoDl";
public static final String PREF_ENABLE_AUTODL_WIFI_FILTER = "prefEnableAutoDownloadWifiFilter";
private static final String PREF_AUTODL_SELECTED_NETWORKS = "prefAutodownloadSelectedNetworks";
public static final String PREF_EPISODE_CACHE_SIZE = "prefEpisodeCacheSize";
private static final String PREF_PLAYBACK_SPEED = "prefPlaybackSpeed";
private static final String PREF_PLAYBACK_SPEED_ARRAY = "prefPlaybackSpeedArray";
public static final String PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS = "prefPauseForFocusLoss";
private static int EPISODE_CACHE_SIZE_UNLIMITED = -1;
private static UserPreferences instance;
private final Context context;
// Preferences
private boolean pauseOnHeadsetDisconnect;
private boolean followQueue;
private boolean downloadMediaOnWifiOnly;
private long updateInterval;
private boolean allowMobileUpdate;
private boolean displayOnlyEpisodes;
private boolean autoDelete;
private int theme;
private boolean enableAutodownload;
private boolean enableAutodownloadWifiFilter;
private String[] autodownloadSelectedNetworks;
private int episodeCacheSize;
private String playbackSpeed;
private String[] playbackSpeedArray;
private boolean pauseForFocusLoss;
private UserPreferences(Context context) {
this.context = context;
loadPreferences();
}
/**
* Sets up the UserPreferences class.
*
* @throws IllegalArgumentException if context is null
*/
public static void createInstance(Context context) {
if (AppConfig.DEBUG)
Log.d(TAG, "Creating new instance of UserPreferences");
if (context == null)
throw new IllegalArgumentException("Context must not be null");
instance = new UserPreferences(context);
createNoMediaFile();
PreferenceManager.getDefaultSharedPreferences(context)
.registerOnSharedPreferenceChangeListener(instance);
}
private void loadPreferences() {
SharedPreferences sp = PreferenceManager
.getDefaultSharedPreferences(context);
EPISODE_CACHE_SIZE_UNLIMITED = context.getResources().getInteger(
R.integer.episode_cache_size_unlimited);
pauseOnHeadsetDisconnect = sp.getBoolean(
PREF_PAUSE_ON_HEADSET_DISCONNECT, true);
followQueue = sp.getBoolean(PREF_FOLLOW_QUEUE, false);
downloadMediaOnWifiOnly = sp.getBoolean(
PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY, true);
updateInterval = readUpdateInterval(sp.getString(PREF_UPDATE_INTERVAL,
"0"));
allowMobileUpdate = sp.getBoolean(PREF_MOBILE_UPDATE, false);
displayOnlyEpisodes = sp.getBoolean(PREF_DISPLAY_ONLY_EPISODES, false);
autoDelete = sp.getBoolean(PREF_AUTO_DELETE, false);
theme = readThemeValue(sp.getString(PREF_THEME, "0"));
enableAutodownloadWifiFilter = sp.getBoolean(
PREF_ENABLE_AUTODL_WIFI_FILTER, false);
autodownloadSelectedNetworks = StringUtils.split(
sp.getString(PREF_AUTODL_SELECTED_NETWORKS, ""), ',');
episodeCacheSize = readEpisodeCacheSizeInternal(sp.getString(
PREF_EPISODE_CACHE_SIZE, "20"));
enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
playbackSpeedArray = readPlaybackSpeedArray(sp.getString(
PREF_PLAYBACK_SPEED_ARRAY, null));
pauseForFocusLoss = sp.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false);
}
private int readThemeValue(String valueFromPrefs) {
switch (Integer.parseInt(valueFromPrefs)) {
case 0:
return R.style.Theme_AntennaPod_Light;
case 1:
return R.style.Theme_AntennaPod_Dark;
default:
return R.style.Theme_AntennaPod_Light;
}
}
private long readUpdateInterval(String valueFromPrefs) {
int hours = Integer.parseInt(valueFromPrefs);
return TimeUnit.HOURS.toMillis(hours);
}
private int readEpisodeCacheSizeInternal(String valueFromPrefs) {
if (valueFromPrefs.equals(Integer.toString(-1))) {
return EPISODE_CACHE_SIZE_UNLIMITED;
} else {
return Integer.valueOf(valueFromPrefs);
}
}
private String[] readPlaybackSpeedArray(String valueFromPrefs) {
String[] selectedSpeeds = null;
// If this preference hasn't been set yet, return the default options
if (valueFromPrefs == null) {
String[] allSpeeds = context.getResources().getStringArray(
R.array.playback_speed_values);
List<String> speedList = new LinkedList<String>();
for (String speedStr : allSpeeds) {
float speed = Float.parseFloat(speedStr);
if (speed < 2.0001 && speed * 10 % 1 == 0) {
speedList.add(speedStr);
}
}
selectedSpeeds = speedList.toArray(new String[speedList.size()]);
} else {
try {
JSONArray jsonArray = new JSONArray(valueFromPrefs);
selectedSpeeds = new String[jsonArray.length()];
for (int i = 0; i < jsonArray.length(); i++) {
selectedSpeeds[i] = jsonArray.getString(i);
}
} catch (JSONException e) {
Log.e(TAG,
"Got JSON error when trying to get speeds from JSONArray");
e.printStackTrace();
}
}
return selectedSpeeds;
}
private static void instanceAvailable() {
if (instance == null) {
throw new IllegalStateException(
"UserPreferences was used before being set up");
}
}
public static boolean isPauseOnHeadsetDisconnect() {
instanceAvailable();
return instance.pauseOnHeadsetDisconnect;
}
public static boolean isFollowQueue() {
instanceAvailable();
return instance.followQueue;
}
public static boolean isDownloadMediaOnWifiOnly() {
instanceAvailable();
return instance.downloadMediaOnWifiOnly;
}
public static long getUpdateInterval() {
instanceAvailable();
return instance.updateInterval;
}
public static boolean isAllowMobileUpdate() {
instanceAvailable();
return instance.allowMobileUpdate;
}
public static boolean isDisplayOnlyEpisodes() {
instanceAvailable();
//return instance.displayOnlyEpisodes;
return false;
}
public static boolean isAutoDelete() {
instanceAvailable();
return instance.autoDelete;
}
public static int getTheme() {
instanceAvailable();
return instance.theme;
}
public static boolean isEnableAutodownloadWifiFilter() {
instanceAvailable();
return instance.enableAutodownloadWifiFilter;
}
public static String[] getAutodownloadSelectedNetworks() {
instanceAvailable();
return instance.autodownloadSelectedNetworks;
}
public static int getEpisodeCacheSizeUnlimited() {
return EPISODE_CACHE_SIZE_UNLIMITED;
}
public static String getPlaybackSpeed() {
instanceAvailable();
return instance.playbackSpeed;
}
public static String[] getPlaybackSpeedArray() {
instanceAvailable();
return instance.playbackSpeedArray;
}
/**
* Returns the capacity of the episode cache. This method will return the
* negative integer EPISODE_CACHE_SIZE_UNLIMITED if the cache size is set to
* 'unlimited'.
*/
public static int getEpisodeCacheSize() {
instanceAvailable();
return instance.episodeCacheSize;
}
public static boolean isEnableAutodownload() {
instanceAvailable();
return instance.enableAutodownload;
}
public static boolean shouldPauseForFocusLoss() {
instanceAvailable();
return instance.pauseForFocusLoss;
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
if (AppConfig.DEBUG)
Log.d(TAG, "Registered change of user preferences. Key: " + key);
if (key.equals(PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY)) {
downloadMediaOnWifiOnly = sp.getBoolean(
PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY, true);
} else if (key.equals(PREF_MOBILE_UPDATE)) {
allowMobileUpdate = sp.getBoolean(PREF_MOBILE_UPDATE, false);
} else if (key.equals(PREF_FOLLOW_QUEUE)) {
followQueue = sp.getBoolean(PREF_FOLLOW_QUEUE, false);
} else if (key.equals(PREF_UPDATE_INTERVAL)) {
updateInterval = readUpdateInterval(sp.getString(
PREF_UPDATE_INTERVAL, "0"));
new AppPreferences().setUpdateAlarm();
} else if (key.equals(PREF_AUTO_DELETE)) {
autoDelete = sp.getBoolean(PREF_AUTO_DELETE, false);
} else if (key.equals(PREF_DISPLAY_ONLY_EPISODES)) {
displayOnlyEpisodes = sp.getBoolean(PREF_DISPLAY_ONLY_EPISODES,
false);
} else if (key.equals(PREF_THEME)) {
theme = readThemeValue(sp.getString(PREF_THEME, ""));
} else if (key.equals(PREF_ENABLE_AUTODL_WIFI_FILTER)) {
enableAutodownloadWifiFilter = sp.getBoolean(
PREF_ENABLE_AUTODL_WIFI_FILTER, false);
} else if (key.equals(PREF_AUTODL_SELECTED_NETWORKS)) {
autodownloadSelectedNetworks = StringUtils.split(
sp.getString(PREF_AUTODL_SELECTED_NETWORKS, ""), ',');
} else if (key.equals(PREF_EPISODE_CACHE_SIZE)) {
episodeCacheSize = readEpisodeCacheSizeInternal(sp.getString(
PREF_EPISODE_CACHE_SIZE, "20"));
} else if (key.equals(PREF_ENABLE_AUTODL)) {
enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
} else if (key.equals(PREF_PLAYBACK_SPEED)) {
playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
} else if (key.equals(PREF_PLAYBACK_SPEED_ARRAY)) {
playbackSpeedArray = readPlaybackSpeedArray(sp.getString(
PREF_PLAYBACK_SPEED_ARRAY, null));
} else if (key.equals(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS)) {
pauseForFocusLoss = sp.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false);
} else if (key.equals(PREF_PAUSE_ON_HEADSET_DISCONNECT)) {
pauseOnHeadsetDisconnect = sp.getBoolean(PREF_PAUSE_ON_HEADSET_DISCONNECT, true);
}
}
public static void setPlaybackSpeed(String speed) {
PreferenceManager.getDefaultSharedPreferences(instance.context).edit()
.putString(PREF_PLAYBACK_SPEED, speed).apply();
}
public static void setPlaybackSpeedArray(String[] speeds) {
JSONArray jsonArray = new JSONArray();
for (String speed : speeds) {
jsonArray.put(speed);
}
PreferenceManager.getDefaultSharedPreferences(instance.context).edit()
.putString(PREF_PLAYBACK_SPEED_ARRAY, jsonArray.toString())
.apply();
}
public static void setAutodownloadSelectedNetworks(Context context,
String[] value) {
SharedPreferences.Editor editor = PreferenceManager
.getDefaultSharedPreferences(context.getApplicationContext())
.edit();
editor.putString(PREF_AUTODL_SELECTED_NETWORKS,
StringUtils.join(value, ','));
editor.commit();
}
/**
* Sets the update interval value. Should only be used for testing purposes!
*/
public static void setUpdateInterval(Context context, long newValue) {
instanceAvailable();
SharedPreferences.Editor editor = PreferenceManager
.getDefaultSharedPreferences(context.getApplicationContext())
.edit();
editor.putString(PREF_UPDATE_INTERVAL,
String.valueOf(newValue));
editor.commit();
instance.updateInterval = newValue;
}
/**
* Return the folder where the app stores all of its data. This method will
* return the standard data folder if none has been set by the user.
*
* @param type The name of the folder inside the data folder. May be null
* when accessing the root of the data folder.
* @return The data folder that has been requested or null if the folder
* could not be created.
*/
public static File getDataFolder(Context context, String type) {
instanceAvailable();
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(context.getApplicationContext());
String strDir = prefs.getString(PREF_DATA_FOLDER, null);
if (strDir == null) {
if (AppConfig.DEBUG)
Log.d(TAG, "Using default data folder");
return context.getExternalFilesDir(type);
} else {
File dataDir = new File(strDir);
if (!dataDir.exists()) {
if (!dataDir.mkdir()) {
Log.w(TAG, "Could not create data folder");
return null;
}
}
if (type == null) {
return dataDir;
} else {
// handle path separators
String[] dirs = type.split("/");
for (int i = 0; i < dirs.length; i++) {
if (dirs.length > 0) {
if (i < dirs.length - 1) {
dataDir = getDataFolder(context, dirs[i]);
if (dataDir == null) {
return null;
}
}
type = dirs[i];
}
}
File typeDir = new File(dataDir, type);
if (!typeDir.exists()) {
if (dataDir.canWrite()) {
if (!typeDir.mkdir()) {
Log.e(TAG, "Could not create data folder named "
+ type);
return null;
}
}
}
return typeDir;
}
}
}
public static void setDataFolder(String dir) {
if (AppConfig.DEBUG)
Log.d(TAG, "Result from DirectoryChooser: " + dir);
instanceAvailable();
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(instance.context);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(PREF_DATA_FOLDER, dir);
editor.commit();
}
/**
* Create a .nomedia file to prevent scanning by the media scanner.
*/
private static void createNoMediaFile() {
File f = new File(instance.context.getExternalFilesDir(null),
".nomedia");
if (!f.exists()) {
try {
f.createNewFile();
} catch (IOException e) {
Log.e(TAG, "Could not create .nomedia file");
e.printStackTrace();
}
if (AppConfig.DEBUG)
Log.d(TAG, ".nomedia file created");
}
}
/**
* Updates alarm registered with the AlarmManager service or deactivates it.
*/
public static void restartUpdateAlarm(long triggerAtMillis, long intervalMillis) {
instanceAvailable();
if (AppConfig.DEBUG)
Log.d(TAG, "Restarting update alarm.");
AlarmManager alarmManager = (AlarmManager) instance.context
.getSystemService(Context.ALARM_SERVICE);
PendingIntent updateIntent = PendingIntent.getBroadcast(
instance.context, 0, new Intent(
FeedUpdateReceiver.ACTION_REFRESH_FEEDS), 0);
alarmManager.cancel(updateIntent);
if (intervalMillis != 0) {
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtMillis, intervalMillis,
updateIntent);
if (AppConfig.DEBUG)
Log.d(TAG, "Changed alarm to new interval");
} else {
if (AppConfig.DEBUG)
Log.d(TAG, "Automatic update was deactivated");
}
}
/**
* Reads episode cache size as it is saved in the episode_cache_size_values array.
*/
public static int readEpisodeCacheSize(String valueFromPrefs) {
instanceAvailable();
return instance.readEpisodeCacheSizeInternal(valueFromPrefs);
}
}