package org.yaxim.androidclient.data; import org.yaxim.androidclient.R; import org.yaxim.androidclient.exceptions.YaximXMPPAdressMalformedException; import org.yaxim.androidclient.util.PreferenceConstants; import org.yaxim.androidclient.util.StatusMode; import org.yaxim.androidclient.util.XMPPHelper; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.net.Uri; import android.util.Log; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; public class YaximConfiguration implements OnSharedPreferenceChangeListener { private static final String TAG = "yaxim.Configuration"; private static final String GMAIL_SERVER = "talk.google.com"; private static final long DEFAULT_INVITATION_TIME = 14*24*3600; // two weeks in seconds private static final HashSet<String> RECONNECT_PREFS = new HashSet<String>(Arrays.asList( PreferenceConstants.JID, PreferenceConstants.PASSWORD, PreferenceConstants.CUSTOM_SERVER, PreferenceConstants.PORT, PreferenceConstants.RESSOURCE, PreferenceConstants.FOREGROUND, PreferenceConstants.REQUIRE_SSL, PreferenceConstants.SMACKDEBUG )); private static final HashSet<String> PRESENCE_PREFS = new HashSet<String>(Arrays.asList( PreferenceConstants.MESSAGE_CARBONS, PreferenceConstants.PRIORITY, PreferenceConstants.STATUS_DNDSILENT, PreferenceConstants.STATUS_MODE, PreferenceConstants.STATUS_MESSAGE )); public String password; public String ressource; public int port; public int priority; public boolean foregroundService; public boolean autoConnect; public boolean messageCarbons; public boolean reportCrash; public String userName; public String server; public String customServer; public String jabberID; public boolean jid_configured; public boolean require_ssl; public String statusMode; public boolean statusDndSilent; public StatusMode smartAwayMode; public String statusMessage; public String[] statusMessageHistory; public boolean isLEDNotify; public String vibraNotify; public Uri notifySound; public boolean ticker; public boolean highlightNickMuc; public boolean isLEDNotifyMuc; public String vibraNotifyMuc; public Uri notifySoundMuc; public boolean tickerMuc; public String mucDomain = null; // used in AutoCompleteJidEdit, null fallbacks to first static entry public boolean smackdebug; public String theme; public String chatFontSize; public boolean showOffline; public boolean enableGroups; public boolean reconnect_required = false; public boolean presence_required = false; /// this stores tuples of (JID, valid_until) or (token, valid_until) for PARS private HashMap<String, Long> invitationCodes = new HashMap<String, Long>(); private final SharedPreferences prefs; public YaximConfiguration(SharedPreferences _prefs) { prefs = _prefs; prefs.registerOnSharedPreferenceChangeListener(this); loadPrefs(prefs); } @Override protected void finalize() { prefs.unregisterOnSharedPreferenceChangeListener(this); } public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { Log.i(TAG, "onSharedPreferenceChanged(): " + key); loadPrefs(prefs); if (RECONNECT_PREFS.contains(key)) reconnect_required = true; if (PRESENCE_PREFS.contains(key)) presence_required = true; } private void splitAndSetJabberID(String jid) { String[] res = jid.split("@"); this.userName = res[0]; this.server = res[1]; // check for gmail.com and other google hosted jabber accounts if ("gmail.com".equals(res[1]) || "googlemail.com".equals(res[1]) || GMAIL_SERVER.equals(this.customServer)) { // work around for gmail's incompatible jabber implementation: // send the whole JID as the login, connect to talk.google.com this.userName = jid; if (this.customServer.length() == 0) this.customServer = GMAIL_SERVER; } } private int validatePriority(int jabPriority) { if (jabPriority > 127) return 127; else if (jabPriority < -127) return -127; return jabPriority; } private void loadPrefs(SharedPreferences prefs) { this.jid_configured = false; this.highlightNickMuc = prefs.getBoolean(PreferenceConstants.HIGHLIGHTMUC, false); this.isLEDNotifyMuc = prefs.getBoolean(PreferenceConstants.LEDNOTIFYMUC, false); this.vibraNotifyMuc = prefs.getString( PreferenceConstants.VIBRATIONNOTIFYMUC, "OFF"); this.notifySoundMuc = Uri.parse(prefs.getString( PreferenceConstants.RINGTONENOTIFYMUC, "")); this.tickerMuc = prefs.getBoolean(PreferenceConstants.TICKERMUC, true); this.isLEDNotify = prefs.getBoolean(PreferenceConstants.LEDNOTIFY, false); this.vibraNotify = prefs.getString( PreferenceConstants.VIBRATIONNOTIFY, "SYSTEM"); this.notifySound = Uri.parse(prefs.getString( PreferenceConstants.RINGTONENOTIFY, "")); this.ticker = prefs.getBoolean(PreferenceConstants.TICKER, true); this.password = prefs.getString(PreferenceConstants.PASSWORD, ""); this.ressource = prefs .getString(PreferenceConstants.RESSOURCE, "yaxim"); this.port = XMPPHelper.tryToParseInt(prefs.getString( PreferenceConstants.PORT, PreferenceConstants.DEFAULT_PORT), PreferenceConstants.DEFAULT_PORT_INT); this.priority = validatePriority(XMPPHelper.tryToParseInt(prefs .getString(PreferenceConstants.PRIORITY, "0"), 0)); this.foregroundService = prefs.getBoolean(PreferenceConstants.FOREGROUND, true); this.autoConnect = prefs.getBoolean(PreferenceConstants.CONN_STARTUP, false); this.messageCarbons = prefs.getBoolean( PreferenceConstants.MESSAGE_CARBONS, true); this.smackdebug = prefs.getBoolean(PreferenceConstants.SMACKDEBUG, false); this.reportCrash = prefs.getBoolean(PreferenceConstants.REPORT_CRASH, false); this.jabberID = prefs.getString(PreferenceConstants.JID, ""); this.customServer = prefs.getString(PreferenceConstants.CUSTOM_SERVER, ""); this.require_ssl = prefs.getBoolean(PreferenceConstants.REQUIRE_SSL, false); this.statusMode = prefs.getString(PreferenceConstants.STATUS_MODE, "available"); this.statusDndSilent = prefs.getBoolean(PreferenceConstants.STATUS_DNDSILENT, true); this.statusMessage = prefs.getString(PreferenceConstants.STATUS_MESSAGE, ""); this.statusMessageHistory = prefs.getString(PreferenceConstants.STATUS_MESSAGE_HISTORY, statusMessage).split("\036"); this.theme = prefs.getString(PreferenceConstants.THEME, "dark"); this.chatFontSize = prefs.getString("setSizeChat", "18"); this.showOffline = prefs.getBoolean(PreferenceConstants.SHOW_OFFLINE, false); this.enableGroups = prefs.getBoolean(PreferenceConstants.ENABLE_GROUPS, true); this.invitationCodes.clear(); Log.d(TAG, "invitation_codes = " + prefs.getString(PreferenceConstants.INVITATION_CODES, "")); try { for (String record : prefs.getString(PreferenceConstants.INVITATION_CODES, "").split("\036")) { if (record.length() >= 3) { // records are JID or token plus timestamp, separated by Unit Separator String[] r = record.split("\037"); long valid = Long.parseLong(r[1]); invitationCodes.put(r[0], valid); } } } catch (Exception e) { Log.e(TAG, "Exception parsing tokens: " + e); } try { splitAndSetJabberID(XMPPHelper.verifyJabberID(jabberID)); this.jid_configured = true; } catch (YaximXMPPAdressMalformedException e) { Log.e(TAG, "Exception in getPreferences(): " + e); } } public boolean needMucNotification(String nick, String message) { if (!highlightNickMuc) return true; return message.toLowerCase().contains(nick.toLowerCase()); } public int getTheme() { if (theme.equals("light")) { return R.style.YaximLightTheme; } else { return R.style.YaximDarkTheme; } } private synchronized void storeInvitationCodes() { Iterator<String> it = invitationCodes.keySet().iterator(); // remove all expired codes long ts = System.currentTimeMillis()/1000; while (it.hasNext()) { Long valid = invitationCodes.get(it.next()); if (valid < ts) it.remove(); } String[] records = new String[invitationCodes.size()]; int i = 0; it = invitationCodes.keySet().iterator(); // create string records with code|timestamp while (it.hasNext()) { String code = it.next(); records[i++] = code + "\037" + invitationCodes.get(code); } // concatenate all records to one string String ic_joined = android.text.TextUtils.join("\036", records); Log.d(TAG, "invitation_codes = " + ic_joined); prefs.edit().putString(PreferenceConstants.INVITATION_CODES, ic_joined).commit(); } public boolean redeemInvitationCode(String code_or_jid) { if (invitationCodes.containsKey(code_or_jid) && (invitationCodes.get(code_or_jid) > System.currentTimeMillis()/1000)) { invitationCodes.remove(code_or_jid); storeInvitationCodes(); return true; } else return false; } public String createInvitationCode(long validity) { // create an 80-bit random code, encode as BASE-36 String code = new java.math.BigInteger(80, new java.security.SecureRandom()).toString(36); invitationCodes.put(code, System.currentTimeMillis()/1000 + validity); Log.d(TAG, "createInvitationCode: " + code + " for " + validity + "s"); storeInvitationCodes(); return code; } public String createInvitationCode() { return createInvitationCode(DEFAULT_INVITATION_TIME); } public void whitelistInvitationJID(String jid) { invitationCodes.put(jid, System.currentTimeMillis()/1000 + DEFAULT_INVITATION_TIME); storeInvitationCodes(); } public StatusMode getPresenceMode() { StatusMode sm = StatusMode.fromString(statusMode); if (!statusDndSilent || smartAwayMode == null) return sm; return (smartAwayMode.compareTo(sm) < 0) ? smartAwayMode : sm; } }