package gsingh.learnkirtan.settings; import static gsingh.learnkirtan.Constants.Duration.DAY; import static gsingh.learnkirtan.Constants.Duration.MONTH; import static gsingh.learnkirtan.Constants.Duration.WEEK; import gsingh.learnkirtan.Constants.Duration; import gsingh.learnkirtan.FileManager; import gsingh.learnkirtan.listener.SettingsChangedListener; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.LinkedList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.xml.sax.SAXException; /** * A class for managing persistent settings * * @author Gulshan * */ public class SettingsManager { private static final XPath xPath = XPathFactory.newInstance().newXPath(); private List<SettingsChangedListener> listeners = new LinkedList<SettingsChangedListener>(); // TODO: Update the settings names here and in the config file /** XPath statement to access the Sa key setting */ public static final String SA_KEY_XPATH = "/settings/sakey[1]"; /** XPath statement to access the check for update interval setting */ private static final String CHECK_FOR_UPDATE_INTERVAL_XPATH = "/settings/updatereminder/until[1]"; /** XPath statement to access the check for update setting */ private static final String CHECK_FOR_UPDATE_XPATH = "/settings/updatereminder/remind[1]"; /** XPath statement to access the show keyboard labels setting */ public static final String SHOW_KEYBOARD_LABELS_XPATH = "/settings/labels/showkeys[1]"; /** XPath statement to access the show sargam labels setting */ public static final String SHOW_SARGAM_LABELS_XPATH = "/settings/labels/showsargam[1]"; /** The number of milliseconds in a day */ private static final int DAY_MILLISECONDS = 24 * 60 * 60 * 1000; /** The file from which to retrieve and store settings */ private static final String CONFIG_FILE = "config"; /** The singleton instance of this class */ private static SettingsManager instance; /** The key corresponding to the user's Sa */ private int saKey; /** Whether or not to check for an update */ private boolean checkForUpdate; /** The interval until the next update check */ private long checkForUpdateInterval; /** Whether to show the sargam labels */ private boolean showSargamLabels; /** Whether to show the keyboard labels */ private boolean showKeyboardLabels; /** The settings XML document */ private Document dom = null; /** The file manager for this class */ private FileManager fileManager; private SettingsManager() { // Retrieve DOM from XML file DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db; try { File configFile = new File(CONFIG_FILE); if (!configFile.exists()) { configFile.createNewFile(); FileWriter writer = new FileWriter(configFile); writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><settings>" + " <sakey>10</sakey>" + " <updatereminder>" + " <remind>yes</remind>" + " <until>1</until>" + " </updatereminder>" + " <labels>" + " <showsargam>yes</showsargam>" + " <showkeys>yes</showkeys>" + " </labels>" + "</settings>"); writer.close(); } db = dbf.newDocumentBuilder(); dom = db.parse(CONFIG_FILE); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } try { initSettings(); } catch (XPathExpressionException e) { e.printStackTrace(); } } public static SettingsManager getInstance() { if (instance == null) instance = new SettingsManager(); return instance; } public void addSettingsChangedListener(SettingsChangedListener l) { listeners.add(l); } // TODO: Look into a better way to get XML settings /** * Initializes the settings variables by reading the config file * */ private void initSettings() throws XPathExpressionException { // Get saKey saKey = Integer.valueOf((String) xPath.evaluate(SA_KEY_XPATH, dom, XPathConstants.STRING)); // Get checkForUpdate settings checkForUpdateInterval = Long.valueOf((String)xPath.evaluate( CHECK_FOR_UPDATE_INTERVAL_XPATH, dom, XPathConstants.STRING)); checkForUpdate = (Boolean) xPath.evaluate(CHECK_FOR_UPDATE_XPATH, dom, XPathConstants.BOOLEAN); // Get label values showSargamLabels = (Boolean) xPath.evaluate(SHOW_SARGAM_LABELS_XPATH, dom, XPathConstants.BOOLEAN); showKeyboardLabels = (Boolean) xPath.evaluate( SHOW_KEYBOARD_LABELS_XPATH, dom, XPathConstants.BOOLEAN); } /** Sets the FileManager for this class */ public void setFileManager(FileManager fileManager) { this.fileManager = fileManager; } /** * Sets the show sargam labels setting * * @param bool * whether to enable or disable this setting */ public void setShowSargamLabels(boolean bool) { showSargamLabels = bool; changeSetting(SHOW_SARGAM_LABELS_XPATH, Boolean.toString(bool)); } /** * Sets the show keyboard labels setting * * @param bool * whether to enable or disable this setting */ public void setShowKeyboardLabels(boolean bool) { showKeyboardLabels = bool; changeSetting(SHOW_KEYBOARD_LABELS_XPATH, Boolean.toString(bool)); } /** @return the value of the show sargam labels settings */ public boolean getShowSargamLabels() { return showSargamLabels; } /** @return the value of the show keyboard labels setting */ public boolean getShowKeyboardLabels() { return showKeyboardLabels; } /** * Sets the value of the checkForUpdate setting * * @param bool * whether to turn update checks * @param duration * used to specify length of time not to remind. */ public void setCheckForUpdate(boolean bool, Duration duration) { if (!bool) { String d = calculateDate(duration); checkForUpdate = false; checkForUpdateInterval = Long.valueOf(d); changeSetting(CHECK_FOR_UPDATE_XPATH, Boolean.toString(false)); changeSetting(CHECK_FOR_UPDATE_INTERVAL_XPATH, d); } else { checkForUpdate = true; changeSetting(CHECK_FOR_UPDATE_XPATH, Boolean.toString(true)); } } /** * Calculates the date (in milliseconds) until reminders will be turned back * on * */ private String calculateDate(Duration duration) { long time = System.currentTimeMillis(); long dur = 0; if (duration == DAY) { dur = DAY_MILLISECONDS; } else if (duration == WEEK) { dur = 7 * DAY_MILLISECONDS; } else if (duration == MONTH) { dur = 24 * DAY_MILLISECONDS; } return String.valueOf(time + dur); } /** * @return the value of the reminder setting */ public boolean getCheckForUpdate() { return checkForUpdate; } /** * Checks whether the duration for not checking reminding about updates is * completed * */ public void checkReminderOffDurationReached() { long time = System.currentTimeMillis(); if (checkForUpdateInterval < time) { changeSetting(CHECK_FOR_UPDATE_XPATH, Boolean.toString(true)); checkForUpdate = true; } } /** * Sets {@code saKey} to {@code key} * * @param key * the key number input by the user from 1 to 36 */ public void setSaKey(int key) { saKey = key; changeSetting(SA_KEY_XPATH, String.valueOf(saKey)); } /** * Returns the key id of the Sa key */ public int getSaKey() { return saKey; } /** * Changes a setting in the config file * * @param name * full name of the setting, with '.'s to delimit nodes * @param newValue * value to change the setting to */ private void changeSetting(String xPathQuery, String newValue) { try { Node node = (Node) xPath.evaluate(xPathQuery, dom, XPathConstants.NODE); String oldValue = node.getTextContent(); node.setTextContent(newValue); // TODO Handle error fileManager.saveSettings(dom); for (SettingsChangedListener l : listeners) { l.onSettingsChanged(xPathQuery, oldValue, newValue); } } catch (TransformerFactoryConfigurationError e) { e.printStackTrace(); } catch (TransformerException e) { e.printStackTrace(); } catch (XPathExpressionException e) { e.printStackTrace(); } } }