/* * Copyright © 2013 – 2015 Ricki Hirner (bitfire web engineering). * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html */ package at.bitfire.davdroid; import android.accounts.Account; import android.accounts.AccountManager; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.PeriodicSync; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import java.util.logging.Level; import at.bitfire.vcard4android.GroupMethod; public class AccountSettings { private final static int CURRENT_VERSION = 1; private final static String KEY_SETTINGS_VERSION = "version", KEY_URI = "uri", KEY_USERNAME = "user_name", KEY_TOKEN = "auth_token", KEY_WIFI_ONLY = "wifi_only", // sync on WiFi only (default: false) KEY_WIFI_ONLY_SSID = "wifi_only_ssid"; // restrict sync to specific WiFi SSID /** * Time range limitation to the past [in days] * value = null default value (DEFAULT_TIME_RANGE_PAST_DAYS) * < 0 (-1) no limit * >= 0 entries more than n days in the past won't be synchronized */ private final static String KEY_TIME_RANGE_PAST_DAYS = "time_range_past_days"; private final static int DEFAULT_TIME_RANGE_PAST_DAYS = 90; /* Whether DAVdroid sets the local calendar color to the value from service DB at every sync value = null (not existing) true (default) "0" false */ private final static String KEY_MANAGE_CALENDAR_COLORS = "manage_calendar_colors"; /** * Contact group method: * value = null (not existing) groups as separate VCards (default) * "CATEGORIES" groups are per-contact CATEGORIES */ private final static String KEY_CONTACT_GROUP_METHOD = "contact_group_method"; public final static long SYNC_INTERVAL_MANUALLY = -1; final Context context; final AccountManager accountManager; final Account account; @TargetApi(Build.VERSION_CODES.LOLLIPOP) public AccountSettings(@NonNull Context context, @NonNull Account account) throws InvalidAccountException { this.context = context; this.account = account; accountManager = AccountManager.get(context); synchronized (AccountSettings.class) { String versionStr = accountManager.getUserData(account, KEY_SETTINGS_VERSION); if (versionStr == null) throw new InvalidAccountException(account); int version = 0; try { version = Integer.parseInt(versionStr); } catch (NumberFormatException ignored) { } App.log.fine("Account " + account.name + " has version " + version + ", current version: " + CURRENT_VERSION); if (version < CURRENT_VERSION) update(version); } } public static Bundle initialUserData(URI uri, String userName) { Bundle bundle = new Bundle(); bundle.putString(KEY_SETTINGS_VERSION, String.valueOf(CURRENT_VERSION)); bundle.putString(KEY_USERNAME, userName); bundle.putString(KEY_URI, uri.toString()); return bundle; } // authentication settings public URI getUri() { try { return new URI(accountManager.getUserData(account, KEY_URI)); } catch (URISyntaxException e) { return null; } } public void setUri(@NonNull URI uri) { accountManager.setUserData(account, KEY_URI, uri.toString()); } public String getAuthToken() { return accountManager.getUserData(account, KEY_TOKEN); } public void setAuthToken(@NonNull String token) { accountManager.setUserData(account, KEY_TOKEN, token); } public String username() { return accountManager.getUserData(account, KEY_USERNAME); } public void username(@NonNull String userName) { accountManager.setUserData(account, KEY_USERNAME, userName); } public String password() { return accountManager.getPassword(account); } public void password(@NonNull String password) { accountManager.setPassword(account, password); } // sync. settings public Long getSyncInterval(@NonNull String authority) { if (ContentResolver.getIsSyncable(account, authority) <= 0) return null; if (ContentResolver.getSyncAutomatically(account, authority)) { List syncs = ContentResolver.getPeriodicSyncs(account, authority); if (syncs.isEmpty()) return SYNC_INTERVAL_MANUALLY; else return syncs.get(0).period; } else return SYNC_INTERVAL_MANUALLY; } public void setSyncInterval(@NonNull String authority, long seconds) { if (seconds == SYNC_INTERVAL_MANUALLY) { ContentResolver.setSyncAutomatically(account, authority, false); } else { ContentResolver.setSyncAutomatically(account, authority, true); ContentResolver.addPeriodicSync(account, authority, new Bundle(), seconds); } } public boolean getSyncWifiOnly() { return accountManager.getUserData(account, KEY_WIFI_ONLY) != null; } public void setSyncWiFiOnly(boolean wiFiOnly) { accountManager.setUserData(account, KEY_WIFI_ONLY, wiFiOnly ? "1" : null); } @Nullable public String getSyncWifiOnlySSID() { return accountManager.getUserData(account, KEY_WIFI_ONLY_SSID); } public void setSyncWifiOnlySSID(String ssid) { accountManager.setUserData(account, KEY_WIFI_ONLY_SSID, ssid); } // CalDAV settings @Nullable public Integer getTimeRangePastDays() { String strDays = accountManager.getUserData(account, KEY_TIME_RANGE_PAST_DAYS); if (strDays != null) { int days = Integer.valueOf(strDays); return days < 0 ? null : days; } else return DEFAULT_TIME_RANGE_PAST_DAYS; } public void setTimeRangePastDays(@Nullable Integer days) { accountManager.setUserData(account, KEY_TIME_RANGE_PAST_DAYS, String.valueOf(days == null ? -1 : days)); } public boolean getManageCalendarColors() { return accountManager.getUserData(account, KEY_MANAGE_CALENDAR_COLORS) == null; } public void setManageCalendarColors(boolean manage) { accountManager.setUserData(account, KEY_MANAGE_CALENDAR_COLORS, manage ? null : "0"); } // CardDAV settings @NonNull public GroupMethod getGroupMethod() { final String name = accountManager.getUserData(account, KEY_CONTACT_GROUP_METHOD); return name != null ? GroupMethod.valueOf(name) : GroupMethod.GROUP_VCARDS; } public void setGroupMethod(@NonNull GroupMethod method) { final String name = method == GroupMethod.GROUP_VCARDS ? null : method.name(); accountManager.setUserData(account, KEY_CONTACT_GROUP_METHOD, name); } // update from previous account settings private void update(int fromVersion) { for (int toVersion = fromVersion + 1; toVersion <= CURRENT_VERSION; toVersion++) { App.log.info("Updating account " + account.name + " from version " + fromVersion + " to " + toVersion); try { Method updateProc = getClass().getDeclaredMethod("update_" + fromVersion + "_" + toVersion); updateProc.invoke(this); accountManager.setUserData(account, KEY_SETTINGS_VERSION, String.valueOf(toVersion)); } catch (Exception e) { App.log.log(Level.SEVERE, "Couldn't update account settings", e); } fromVersion = toVersion; } } public static class AppUpdatedReceiver extends BroadcastReceiver { @Override @SuppressLint("UnsafeProtectedBroadcastReceiver,MissingPermission") public void onReceive(Context context, Intent intent) { App.log.info("DAVdroid was updated, checking for AccountSettings version"); // peek into AccountSettings to initiate a possible migration AccountManager accountManager = AccountManager.get(context); for (Account account : accountManager.getAccountsByType(Constants.ACCOUNT_TYPE)) try { App.log.info("Checking account " + account.name); new AccountSettings(context, account); } catch (InvalidAccountException e) { App.log.log(Level.SEVERE, "Couldn't check for updated account settings", e); } } } }