mirror of https://github.com/M66B/FairEmail.git
Cloud sync: account sync
This commit is contained in:
parent
a026a6221e
commit
f64983b929
|
@ -21,6 +21,7 @@ package eu.faircode.email;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Pair;
|
||||
|
||||
|
@ -32,7 +33,11 @@ import org.json.JSONObject;
|
|||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
@ -70,21 +75,31 @@ public class CloudSync {
|
|||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
String user = prefs.getString("cloud_user", null);
|
||||
String password = prefs.getString("cloud_password", null);
|
||||
if (TextUtils.isEmpty(user) || TextUtils.isEmpty(password))
|
||||
return;
|
||||
if (!ActivityBilling.isPro(context))
|
||||
return;
|
||||
|
||||
JSONObject jrequest = new JSONObject();
|
||||
|
||||
if ("sync".equals(command)) {
|
||||
DB db = DB.getInstance(context);
|
||||
|
||||
long lrevision = prefs.getLong("sync_status", new Date().getTime());
|
||||
long lrevision = prefs.getLong("cloud_revision", new Date().getTime());
|
||||
Log.i("Cloud local revision=" + lrevision + " (" + new Date(lrevision) + ")");
|
||||
|
||||
JSONObject jsync = new JSONObject();
|
||||
jsync.put("key", "sync.status");
|
||||
jsync.put("rev", lrevision);
|
||||
Long lastUpdate = updateSyncdata(context);
|
||||
Log.i("Cloud last update=" + (lastUpdate == null ? null : new Date(lastUpdate)));
|
||||
if (lastUpdate != null && lrevision > lastUpdate) {
|
||||
Log.w("Cloud invalid local revision" +
|
||||
" lrevision=" + lrevision + " last=" + lastUpdate);
|
||||
prefs.edit().putLong("cloud_revision", lastUpdate).apply();
|
||||
}
|
||||
|
||||
JSONObject jsyncstatus = new JSONObject();
|
||||
jsyncstatus.put("key", "sync.status");
|
||||
jsyncstatus.put("rev", lrevision);
|
||||
|
||||
JSONArray jitems = new JSONArray();
|
||||
jitems.put(jsync);
|
||||
jitems.put(jsyncstatus);
|
||||
|
||||
jrequest.put("items", jitems);
|
||||
|
||||
|
@ -93,32 +108,37 @@ public class CloudSync {
|
|||
|
||||
if (jitems.length() == 0) {
|
||||
Log.i("Cloud server is empty");
|
||||
|
||||
JSONObject jstatusdata = new JSONObject();
|
||||
jstatusdata.put("sync.version", 1);
|
||||
jstatusdata.put("app.version", BuildConfig.VERSION_CODE);
|
||||
|
||||
jsync = new JSONObject();
|
||||
jsync.put("key", "sync.status");
|
||||
jsync.put("val", jstatusdata.toString());
|
||||
jsync.put("rev", lrevision);
|
||||
jitems.put(jsync);
|
||||
|
||||
jrequest = new JSONObject();
|
||||
jrequest.put("items", jitems);
|
||||
call(context, user, password, "write", jrequest);
|
||||
|
||||
prefs.edit().putLong("sync_status", lrevision).apply();
|
||||
sendLocalData(context, user, password, lrevision);
|
||||
} else if (jitems.length() == 1) {
|
||||
Log.i("Cloud sync check");
|
||||
jsync = jitems.getJSONObject(0);
|
||||
long rrevision = jsync.getLong("rev");
|
||||
JSONObject jstatusdata = new JSONObject(jsync.getString("val"));
|
||||
|
||||
int sync_version = jstatusdata.optInt("sync.version", 0);
|
||||
int app_version = jstatusdata.optInt("app.version", 0);
|
||||
jsyncstatus = jitems.getJSONObject(0);
|
||||
long rrevision = jsyncstatus.getLong("rev");
|
||||
JSONObject jstatus = new JSONObject(jsyncstatus.getString("val"));
|
||||
int sync_version = jstatus.optInt("sync.version", 0);
|
||||
int app_version = jstatus.optInt("app.version", 0);
|
||||
Log.i("Cloud version sync=" + sync_version + " app=" + app_version +
|
||||
" local=" + lrevision + " remote=" + rrevision);
|
||||
" local=" + lrevision + " last=" + lastUpdate + " remote=" + rrevision);
|
||||
|
||||
// last > local (local mods) && remote > local (remote mods) = CONFLICT
|
||||
// local > last = ignorable ERROR
|
||||
// remote > local = fetch remote
|
||||
// last > remote = send local
|
||||
|
||||
if (lastUpdate != null && lastUpdate > rrevision) // local newer than remote
|
||||
sendLocalData(context, user, password, lastUpdate);
|
||||
else if (rrevision > lrevision) // remote changes
|
||||
if (lastUpdate != null && lastUpdate > lrevision) { // local changes
|
||||
Log.w("Cloud conflict" +
|
||||
" lrevision=" + lrevision + " last=" + lastUpdate + " rrevision=" + rrevision);
|
||||
if (manual)
|
||||
if (lastUpdate >= rrevision)
|
||||
sendLocalData(context, user, password, lastUpdate);
|
||||
else
|
||||
receiveRemoteData(context, user, password, lrevision, jstatus);
|
||||
} else
|
||||
receiveRemoteData(context, user, password, lrevision, jstatus);
|
||||
else if (BuildConfig.DEBUG)
|
||||
receiveRemoteData(context, user, password, lrevision - 1, jstatus);
|
||||
} else
|
||||
throw new IllegalArgumentException("Expected one status item");
|
||||
} else {
|
||||
|
@ -130,6 +150,224 @@ public class CloudSync {
|
|||
prefs.edit().putLong("cloud_last_sync", new Date().getTime()).apply();
|
||||
}
|
||||
|
||||
private static Long updateSyncdata(Context context) throws IOException, JSONException {
|
||||
DB db = DB.getInstance(context);
|
||||
File dir = Helper.ensureExists(new File(context.getFilesDir(), "syncdata"));
|
||||
|
||||
Long last = null;
|
||||
|
||||
List<EntityAccount> accounts = db.account().getSynchronizingAccounts(null);
|
||||
if (accounts != null)
|
||||
for (EntityAccount account : accounts)
|
||||
if (!TextUtils.isEmpty(account.uuid)) {
|
||||
EntityAccount aexisting = null;
|
||||
File afile = new File(dir, "account." + account.uuid + ".json");
|
||||
if (afile.exists())
|
||||
try (InputStream is = new FileInputStream(afile)) {
|
||||
aexisting = EntityAccount.fromJSON(new JSONObject(Helper.readStream(is)));
|
||||
}
|
||||
|
||||
boolean apassword = (account.auth_type == ServiceAuthenticator.AUTH_TYPE_PASSWORD);
|
||||
if (aexisting == null ||
|
||||
!EntityAccount.areEqual(account, aexisting, apassword, false))
|
||||
Helper.writeText(afile, account.toJSON().toString());
|
||||
|
||||
long atime = afile.lastModified();
|
||||
if (last == null || atime > last)
|
||||
last = atime;
|
||||
|
||||
List<EntityIdentity> identities = db.identity().getIdentities(account.id);
|
||||
if (identities != null)
|
||||
for (EntityIdentity identity : identities)
|
||||
if (!TextUtils.isEmpty(identity.uuid)) {
|
||||
EntityIdentity iexisting = null;
|
||||
File ifile = new File(dir, "identity." + identity.uuid + ".json");
|
||||
if (ifile.exists())
|
||||
try (InputStream is = new FileInputStream(ifile)) {
|
||||
iexisting = EntityIdentity.fromJSON(new JSONObject(Helper.readStream(is)));
|
||||
}
|
||||
|
||||
boolean ipassword = (account.auth_type == ServiceAuthenticator.AUTH_TYPE_PASSWORD);
|
||||
if (iexisting == null ||
|
||||
EntityIdentity.areEqual(identity, iexisting, ipassword, false))
|
||||
Helper.writeText(ifile, identity.toJSON().toString());
|
||||
|
||||
long itime = ifile.lastModified();
|
||||
if (last == null || itime > last)
|
||||
last = itime;
|
||||
}
|
||||
}
|
||||
|
||||
return last;
|
||||
}
|
||||
|
||||
private static void sendLocalData(Context context, String user, String password, long lrevision)
|
||||
throws JSONException, GeneralSecurityException, IOException {
|
||||
DB db = DB.getInstance(context);
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
List<EntityAccount> accounts = db.account().getSynchronizingAccounts(null);
|
||||
Log.i("Cloud accounts=" + (accounts == null ? null : accounts.size()));
|
||||
if (accounts == null || accounts.size() == 0) {
|
||||
Log.i("Cloud no accounts");
|
||||
return;
|
||||
}
|
||||
|
||||
JSONArray jupload = new JSONArray();
|
||||
|
||||
JSONArray jaccountuuidlist = new JSONArray();
|
||||
for (EntityAccount account : accounts)
|
||||
if (!TextUtils.isEmpty(account.uuid)) {
|
||||
jaccountuuidlist.put(account.uuid);
|
||||
|
||||
JSONArray jidentitieuuids = new JSONArray();
|
||||
List<EntityIdentity> identities = db.identity().getIdentities(account.id);
|
||||
if (identities != null)
|
||||
for (EntityIdentity identity : identities)
|
||||
if (!TextUtils.isEmpty(identity.uuid)) {
|
||||
jidentitieuuids.put(identity.uuid);
|
||||
|
||||
JSONObject jidentitykv = new JSONObject();
|
||||
jidentitykv.put("key", "identity." + identity.uuid);
|
||||
jidentitykv.put("val", identity.toJSON().toString());
|
||||
jidentitykv.put("rev", lrevision);
|
||||
jupload.put(jidentitykv);
|
||||
}
|
||||
|
||||
JSONObject jaccount = account.toJSON();
|
||||
if (account.swipe_left != null && account.swipe_left > 0) {
|
||||
EntityFolder f = db.folder().getFolder(account.swipe_left);
|
||||
if (f != null)
|
||||
jaccount.put("swipe_left_folder", f.name);
|
||||
}
|
||||
if (account.swipe_right != null && account.swipe_right > 0) {
|
||||
EntityFolder f = db.folder().getFolder(account.swipe_right);
|
||||
if (f != null)
|
||||
jaccount.put("swipe_right_folder", f.name);
|
||||
}
|
||||
|
||||
JSONObject jaccountdata = new JSONObject();
|
||||
jaccountdata.put("account", jaccount);
|
||||
jaccountdata.put("identities", jidentitieuuids);
|
||||
|
||||
JSONObject jaccountkv = new JSONObject();
|
||||
jaccountkv.put("key", "account." + account.uuid);
|
||||
jaccountkv.put("val", jaccountdata.toString());
|
||||
jaccountkv.put("rev", lrevision);
|
||||
jupload.put(jaccountkv);
|
||||
}
|
||||
|
||||
JSONObject jaccountuuids = new JSONObject();
|
||||
jaccountuuids.put("uuids", jaccountuuidlist);
|
||||
|
||||
JSONObject jstatus = new JSONObject();
|
||||
jstatus.put("sync.version", 1);
|
||||
jstatus.put("app.version", BuildConfig.VERSION_CODE);
|
||||
jstatus.put("accounts", jaccountuuids);
|
||||
|
||||
JSONObject jstatuskv = new JSONObject();
|
||||
jstatuskv.put("key", "sync.status");
|
||||
jstatuskv.put("val", jstatus.toString());
|
||||
jstatuskv.put("rev", lrevision);
|
||||
jupload.put(jstatuskv);
|
||||
|
||||
JSONObject jrequest = new JSONObject();
|
||||
jrequest.put("items", jupload);
|
||||
call(context, user, password, "write", jrequest);
|
||||
|
||||
prefs.edit().putLong("cloud_revision", lrevision).apply();
|
||||
}
|
||||
|
||||
private static void receiveRemoteData(Context context, String user, String password, long lrevision, JSONObject jstatus)
|
||||
throws JSONException, GeneralSecurityException, IOException {
|
||||
DB db = DB.getInstance(context);
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
// New revision
|
||||
boolean updates = false;
|
||||
JSONArray jdownload = new JSONArray();
|
||||
|
||||
// Get accounts
|
||||
JSONObject jaccountstatus = jstatus.getJSONObject("accounts");
|
||||
JSONArray jaccountuuidlist = jaccountstatus.getJSONArray("uuids");
|
||||
for (int i = 0; i < jaccountuuidlist.length(); i++) {
|
||||
String uuid = jaccountuuidlist.getString(i);
|
||||
JSONObject jaccountkv = new JSONObject();
|
||||
jaccountkv.put("key", "account." + uuid);
|
||||
jaccountkv.put("rev", lrevision);
|
||||
jdownload.put(jaccountkv);
|
||||
Log.i("Cloud account uuid=" + uuid);
|
||||
}
|
||||
|
||||
if (jdownload.length() > 0) {
|
||||
Log.i("Cloud getting accounts");
|
||||
JSONObject jrequest = new JSONObject();
|
||||
jrequest.put("items", jdownload);
|
||||
JSONObject jresponse = call(context, user, password, "sync", jrequest);
|
||||
|
||||
// Process accounts
|
||||
Log.i("Cloud processing accounts");
|
||||
JSONArray jitems = jresponse.getJSONArray("items");
|
||||
jdownload = new JSONArray();
|
||||
for (int i = 0; i < jitems.length(); i++) {
|
||||
JSONObject jaccountkv = jitems.getJSONObject(i);
|
||||
String value = jaccountkv.getString("val");
|
||||
long revision = jaccountkv.getLong("rev");
|
||||
|
||||
JSONObject jaccountdata = new JSONObject(value);
|
||||
JSONObject jaccount = jaccountdata.getJSONObject("account");
|
||||
EntityAccount raccount = EntityAccount.fromJSON(jaccount);
|
||||
EntityAccount laccount = db.account().getAccountByUUID(raccount.uuid);
|
||||
|
||||
JSONArray jidentities = jaccountdata.getJSONArray("identities");
|
||||
Log.i("Cloud account " + raccount.uuid + "=" +
|
||||
(laccount == null ? "insert" :
|
||||
(EntityAccount.areEqual(raccount, laccount, laccount.auth_type == ServiceAuthenticator.AUTH_TYPE_PASSWORD, true)
|
||||
? "equal" : "update")) +
|
||||
" rev=" + revision +
|
||||
" identities=" + jidentities +
|
||||
" size=" + value.length());
|
||||
|
||||
for (int j = 0; j < jidentities.length(); j++) {
|
||||
JSONObject jidentitykv = new JSONObject();
|
||||
jidentitykv.put("key", "identity." + jidentities.getString(j));
|
||||
jidentitykv.put("rev", lrevision);
|
||||
jdownload.put(jidentitykv);
|
||||
}
|
||||
}
|
||||
|
||||
if (jdownload.length() > 0) {
|
||||
// Get identities
|
||||
Log.i("Cloud getting identities");
|
||||
jrequest.put("items", jdownload);
|
||||
jresponse = call(context, user, password, "sync", jrequest);
|
||||
|
||||
// Process identities
|
||||
Log.i("Cloud processing identities");
|
||||
jitems = jresponse.getJSONArray("items");
|
||||
for (int i = 0; i < jitems.length(); i++) {
|
||||
JSONObject jidentitykv = jitems.getJSONObject(i);
|
||||
long revision = jidentitykv.getLong("rev");
|
||||
String value = jidentitykv.getString("val");
|
||||
JSONObject jidentity = new JSONObject(value);
|
||||
EntityIdentity ridentity = EntityIdentity.fromJSON(jidentity);
|
||||
EntityIdentity lidentity = db.identity().getIdentityByUUID(ridentity.uuid);
|
||||
|
||||
Log.i("Cloud identity " + ridentity.uuid + "=" +
|
||||
(lidentity == null ? "insert" :
|
||||
(EntityIdentity.areEqual(ridentity, lidentity, lidentity.auth_type == ServiceAuthenticator.AUTH_TYPE_PASSWORD, true)
|
||||
? "equal" : "update")) +
|
||||
" rev=" + revision +
|
||||
" size=" + value.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prefs.edit().putLong("cloud_revision", lrevision).apply();
|
||||
|
||||
if (updates)
|
||||
ServiceSynchronize.reload(context, null, true, "sync");
|
||||
}
|
||||
// Lower level
|
||||
|
||||
public static JSONObject call(Context context, String user, String password, String command, JSONObject jrequest)
|
||||
|
|
|
@ -422,55 +422,61 @@ public class EntityAccount extends EntityOrder implements Serializable {
|
|||
public boolean equals(Object obj) {
|
||||
if (obj instanceof EntityAccount) {
|
||||
EntityAccount other = (EntityAccount) obj;
|
||||
return (Objects.equals(this.uuid, other.uuid) &&
|
||||
Objects.equals(this.order, other.order) &&
|
||||
this.protocol.equals(other.protocol) &&
|
||||
this.host.equals(other.host) &&
|
||||
this.encryption.equals(other.encryption) &&
|
||||
this.insecure == other.insecure &&
|
||||
this.port.equals(other.port) &&
|
||||
this.auth_type.equals(other.auth_type) &&
|
||||
this.user.equals(other.user) &&
|
||||
this.password.equals(other.password) &&
|
||||
Objects.equals(this.realm, other.realm) &&
|
||||
Objects.equals(this.name, other.name) &&
|
||||
Objects.equals(this.category, other.category) &&
|
||||
Objects.equals(this.color, other.color) &&
|
||||
Objects.equals(this.calendar, other.calendar) &&
|
||||
this.synchronize.equals(other.synchronize) &&
|
||||
this.primary.equals(other.primary) &&
|
||||
this.notify.equals(other.notify) &&
|
||||
this.browse.equals(other.browse) &&
|
||||
this.leave_on_server.equals(other.leave_on_server) &&
|
||||
this.leave_on_device.equals(other.leave_on_device) &&
|
||||
Objects.equals(this.max_messages, other.max_messages) &&
|
||||
this.auto_seen.equals(other.auto_seen) &&
|
||||
Objects.equals(this.swipe_left, other.swipe_left) &&
|
||||
Objects.equals(this.swipe_right, other.swipe_right) &&
|
||||
this.poll_interval.equals(other.poll_interval) &&
|
||||
this.partial_fetch == other.partial_fetch &&
|
||||
this.ignore_size == other.ignore_size &&
|
||||
this.use_date == other.use_date &&
|
||||
this.use_received == other.use_received &&
|
||||
this.unicode == other.unicode &&
|
||||
Objects.equals(this.conditions, other.conditions) &&
|
||||
Objects.equals(this.quota_usage, other.quota_usage) &&
|
||||
Objects.equals(this.quota_limit, other.quota_limit) &&
|
||||
Objects.equals(this.created, other.created) &&
|
||||
Objects.equals(this.tbd, other.tbd) &&
|
||||
Objects.equals(this.state, other.state) &&
|
||||
Objects.equals(this.warning, other.warning) &&
|
||||
Objects.equals(this.error, other.error) &&
|
||||
Objects.equals(this.last_connected, other.last_connected) &&
|
||||
Objects.equals(this.backoff_until, other.backoff_until) &&
|
||||
Objects.equals(this.max_size, other.max_size) &&
|
||||
Objects.equals(this.capabilities, other.capabilities) &&
|
||||
Objects.equals(this.capability_idle, other.capability_idle) &&
|
||||
Objects.equals(this.capability_utf8, other.capability_utf8));
|
||||
return areEqual(this, other, true, true);
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean areEqual(EntityAccount a1, EntityAccount other, boolean auth, boolean state) {
|
||||
return (Objects.equals(a1.uuid, other.uuid) &&
|
||||
Objects.equals(a1.order, other.order) &&
|
||||
a1.protocol.equals(other.protocol) &&
|
||||
a1.host.equals(other.host) &&
|
||||
a1.encryption.equals(other.encryption) &&
|
||||
a1.insecure == other.insecure &&
|
||||
a1.port.equals(other.port) &&
|
||||
a1.auth_type.equals(other.auth_type) &&
|
||||
Objects.equals(a1.provider, other.provider) &&
|
||||
a1.user.equals(other.user) &&
|
||||
(!auth || a1.password.equals(other.password)) &&
|
||||
Objects.equals(a1.certificate_alias, other.certificate_alias) &&
|
||||
Objects.equals(a1.realm, other.realm) &&
|
||||
Objects.equals(a1.name, other.name) &&
|
||||
Objects.equals(a1.category, other.category) &&
|
||||
Objects.equals(a1.color, other.color) &&
|
||||
Objects.equals(a1.calendar, other.calendar) &&
|
||||
a1.synchronize.equals(other.synchronize) &&
|
||||
a1.primary.equals(other.primary) &&
|
||||
a1.notify.equals(other.notify) &&
|
||||
a1.browse.equals(other.browse) &&
|
||||
a1.leave_on_server.equals(other.leave_on_server) &&
|
||||
a1.leave_on_device.equals(other.leave_on_device) &&
|
||||
Objects.equals(a1.max_messages, other.max_messages) &&
|
||||
a1.auto_seen.equals(other.auto_seen) &&
|
||||
Objects.equals(a1.swipe_left, other.swipe_left) &&
|
||||
Objects.equals(a1.swipe_right, other.swipe_right) &&
|
||||
a1.poll_interval.equals(other.poll_interval) &&
|
||||
a1.partial_fetch == other.partial_fetch &&
|
||||
a1.ignore_size == other.ignore_size &&
|
||||
a1.use_date == other.use_date &&
|
||||
a1.use_received == other.use_received &&
|
||||
a1.unicode == other.unicode &&
|
||||
Objects.equals(a1.conditions, other.conditions) &&
|
||||
(!state || Objects.equals(a1.quota_usage, other.quota_usage)) &&
|
||||
(!state || Objects.equals(a1.quota_limit, other.quota_limit)) &&
|
||||
(!state || Objects.equals(a1.created, other.created)) &&
|
||||
Objects.equals(a1.tbd, other.tbd) &&
|
||||
(!state || Objects.equals(a1.state, other.state)) &&
|
||||
(!state || Objects.equals(a1.warning, other.warning)) &&
|
||||
(!state || Objects.equals(a1.error, other.error)) &&
|
||||
(!state || Objects.equals(a1.last_connected, other.last_connected)) &&
|
||||
(!state || Objects.equals(a1.backoff_until, other.backoff_until)) &&
|
||||
(!state || Objects.equals(a1.max_size, other.max_size)) &&
|
||||
(!state || Objects.equals(a1.capabilities, other.capabilities)) &&
|
||||
(!state || Objects.equals(a1.capability_idle, other.capability_idle)) &&
|
||||
(!state || Objects.equals(a1.capability_utf8, other.capability_utf8)));
|
||||
}
|
||||
|
||||
@Override
|
||||
Comparator getComparator(final Context context) {
|
||||
final Collator collator = Collator.getInstance(Locale.getDefault());
|
||||
|
|
|
@ -323,43 +323,49 @@ public class EntityIdentity {
|
|||
public boolean equals(Object obj) {
|
||||
if (obj instanceof EntityIdentity) {
|
||||
EntityIdentity other = (EntityIdentity) obj;
|
||||
return (Objects.equals(this.uuid, other.uuid) &&
|
||||
this.name.equals(other.name) &&
|
||||
this.email.equals(other.email) &&
|
||||
this.account.equals(other.account) &&
|
||||
Objects.equals(this.display, other.display) &&
|
||||
Objects.equals(this.color, other.color) &&
|
||||
Objects.equals(this.signature, other.signature) &&
|
||||
this.host.equals(other.host) &&
|
||||
this.encryption.equals(other.encryption) &&
|
||||
this.insecure.equals(other.insecure) &&
|
||||
this.port.equals(other.port) &&
|
||||
this.auth_type.equals(other.auth_type) &&
|
||||
this.user.equals(other.user) &&
|
||||
this.password.equals(other.password) &&
|
||||
Objects.equals(this.realm, other.realm) &&
|
||||
this.use_ip == other.use_ip &&
|
||||
Objects.equals(this.ehlo, other.ehlo) &&
|
||||
this.synchronize.equals(other.synchronize) &&
|
||||
this.primary.equals(other.primary) &&
|
||||
this.self.equals(other.self) &&
|
||||
this.sender_extra.equals(other.sender_extra) &&
|
||||
this.sender_extra_name.equals(other.sender_extra_name) &&
|
||||
Objects.equals(this.sender_extra_regex, other.sender_extra_regex) &&
|
||||
Objects.equals(this.replyto, other.replyto) &&
|
||||
Objects.equals(this.cc, other.cc) &&
|
||||
Objects.equals(this.bcc, other.bcc) &&
|
||||
Objects.equals(this.internal, other.internal) &&
|
||||
Objects.equals(this.sign_key, other.sign_key) &&
|
||||
Objects.equals(this.sign_key_alias, other.sign_key_alias) &&
|
||||
Objects.equals(this.state, other.state) &&
|
||||
Objects.equals(this.error, other.error) &&
|
||||
Objects.equals(this.last_connected, other.last_connected) &&
|
||||
Objects.equals(this.max_size, other.max_size));
|
||||
return areEqual(this, other, true, true);
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean areEqual(EntityIdentity i1, EntityIdentity other, boolean auth, boolean state) {
|
||||
return (Objects.equals(i1.uuid, other.uuid) &&
|
||||
i1.name.equals(other.name) &&
|
||||
i1.email.equals(other.email) &&
|
||||
Objects.equals(i1.account, other.account) &&
|
||||
Objects.equals(i1.display, other.display) &&
|
||||
Objects.equals(i1.color, other.color) &&
|
||||
Objects.equals(i1.signature, other.signature) &&
|
||||
i1.host.equals(other.host) &&
|
||||
i1.encryption.equals(other.encryption) &&
|
||||
i1.insecure.equals(other.insecure) &&
|
||||
i1.port.equals(other.port) &&
|
||||
i1.auth_type.equals(other.auth_type) &&
|
||||
Objects.equals(i1.provider, other.provider) &&
|
||||
i1.user.equals(other.user) &&
|
||||
(!auth || i1.password.equals(other.password)) &&
|
||||
Objects.equals(i1.certificate_alias, other.certificate_alias) &&
|
||||
Objects.equals(i1.realm, other.realm) &&
|
||||
i1.use_ip == other.use_ip &&
|
||||
Objects.equals(i1.ehlo, other.ehlo) &&
|
||||
i1.synchronize.equals(other.synchronize) &&
|
||||
i1.primary.equals(other.primary) &&
|
||||
i1.self.equals(other.self) &&
|
||||
i1.sender_extra.equals(other.sender_extra) &&
|
||||
i1.sender_extra_name.equals(other.sender_extra_name) &&
|
||||
Objects.equals(i1.sender_extra_regex, other.sender_extra_regex) &&
|
||||
Objects.equals(i1.replyto, other.replyto) &&
|
||||
Objects.equals(i1.cc, other.cc) &&
|
||||
Objects.equals(i1.bcc, other.bcc) &&
|
||||
Objects.equals(i1.internal, other.internal) &&
|
||||
Objects.equals(i1.sign_key, other.sign_key) &&
|
||||
Objects.equals(i1.sign_key_alias, other.sign_key_alias) &&
|
||||
(!state || Objects.equals(i1.state, other.state)) &&
|
||||
(!state || Objects.equals(i1.error, other.error)) &&
|
||||
(!state || Objects.equals(i1.last_connected, other.last_connected)) &&
|
||||
(!state || Objects.equals(i1.max_size, other.max_size)));
|
||||
}
|
||||
|
||||
String getDisplayName() {
|
||||
return (display == null ? name : display);
|
||||
}
|
||||
|
|
|
@ -118,7 +118,6 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere
|
|||
private TextInputLayout tilPassword;
|
||||
private Button btnLogin;
|
||||
private TextView tvLogin;
|
||||
private CheckBox cbBlockedSenders;
|
||||
private ImageButton ibSync;
|
||||
private TextView tvLastSync;
|
||||
private Button btnLogout;
|
||||
|
@ -159,7 +158,6 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere
|
|||
tilPassword = view.findViewById(R.id.tilPassword);
|
||||
btnLogin = view.findViewById(R.id.btnLogin);
|
||||
tvLogin = view.findViewById(R.id.tvLogin);
|
||||
cbBlockedSenders = view.findViewById(R.id.cbBlockedSenders);
|
||||
ibSync = view.findViewById(R.id.ibSync);
|
||||
tvLastSync = view.findViewById(R.id.tvLastSync);
|
||||
btnLogout = view.findViewById(R.id.btnLogout);
|
||||
|
@ -206,13 +204,6 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere
|
|||
}
|
||||
});
|
||||
|
||||
cbBlockedSenders.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
prefs.edit().putBoolean("cloud_sync_blocked_senders", isChecked).apply();
|
||||
}
|
||||
});
|
||||
|
||||
ibSync.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
@ -238,7 +229,6 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere
|
|||
Helper.linkPro(tvCloudPro);
|
||||
|
||||
prefs.registerOnSharedPreferenceChangeListener(this);
|
||||
cbBlockedSenders.setChecked(prefs.getBoolean("cloud_sync_blocked_senders", true));
|
||||
onSharedPreferenceChanged(prefs, null);
|
||||
|
||||
return view;
|
||||
|
@ -1455,9 +1445,15 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere
|
|||
}
|
||||
|
||||
private void onCloudLogin() {
|
||||
final Context context = getContext();
|
||||
String username = etUser.getText().toString().trim();
|
||||
String password = tilPassword.getEditText().getText().toString();
|
||||
|
||||
if (!ActivityBilling.isPro(context)) {
|
||||
context.startActivity(new Intent(context, ActivityBilling.class));
|
||||
return;
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(username)) {
|
||||
etUser.requestFocus();
|
||||
return;
|
||||
|
@ -1468,7 +1464,7 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere
|
|||
return;
|
||||
}
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
prefs.edit()
|
||||
.putString("cloud_user", username)
|
||||
.putString("cloud_password", password)
|
||||
|
|
|
@ -303,22 +303,13 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvRegister" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/cbBlockedSenders"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="@string/title_advanced_cloud_blocked_senders"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvLogin" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/ibSync"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/cbBlockedSenders"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvLogin"
|
||||
app:srcCompat="@drawable/twotone_compare_arrows_24" />
|
||||
|
||||
<TextView
|
||||
|
|
|
@ -976,13 +976,12 @@
|
|||
<string name="title_advanced_keywords_hint" translatable="false">Space separated</string>
|
||||
|
||||
<string name="title_experimental" translatable="false">This is an experimental feature!</string>
|
||||
|
||||
<string name="title_advanced_cloud_security" translatable="false">All data is encrypted end-to-end and the cloud server will never see the username, password and data</string>
|
||||
<string name="title_advanced_cloud_login" translatable="false">Login</string>
|
||||
<string name="title_advanced_cloud_register" translatable="false">Logging in for the first time will automatically create an account</string>
|
||||
<string name="title_advanced_cloud_invalid" translatable="false">Invalid username or password</string>
|
||||
<string name="title_advanced_cloud_blocked_senders" translatable="false">Sync blocked senders</string>
|
||||
<string name="title_advanced_cloud_last_sync" translatable="false">Last sync: %1$s</string>
|
||||
<string name="title_advanced_cloud_update" translatable="false">Update settings</string>
|
||||
<string name="title_advanced_cloud_logout" translatable="false">Logout</string>
|
||||
<string name="title_advanced_cloud_wipe" translatable="false">Wipe cloud data on logout</string>
|
||||
|
||||
|
|
Loading…
Reference in New Issue