diff --git a/.gitignore b/.gitignore
index 4e34adafcc..8dc9f437b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@ captures/
*.iml
.idea/workspace.xml
.idea/libraries
+.idea/misc.xml
# Keystore files
*.jks
diff --git a/FlowCrypt/src/main/AndroidManifest.xml b/FlowCrypt/src/main/AndroidManifest.xml
index ff8ebf86c6..c4c7b92dd4 100644
--- a/FlowCrypt/src/main/AndroidManifest.xml
+++ b/FlowCrypt/src/main/AndroidManifest.xml
@@ -106,6 +106,11 @@
android:label="@string/app_name"
android:screenOrientation="portrait" />
+
+
String[] An array of public keys.
*/
- private String[] getPubKeys(Js js, Context context) {
+ private String[] getPubKeys(Context context, Js js, AccountDao accountDao) {
ArrayList publicKeys = new ArrayList<>();
for (PgpContact pgpContact : outgoingMessageInfo.getToPgpContacts()) {
if (!TextUtils.isEmpty(pgpContact.getPubkey())) {
@@ -300,30 +301,46 @@ private String[] getPubKeys(Js js, Context context) {
}
}
- publicKeys.addAll(generateOwnPublicKeys(js, context));
+ publicKeys.add(getAccountPublicKey(context, js, accountDao));
return publicKeys.toArray(new String[0]);
}
/**
- * Get public keys of the sender;
+ * Get a public key of the sender;
*
- * @param js - {@link Js} util class.
- * @param context - Interface to global information about an application environment.
- * @return String[] An array of the sender public keys.
+ * @param context Interface to global information about an application environment.
+ * @param js {@link Js} util class.
+ * @param accountDao The {@link AccountDao} which contains information about account.
+ * @return String The sender public key.
*/
- private ArrayList generateOwnPublicKeys(Js js, Context context) {
- ArrayList publicKeys = new ArrayList<>();
+ private String getAccountPublicKey(Context context, Js js, AccountDao accountDao) {
+ PgpContact pgpContact = new ContactsDaoSource().getPgpContact(context, accountDao.getEmail());
- SecurityStorageConnector securityStorageConnector = new SecurityStorageConnector(context);
- PgpKeyInfo[] pgpKeyInfoArray = securityStorageConnector.getAllPgpPrivateKeys();
+ if (pgpContact != null && !TextUtils.isEmpty(pgpContact.getPubkey())) {
+ return pgpContact.getPubkey();
+ }
+ PgpKeyInfo[] pgpKeyInfoArray = new SecurityStorageConnector(context).getAllPgpPrivateKeys();
for (PgpKeyInfo pgpKeyInfo : pgpKeyInfoArray) {
PgpKey pgpKey = js.crypto_key_read(pgpKeyInfo.getArmored());
- publicKeys.add(pgpKey.toPublic().armor());
+ if (pgpKey != null) {
+ PgpKey publicKey = pgpKey.toPublic();
+ if (publicKey != null) {
+ PgpContact primaryUserId = pgpKey.getPrimaryUserId();
+ if (primaryUserId != null) {
+ if (!TextUtils.isEmpty(publicKey.armor())) {
+ primaryUserId.setPubkey(publicKey.armor());
+ new ContactsDaoSource().addRow(context, primaryUserId);
+ return primaryUserId.getPubkey();
+ }
+ break;
+ }
+ }
+ }
}
- return publicKeys;
+ throw new IllegalArgumentException("The sender doesn't have a public key");
}
/**
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/database/dao/source/AccountDao.java b/FlowCrypt/src/main/java/com/flowcrypt/email/database/dao/source/AccountDao.java
index ef15abbc37..8f3cb8e1a2 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/database/dao/source/AccountDao.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/database/dao/source/AccountDao.java
@@ -10,6 +10,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Nullable;
+import android.text.TextUtils;
import com.flowcrypt.email.api.email.model.AuthCredentials;
@@ -47,7 +48,13 @@ public AccountDao[] newArray(int size) {
public AccountDao(String email, String accountType, String displayName, String givenName,
String familyName, String photoUrl, AuthCredentials authCredentials) {
this.email = email;
- this.accountType = accountType;
+ if (TextUtils.isEmpty(accountType)) {
+ if (!TextUtils.isEmpty(email)) {
+ this.accountType = email.substring(email.indexOf('@') + 1, email.length());
+ }
+ } else {
+ this.accountType = accountType;
+ }
this.displayName = displayName;
this.givenName = givenName;
this.familyName = familyName;
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/database/dao/source/AccountDaoSource.java b/FlowCrypt/src/main/java/com/flowcrypt/email/database/dao/source/AccountDaoSource.java
index cc44d96fce..f7e7d64122 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/database/dao/source/AccountDaoSource.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/database/dao/source/AccountDaoSource.java
@@ -26,6 +26,8 @@
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.List;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
@@ -335,6 +337,66 @@ public int deleteAccountInformation(Context context, AccountDao accountDao) {
} else return -1;
}
+ /**
+ * Get the list of all added account without the active account.
+ *
+ * @param context Interface to global information about an application environment.
+ * @param email An email of the active account.
+ * @return The list of all added account without the active account
+ */
+ public List getAccountsWithoutActive(Context context, String email) {
+ if (email != null) {
+ email = email.toLowerCase();
+ }
+
+ Cursor cursor = context.getContentResolver().query(getBaseContentUri(), null,
+ AccountDaoSource.COL_EMAIL + " != ?", new String[]{email}, null);
+
+ List accountDaoList = new ArrayList<>();
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ accountDaoList.add(getCurrentAccountDao(context, cursor));
+ }
+ }
+
+ if (cursor != null) {
+ cursor.close();
+ }
+
+ return accountDaoList;
+ }
+
+ /**
+ * Mark some account as active.
+ *
+ * @param context Interface to global information about an application environment.
+ * @param email The account which will be set as active.
+ * @return The count of updated rows.
+ */
+ public int setActiveAccount(Context context, String email) {
+ if (email == null) {
+ return -1;
+ } else {
+ email = email.toLowerCase();
+ }
+
+ ContentResolver contentResolver = context.getContentResolver();
+ if (contentResolver != null) {
+ ContentValues contentValuesDeactivateAllAccount = new ContentValues();
+ contentValuesDeactivateAllAccount.put(COL_IS_ACTIVE, 0);
+ int updateRowCount = contentResolver.update(getBaseContentUri(), contentValuesDeactivateAllAccount,
+ null, null);
+
+ ContentValues contentValuesActivateAccount = new ContentValues();
+ contentValuesActivateAccount.put(COL_IS_ACTIVE, 1);
+ updateRowCount += contentResolver.update(getBaseContentUri(), contentValuesActivateAccount,
+ COL_EMAIL + " = ? ", new String[]{email});
+
+ return updateRowCount;
+
+ } else return -1;
+ }
+
/**
* Generate a {@link ContentValues} using {@link GoogleSignInAccount}.
*
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/js/Js.java b/FlowCrypt/src/main/java/com/flowcrypt/email/js/Js.java
index 4107871e1e..c9c2f72fc2 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/js/Js.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/js/Js.java
@@ -7,6 +7,7 @@
package com.flowcrypt.email.js;
import android.content.Context;
+import com.flowcrypt.email.BuildConfig;
import android.os.Build;
import android.text.Html;
import android.text.TextUtils;
@@ -65,37 +66,29 @@ public Js(Context context, StorageConnectorInterface storage) throws IOException
}
public Boolean str_is_email_valid(String email) {
- return (Boolean) this.call(Boolean.class, new String[]{"str", "is_email_valid"}, new
- V8Array(v8).push(email));
+ return (Boolean) this.call(Boolean.class, new String[]{"str", "is_email_valid"}, new V8Array(v8).push(email));
}
public PgpContact str_parse_email(String email) {
- V8Object e = (V8Object) this.call(Object.class, new String[]{"str", "parse_email"}, new
- V8Array(v8).push(email));
+ V8Object e = (V8Object) this.call(Object.class, new String[]{"str", "parse_email"}, new V8Array(v8).push(email));
return new PgpContact(e.getString("email"), e.getString("name"));
}
public String str_base64url_encode(String str) {
- return (String) this.call(String.class, new String[]{"str", "base64url_encode"}, new
- V8Array(v8).push(str));
+ return (String) this.call(String.class, new String[]{"str", "base64url_encode"}, new V8Array(v8).push(str));
}
public String str_base64url_decode(String str) {
- return (String) this.call(String.class, new String[]{"str", "base64url_decode"}, new
- V8Array(v8).push(str));
+ return (String) this.call(String.class, new String[]{"str", "base64url_decode"}, new V8Array(v8).push(str));
}
public long time_to_utc_timestamp(String str) {
- return Long.parseLong((String) this.call(String.class, new String[]{"time",
- "to_utc_timestamp"}, new V8Array(v8).push(str).push(true)));
+ return Long.parseLong((String) this.call(String.class, new String[]{"time", "to_utc_timestamp"},
+ new V8Array(v8).push(str).push(true)));
}
public MimeMessage mime_decode(String mime_message) {
- Long start = System.currentTimeMillis();
- this.call(Object.class, new String[]{"mime", "decode"}, new V8Array(v8).push
- (mime_message).push(cb_catcher));
- Long end = System.currentTimeMillis();
- System.out.println("duration decode: " + (end - start));
+ this.call(Object.class, new String[]{"mime", "decode"}, new V8Array(v8).push(mime_message).push(cb_catcher));
if ((Boolean) cb_last_value[0]) {
return new MimeMessage((V8Object) cb_last_value[1], this);
} else {
@@ -121,49 +114,46 @@ public String mime_encode(String body, PgpContact[] to, PgpContact from, String
}
public ProcessedMime mime_process(String mime_message) {
- this.call(Object.class, new String[]{"mime", "process"}, new V8Array(v8).push
- (mime_message).push(cb_catcher));
+ this.call(Object.class, new String[]{"mime", "process"}, new V8Array(v8).push(mime_message).push(cb_catcher));
return new ProcessedMime((V8Object) cb_last_value[0], this);
}
public String crypto_key_normalize(String armored_key) {
- return (String) this.call(String.class, new String[]{"crypto", "key", "normalize"}, new
- V8Array(v8).push(armored_key));
+ return (String) this.call(String.class, new String[]{"crypto", "key", "normalize"}, new V8Array(v8)
+ .push(armored_key));
}
public PgpKey crypto_key_read(String armored_key) {
- return new PgpKey((V8Object) this.call(Object.class, new String[]{"crypto", "key",
- "read"}, new V8Array(v8).push(armored_key)), this);
+ return new PgpKey((V8Object) this.call(Object.class, new String[]{"crypto", "key", "read"}, new V8Array(v8)
+ .push(armored_key)), this);
}
public V8Object crypto_key_decrypt(PgpKey private_key, String passphrase) {
- return (V8Object) this.call(Object.class, new String[]{"crypto", "key", "decrypt"}, new
- V8Array(v8).push(private_key.getV8Object()).push(passphrase));
+ return (V8Object) this.call(Object.class, new String[]{"crypto", "key", "decrypt"}, new V8Array(v8)
+ .push(private_key.getV8Object()).push(passphrase));
}
public String crypto_key_fingerprint(PgpKey key) {
- return (String) this.call(String.class, new String[]{"crypto", "key", "fingerprint"}, new
- V8Array(v8).push(key.getV8Object()));
+ return (String) this.call(String.class, new String[]{"crypto", "key", "fingerprint"}, new V8Array(v8)
+ .push(key.getV8Object()));
}
public String crypto_key_longid(PgpKey key) {
- return (String) this.call(String.class, new String[]{"crypto", "key", "longid"}, new
- V8Array(v8).push(key.getV8Object()));
+ return (String) this.call(String.class, new String[]{"crypto", "key", "longid"}, new V8Array(v8)
+ .push(key.getV8Object()));
}
public String crypto_key_longid(String fingerprint) {
- return (String) this.call(String.class, new String[]{"crypto", "key", "longid"}, new
- V8Array(v8).push(fingerprint));
+ return (String) this.call(String.class, new String[]{"crypto", "key", "longid"}, new V8Array(v8)
+ .push(fingerprint));
}
public String crypto_armor_clip(String text) {
- return (String) this.call(String.class, new String[]{"crypto", "armor", "clip"}, new
- V8Array(v8).push(text));
+ return (String) this.call(String.class, new String[]{"crypto", "armor", "clip"}, new V8Array(v8).push(text));
}
public String mnemonic(String longid) {
- return (String) this.call(String.class, v8, new String[]{"mnemonic"}, new V8Array(v8)
- .push(longid));
+ return (String) this.call(String.class, v8, new String[]{"mnemonic"}, new V8Array(v8).push(longid));
}
public String crypto_message_encrypt(String pubkeys[], String text) {
@@ -184,12 +174,8 @@ public byte[] crypto_message_encrypt(String pubkeys[], byte[] content, String fi
public PgpDecrypted crypto_message_decrypt(String data, String password) {
// db,account_email,encrypted_data,one_time_message_password,callback,force_output_format
- Long start = System.currentTimeMillis();
- V8Array params = new V8Array(v8).push(NULL).push("").push(data).push(password).push
- (cb_catcher).push(NULL);
+ V8Array params = new V8Array(v8).push(NULL).push("").push(data).push(password).push(cb_catcher).push(NULL);
this.call(void.class, new String[]{"crypto", "message", "decrypt"}, params);
- Long end = System.currentTimeMillis();
- System.out.println("duration decrypt: " + (end - start));
return new PgpDecrypted((V8Object) cb_last_value[0]);
}
@@ -197,14 +183,21 @@ public PgpDecrypted crypto_message_decrypt(String data) {
return crypto_message_decrypt(data, "");
}
+ public PgpDecrypted crypto_message_decrypt(byte[] bytes) {
+ // db,account_email,encrypted_data,one_time_message_password,callback,force_output_format
+ V8Array params = new V8Array(v8).push(NULL).push("").push(uint8(bytes)).push("").push(cb_catcher).push(NULL);
+ this.call(void.class, new String[]{"crypto", "message", "decrypt"}, params);
+ return new PgpDecrypted((V8Object) cb_last_value[0]);
+ }
+
public String api_gmail_query_backups(String account_email) {
return (String) this.call(String.class, new String[]{"api", "gmail", "query", "backups"},
new V8Array(v8).push(account_email));
}
public IdToken api_auth_parse_id_token(String id_token) {
- return new IdToken((V8Object) this.call(Object.class, new String[]{"api", "auth",
- "parse_id_token"}, new V8Array(v8).push(id_token)));
+ return new IdToken((V8Object) this.call(Object.class, new String[]{"api", "auth", "parse_id_token"},
+ new V8Array(v8).push(id_token)));
}
/**
@@ -255,8 +248,8 @@ private static String read(InputStream inputStream) throws IOException {
}
private V8Object mime_reply_headers(MimeMessage original) {
- return (V8Object) this.call(Object.class, new String[]{"mime", "reply_headers"}, new
- V8Array(v8).push(original.getV8Object()));
+ return (V8Object) this.call(Object.class, new String[]{"mime", "reply_headers"}, new V8Array(v8)
+ .push(original.getV8Object()));
}
private Object call(Class> return_type, String path[], V8Array args) {
@@ -295,31 +288,24 @@ private V8Array array(String arr[]) {
private void bindJavaMethods() {
JavaMethodsForJavascript methods = new JavaMethodsForJavascript(v8, storage);
- v8.registerJavaMethod(methods, "console_log", "engine_host_console_log", new
- Class[]{String.class});
- v8.registerJavaMethod(methods, "console_error", "engine_host_console_error", new
- Class[]{String.class});
+ v8.registerJavaMethod(methods, "console_log", "engine_host_console_log", new Class[]{String.class});
+ v8.registerJavaMethod(methods, "console_error", "engine_host_console_error", new Class[]{String.class});
v8.registerJavaMethod(methods, "alert", "engine_host_alert", new Class[]{String.class});
- v8.registerJavaMethod(methods, "private_keys_get", "private_keys_get", new Class[]{String
- .class, V8Array.class});
- v8.registerJavaMethod(methods, "private_keys_get", "private_keys_get", new Class[]{String
- .class, String.class});
- v8.registerJavaMethod(methods, "private_keys_get", "private_keys_get", new Class[]{String
- .class});
- v8.registerJavaMethod(methods, "get_passphrase", "get_passphrase", new Class[]{String
- .class, String.class});
- v8.registerJavaMethod(methods, "java_mod_pow_strings", "java_mod_pow", new Class[]{String
- .class, String.class, String.class});
- v8.registerJavaMethod(methods, "secure_random", "engine_host_secure_random", new Class[]{
- Integer.class});
+ v8.registerJavaMethod(methods, "private_keys_get", "private_keys_get", new Class[]{String.class, V8Array.class});
+ v8.registerJavaMethod(methods, "private_keys_get", "private_keys_get", new Class[]{String.class, String.class});
+ v8.registerJavaMethod(methods, "private_keys_get", "private_keys_get", new Class[]{String.class});
+ v8.registerJavaMethod(methods, "get_passphrase", "get_passphrase", new Class[]{String.class, String.class});
+ v8.registerJavaMethod(methods, "java_mod_pow_strings", "java_mod_pow", new Class[]{String.class, String.class,
+ String.class});
+ v8.registerJavaMethod(methods, "secure_random", "engine_host_secure_random", new Class[]{Integer.class});
v8.registerJavaMethod(methods, "html_to_text", "html_to_text", new Class[]{String.class});
- v8.registerJavaMethod(methods, "rsa_decrypt", "java_rsa_decrypt", new Class[]{String.class,
- String.class, V8Array.class});
+ v8.registerJavaMethod(methods, "rsa_decrypt", "java_rsa_decrypt", new Class[]{String.class, String.class,
+ V8Array.class});
}
private V8Object loadJavascriptCode() throws IOException {
- v8.executeScript("var engine_host_version = 'Android 0.1';");
+ v8.executeScript("var engine_host_version = 'Android " + BuildConfig.VERSION_NAME.split("_")[0] + "';");
v8.executeScript(read(context.getAssets().open("js/window.js")));
v8.executeScript(read(context.getAssets().open("js/openpgp.js")));
v8.executeScript(read(context.getAssets().open("js/emailjs/punycode.js")));
@@ -361,11 +347,31 @@ class MeaningfulV8ObjectContainer {
v8object = o;
}
- public V8Array getAttributeAsArray(String k) {
+ V8Array getAttributeAsArray(String k) {
return getAttributeAsArray(v8object, k);
}
- public V8Array getAttributeAsArray(V8Object obj, String k) {
+ V8Object getAttributeAsObject(String name) {
+ return getAttributeAsObject(v8object, name);
+ }
+
+ Boolean getAttributeAsBoolean(String name) {
+ return getAttributeAsBoolean(v8object, name);
+ }
+
+ Integer getAttributeAsInteger(String name) {
+ return getAttributeAsInteger(v8object, name);
+ }
+
+ String getAttributeAsString(String k) {
+ return getAttributeAsString(v8object, k);
+ }
+
+ byte[] getAttributeAsBytes(String k) {
+ return getAttributeAsBytes(v8object, k);
+ }
+
+ static V8Array getAttributeAsArray(V8Object obj, String k) {
try {
return obj.getArray(k);
} catch (V8ResultUndefined e) {
@@ -373,11 +379,7 @@ public V8Array getAttributeAsArray(V8Object obj, String k) {
}
}
- public V8Object getAttributeAsObject(String name) {
- return getAttributeAsObject(v8object, name);
- }
-
- public V8Object getAttributeAsObject(V8Object obj, String k) {
+ static V8Object getAttributeAsObject(V8Object obj, String k) {
try {
return obj.getObject(k);
} catch (V8ResultUndefined e) {
@@ -385,11 +387,7 @@ public V8Object getAttributeAsObject(V8Object obj, String k) {
}
}
- public Boolean getAttributeAsBoolean(String name) {
- return getAttributeAsBoolean(v8object, name);
- }
-
- public Boolean getAttributeAsBoolean(V8Object obj, String k) {
+ static Boolean getAttributeAsBoolean(V8Object obj, String k) {
try {
return obj.getBoolean(k);
} catch (V8ResultUndefined e) {
@@ -397,11 +395,7 @@ public Boolean getAttributeAsBoolean(V8Object obj, String k) {
}
}
- public Integer getAttributeAsInteger(String name) {
- return getAttributeAsInteger(v8object, name);
- }
-
- public Integer getAttributeAsInteger(V8Object obj, String k) {
+ static Integer getAttributeAsInteger(V8Object obj, String k) {
try {
return obj.getInteger(k);
} catch (V8ResultUndefined e) {
@@ -409,13 +403,18 @@ public Integer getAttributeAsInteger(V8Object obj, String k) {
}
}
- protected String getAttributeAsString(String k) {
- return getAttributeAsString(v8object, k);
+ static String getAttributeAsString(V8Object obj, String k) {
+ try {
+ return obj.getString(k);
+ } catch (V8ResultUndefined e) {
+ return null;
+ }
}
- protected String getAttributeAsString(V8Object obj, String k) {
+ static byte[] getAttributeAsBytes(V8Object obj, String k) {
try {
- return obj.getString(k);
+ V8TypedArray typedArray = (V8TypedArray) obj.getObject(k);
+ return typedArray.getBytes(0, typedArray.length());
} catch (V8ResultUndefined e) {
return null;
}
@@ -515,13 +514,11 @@ public BigInteger java_mod_pow(BigInteger b, BigInteger e, BigInteger m) {
public String rsa_decrypt(String modulus, String exponent, V8Array encrypted) {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
- KeySpec keySpec = new RSAPrivateKeySpec(new BigInteger(modulus), new BigInteger
- (exponent));
+ KeySpec keySpec = new RSAPrivateKeySpec(new BigInteger(modulus), new BigInteger(exponent));
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
Cipher decryptCipher = Cipher.getInstance("RSA/ECB/NoPadding");
decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);
- byte[] decrypted_bytes = decryptCipher.doFinal(encrypted.getBytes(0, encrypted.length
- ()));
+ byte[] decrypted_bytes = decryptCipher.doFinal(encrypted.getBytes(0, encrypted.length()));
return new BigInteger(decrypted_bytes).toString();
} catch (Exception e) {
System.out.println("JAVA RSA ERROR:" + e.getClass() + " --- " + e.getMessage());
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/js/PgpDecrypted.java b/FlowCrypt/src/main/java/com/flowcrypt/email/js/PgpDecrypted.java
index ace801e0b8..93182050c0 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/js/PgpDecrypted.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/js/PgpDecrypted.java
@@ -47,6 +47,10 @@ public Integer countPotentiallyMatchingKeys() {
return getCount("key_mismatch");
}
+ public Integer countAttempts() {
+ return getCount("attempts");
+ }
+
public String[] getEncryptedForLongids() {
return getStrings("encrypted_for");
}
@@ -63,12 +67,20 @@ public String[] getOtherErrors() {
return getStrings("errors");
}
- public String getContent() {
+ public String getString() {
+ V8Object content = this.getAttributeAsObject("content");
+ if(content == null) {
+ return null;
+ }
+ return getAttributeAsString(content, "data");
+ }
+
+ public byte[] getBytes() {
V8Object content = this.getAttributeAsObject("content");
if(content == null) {
return null;
}
- return this.getAttributeAsString(content, "data"); // todo - may need to convert from uint8
+ return getAttributeAsBytes(content, "data");
}
private Integer getCount(String name) {
@@ -76,7 +88,7 @@ private Integer getCount(String name) {
if(counts == null) {
return null;
}
- return this.getAttributeAsInteger(counts, name);
+ return getAttributeAsInteger(counts, name);
}
private String[] getStrings(String name) {
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/service/EmailSyncService.java b/FlowCrypt/src/main/java/com/flowcrypt/email/service/EmailSyncService.java
index 42b75a3ab0..757d296da7 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/service/EmailSyncService.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/service/EmailSyncService.java
@@ -64,6 +64,9 @@
* E-mail: DenBond7@gmail.com
*/
public class EmailSyncService extends Service implements SyncListener {
+ public static final String ACTION_SWITCH_ACCOUNT = "ACTION_SWITCH_ACCOUNT";
+ public static final String ACTION_BEGIN_SYNC = "ACTION_BEGIN_SYNC";
+
public static final int REPLY_RESULT_CODE_ACTION_OK = 0;
public static final int REPLY_RESULT_CODE_ACTION_ERROR = 1;
public static final int REPLY_RESULT_CODE_NEED_UPDATE = 2;
@@ -108,9 +111,22 @@ public EmailSyncService() {
*/
public static void startEmailSyncService(Context context) {
Intent startEmailServiceIntent = new Intent(context, EmailSyncService.class);
+ startEmailServiceIntent.setAction(ACTION_BEGIN_SYNC);
context.startService(startEmailServiceIntent);
}
+ /**
+ * This method can bu used to start {@link EmailSyncService} with the action {@link #ACTION_SWITCH_ACCOUNT}.
+ *
+ * @param context Interface to global information about an application environment.
+ */
+ public static void switchAccount(Context context) {
+ Intent startEmailServiceIntent = new Intent(context, EmailSyncService.class);
+ startEmailServiceIntent.setAction(ACTION_SWITCH_ACCOUNT);
+ context.startService(startEmailServiceIntent);
+ }
+
+
@Override
public void onCreate() {
super.onCreate();
@@ -125,7 +141,20 @@ public void onCreate() {
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand |intent =" + intent + "|flags = " + flags + "|startId = " + startId);
isServiceStarted = true;
- emailSyncManager.beginSync(false);
+
+ if (intent != null) {
+ switch (intent.getAction()) {
+ case ACTION_SWITCH_ACCOUNT:
+ emailSyncManager.setAccount(new AccountDaoSource().getActiveAccountInformation(this));
+ emailSyncManager.beginSync(true);
+ break;
+
+ default:
+ emailSyncManager.beginSync(false);
+ break;
+ }
+ }
+
return super.onStartCommand(intent, flags, startId);
}
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/service/attachment/AttachmentDownloadManagerService.java b/FlowCrypt/src/main/java/com/flowcrypt/email/service/attachment/AttachmentDownloadManagerService.java
index dc908cf18c..cedb888905 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/service/attachment/AttachmentDownloadManagerService.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/service/attachment/AttachmentDownloadManagerService.java
@@ -26,6 +26,7 @@
import android.widget.Toast;
import com.flowcrypt.email.BuildConfig;
+import com.flowcrypt.email.Constants;
import com.flowcrypt.email.R;
import com.flowcrypt.email.api.email.JavaEmailConstants;
import com.flowcrypt.email.api.email.model.AttachmentInfo;
@@ -34,13 +35,18 @@
import com.flowcrypt.email.database.dao.source.AccountDao;
import com.flowcrypt.email.database.dao.source.AccountDaoSource;
import com.flowcrypt.email.database.dao.source.imap.ImapLabelsDaoSource;
+import com.flowcrypt.email.js.Js;
+import com.flowcrypt.email.js.PgpDecrypted;
+import com.flowcrypt.email.security.SecurityStorageConnector;
import com.flowcrypt.email.util.GeneralUtil;
import com.sun.mail.imap.IMAPFolder;
import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -458,6 +464,8 @@ public void run() {
AccountDao accountDao = new AccountDaoSource().getAccountInformation(context, attachmentInfo.getEmail());
try {
+ checkMaxDecryptedFileSize();
+
Session session = OpenStoreHelper.getAttachmentSession(accountDao);
Store store = OpenStoreHelper.openAndConnectToStore(context, accountDao, session);
IMAPFolder imapFolder = (IMAPFolder) store.getFolder(new ImapLabelsDaoSource()
@@ -470,10 +478,9 @@ public void run() {
attachmentInfo.getId());
if (attachment != null) {
- InputStream input = attachment.getInputStream();
- OutputStream output = FileUtils.openOutputStream(attachmentFile);
+ InputStream inputStream = attachment.getInputStream();
- try {
+ try (OutputStream outputStream = FileUtils.openOutputStream(attachmentFile)) {
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
double count = 0;
double size = attachmentInfo.getEncodedSize();
@@ -483,9 +490,9 @@ public void run() {
long startTime, elapsedTime;
long lastUpdateTime = startTime = System.currentTimeMillis();
updateProgress(currentPercentage, 0);
- while (IOUtils.EOF != (numberOfReadBytes = input.read(buffer))) {
+ while (IOUtils.EOF != (numberOfReadBytes = inputStream.read(buffer))) {
if (!Thread.currentThread().isInterrupted()) {
- output.write(buffer, 0, numberOfReadBytes);
+ outputStream.write(buffer, 0, numberOfReadBytes);
count += numberOfReadBytes;
currentPercentage = (int) ((count / size) * 100f);
if (currentPercentage - lastPercentage >= 1
@@ -505,15 +512,15 @@ public void run() {
if (!Thread.currentThread().isInterrupted()) {
updateProgress(100, 0);
}
-
- output.close();
} finally {
- IOUtils.closeQuietly(output);
if (Thread.currentThread().isInterrupted()) {
removeNotCompleteDownloadFile(attachmentFile);
}
}
+ attachmentFile = decryptFileIfNeed(context, attachmentFile);
+ attachmentInfo.setName(attachmentFile.getName());
+
if (!Thread.currentThread().isInterrupted()) {
if (onDownloadAttachmentListener != null) {
onDownloadAttachmentListener.onAttachmentSuccessDownloaded(startId, attachmentInfo,
@@ -538,16 +545,81 @@ public void setOnDownloadAttachmentListener(OnDownloadAttachmentListener onDownl
this.onDownloadAttachmentListener = onDownloadAttachmentListener;
}
+ /**
+ * Check is decrypted file has size not more than
+ * {@link Constants#MAX_ATTACHMENT_SIZE_WHICH_CAN_BE_DECRYPTED}. If the file greater then
+ * {@link Constants#MAX_ATTACHMENT_SIZE_WHICH_CAN_BE_DECRYPTED} we throw an exception. This is only for files
+ * with the "pgp" extension.
+ */
+ private void checkMaxDecryptedFileSize() {
+ if ("pgp".equalsIgnoreCase(FilenameUtils.getExtension(attachmentInfo.getName()))) {
+ if (attachmentInfo.getEncodedSize() > Constants.MAX_ATTACHMENT_SIZE_WHICH_CAN_BE_DECRYPTED) {
+ throw new IllegalArgumentException(context.getString(R.string
+ .template_warning_max_attachments_size_for_decryption,
+ FileUtils.byteCountToDisplaySize(Constants
+ .MAX_ATTACHMENT_SIZE_WHICH_CAN_BE_DECRYPTED)));
+ }
+ }
+ }
+
+ /**
+ * Do decryption of the downloaded file if it need.
+ *
+ * @param context Interface to global information about an application environment;
+ * @param file The downloaded file which can be encrypted.
+ * @return The decrypted or the original file.
+ */
+ private File decryptFileIfNeed(Context context, File file) throws IOException {
+ if (file == null) {
+ return null;
+ }
+
+ if (!"pgp".equalsIgnoreCase(FilenameUtils.getExtension(file.getName()))) {
+ return file;
+ }
+
+ try (InputStream inputStream = new FileInputStream(file)) {
+ PgpDecrypted pgpDecrypted = new Js(context, new SecurityStorageConnector(context))
+ .crypto_message_decrypt(IOUtils.toByteArray(inputStream));
+ byte[] decryptedBytes = pgpDecrypted.getBytes();
+
+ File decryptedFile = new File(file.getParent(),
+ file.getName().substring(0, file.getName().lastIndexOf(".")));
+
+ boolean isInnerExceptionHappened = false;
+
+ try (OutputStream outputStream = FileUtils.openOutputStream(decryptedFile)) {
+ IOUtils.write(decryptedBytes, outputStream);
+ return decryptedFile;
+ } catch (IOException e) {
+ if (!decryptedFile.delete()) {
+ Log.d(TAG, "Cannot delete file: " + file);
+ }
+
+ isInnerExceptionHappened = true;
+ throw e;
+ } finally {
+ if (!isInnerExceptionHappened) {
+ if (!file.delete()) {
+ Log.d(TAG, "Cannot delete file: " + file);
+ }
+ }
+ }
+ }
+ }
+
/**
* Remove the file which not downloaded fully.
*
* @param attachmentFile The file which will be removed.
*/
private void removeNotCompleteDownloadFile(File attachmentFile) {
- if (!attachmentFile.delete()) {
- Log.d(TAG, "Cannot delete file: " + attachmentFile);
- } else {
- Log.d(TAG, "Canceled attachment \"" + attachmentFile + "\" was deleted");
+ if (attachmentFile != null && attachmentFile.exists()) {
+ if (!attachmentFile.delete()) {
+ Log.d(TAG, "Cannot delete file: " + attachmentFile);
+ } else {
+ Log.d(TAG, "Canceled attachment \"" + attachmentFile + "\" was deleted");
+ }
}
}
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/service/attachment/AttachmentNotificationManager.java b/FlowCrypt/src/main/java/com/flowcrypt/email/service/attachment/AttachmentNotificationManager.java
index 0eff3aad34..ba8a8756e7 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/service/attachment/AttachmentNotificationManager.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/service/attachment/AttachmentNotificationManager.java
@@ -18,6 +18,7 @@
import com.flowcrypt.email.R;
import com.flowcrypt.email.api.email.model.AttachmentInfo;
+import com.flowcrypt.email.ui.NotificationChannelManager;
/**
* This manager is responsible for displaying attachment notifications.
@@ -45,7 +46,8 @@ public AttachmentNotificationManager(Context context) {
* @param attachmentInfo {@link AttachmentInfo} object which contains a detail information about an attachment.
*/
public void attachmentAddedToLoadQueue(Context context, int startId, AttachmentInfo attachmentInfo) {
- NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
+ NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context,
+ NotificationChannelManager.CHANNEL_ID_ATTACHMENTS)
.setWhen(startId)
.setShowWhen(false)
.setProgress(0, 0, true)
@@ -72,7 +74,8 @@ public void attachmentAddedToLoadQueue(Context context, int startId, AttachmentI
*/
public void updateLoadingProgress(Context context, int startId, AttachmentInfo attachmentInfo,
int progress, long timeLeftInMillisecond) {
- NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
+ NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context,
+ NotificationChannelManager.CHANNEL_ID_ATTACHMENTS)
.setProgress(MAX_FILE_SIZE_IN_PERCENTAGE, progress, timeLeftInMillisecond == 0)
.setWhen(startId)
.setShowWhen(false)
@@ -103,7 +106,8 @@ public void downloadComplete(Context context, int startId, AttachmentInfo attach
PendingIntent pendingIntentOpenFile = PendingIntent.getActivity(context, 0, intentOpenFile, 0);
- NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
+ NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context,
+ NotificationChannelManager.CHANNEL_ID_ATTACHMENTS)
.setProgress(0, 0, false)
.setAutoCancel(true)
.setOngoing(false)
@@ -128,7 +132,8 @@ public void downloadComplete(Context context, int startId, AttachmentInfo attach
* @param e The {@link Exception} which contains a detail information about happened error..
*/
public void errorHappened(Context context, int startId, AttachmentInfo attachmentInfo, Exception e) {
- NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
+ NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context,
+ NotificationChannelManager.CHANNEL_ID_ATTACHMENTS)
.setProgress(0, 0, false)
.setAutoCancel(true)
.setOngoing(false)
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/NotificationChannelManager.java b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/NotificationChannelManager.java
new file mode 100644
index 0000000000..c2141be925
--- /dev/null
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/NotificationChannelManager.java
@@ -0,0 +1,65 @@
+/*
+ * Business Source License 1.0 © 2017 FlowCrypt Limited (tom@cryptup.org).
+ * Use limitations apply. See https://github.com/FlowCrypt/flowcrypt-android/blob/master/LICENSE
+ * Contributors: DenBond7
+ */
+
+package com.flowcrypt.email.ui;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
+
+import com.flowcrypt.email.R;
+
+/**
+ * This manager does job of register {@link NotificationChannel} of the app. The {@link NotificationChannel} was
+ * added in the {@link Build.VERSION_CODES#O} and doesn't work on previous Android versions.
+ *
+ * @author Denis Bondarenko
+ * Date: 17.10.2017
+ * Time: 12:12
+ * E-mail: DenBond7@gmail.com
+ */
+
+public class NotificationChannelManager {
+ public static final String CHANNEL_ID_ATTACHMENTS = "Attachments";
+
+ /**
+ * Register {@link NotificationChannel}(s) of the app in the system.
+ *
+ * @param context Interface to global information about an application environment.
+ */
+ public static void registerNotificationChannels(Context context) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationManager notificationManager =
+ (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ if (notificationManager != null) {
+ notificationManager.createNotificationChannel(generateAttachmentsNotificationChannel(context));
+ }
+ }
+ }
+
+ /**
+ * Generate attachments notification channel.
+ *
+ * @param context Interface to global information about an application environment.
+ * @return Generated {@link NotificationChannel}
+ */
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ private static NotificationChannel generateAttachmentsNotificationChannel(Context context) {
+ CharSequence name = context.getString(R.string.attachments);
+ String description = context.getString(R.string.download_attachments_notification_channel);
+ int importance = NotificationManager.IMPORTANCE_DEFAULT;
+
+ NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID_ATTACHMENTS, name, importance);
+ notificationChannel.setDescription(description);
+ notificationChannel.enableLights(false);
+ notificationChannel.enableVibration(false);
+
+ return notificationChannel;
+ }
+}
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/AddNewAccountActivity.java b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/AddNewAccountActivity.java
index 66dcb61860..d915be2882 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/AddNewAccountActivity.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/AddNewAccountActivity.java
@@ -9,71 +9,63 @@
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
-import android.support.v7.preference.PreferenceManager;
-import android.text.Editable;
import android.text.TextUtils;
-import android.text.TextWatcher;
import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.EditText;
-import android.widget.Spinner;
-
-import com.flowcrypt.email.Constants;
+import android.widget.Toast;
+
import com.flowcrypt.email.R;
import com.flowcrypt.email.api.email.model.AuthCredentials;
-import com.flowcrypt.email.api.email.model.SecurityType;
import com.flowcrypt.email.database.dao.source.AccountDao;
import com.flowcrypt.email.database.dao.source.AccountDaoSource;
+import com.flowcrypt.email.model.KeyDetails;
import com.flowcrypt.email.model.results.LoaderResult;
-import com.flowcrypt.email.service.EmailSyncService;
-import com.flowcrypt.email.ui.activity.base.BaseActivity;
-import com.flowcrypt.email.ui.loader.CheckEmailSettingsAsyncTaskLoader;
+import com.flowcrypt.email.security.SecurityUtils;
+import com.flowcrypt.email.ui.activity.base.BaseSignInActivity;
+import com.flowcrypt.email.ui.loader.LoadPrivateKeysFromMailAsyncTaskLoader;
import com.flowcrypt.email.util.GeneralUtil;
-import com.flowcrypt.email.util.SharedPreferencesHelper;
import com.flowcrypt.email.util.UIUtil;
-import com.google.gson.Gson;
-import com.google.gson.JsonSyntaxException;
+import com.google.android.gms.auth.api.Auth;
+import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
+import com.google.android.gms.auth.api.signin.GoogleSignInResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+
+import org.acra.ACRA;
+
+import java.util.ArrayList;
/**
- * This activity describes a logic of adding a new account of other email providers.
+ * This activity describes a logic of add a new email account.
*
* @author Denis Bondarenko
- * Date: 12.09.2017
- * Time: 17:21
+ * Date: 05.10.2017
+ * Time: 10:34
* E-mail: DenBond7@gmail.com
*/
-public class AddNewAccountActivity extends BaseActivity implements CompoundButton.OnCheckedChangeListener,
- AdapterView.OnItemSelectedListener, View.OnClickListener, TextWatcher,
- LoaderManager.LoaderCallbacks {
- private static final int REQUEST_CODE_ADD_NEW_ACCOUNT = 10;
-
- private EditText editTextEmail;
- private EditText editTextUserName;
- private EditText editTextPassword;
- private EditText editTextImapServer;
- private EditText editTextImapPort;
- private EditText editTextSmtpServer;
- private EditText editTextSmtpPort;
- private EditText editTextSmtpUsername;
- private EditText editTextSmtpPassword;
- private Spinner spinnerImapSecyrityType;
- private Spinner spinnerSmtpSecyrityType;
- private View layoutSmtpSignIn;
+public class AddNewAccountActivity extends BaseSignInActivity implements View.OnClickListener, GoogleApiClient
+ .OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks, LoaderManager.LoaderCallbacks {
+
+ public static final String KEY_EXTRA_NEW_ACCOUNT =
+ GeneralUtil.generateUniqueExtraKey("KEY_EXTRA_NEW_ACCOUNT", AddNewAccountActivity.class);
+
+ private static final int REQUEST_CODE_CREATE_OR_IMPORT_KEY_FOR_GMAIL = 100;
+ private static final int REQUEST_CODE_CHECK_PRIVATE_KEYS_FROM_GMAIL = 101;
+
private View progressView;
private View contentView;
- private CheckBox checkBoxRequireSignInForSmtp;
- private AuthCredentials authCredentials;
+ private GoogleSignInAccount googleSignInAccount;
- private boolean isImapSpinnerInitAfterStart;
- private boolean isSmtpSpinnerInitAfterStart;
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ progressView = findViewById(R.id.progressView);
+ contentView = findViewById(R.id.layoutContent);
+ }
@Override
public boolean isDisplayHomeAsUpEnabled() {
@@ -87,142 +79,93 @@ public int getContentViewResourceId() {
@Override
public View getRootView() {
- return contentView;
- }
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- this.authCredentials = getTempAuthCredentialsFromPreferences();
-
- if (authCredentials == null) {
- isImapSpinnerInitAfterStart = true;
- isSmtpSpinnerInitAfterStart = true;
- }
-
- initViews(savedInstanceState);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- saveTempCredentialsToPreferences();
+ return findViewById(R.id.screenContent);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
- case REQUEST_CODE_ADD_NEW_ACCOUNT:
+ case REQUEST_CODE_SIGN_IN:
+ GoogleSignInResult googleSignInResult = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
+ if (googleSignInResult.isSuccess()) {
+ googleSignInAccount = googleSignInResult.getSignInAccount();
+ if (googleSignInAccount != null) {
+ if (new AccountDaoSource().getAccountInformation(this,
+ googleSignInAccount.getEmail()) == null) {
+ getSupportLoaderManager().restartLoader(R.id.loader_id_load_private_key_backups_from_email,
+ null, this);
+ } else {
+ showInfoSnackbar(getRootView(), getString(R.string.template_email_alredy_added,
+ googleSignInAccount.getEmail()), Snackbar.LENGTH_LONG);
+ }
+ } else throw new NullPointerException("GoogleSignInAccount is null!");
+ }
+ break;
+
+ case REQUEST_CODE_ADD_OTHER_ACCOUNT:
switch (resultCode) {
- case Activity.RESULT_OK:
+ case RESULT_OK:
try {
- authCredentials = generateAuthCredentials();
+ AuthCredentials authCredentials = data.getParcelableExtra(AddNewAccountManuallyActivity
+ .KEY_EXTRA_AUTH_CREDENTIALS);
AccountDaoSource accountDaoSource = new AccountDaoSource();
accountDaoSource.addRow(this, authCredentials);
- AccountDao accountDao = accountDaoSource.getAccountInformation(this,
- authCredentials.getEmail());
- EmailSyncService.startEmailSyncService(this);
- EmailManagerActivity.runEmailManagerActivity(this, accountDao);
- setResult(Activity.RESULT_OK);
+ accountDaoSource.setActiveAccount(this, authCredentials.getEmail());
+
+ Intent intent = new Intent();
+ intent.putExtra(KEY_EXTRA_NEW_ACCOUNT, accountDaoSource.getActiveAccountInformation(this));
+
+ setResult(Activity.RESULT_OK, intent);
finish();
} catch (Exception e) {
e.printStackTrace();
- throw new IllegalStateException("Something wrong happened", e);
+ ACRA.getErrorReporter().handleException(e);
+ Toast.makeText(this, R.string.unknown_error, Toast.LENGTH_SHORT).show();
}
break;
}
break;
- default:
- super.onActivityResult(requestCode, resultCode, data);
- }
- }
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- switch (buttonView.getId()) {
- case R.id.checkBoxRequireSignInForSmtp:
- layoutSmtpSignIn.setVisibility(isChecked ? View.VISIBLE : View.GONE);
- break;
- }
- }
-
- @Override
- public void onItemSelected(AdapterView> parent, View view, int position, long id) {
- switch (parent.getId()) {
- case R.id.spinnerImapSecurityType:
- SecurityType securityTypeForImap = (SecurityType) parent.getAdapter().getItem(position);
- if (isImapSpinnerInitAfterStart) {
- editTextImapPort.setText(String.valueOf(securityTypeForImap.getDefaultImapPort()));
- } else {
- isImapSpinnerInitAfterStart = true;
- }
- break;
+ case REQUEST_CODE_CHECK_PRIVATE_KEYS_FROM_GMAIL:
+ switch (resultCode) {
+ case Activity.RESULT_OK:
+ case CheckKeysActivity.RESULT_NEUTRAL:
+ returnResultOk();
+ break;
- case R.id.spinnerSmtpSecyrityType:
- SecurityType securityTypeForSmtp = (SecurityType) parent.getAdapter().getItem(position);
- if (isSmtpSpinnerInitAfterStart) {
- editTextSmtpPort.setText(String.valueOf(securityTypeForSmtp.getDefaultSmtpPort()));
- } else {
- isSmtpSpinnerInitAfterStart = true;
+ case Activity.RESULT_CANCELED:
+ case CheckKeysActivity.RESULT_NEGATIVE:
+ UIUtil.exchangeViewVisibility(this, false, progressView, contentView);
+ break;
}
break;
- }
- }
- @Override
- public void onNothingSelected(AdapterView> parent) {
-
- }
+ case REQUEST_CODE_CREATE_OR_IMPORT_KEY_FOR_GMAIL:
+ switch (resultCode) {
+ case Activity.RESULT_OK:
+ returnResultOk();
+ break;
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.buttonTryToConnect:
- authCredentials = generateAuthCredentials();
- if (isAllInformationCorrect()) {
- UIUtil.hideSoftInput(this, getRootView());
- if (isNotDuplicate()) {
- getSupportLoaderManager().restartLoader(R.id.loader_id_check_email_settings, null, this);
- } else {
- showInfoSnackbar(getRootView(), getString(R.string.template_email_alredy_added,
- authCredentials.getEmail()), Snackbar.LENGTH_LONG);
- }
+ case Activity.RESULT_CANCELED:
+ case CreateOrImportKeyActivity.RESULT_CODE_USE_ANOTHER_ACCOUNT:
+ this.googleSignInAccount = null;
+ break;
}
break;
- }
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable editable) {
- if (GeneralUtil.isEmailValid(editable)) {
- String email = editable.toString();
- String mainDomain = email.substring(email.indexOf('@') + 1, email.length());
- editTextImapServer.setText(getString(R.string.template_imap_server, mainDomain));
- editTextSmtpServer.setText(getString(R.string.template_smtp_server, mainDomain));
- editTextUserName.setText(email.substring(0, email.indexOf('@')));
- editTextSmtpUsername.setText(email.substring(0, email.indexOf('@')));
+ default:
+ super.onActivityResult(requestCode, resultCode, data);
}
}
@Override
public Loader onCreateLoader(int id, Bundle args) {
switch (id) {
- case R.id.loader_id_check_email_settings:
+ case R.id.loader_id_load_private_key_backups_from_email:
UIUtil.exchangeViewVisibility(this, true, progressView, contentView);
-
- authCredentials = generateAuthCredentials();
- return new CheckEmailSettingsAsyncTaskLoader(this, authCredentials);
+ AccountDao accountDao = new AccountDao(googleSignInAccount.getEmail(),
+ AccountDao.ACCOUNT_TYPE_GOOGLE, null, null, null, null, null);
+ return new LoadPrivateKeysFromMailAsyncTaskLoader(this, accountDao);
default:
return null;
@@ -239,20 +182,27 @@ public void onLoaderReset(Loader loader) {
}
+ @SuppressWarnings("unchecked")
@Override
public void handleSuccessLoaderResult(int loaderId, Object result) {
switch (loaderId) {
- case R.id.loader_id_check_email_settings:
- boolean isSettingsValid = (boolean) result;
- if (isSettingsValid) {
- AccountDao accountDao = new AccountDao(authCredentials.getEmail(),
- null, null, null, null, null, authCredentials);
+ case R.id.loader_id_load_private_key_backups_from_email:
+ ArrayList keyDetailsList = (ArrayList) result;
+ if (keyDetailsList.isEmpty()) {
+ AccountDao accountDao = new AccountDao(googleSignInAccount.getEmail(),
+ AccountDao.ACCOUNT_TYPE_GOOGLE, null, null, null, null, null);
startActivityForResult(CreateOrImportKeyActivity.newIntent(this, accountDao, true),
- REQUEST_CODE_ADD_NEW_ACCOUNT);
+ REQUEST_CODE_CREATE_OR_IMPORT_KEY_FOR_GMAIL);
UIUtil.exchangeViewVisibility(this, false, progressView, contentView);
} else {
- UIUtil.exchangeViewVisibility(this, false, progressView, contentView);
- showInfoSnackbar(getRootView(), getString(R.string.settings_not_valid), Snackbar.LENGTH_LONG);
+ startActivityForResult(CheckKeysActivity.newIntent(this,
+ keyDetailsList,
+ getString(R.string.found_backup_of_your_account_key),
+ getString(R.string.continue_),
+ SecurityUtils.isBackupKeysExist(this) ? getString(R.string
+ .use_existing_keys) : null,
+ getString(R.string.use_another_account), true),
+ REQUEST_CODE_CHECK_PRIVATE_KEYS_FROM_GMAIL);
}
break;
@@ -265,7 +215,7 @@ public void handleSuccessLoaderResult(int loaderId, Object result) {
@Override
public void handleFailureLoaderResult(int loaderId, Exception e) {
switch (loaderId) {
- case R.id.loader_id_check_email_settings:
+ case R.id.loader_id_load_private_key_backups_from_email:
UIUtil.exchangeViewVisibility(this, false, progressView, contentView);
showInfoSnackbar(getRootView(), e != null && !TextUtils.isEmpty(e.getMessage()) ? e.getMessage()
: getString(R.string.unknown_error), Snackbar.LENGTH_LONG);
@@ -277,189 +227,21 @@ public void handleFailureLoaderResult(int loaderId, Exception e) {
}
}
- /**
- * Check that current email is not duplicate and not added yet.
- *
- * @return true if email not added yet, otherwise false.
- */
- private boolean isNotDuplicate() {
- return new AccountDaoSource().getAccountInformation(this, authCredentials.getEmail()) == null;
- }
-
- private void initViews(Bundle savedInstanceState) {
- editTextEmail = (EditText) findViewById(R.id.editTextEmail);
- editTextUserName = (EditText) findViewById(R.id.editTextUserName);
- editTextPassword = (EditText) findViewById(R.id.editTextPassword);
- editTextImapServer = (EditText) findViewById(R.id.editTextImapServer);
- editTextImapPort = (EditText) findViewById(R.id.editTextImapPort);
- editTextSmtpServer = (EditText) findViewById(R.id.editTextSmtpServer);
- editTextSmtpPort = (EditText) findViewById(R.id.editTextSmtpPort);
- editTextSmtpUsername = (EditText) findViewById(R.id.editTextSmtpUsername);
- editTextSmtpPassword = (EditText) findViewById(R.id.editTextSmtpPassword);
+ private void returnResultOk() {
+ AccountDaoSource accountDaoSource = saveGmailAccountToDatabase();
- editTextEmail.addTextChangedListener(this);
+ Intent intent = new Intent();
+ intent.putExtra(KEY_EXTRA_NEW_ACCOUNT, accountDaoSource.getActiveAccountInformation(this));
- layoutSmtpSignIn = findViewById(R.id.layoutSmtpSignIn);
- progressView = findViewById(R.id.progressView);
- contentView = findViewById(R.id.layoutContent);
- checkBoxRequireSignInForSmtp = (CheckBox) findViewById(R.id.checkBoxRequireSignInForSmtp);
- checkBoxRequireSignInForSmtp.setOnCheckedChangeListener(this);
-
- spinnerImapSecyrityType = (Spinner) findViewById(R.id.spinnerImapSecurityType);
- spinnerSmtpSecyrityType = (Spinner) findViewById(R.id.spinnerSmtpSecyrityType);
-
- ArrayAdapter userAdapter = new ArrayAdapter<>(this,
- android.R.layout.simple_spinner_dropdown_item, SecurityType.generateAvailableSecurityTypes(this));
-
- spinnerImapSecyrityType.setAdapter(userAdapter);
- spinnerSmtpSecyrityType.setAdapter(userAdapter);
-
- spinnerImapSecyrityType.setOnItemSelectedListener(this);
- spinnerSmtpSecyrityType.setOnItemSelectedListener(this);
-
- if (findViewById(R.id.buttonTryToConnect) != null) {
- findViewById(R.id.buttonTryToConnect).setOnClickListener(this);
- }
-
- if (savedInstanceState == null) {
- updateView();
- }
+ setResult(Activity.RESULT_OK, intent);
+ finish();
}
- /**
- * Update the current views if {@link AuthCredentials} not null.l
- */
- private void updateView() {
- if (authCredentials != null) {
-
- editTextEmail.setText(authCredentials.getEmail());
- editTextUserName.setText(authCredentials.getUsername());
- editTextImapServer.setText(authCredentials.getImapServer());
- editTextImapPort.setText(String.valueOf(authCredentials.getImapPort()));
- editTextSmtpServer.setText(authCredentials.getSmtpServer());
- editTextSmtpPort.setText(String.valueOf(authCredentials.getSmtpPort()));
- checkBoxRequireSignInForSmtp.setChecked(authCredentials.isUseCustomSignInForSmtp());
- editTextSmtpUsername.setText(authCredentials.getSmtpSigInUsername());
-
- int imapOptionsCount = spinnerImapSecyrityType.getAdapter().getCount();
- for (int i = 0; i < imapOptionsCount; i++) {
- if (authCredentials.getImapSecurityTypeOption() ==
- ((SecurityType) spinnerImapSecyrityType.getAdapter().getItem(i)).getOption()) {
- spinnerImapSecyrityType.setSelection(i);
- }
- }
-
- int smtpOptionsCount = spinnerSmtpSecyrityType.getAdapter().getCount();
- for (int i = 0; i < smtpOptionsCount; i++) {
- if (authCredentials.getSmtpSecurityTypeOption() ==
- ((SecurityType) spinnerSmtpSecyrityType.getAdapter().getItem(i)).getOption()) {
- spinnerSmtpSecyrityType.setSelection(i);
- }
- }
- }
- }
-
- /**
- * Save the current {@link AuthCredentials} to the shared preferences.
- */
- private void saveTempCredentialsToPreferences() {
- authCredentials = generateAuthCredentials();
- Gson gson = new Gson();
- authCredentials.setPassword(null);
- authCredentials.setSmtpSignInPassword(null);
- SharedPreferencesHelper.setString(PreferenceManager.getDefaultSharedPreferences(this),
- Constants.PREFERENCES_KEY_TEMP_LAST_AUTH_CREDENTIALS, gson.toJson(authCredentials));
- }
-
- /**
- * Retrieve a temp {@link AuthCredentials} from the shared preferences.
- */
- private AuthCredentials getTempAuthCredentialsFromPreferences() {
- String authCredentialsJson = SharedPreferencesHelper.getString(PreferenceManager.getDefaultSharedPreferences
- (this), Constants.PREFERENCES_KEY_TEMP_LAST_AUTH_CREDENTIALS, "");
-
- if (!TextUtils.isEmpty(authCredentialsJson)) {
- try {
- return new Gson().fromJson(authCredentialsJson, AuthCredentials.class);
- } catch (JsonSyntaxException e) {
- e.printStackTrace();
- }
- }
-
- return null;
- }
-
- /**
- * Generate the {@link AuthCredentials} using user input.
- *
- * @return {@link AuthCredentials}.
- */
- private AuthCredentials generateAuthCredentials() {
- return new AuthCredentials.Builder().setEmail(editTextEmail.getText().toString())
- .setUsername(editTextUserName.getText().toString())
- .setPassword(editTextPassword.getText().toString())
- .setImapServer(editTextImapServer.getText().toString())
- .setImapPort(Integer.parseInt(editTextImapPort.getText().toString()))
- .setImapSecurityTypeOption(((SecurityType) spinnerImapSecyrityType.getSelectedItem()).getOption())
- .setSmtpServer(editTextSmtpServer.getText().toString())
- .setSmtpPort(Integer.parseInt(editTextSmtpPort.getText().toString()))
- .setSmtpSecurityTypeOption(((SecurityType) spinnerSmtpSecyrityType.getSelectedItem()).getOption())
- .setIsUseCustomSignInForSmtp(checkBoxRequireSignInForSmtp.isChecked())
- .setSmtpSigInUsername(editTextSmtpUsername.getText().toString())
- .setSmtpSignInPassword(editTextSmtpPassword.getText().toString())
- .build();
- }
-
- /**
- * Do a lot of checks to validate an outgoing message info.
- *
- * @return Boolean true if all information is correct, false otherwise.
- */
- private boolean isAllInformationCorrect() {
- if (TextUtils.isEmpty(editTextEmail.getText())) {
- showInfoSnackbar(editTextEmail, getString(R.string.text_must_not_be_empty, getString(R.string.e_mail)));
- editTextEmail.requestFocus();
- } else if (GeneralUtil.isEmailValid(editTextEmail.getText())) {
- if (TextUtils.isEmpty(editTextUserName.getText())) {
- showInfoSnackbar(editTextUserName, getString(R.string.text_must_not_be_empty,
- getString(R.string.username)));
- editTextUserName.requestFocus();
- } else if (TextUtils.isEmpty(editTextPassword.getText())) {
- showInfoSnackbar(editTextPassword, getString(R.string.text_must_not_be_empty,
- getString(R.string.password)));
- editTextPassword.requestFocus();
- } else if (TextUtils.isEmpty(editTextImapServer.getText())) {
- showInfoSnackbar(editTextImapServer, getString(R.string.text_must_not_be_empty,
- getString(R.string.imap_server)));
- editTextImapServer.requestFocus();
- } else if (TextUtils.isEmpty(editTextImapPort.getText())) {
- showInfoSnackbar(editTextImapPort, getString(R.string.text_must_not_be_empty,
- getString(R.string.imap_port)));
- editTextImapPort.requestFocus();
- } else if (TextUtils.isEmpty(editTextSmtpServer.getText())) {
- showInfoSnackbar(editTextSmtpServer, getString(R.string.text_must_not_be_empty,
- getString(R.string.smtp_server)));
- editTextSmtpServer.requestFocus();
- } else if (TextUtils.isEmpty(editTextSmtpPort.getText())) {
- showInfoSnackbar(editTextSmtpPort, getString(R.string.text_must_not_be_empty,
- getString(R.string.smtp_port)));
- editTextSmtpPort.requestFocus();
- } else if (checkBoxRequireSignInForSmtp.isChecked()) {
- if (TextUtils.isEmpty(editTextSmtpUsername.getText())) {
- showInfoSnackbar(editTextSmtpUsername, getString(R.string.text_must_not_be_empty,
- getString(R.string.smtp_username)));
- editTextSmtpUsername.requestFocus();
- } else if (TextUtils.isEmpty(editTextSmtpPassword.getText())) {
- showInfoSnackbar(editTextSmtpPassword, getString(R.string.text_must_not_be_empty,
- getString(R.string.smtp_password)));
- editTextSmtpPassword.requestFocus();
- } else return true;
- } else return true;
- } else {
- showInfoSnackbar(editTextEmail, getString(R.string.error_email_is_not_valid));
- editTextEmail.requestFocus();
- }
-
- return false;
+ @NonNull
+ private AccountDaoSource saveGmailAccountToDatabase() {
+ AccountDaoSource accountDaoSource = new AccountDaoSource();
+ accountDaoSource.addRow(this, googleSignInAccount);
+ accountDaoSource.setActiveAccount(this, googleSignInAccount.getEmail());
+ return accountDaoSource;
}
}
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/AddNewAccountManuallyActivity.java b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/AddNewAccountManuallyActivity.java
new file mode 100644
index 0000000000..f3b59d2177
--- /dev/null
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/AddNewAccountManuallyActivity.java
@@ -0,0 +1,518 @@
+/*
+ * Business Source License 1.0 © 2017 FlowCrypt Limited (tom@cryptup.org).
+ * Use limitations apply. See https://github.com/FlowCrypt/flowcrypt-android/blob/master/LICENSE
+ * Contributors: DenBond7
+ */
+
+package com.flowcrypt.email.ui.activity;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.Loader;
+import android.support.v7.preference.PreferenceManager;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.Spinner;
+
+import com.flowcrypt.email.Constants;
+import com.flowcrypt.email.R;
+import com.flowcrypt.email.api.email.model.AuthCredentials;
+import com.flowcrypt.email.api.email.model.SecurityType;
+import com.flowcrypt.email.database.dao.source.AccountDao;
+import com.flowcrypt.email.database.dao.source.AccountDaoSource;
+import com.flowcrypt.email.model.KeyDetails;
+import com.flowcrypt.email.model.results.LoaderResult;
+import com.flowcrypt.email.security.SecurityUtils;
+import com.flowcrypt.email.ui.activity.base.BaseActivity;
+import com.flowcrypt.email.ui.loader.CheckEmailSettingsAsyncTaskLoader;
+import com.flowcrypt.email.ui.loader.LoadPrivateKeysFromMailAsyncTaskLoader;
+import com.flowcrypt.email.util.GeneralUtil;
+import com.flowcrypt.email.util.SharedPreferencesHelper;
+import com.flowcrypt.email.util.UIUtil;
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+
+import java.util.ArrayList;
+
+/**
+ * This activity describes a logic of adding a new account of other email providers.
+ *
+ * @author Denis Bondarenko
+ * Date: 12.09.2017
+ * Time: 17:21
+ * E-mail: DenBond7@gmail.com
+ */
+
+public class AddNewAccountManuallyActivity extends BaseActivity implements CompoundButton.OnCheckedChangeListener,
+ AdapterView.OnItemSelectedListener, View.OnClickListener, TextWatcher,
+ LoaderManager.LoaderCallbacks {
+ public static final String KEY_EXTRA_AUTH_CREDENTIALS =
+ GeneralUtil.generateUniqueExtraKey("KEY_EXTRA_AUTH_CREDENTIALS", ImportPublicKeyActivity.class);
+
+ private static final int REQUEST_CODE_ADD_NEW_ACCOUNT = 10;
+ private static final int REQUEST_CODE_CHECK_PRIVATE_KEYS_FROM_EMAIL = 11;
+
+ private EditText editTextEmail;
+ private EditText editTextUserName;
+ private EditText editTextPassword;
+ private EditText editTextImapServer;
+ private EditText editTextImapPort;
+ private EditText editTextSmtpServer;
+ private EditText editTextSmtpPort;
+ private EditText editTextSmtpUsername;
+ private EditText editTextSmtpPassword;
+ private Spinner spinnerImapSecyrityType;
+ private Spinner spinnerSmtpSecyrityType;
+ private View layoutSmtpSignIn;
+ private View progressView;
+ private View contentView;
+ private CheckBox checkBoxRequireSignInForSmtp;
+ private AuthCredentials authCredentials;
+
+ private boolean isImapSpinnerInitAfterStart;
+ private boolean isSmtpSpinnerInitAfterStart;
+
+ @Override
+ public boolean isDisplayHomeAsUpEnabled() {
+ return true;
+ }
+
+ @Override
+ public int getContentViewResourceId() {
+ return R.layout.activity_add_new_account_manually;
+ }
+
+ @Override
+ public View getRootView() {
+ return contentView;
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ this.authCredentials = getTempAuthCredentialsFromPreferences();
+
+ if (authCredentials == null) {
+ isImapSpinnerInitAfterStart = true;
+ isSmtpSpinnerInitAfterStart = true;
+ }
+
+ initViews(savedInstanceState);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ saveTempCredentialsToPreferences();
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_CODE_ADD_NEW_ACCOUNT:
+ switch (resultCode) {
+ case Activity.RESULT_OK:
+ returnOkResult();
+ break;
+
+ case CreateOrImportKeyActivity.RESULT_CODE_USE_ANOTHER_ACCOUNT:
+ setResult(CreateOrImportKeyActivity.RESULT_CODE_USE_ANOTHER_ACCOUNT, data);
+ finish();
+ break;
+ }
+ break;
+
+ case REQUEST_CODE_CHECK_PRIVATE_KEYS_FROM_EMAIL:
+ switch (resultCode) {
+ case Activity.RESULT_OK:
+ case CheckKeysActivity.RESULT_NEUTRAL:
+ returnOkResult();
+ break;
+
+ case Activity.RESULT_CANCELED:
+ UIUtil.exchangeViewVisibility(this, false, progressView, contentView);
+ break;
+
+ case CheckKeysActivity.RESULT_NEGATIVE:
+ setResult(CreateOrImportKeyActivity.RESULT_CODE_USE_ANOTHER_ACCOUNT, data);
+ finish();
+ break;
+ }
+ break;
+
+ default:
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ switch (buttonView.getId()) {
+ case R.id.checkBoxRequireSignInForSmtp:
+ layoutSmtpSignIn.setVisibility(isChecked ? View.VISIBLE : View.GONE);
+ break;
+ }
+ }
+
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ switch (parent.getId()) {
+ case R.id.spinnerImapSecurityType:
+ SecurityType securityTypeForImap = (SecurityType) parent.getAdapter().getItem(position);
+ if (isImapSpinnerInitAfterStart) {
+ editTextImapPort.setText(String.valueOf(securityTypeForImap.getDefaultImapPort()));
+ } else {
+ isImapSpinnerInitAfterStart = true;
+ }
+ break;
+
+ case R.id.spinnerSmtpSecyrityType:
+ SecurityType securityTypeForSmtp = (SecurityType) parent.getAdapter().getItem(position);
+ if (isSmtpSpinnerInitAfterStart) {
+ editTextSmtpPort.setText(String.valueOf(securityTypeForSmtp.getDefaultSmtpPort()));
+ } else {
+ isSmtpSpinnerInitAfterStart = true;
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.buttonTryToConnect:
+ authCredentials = generateAuthCredentials();
+ if (isAllInformationCorrect()) {
+ UIUtil.hideSoftInput(this, getRootView());
+ if (isNotDuplicate()) {
+ getSupportLoaderManager().restartLoader(R.id.loader_id_check_email_settings, null, this);
+ } else {
+ showInfoSnackbar(getRootView(), getString(R.string.template_email_alredy_added,
+ authCredentials.getEmail()), Snackbar.LENGTH_LONG);
+ }
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ if (GeneralUtil.isEmailValid(editable)) {
+ String email = editable.toString();
+ String mainDomain = email.substring(email.indexOf('@') + 1, email.length());
+ editTextImapServer.setText(getString(R.string.template_imap_server, mainDomain));
+ editTextSmtpServer.setText(getString(R.string.template_smtp_server, mainDomain));
+ editTextUserName.setText(email.substring(0, email.indexOf('@')));
+ editTextSmtpUsername.setText(email.substring(0, email.indexOf('@')));
+ }
+ }
+
+ @Override
+ public Loader onCreateLoader(int id, Bundle args) {
+ switch (id) {
+ case R.id.loader_id_check_email_settings:
+ UIUtil.exchangeViewVisibility(this, true, progressView, contentView);
+
+ authCredentials = generateAuthCredentials();
+ return new CheckEmailSettingsAsyncTaskLoader(this, authCredentials);
+
+ case R.id.loader_id_load_private_key_backups_from_email:
+ UIUtil.exchangeViewVisibility(this, true, progressView, contentView);
+ AccountDao accountDao = new AccountDao(authCredentials.getEmail(), null
+ , null, null, null, null, authCredentials);
+ return new LoadPrivateKeysFromMailAsyncTaskLoader(this, accountDao);
+
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public void onLoadFinished(Loader loader, LoaderResult loaderResult) {
+ handleLoaderResult(loader, loaderResult);
+ }
+
+ @Override
+ public void onLoaderReset(Loader loader) {
+
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void handleSuccessLoaderResult(int loaderId, Object result) {
+ switch (loaderId) {
+ case R.id.loader_id_check_email_settings:
+ boolean isSettingsValid = (boolean) result;
+ if (isSettingsValid) {
+ getSupportLoaderManager().restartLoader(R.id.loader_id_load_private_key_backups_from_email,
+ null, this);
+ } else {
+ UIUtil.exchangeViewVisibility(this, false, progressView, contentView);
+ showInfoSnackbar(getRootView(), getString(R.string.settings_not_valid), Snackbar.LENGTH_LONG);
+ }
+ break;
+
+ case R.id.loader_id_load_private_key_backups_from_email:
+ ArrayList keyDetailsList = (ArrayList) result;
+ if (keyDetailsList.isEmpty()) {
+ AccountDao accountDao = new AccountDao(authCredentials.getEmail(),
+ null, null, null, null, null, authCredentials);
+ startActivityForResult(CreateOrImportKeyActivity.newIntent(this, accountDao, true),
+ REQUEST_CODE_ADD_NEW_ACCOUNT);
+ UIUtil.exchangeViewVisibility(this, false, progressView, contentView);
+ } else {
+ startActivityForResult(CheckKeysActivity.newIntent(this,
+ keyDetailsList,
+ getString(R.string.found_backup_of_your_account_key),
+ getString(R.string.continue_),
+ SecurityUtils.isBackupKeysExist(this) ? getString(R.string.use_existing_keys) : null,
+ getString(R.string.use_another_account), true),
+ REQUEST_CODE_CHECK_PRIVATE_KEYS_FROM_EMAIL);
+ }
+ break;
+
+ default:
+ super.handleSuccessLoaderResult(loaderId, result);
+ break;
+ }
+ }
+
+ @Override
+ public void handleFailureLoaderResult(int loaderId, Exception e) {
+ switch (loaderId) {
+ case R.id.loader_id_check_email_settings:
+ case R.id.loader_id_load_private_key_backups_from_email:
+ UIUtil.exchangeViewVisibility(this, false, progressView, contentView);
+ showInfoSnackbar(getRootView(), e != null && !TextUtils.isEmpty(e.getMessage()) ? e.getMessage()
+ : getString(R.string.unknown_error), Snackbar.LENGTH_LONG);
+ break;
+
+ default:
+ super.handleFailureLoaderResult(loaderId, e);
+ break;
+ }
+ }
+
+ /**
+ * Return the {@link Activity#RESULT_OK} to the initiator-activity.
+ */
+ private void returnOkResult() {
+ authCredentials = generateAuthCredentials();
+ Intent intent = new Intent();
+ intent.putExtra(KEY_EXTRA_AUTH_CREDENTIALS, authCredentials);
+ setResult(Activity.RESULT_OK, intent);
+ finish();
+ }
+
+ /**
+ * Check that current email is not duplicate and not added yet.
+ *
+ * @return true if email not added yet, otherwise false.
+ */
+ private boolean isNotDuplicate() {
+ return new AccountDaoSource().getAccountInformation(this, authCredentials.getEmail()) == null;
+ }
+
+ private void initViews(Bundle savedInstanceState) {
+ editTextEmail = findViewById(R.id.editTextEmail);
+ editTextUserName = findViewById(R.id.editTextUserName);
+ editTextPassword = findViewById(R.id.editTextPassword);
+ editTextImapServer = findViewById(R.id.editTextImapServer);
+ editTextImapPort = findViewById(R.id.editTextImapPort);
+ editTextSmtpServer = findViewById(R.id.editTextSmtpServer);
+ editTextSmtpPort = findViewById(R.id.editTextSmtpPort);
+ editTextSmtpUsername = findViewById(R.id.editTextSmtpUsername);
+ editTextSmtpPassword = findViewById(R.id.editTextSmtpPassword);
+
+ editTextEmail.addTextChangedListener(this);
+
+ layoutSmtpSignIn = findViewById(R.id.layoutSmtpSignIn);
+ progressView = findViewById(R.id.progressView);
+ contentView = findViewById(R.id.layoutContent);
+ checkBoxRequireSignInForSmtp = findViewById(R.id.checkBoxRequireSignInForSmtp);
+ checkBoxRequireSignInForSmtp.setOnCheckedChangeListener(this);
+
+ spinnerImapSecyrityType = findViewById(R.id.spinnerImapSecurityType);
+ spinnerSmtpSecyrityType = findViewById(R.id.spinnerSmtpSecyrityType);
+
+ ArrayAdapter userAdapter = new ArrayAdapter<>(this,
+ android.R.layout.simple_spinner_dropdown_item, SecurityType.generateAvailableSecurityTypes(this));
+
+ spinnerImapSecyrityType.setAdapter(userAdapter);
+ spinnerSmtpSecyrityType.setAdapter(userAdapter);
+
+ spinnerImapSecyrityType.setOnItemSelectedListener(this);
+ spinnerSmtpSecyrityType.setOnItemSelectedListener(this);
+
+ if (findViewById(R.id.buttonTryToConnect) != null) {
+ findViewById(R.id.buttonTryToConnect).setOnClickListener(this);
+ }
+
+ if (savedInstanceState == null) {
+ updateView();
+ }
+ }
+
+ /**
+ * Update the current views if {@link AuthCredentials} not null.l
+ */
+ private void updateView() {
+ if (authCredentials != null) {
+
+ editTextEmail.setText(authCredentials.getEmail());
+ editTextUserName.setText(authCredentials.getUsername());
+ editTextImapServer.setText(authCredentials.getImapServer());
+ editTextImapPort.setText(String.valueOf(authCredentials.getImapPort()));
+ editTextSmtpServer.setText(authCredentials.getSmtpServer());
+ editTextSmtpPort.setText(String.valueOf(authCredentials.getSmtpPort()));
+ checkBoxRequireSignInForSmtp.setChecked(authCredentials.isUseCustomSignInForSmtp());
+ editTextSmtpUsername.setText(authCredentials.getSmtpSigInUsername());
+
+ int imapOptionsCount = spinnerImapSecyrityType.getAdapter().getCount();
+ for (int i = 0; i < imapOptionsCount; i++) {
+ if (authCredentials.getImapSecurityTypeOption() ==
+ ((SecurityType) spinnerImapSecyrityType.getAdapter().getItem(i)).getOption()) {
+ spinnerImapSecyrityType.setSelection(i);
+ }
+ }
+
+ int smtpOptionsCount = spinnerSmtpSecyrityType.getAdapter().getCount();
+ for (int i = 0; i < smtpOptionsCount; i++) {
+ if (authCredentials.getSmtpSecurityTypeOption() ==
+ ((SecurityType) spinnerSmtpSecyrityType.getAdapter().getItem(i)).getOption()) {
+ spinnerSmtpSecyrityType.setSelection(i);
+ }
+ }
+ }
+ }
+
+ /**
+ * Save the current {@link AuthCredentials} to the shared preferences.
+ */
+ private void saveTempCredentialsToPreferences() {
+ authCredentials = generateAuthCredentials();
+ Gson gson = new Gson();
+ authCredentials.setPassword(null);
+ authCredentials.setSmtpSignInPassword(null);
+ SharedPreferencesHelper.setString(PreferenceManager.getDefaultSharedPreferences(this),
+ Constants.PREFERENCES_KEY_TEMP_LAST_AUTH_CREDENTIALS, gson.toJson(authCredentials));
+ }
+
+ /**
+ * Retrieve a temp {@link AuthCredentials} from the shared preferences.
+ */
+ private AuthCredentials getTempAuthCredentialsFromPreferences() {
+ String authCredentialsJson = SharedPreferencesHelper.getString(PreferenceManager.getDefaultSharedPreferences
+ (this), Constants.PREFERENCES_KEY_TEMP_LAST_AUTH_CREDENTIALS, "");
+
+ if (!TextUtils.isEmpty(authCredentialsJson)) {
+ try {
+ return new Gson().fromJson(authCredentialsJson, AuthCredentials.class);
+ } catch (JsonSyntaxException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Generate the {@link AuthCredentials} using user input.
+ *
+ * @return {@link AuthCredentials}.
+ */
+ private AuthCredentials generateAuthCredentials() {
+ return new AuthCredentials.Builder().setEmail(editTextEmail.getText().toString())
+ .setUsername(editTextUserName.getText().toString())
+ .setPassword(editTextPassword.getText().toString())
+ .setImapServer(editTextImapServer.getText().toString())
+ .setImapPort(Integer.parseInt(editTextImapPort.getText().toString()))
+ .setImapSecurityTypeOption(((SecurityType) spinnerImapSecyrityType.getSelectedItem()).getOption())
+ .setSmtpServer(editTextSmtpServer.getText().toString())
+ .setSmtpPort(Integer.parseInt(editTextSmtpPort.getText().toString()))
+ .setSmtpSecurityTypeOption(((SecurityType) spinnerSmtpSecyrityType.getSelectedItem()).getOption())
+ .setIsUseCustomSignInForSmtp(checkBoxRequireSignInForSmtp.isChecked())
+ .setSmtpSigInUsername(editTextSmtpUsername.getText().toString())
+ .setSmtpSignInPassword(editTextSmtpPassword.getText().toString())
+ .build();
+ }
+
+ /**
+ * Do a lot of checks to validate an outgoing message info.
+ *
+ * @return Boolean true if all information is correct, false otherwise.
+ */
+ private boolean isAllInformationCorrect() {
+ if (TextUtils.isEmpty(editTextEmail.getText())) {
+ showInfoSnackbar(editTextEmail, getString(R.string.text_must_not_be_empty, getString(R.string.e_mail)));
+ editTextEmail.requestFocus();
+ } else if (GeneralUtil.isEmailValid(editTextEmail.getText())) {
+ if (TextUtils.isEmpty(editTextUserName.getText())) {
+ showInfoSnackbar(editTextUserName, getString(R.string.text_must_not_be_empty,
+ getString(R.string.username)));
+ editTextUserName.requestFocus();
+ } else if (TextUtils.isEmpty(editTextPassword.getText())) {
+ showInfoSnackbar(editTextPassword, getString(R.string.text_must_not_be_empty,
+ getString(R.string.password)));
+ editTextPassword.requestFocus();
+ } else if (TextUtils.isEmpty(editTextImapServer.getText())) {
+ showInfoSnackbar(editTextImapServer, getString(R.string.text_must_not_be_empty,
+ getString(R.string.imap_server)));
+ editTextImapServer.requestFocus();
+ } else if (TextUtils.isEmpty(editTextImapPort.getText())) {
+ showInfoSnackbar(editTextImapPort, getString(R.string.text_must_not_be_empty,
+ getString(R.string.imap_port)));
+ editTextImapPort.requestFocus();
+ } else if (TextUtils.isEmpty(editTextSmtpServer.getText())) {
+ showInfoSnackbar(editTextSmtpServer, getString(R.string.text_must_not_be_empty,
+ getString(R.string.smtp_server)));
+ editTextSmtpServer.requestFocus();
+ } else if (TextUtils.isEmpty(editTextSmtpPort.getText())) {
+ showInfoSnackbar(editTextSmtpPort, getString(R.string.text_must_not_be_empty,
+ getString(R.string.smtp_port)));
+ editTextSmtpPort.requestFocus();
+ } else if (checkBoxRequireSignInForSmtp.isChecked()) {
+ if (TextUtils.isEmpty(editTextSmtpUsername.getText())) {
+ showInfoSnackbar(editTextSmtpUsername, getString(R.string.text_must_not_be_empty,
+ getString(R.string.smtp_username)));
+ editTextSmtpUsername.requestFocus();
+ } else if (TextUtils.isEmpty(editTextSmtpPassword.getText())) {
+ showInfoSnackbar(editTextSmtpPassword, getString(R.string.text_must_not_be_empty,
+ getString(R.string.smtp_password)));
+ editTextSmtpPassword.requestFocus();
+ } else return true;
+ } else return true;
+ } else {
+ showInfoSnackbar(editTextEmail, getString(R.string.error_email_is_not_valid));
+ editTextEmail.requestFocus();
+ }
+
+ return false;
+ }
+}
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/CheckKeysActivity.java b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/CheckKeysActivity.java
index 377840c055..716e527529 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/CheckKeysActivity.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/CheckKeysActivity.java
@@ -45,16 +45,19 @@ public class CheckKeysActivity extends BaseActivity implements View.OnClickListe
LoaderManager.LoaderCallbacks {
public static final int RESULT_NEGATIVE = 10;
+ public static final int RESULT_NEUTRAL = 11;
public static final String KEY_EXTRA_PRIVATE_KEYS = GeneralUtil.generateUniqueExtraKey(
"KEY_EXTRA_PRIVATE_KEYS", CheckKeysActivity.class);
public static final String KEY_EXTRA_BOTTOM_TITLE = GeneralUtil.generateUniqueExtraKey(
"KEY_EXTRA_BOTTOM_TITLE", CheckKeysActivity.class);
- public static final String KEY_EXTRA_CHECK_BUTTON_TITLE = GeneralUtil.generateUniqueExtraKey(
- "KEY_EXTRA_CHECK_BUTTON_TITLE", CheckKeysActivity.class);
- public static final String KEY_EXTRA_NEGATIVE_ACTION_BUTTON_TITLE =
+ public static final String KEY_EXTRA_POSITIVE_BUTTON_TITLE = GeneralUtil.generateUniqueExtraKey(
+ "KEY_EXTRA_POSITIVE_BUTTON_TITLE", CheckKeysActivity.class);
+ public static final String KEY_EXTRA_NEUTRAL_BUTTON_TITLE = GeneralUtil.generateUniqueExtraKey(
+ "KEY_EXTRA_NEUTRAL_BUTTON_TITLE", CheckKeysActivity.class);
+ public static final String KEY_EXTRA_NEGATIVE_BUTTON_TITLE =
GeneralUtil.generateUniqueExtraKey(
- "KEY_EXTRA_NEGATIVE_ACTION_BUTTON_TITLE", CheckKeysActivity.class);
+ "KEY_EXTRA_NEGATIVE_BUTTON_TITLE", CheckKeysActivity.class);
public static final String KEY_EXTRA_IS_THROW_ERROR_IF_DUPLICATE_FOUND =
GeneralUtil.generateUniqueExtraKey(
"KEY_EXTRA_IS_THROW_ERROR_IF_DUPLICATE_FOUND", CheckKeysActivity.class);
@@ -63,19 +66,29 @@ public class CheckKeysActivity extends BaseActivity implements View.OnClickListe
private EditText editTextKeyPassword;
private View progressBar;
private String bottomTitle;
- private String checkButtonTitle;
- private String anotherAccountButtonTitle;
+ private String positiveButtonTitle;
+ private String neutralButtonTitle;
+ private String negativeButtonTitle;
private boolean isThrowErrorIfDuplicateFound;
public static Intent newIntent(Context context, ArrayList privateKeys,
- String bottomTitle, String checkButtonTitle,
- String negativeActionButtonTitle,
+ String bottomTitle, String positiveButtonTitle,
+ String negativeButtonTitle,
+ boolean isThrowErrorIfDuplicateFound) {
+ return newIntent(context, privateKeys, bottomTitle, positiveButtonTitle, null, negativeButtonTitle,
+ isThrowErrorIfDuplicateFound);
+ }
+
+ public static Intent newIntent(Context context, ArrayList privateKeys,
+ String bottomTitle, String positiveButtonTitle,
+ String neutralButtonTitle, String negativeButtonTitle,
boolean isThrowErrorIfDuplicateFound) {
Intent intent = new Intent(context, CheckKeysActivity.class);
intent.putExtra(KEY_EXTRA_PRIVATE_KEYS, privateKeys);
intent.putExtra(KEY_EXTRA_BOTTOM_TITLE, bottomTitle);
- intent.putExtra(KEY_EXTRA_CHECK_BUTTON_TITLE, checkButtonTitle);
- intent.putExtra(KEY_EXTRA_NEGATIVE_ACTION_BUTTON_TITLE, negativeActionButtonTitle);
+ intent.putExtra(KEY_EXTRA_POSITIVE_BUTTON_TITLE, positiveButtonTitle);
+ intent.putExtra(KEY_EXTRA_NEUTRAL_BUTTON_TITLE, neutralButtonTitle);
+ intent.putExtra(KEY_EXTRA_NEGATIVE_BUTTON_TITLE, negativeButtonTitle);
intent.putExtra(KEY_EXTRA_IS_THROW_ERROR_IF_DUPLICATE_FOUND, isThrowErrorIfDuplicateFound);
return intent;
}
@@ -99,14 +112,13 @@ public View getRootView() {
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getIntent() != null) {
- this.privateKeyDetailsList = getIntent().getParcelableArrayListExtra
- (KEY_EXTRA_PRIVATE_KEYS);
+ this.privateKeyDetailsList = getIntent().getParcelableArrayListExtra(KEY_EXTRA_PRIVATE_KEYS);
this.bottomTitle = getIntent().getStringExtra(KEY_EXTRA_BOTTOM_TITLE);
- this.checkButtonTitle = getIntent().getStringExtra(KEY_EXTRA_CHECK_BUTTON_TITLE);
- this.anotherAccountButtonTitle = getIntent().getStringExtra
- (KEY_EXTRA_NEGATIVE_ACTION_BUTTON_TITLE);
- this.isThrowErrorIfDuplicateFound = getIntent().getBooleanExtra(
- KEY_EXTRA_IS_THROW_ERROR_IF_DUPLICATE_FOUND, false);
+ this.positiveButtonTitle = getIntent().getStringExtra(KEY_EXTRA_POSITIVE_BUTTON_TITLE);
+ this.neutralButtonTitle = getIntent().getStringExtra(KEY_EXTRA_NEUTRAL_BUTTON_TITLE);
+ this.negativeButtonTitle = getIntent().getStringExtra(KEY_EXTRA_NEGATIVE_BUTTON_TITLE);
+ this.isThrowErrorIfDuplicateFound = getIntent().getBooleanExtra
+ (KEY_EXTRA_IS_THROW_ERROR_IF_DUPLICATE_FOUND, false);
}
initViews();
@@ -115,12 +127,11 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
@Override
public void onClick(View v) {
switch (v.getId()) {
- case R.id.buttonCheck:
+ case R.id.buttonPositiveAction:
UIUtil.hideSoftInput(this, editTextKeyPassword);
if (privateKeyDetailsList != null && !privateKeyDetailsList.isEmpty()) {
if (TextUtils.isEmpty(editTextKeyPassword.getText().toString())) {
- showInfoSnackbar(editTextKeyPassword,
- getString(R.string.passphrase_must_be_non_empty));
+ showInfoSnackbar(editTextKeyPassword, getString(R.string.passphrase_must_be_non_empty));
} else {
if (getSnackBar() != null) {
getSnackBar().dismiss();
@@ -132,6 +143,11 @@ public void onClick(View v) {
}
break;
+ case R.id.buttonNeutralAction:
+ setResult(RESULT_NEUTRAL);
+ finish();
+ break;
+
case R.id.buttonNegativeAction:
setResult(RESULT_NEGATIVE);
finish();
@@ -183,33 +199,38 @@ public void handleSuccessLoaderResult(int loaderId, Object result) {
setResult(Activity.RESULT_OK);
finish();
} else {
- showInfoSnackbar(getRootView(), getString(R.string
- .password_is_incorrect));
+ showInfoSnackbar(getRootView(), getString(R.string.password_is_incorrect));
}
break;
}
}
private void initViews() {
- if (findViewById(R.id.buttonCheck) != null) {
- Button buttonCheck = (Button) findViewById(R.id.buttonCheck);
- buttonCheck.setText(checkButtonTitle);
- buttonCheck.setOnClickListener(this);
+ if (findViewById(R.id.buttonPositiveAction) != null) {
+ Button buttonPositiveAction = findViewById(R.id.buttonPositiveAction);
+ buttonPositiveAction.setText(positiveButtonTitle);
+ buttonPositiveAction.setOnClickListener(this);
+ }
+
+ if (!TextUtils.isEmpty(neutralButtonTitle) && findViewById(R.id.buttonNeutralAction) != null) {
+ Button buttonNeutralAction = findViewById(R.id.buttonNeutralAction);
+ buttonNeutralAction.setVisibility(View.VISIBLE);
+ buttonNeutralAction.setText(neutralButtonTitle);
+ buttonNeutralAction.setOnClickListener(this);
}
if (findViewById(R.id.buttonNegativeAction) != null) {
- Button buttonSelectAnotherAccount =
- (Button) findViewById(R.id.buttonNegativeAction);
- buttonSelectAnotherAccount.setText(anotherAccountButtonTitle);
- buttonSelectAnotherAccount.setOnClickListener(this);
+ Button buttonNegativeAction = findViewById(R.id.buttonNegativeAction);
+ buttonNegativeAction.setText(negativeButtonTitle);
+ buttonNegativeAction.setOnClickListener(this);
}
- TextView textViewCheckKeysTitle = (TextView) findViewById(R.id.textViewCheckKeysTitle);
+ TextView textViewCheckKeysTitle = findViewById(R.id.textViewCheckKeysTitle);
if (textViewCheckKeysTitle != null) {
textViewCheckKeysTitle.setText(bottomTitle);
}
- editTextKeyPassword = (EditText) findViewById(R.id.editTextKeyPassword);
+ editTextKeyPassword = findViewById(R.id.editTextKeyPassword);
progressBar = findViewById(R.id.progressBar);
}
}
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/CreateOrImportKeyActivity.java b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/CreateOrImportKeyActivity.java
index 6972846e67..69ef04189e 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/CreateOrImportKeyActivity.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/CreateOrImportKeyActivity.java
@@ -30,12 +30,14 @@
*/
public class CreateOrImportKeyActivity extends BaseCheckClipboardBackStackActivity implements
View.OnClickListener {
- public static final int REQUEST_CODE_IMPORT_ACTIVITY = 10;
+ public static final int RESULT_CODE_USE_ANOTHER_ACCOUNT = 10;
+ public static final String EXTRA_KEY_ACCOUNT_DAO = GeneralUtil.generateUniqueExtraKey
+ ("EXTRA_KEY_ACCOUNT_DAO", CreateOrImportKeyActivity.class);
+
+ private static final int REQUEST_CODE_IMPORT_ACTIVITY = 10;
private static final String KEY_IS_SHOW_USE_ANOTHER_ACCOUNT_BUTTON =
GeneralUtil.generateUniqueExtraKey("KEY_IS_SHOW_USE_ANOTHER_ACCOUNT_BUTTON",
CreateOrImportKeyActivity.class);
- private static final String EXTRA_KEY_ACCOUNT_DAO = GeneralUtil.generateUniqueExtraKey
- ("EXTRA_KEY_ACCOUNT_DAO", CreateOrImportKeyActivity.class);
private boolean isShowAnotherAccountButton = true;
private AccountDao accountDao;
@@ -93,8 +95,10 @@ public void onClick(View v) {
break;
case R.id.buttonSelectAnotherAccount:
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_KEY_ACCOUNT_DAO, accountDao);
+ setResult(RESULT_CODE_USE_ANOTHER_ACCOUNT, intent);
finish();
- startActivity(SplashActivity.getSignOutIntent(this));
break;
case R.id.buttonSkipSetup:
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/EmailManagerActivity.java b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/EmailManagerActivity.java
index ccb23d8488..8809c210e7 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/EmailManagerActivity.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/EmailManagerActivity.java
@@ -11,9 +11,11 @@
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
+import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.design.widget.NavigationView;
import android.support.v4.app.LoaderManager;
@@ -24,10 +26,13 @@
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.TextView;
import com.bumptech.glide.Glide;
@@ -38,7 +43,9 @@
import com.flowcrypt.email.api.email.FoldersManager;
import com.flowcrypt.email.api.email.sync.SyncErrorTypes;
import com.flowcrypt.email.database.dao.source.AccountDao;
+import com.flowcrypt.email.database.dao.source.AccountDaoSource;
import com.flowcrypt.email.database.dao.source.imap.ImapLabelsDaoSource;
+import com.flowcrypt.email.database.provider.FlowcryptContract;
import com.flowcrypt.email.model.MessageEncryptionType;
import com.flowcrypt.email.service.CheckClipboardToFindPrivateKeyService;
import com.flowcrypt.email.service.EmailSyncService;
@@ -46,7 +53,13 @@
import com.flowcrypt.email.ui.activity.fragment.EmailListFragment;
import com.flowcrypt.email.ui.activity.settings.SettingsActivity;
import com.flowcrypt.email.util.GeneralUtil;
+import com.flowcrypt.email.util.UIUtil;
+import com.flowcrypt.email.util.google.GoogleApiClientHelper;
import com.flowcrypt.email.util.graphics.glide.transformations.CircleTransformation;
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+
+import java.util.List;
/**
* This activity used to show messages list.
@@ -58,10 +71,12 @@
*/
public class EmailManagerActivity extends BaseSyncActivity
implements NavigationView.OnNavigationItemSelectedListener, LoaderManager.LoaderCallbacks,
- View.OnClickListener, EmailListFragment.OnManageEmailsListener {
+ View.OnClickListener, EmailListFragment.OnManageEmailsListener, GoogleApiClient.OnConnectionFailedListener,
+ GoogleApiClient.ConnectionCallbacks {
public static final String EXTRA_KEY_ACCOUNT_DAO = GeneralUtil.generateUniqueExtraKey(
"EXTRA_KEY_ACCOUNT_DAO", EmailManagerActivity.class);
+ public static final int REQUEST_CODE_ADD_NEW_ACCOUNT = 100;
private DrawerLayout drawerLayout;
private ActionBarDrawerToggle actionBarDrawerToggle;
@@ -69,6 +84,8 @@ public class EmailManagerActivity extends BaseSyncActivity
private AccountDao accountDao;
private FoldersManager foldersManager;
private Folder folder;
+ private LinearLayout accountManagementLayout;
+ private GoogleApiClient googleApiClient;
public EmailManagerActivity() {
this.foldersManager = new FoldersManager();
@@ -91,6 +108,9 @@ public static void runEmailManagerActivity(Context context, AccountDao accountDa
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ googleApiClient = GoogleApiClientHelper.generateGoogleApiClient(this, this, this, this, GoogleApiClientHelper
+ .generateGoogleSignInOptions());
+
if (getIntent() != null) {
accountDao = getIntent().getParcelableExtra(EXTRA_KEY_ACCOUNT_DAO);
if (accountDao == null) {
@@ -113,6 +133,27 @@ public void onDestroy() {
}
}
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_CODE_ADD_NEW_ACCOUNT:
+ switch (resultCode) {
+ case RESULT_OK:
+ EmailSyncService.switchAccount(EmailManagerActivity.this);
+ AccountDao accountDao = data.getParcelableExtra(AddNewAccountActivity.KEY_EXTRA_NEW_ACCOUNT);
+ if (accountDao != null) {
+ runEmailManagerActivity(EmailManagerActivity.this, accountDao);
+ finish();
+ }
+ break;
+ }
+ break;
+
+ default:
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
@Override
public void onReplyFromSyncServiceReceived(int requestCode, int resultCode, Object obj) {
switch (requestCode) {
@@ -176,13 +217,7 @@ public void onBackPressed() {
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigationMenuLogOut:
- finish();
- startActivity(SplashActivity.getSignOutIntent(this));
- break;
-
- case R.id.navigationMenuRevokeAccess:
- finish();
- startActivity(SplashActivity.getRevokeAccessIntent(this));
+ logout();
break;
case R.id.navigationMenuActionSettings:
@@ -270,6 +305,10 @@ public void onClick(View v) {
startActivity(CreateMessageActivity.generateIntent(this, accountDao.getEmail(), null,
MessageEncryptionType.ENCRYPTED));
break;
+
+ case R.id.viewIdAddNewAccount:
+ startActivityForResult(new Intent(this, AddNewAccountActivity.class), REQUEST_CODE_ADD_NEW_ACCOUNT);
+ break;
}
}
@@ -283,6 +322,51 @@ public Folder getCurrentFolder() {
return folder;
}
+ @Override
+ public void onConnected(@Nullable Bundle bundle) {
+
+ }
+
+ @Override
+ public void onConnectionSuspended(int i) {
+
+ }
+
+ @Override
+ public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
+ UIUtil.showInfoSnackbar(getRootView(), connectionResult.getErrorMessage());
+ }
+
+ private void logout() {
+ AccountDaoSource accountDaoSource = new AccountDaoSource();
+ List accountDaoList = accountDaoSource.getAccountsWithoutActive(this, accountDao.getEmail());
+
+ switch (accountDao.getAccountType()) {
+ case AccountDao.ACCOUNT_TYPE_GOOGLE:
+ GoogleApiClientHelper.signOutFromGoogleAccount(this, googleApiClient);
+ break;
+ }
+
+ if (accountDao != null) {
+ getContentResolver().delete(Uri.parse(FlowcryptContract.AUTHORITY_URI + "/"
+ + FlowcryptContract.CLEAN_DATABASE), null, new String[]{accountDao.getEmail()});
+ }
+
+ if (accountDaoList != null && !accountDaoList.isEmpty()) {
+ AccountDao newActiveAccount = accountDaoList.get(0);
+ new AccountDaoSource().setActiveAccount(EmailManagerActivity.this, newActiveAccount.getEmail());
+ EmailSyncService.switchAccount(EmailManagerActivity.this);
+ runEmailManagerActivity(EmailManagerActivity.this, newActiveAccount);
+ } else {
+ stopService(new Intent(this, EmailSyncService.class));
+ Intent intent = new Intent(this, SplashActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ }
+
+ finish();
+ }
+
/**
* Handle an error from the sync service.
*
@@ -340,18 +424,16 @@ private void updateEmailsListFragmentAfterFolderChange() {
}
private void initViews() {
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
- actionBarDrawerToggle = new CustomDrawerToggle(this, drawerLayout, toolbar,
+ drawerLayout = findViewById(R.id.drawer_layout);
+ actionBarDrawerToggle = new CustomDrawerToggle(this, drawerLayout, getToolbar(),
R.string.navigation_drawer_open,
R.string.navigation_drawer_close);
drawerLayout.addDrawerListener(actionBarDrawerToggle);
actionBarDrawerToggle.syncState();
- navigationView = (NavigationView) findViewById(R.id.navigationView);
+ navigationView = findViewById(R.id.navigationView);
navigationView.setNavigationItemSelectedListener(this);
+ navigationView.addHeaderView(generateAccountManagementLayout());
MenuItem navigationMenuDevSettings = navigationView.getMenu().findItem(R.id.navigationMenuDevSettings);
if (navigationMenuDevSettings != null) {
@@ -371,12 +453,16 @@ private void initViews() {
* @param view The view which contains user profile views.
*/
private void initUserProfileView(View view) {
- ImageView imageViewUserPhoto = (ImageView) view.findViewById(R.id.imageViewUserPhoto);
- TextView textViewUserDisplayName = (TextView) view.findViewById(R.id.textViewUserDisplayName);
- TextView textViewUserEmail = (TextView) view.findViewById(R.id.textViewUserEmail);
+ ImageView imageViewUserPhoto = view.findViewById(R.id.imageViewUserPhoto);
+ TextView textViewUserDisplayName = view.findViewById(R.id.textViewUserDisplayName);
+ TextView textViewUserEmail = view.findViewById(R.id.textViewUserEmail);
if (accountDao != null) {
- textViewUserDisplayName.setText(accountDao.getDisplayName());
+ if (TextUtils.isEmpty(accountDao.getDisplayName())) {
+ textViewUserDisplayName.setVisibility(View.GONE);
+ } else {
+ textViewUserDisplayName.setText(accountDao.getDisplayName());
+ }
textViewUserEmail.setText(accountDao.getEmail());
if (!TextUtils.isEmpty(accountDao.getPhotoUrl())) {
@@ -392,6 +478,103 @@ private void initUserProfileView(View view) {
}
}
+ View currentAccountDetailsItem = view.findViewById(R.id.layoutUserDetails);
+ final ImageView imageViewExpandAccountManagement = view.findViewById(R.id.imageViewExpandAccountManagement);
+ if (currentAccountDetailsItem != null) {
+ handleClickOnAccountManagementButton(currentAccountDetailsItem, imageViewExpandAccountManagement);
+ }
+ }
+
+ private void handleClickOnAccountManagementButton(View currentAccountDetailsItem, final ImageView imageView) {
+ currentAccountDetailsItem.setOnClickListener(new View.OnClickListener() {
+ private boolean isExpanded;
+
+ @Override
+ public void onClick(View v) {
+ if (isExpanded) {
+ imageView.setImageResource(R.mipmap.ic_arrow_drop_down);
+ navigationView.getMenu().setGroupVisible(0, true);
+ accountManagementLayout.setVisibility(View.GONE);
+ } else {
+ imageView.setImageResource(R.mipmap.ic_arrow_drop_up);
+ navigationView.getMenu().setGroupVisible(0, false);
+ accountManagementLayout.setVisibility(View.VISIBLE);
+ }
+
+ isExpanded = !isExpanded;
+ }
+ });
+ }
+
+ /**
+ * Generate view which contains information about added accounts and using him we can add a new one.
+ *
+ * @return The generated view.
+ */
+ private ViewGroup generateAccountManagementLayout() {
+ accountManagementLayout = new LinearLayout(this);
+ accountManagementLayout.setOrientation(LinearLayout.VERTICAL);
+ accountManagementLayout.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+ accountManagementLayout.setVisibility(View.GONE);
+
+ List accountDaoList = new AccountDaoSource().getAccountsWithoutActive(this, accountDao.getEmail());
+ for (final AccountDao accountDao : accountDaoList) {
+ accountManagementLayout.addView(generateAccountItemView(accountDao));
+ }
+
+ View addNewAccountView = LayoutInflater.from(this).inflate(R.layout.add_account,
+ accountManagementLayout, false);
+ addNewAccountView.setId(R.id.viewIdAddNewAccount);
+ addNewAccountView.setOnClickListener(this);
+ accountManagementLayout.addView(addNewAccountView);
+
+ return accountManagementLayout;
+ }
+
+ private View generateAccountItemView(final AccountDao accountDao) {
+ View accountItemView = LayoutInflater.from(this).inflate(R.layout.nav_menu_account_item,
+ accountManagementLayout, false);
+ accountItemView.setTag(accountDao);
+
+ ImageView imageViewUserPhoto = accountItemView.findViewById(R.id.imageViewUserPhoto);
+ TextView textViewUserDisplayName = accountItemView.findViewById(R.id.textViewUserDisplayName);
+ TextView textViewUserEmail = accountItemView.findViewById(R.id.textViewUserEmail);
+
+ if (accountDao != null) {
+ if (TextUtils.isEmpty(accountDao.getDisplayName())) {
+ textViewUserDisplayName.setVisibility(View.GONE);
+ } else {
+ textViewUserDisplayName.setText(accountDao.getDisplayName());
+ }
+ textViewUserEmail.setText(accountDao.getEmail());
+
+ if (!TextUtils.isEmpty(accountDao.getPhotoUrl())) {
+ RequestOptions requestOptions = new RequestOptions();
+ requestOptions.centerCrop();
+ requestOptions.transform(new CircleTransformation());
+ requestOptions.error(R.mipmap.ic_account_default_photo);
+
+ Glide.with(this)
+ .load(accountDao.getPhotoUrl())
+ .apply(requestOptions)
+ .into(imageViewUserPhoto);
+ }
+ }
+
+ accountItemView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ if (accountDao != null) {
+ new AccountDaoSource().setActiveAccount(EmailManagerActivity.this, accountDao.getEmail());
+ EmailSyncService.switchAccount(EmailManagerActivity.this);
+ runEmailManagerActivity(EmailManagerActivity.this, accountDao);
+ }
+ }
+ });
+
+ return accountItemView;
}
/**
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/SplashActivity.java b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/SplashActivity.java
index 8a96173889..b13b105cda 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/SplashActivity.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/SplashActivity.java
@@ -7,46 +7,35 @@
package com.flowcrypt.email.ui.activity;
import android.app.Activity;
-import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;
-import com.flowcrypt.email.Constants;
import com.flowcrypt.email.R;
+import com.flowcrypt.email.api.email.model.AuthCredentials;
import com.flowcrypt.email.database.dao.source.AccountDao;
import com.flowcrypt.email.database.dao.source.AccountDaoSource;
import com.flowcrypt.email.database.provider.FlowcryptContract;
import com.flowcrypt.email.model.KeyDetails;
-import com.flowcrypt.email.model.SignInType;
import com.flowcrypt.email.model.results.LoaderResult;
import com.flowcrypt.email.security.SecurityUtils;
import com.flowcrypt.email.service.CheckClipboardToFindPrivateKeyService;
import com.flowcrypt.email.service.EmailSyncService;
-import com.flowcrypt.email.ui.activity.base.BaseActivity;
-import com.flowcrypt.email.ui.activity.fragment.SplashActivityFragment;
+import com.flowcrypt.email.ui.activity.base.BaseSignInActivity;
import com.flowcrypt.email.ui.loader.LoadPrivateKeysFromMailAsyncTaskLoader;
-import com.flowcrypt.email.util.GeneralUtil;
import com.flowcrypt.email.util.UIUtil;
import com.google.android.gms.auth.api.Auth;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
-import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.auth.api.signin.GoogleSignInResult;
-import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.common.api.OptionalPendingResult;
-import com.google.android.gms.common.api.ResultCallback;
-import com.google.android.gms.common.api.Scope;
-import com.google.android.gms.common.api.Status;
-import java.io.IOException;
+import org.acra.ACRA;
+
import java.util.ArrayList;
/**
@@ -57,53 +46,32 @@
* Time: 14:50
* E-mail: DenBond7@gmail.com
*/
-public class SplashActivity extends BaseActivity implements SplashActivityFragment.OnSignInButtonClickListener,
+public class SplashActivity extends BaseSignInActivity implements
GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks,
LoaderManager.LoaderCallbacks {
- private static final String ACTION_SIGN_OUT = GeneralUtil.generateUniqueExtraKey
- ("ACTION_SIGN_OUT", SplashActivity.class);
- private static final String ACTION_REVOKE_ACCESS = GeneralUtil.generateUniqueExtraKey
- ("ACTION_REVOKE_ACCESS", SplashActivity.class);
- private static final int REQUEST_CODE_SIGN_IN = 100;
private static final int REQUEST_CODE_CHECK_PRIVATE_KEYS_FROM_GMAIL = 101;
private static final int REQUEST_CODE_CREATE_OR_IMPORT_KEY = 102;
- /**
- * The main entry point for Google Play services integration.
- */
- private GoogleApiClient googleApiClient;
private View signInView;
private View splashView;
- private boolean isSignOutAction;
- private boolean isRevokeAccessAction;
private AccountDao accountDao;
+ private GoogleSignInAccount currentGoogleSignInAccount;
- /**
- * Generate the sign out intent.
- *
- * @param context Interface to global information about an application environment.
- * @return The sign out intent.
- */
- public static Intent getSignOutIntent(Context context) {
- Intent intent = new Intent(context, SplashActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.setAction(ACTION_SIGN_OUT);
- return intent;
- }
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ initViews();
- /**
- * Generate the revoke access intent.
- *
- * @param context Interface to global information about an application environment.
- * @return The revoke access intent.
- */
- public static Intent getRevokeAccessIntent(Context context) {
- Intent intent = new Intent(context, SplashActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.setAction(ACTION_REVOKE_ACCESS);
- return intent;
+ accountDao = new AccountDaoSource().getActiveAccountInformation(this);
+ if (accountDao != null) {
+ if (SecurityUtils.isBackupKeysExist(this)) {
+ EmailSyncService.startEmailSyncService(this);
+ EmailManagerActivity.runEmailManagerActivity(this, accountDao);
+ finish();
+ }
+ }
}
@Override
@@ -121,51 +89,6 @@ public int getContentViewResourceId() {
return R.layout.activity_splash;
}
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- GoogleSignInOptions googleSignInOptions = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
- .requestScopes(new Scope(Constants.SCOPE_MAIL_GOOGLE_COM))
- .requestEmail()
- .build();
-
- googleApiClient = new GoogleApiClient.Builder(this)
- .enableAutoManage(this, this).addConnectionCallbacks(this)
- .addApi(Auth.GOOGLE_SIGN_IN_API, googleSignInOptions)
- .build();
-
- initViews();
-
- if (getIntent() != null && getIntent().getAction() != null) {
- if (ACTION_SIGN_OUT.equals(getIntent().getAction())) {
- isSignOutAction = true;
- UIUtil.exchangeViewVisibility(this, true, splashView, signInView);
- } else if (ACTION_REVOKE_ACCESS.equals(getIntent().getAction())) {
- isRevokeAccessAction = true;
- UIUtil.exchangeViewVisibility(this, true, splashView, signInView);
- }
- }
-
- accountDao = new AccountDaoSource().getActiveAccountInformation(this);
- if (!isSignOutAction && !isRevokeAccessAction) {
- if (accountDao != null) {
- if (SecurityUtils.isBackupKeysExist(this)) {
- EmailSyncService.startEmailSyncService(this);
- EmailManagerActivity.runEmailManagerActivity(this, accountDao);
- finish();
- }
- }
- }
- }
-
- @Override
- public void onStart() {
- super.onStart();
- if (!isSignOutAction && !isRevokeAccessAction) {
- checkGoogleSignResult();
- }
- }
-
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
@@ -177,32 +100,62 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
case REQUEST_CODE_CHECK_PRIVATE_KEYS_FROM_GMAIL:
switch (resultCode) {
case Activity.RESULT_OK:
+ case CheckKeysActivity.RESULT_NEUTRAL:
EmailSyncService.startEmailSyncService(this);
- EmailManagerActivity.runEmailManagerActivity(this, accountDao);
+ EmailManagerActivity.runEmailManagerActivity(this, addGmailAccount(currentGoogleSignInAccount));
finish();
break;
case Activity.RESULT_CANCELED:
- finish();
- break;
-
case CheckKeysActivity.RESULT_NEGATIVE:
- isSignOutAction = true;
+ this.currentGoogleSignInAccount = null;
+ UIUtil.exchangeViewVisibility(this, false, splashView, signInView);
break;
}
break;
-
case REQUEST_CODE_CREATE_OR_IMPORT_KEY:
switch (resultCode) {
case Activity.RESULT_OK:
EmailSyncService.startEmailSyncService(this);
- EmailManagerActivity.runEmailManagerActivity(this, accountDao);
+ EmailManagerActivity.runEmailManagerActivity(this, addGmailAccount(currentGoogleSignInAccount));
finish();
break;
case Activity.RESULT_CANCELED:
- finish();
+ case CreateOrImportKeyActivity.RESULT_CODE_USE_ANOTHER_ACCOUNT:
+ this.currentGoogleSignInAccount = null;
+ UIUtil.exchangeViewVisibility(this, false, splashView, signInView);
+ break;
+ }
+ break;
+
+ case REQUEST_CODE_ADD_OTHER_ACCOUNT:
+ switch (resultCode) {
+ case Activity.RESULT_OK:
+ try {
+ AuthCredentials authCredentials = data.getParcelableExtra(AddNewAccountManuallyActivity
+ .KEY_EXTRA_AUTH_CREDENTIALS);
+ AccountDaoSource accountDaoSource = new AccountDaoSource();
+ accountDaoSource.addRow(this, authCredentials);
+ EmailSyncService.startEmailSyncService(this);
+ EmailManagerActivity.runEmailManagerActivity(this,
+ accountDaoSource.getAccountInformation(this, authCredentials.getEmail()));
+
+ finish();
+ } catch (Exception e) {
+ e.printStackTrace();
+ ACRA.getErrorReporter().handleException(e);
+ Toast.makeText(this, R.string.unknown_error, Toast.LENGTH_SHORT).show();
+ }
+ break;
+
+ case CreateOrImportKeyActivity.RESULT_CODE_USE_ANOTHER_ACCOUNT:
+ this.accountDao = null;
+ if (data != null) {
+ clearInfoAboutOldAccount((AccountDao) data.getParcelableExtra(CreateOrImportKeyActivity
+ .EXTRA_KEY_ACCOUNT_DAO));
+ }
break;
}
break;
@@ -213,43 +166,39 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
}
@Override
- public void onSignInButtonClick(SignInType signInType) {
- switch (signInType) {
- case GMAIL:
- Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient);
- startActivityForResult(signInIntent, REQUEST_CODE_SIGN_IN);
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.buttonPrivacy:
+ startActivity(HtmlViewFromAssetsRawActivity.newIntent(this,
+ getString(R.string.privacy), "html/privacy.htm"));
break;
- }
- }
- @Override
- public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
- UIUtil.showInfoSnackbar(getRootView(), connectionResult.getErrorMessage());
- }
+ case R.id.buttonTerms:
+ startActivity(HtmlViewFromAssetsRawActivity.newIntent(this,
+ getString(R.string.terms), "html/terms.htm"));
+ break;
- @Override
- public void onConnected(@Nullable Bundle bundle) {
- if (isSignOutAction) {
- signOutFromGoogleAccount();
- } else if (isRevokeAccessAction) {
- revokeAccessFomGoogleAccount();
+ case R.id.buttonSecurity:
+ startActivity(HtmlViewFromAssetsRawActivity.newIntent(this,
+ getString(R.string.security), "html/security.htm"));
+ break;
+ default:
+ super.onClick(v);
}
- }
- @Override
- public void onConnectionSuspended(int i) {
- if (isSignOutAction || isRevokeAccessAction) {
- finish();
- Toast.makeText(this, R.string.error_occurred_while_this_action_running, Toast.LENGTH_SHORT).show();
- }
}
@Override
public Loader onCreateLoader(int id, Bundle args) {
switch (id) {
- case R.id.loader_id_load_gmail_backups:
+ case R.id.loader_id_load_private_key_backups_from_email:
+ AccountDao accountDao = null;
UIUtil.exchangeViewVisibility(this, true, splashView, signInView);
- return new LoadPrivateKeysFromMailAsyncTaskLoader(this, accountDao.getAccount());
+ if (currentGoogleSignInAccount != null) {
+ accountDao = new AccountDao(currentGoogleSignInAccount.getEmail(), AccountDao.ACCOUNT_TYPE_GOOGLE,
+ null, null, null, null, null);
+ }
+ return accountDao != null ? new LoadPrivateKeysFromMailAsyncTaskLoader(this, accountDao) : null;
default:
return null;
@@ -260,11 +209,10 @@ public Loader onCreateLoader(int id, Bundle args) {
@Override
public void onLoadFinished(Loader loader, LoaderResult loaderResult) {
switch (loader.getId()) {
- case R.id.loader_id_load_gmail_backups:
+ case R.id.loader_id_load_private_key_backups_from_email:
if (loaderResult.getResult() != null) {
ArrayList keyDetailsList = (ArrayList) loaderResult.getResult();
if (keyDetailsList.isEmpty()) {
- finish();
startActivityForResult(CreateOrImportKeyActivity.newIntent(this, new AccountDaoSource()
.getActiveAccountInformation(this), true), REQUEST_CODE_CREATE_OR_IMPORT_KEY);
} else {
@@ -272,7 +220,9 @@ public void onLoadFinished(Loader loader, LoaderResult loaderResul
keyDetailsList,
getString(R.string.found_backup_of_your_account_key),
getString(R.string.continue_),
- getString(R.string.use_another_account), false),
+ SecurityUtils.isBackupKeysExist(this) ? getString(R.string
+ .use_existing_keys) : null,
+ getString(R.string.use_another_account), true),
REQUEST_CODE_CHECK_PRIVATE_KEYS_FROM_GMAIL);
}
} else if (loaderResult.getException() != null) {
@@ -287,27 +237,24 @@ public void onLoaderReset(Loader loader) {
}
+ /**
+ * Clear information about created but a not used account.
+ *
+ * @param accountDao The account which will be deleted from the local database.
+ */
+ private void clearInfoAboutOldAccount(AccountDao accountDao) {
+ if (accountDao != null) {
+ getContentResolver().delete(Uri.parse(FlowcryptContract.AUTHORITY_URI + "/"
+ + FlowcryptContract.CLEAN_DATABASE), null, new String[]{accountDao.getEmail()});
+ }
+ }
+
private void handleSignInResult(GoogleSignInResult googleSignInResult) {
if (googleSignInResult.isSuccess()) {
- GoogleSignInAccount googleSignInAccount = googleSignInResult.getSignInAccount();
- if (googleSignInAccount != null) {
- accountDao = updateInformationAboutAccountInLocalDatabase(googleSignInAccount);
- } else {
- //todo-denbond7 handle this situation
- }
+ currentGoogleSignInAccount = googleSignInResult.getSignInAccount();
- if (SecurityUtils.isBackupKeysExist(this)) {
- EmailSyncService.startEmailSyncService(this);
- EmailManagerActivity.runEmailManagerActivity(this, accountDao);
- finish();
- } else {
- startService(new Intent(this, CheckClipboardToFindPrivateKeyService.class));
- if (accountDao != null) {
- getSupportLoaderManager().initLoader(R.id.loader_id_load_gmail_backups, null, this);
- } else {
- //todo-denbond7 handle this situation
- }
- }
+ startService(new Intent(this, CheckClipboardToFindPrivateKeyService.class));
+ getSupportLoaderManager().restartLoader(R.id.loader_id_load_private_key_backups_from_email, null, this);
} else {
if (!TextUtils.isEmpty(googleSignInResult.getStatus().getStatusMessage())) {
UIUtil.showInfoSnackbar(signInView, googleSignInResult.getStatus().getStatusMessage());
@@ -316,7 +263,14 @@ private void handleSignInResult(GoogleSignInResult googleSignInResult) {
}
}
- private AccountDao updateInformationAboutAccountInLocalDatabase(GoogleSignInAccount googleSignInAccount) {
+ /**
+ * Created a GMAIL {@link AccountDao} and add it to the database.
+ *
+ * @param googleSignInAccount The {@link GoogleSignInAccount} object which contains information about a Google
+ * account.
+ * @return Generated {@link AccountDao}.
+ */
+ private AccountDao addGmailAccount(GoogleSignInAccount googleSignInAccount) {
AccountDaoSource accountDaoSource = new AccountDaoSource();
boolean isAccountUpdated = accountDaoSource.updateAccountInformation(this, googleSignInAccount) > 0;
@@ -327,91 +281,31 @@ private AccountDao updateInformationAboutAccountInLocalDatabase(GoogleSignInAcco
return new AccountDaoSource().getAccountInformation(this, googleSignInAccount.getEmail());
}
+ /**
+ * In this method we init all used views.
+ */
private void initViews() {
signInView = findViewById(R.id.signInView);
splashView = findViewById(R.id.splashView);
- }
- /**
- * Revoke access to the Gmail account.
- */
- private void revokeAccessFomGoogleAccount() {
- Auth.GoogleSignInApi.revokeAccess(googleApiClient).setResultCallback(
- new ResultCallback() {
- @Override
- public void onResult(@NonNull Status status) {
- try {
- if (status.isSuccess()) {
- resetAppComponents();
- UIUtil.exchangeViewVisibility(SplashActivity.this, false, splashView, signInView);
- } else {
- finish();
- Toast.makeText(SplashActivity.this, R.string.error_occurred_while_this_action_running,
- Toast.LENGTH_SHORT).show();
- }
- } catch (IOException e) {
- e.printStackTrace();
- UIUtil.showInfoSnackbar(getRootView(), e.getMessage());
- }
- }
- });
- }
+ if (findViewById(R.id.buttonSignInWithGmail) != null) {
+ findViewById(R.id.buttonSignInWithGmail).setOnClickListener(this);
+ }
- /**
- * Sign out from the Google account.
- */
- private void signOutFromGoogleAccount() {
- Auth.GoogleSignInApi.signOut(googleApiClient).setResultCallback(
- new ResultCallback() {
- @Override
- public void onResult(@NonNull Status status) {
- try {
- if (status.isSuccess()) {
- resetAppComponents();
- UIUtil.exchangeViewVisibility(SplashActivity.this, false, splashView, signInView);
- } else {
- finish();
- Toast.makeText(SplashActivity.this, R.string.error_occurred_while_this_action_running,
- Toast.LENGTH_SHORT).show();
- }
- } catch (IOException e) {
- e.printStackTrace();
- UIUtil.showInfoSnackbar(getRootView(), e.getMessage());
- }
- }
- });
- }
+ if (findViewById(R.id.buttonOtherEmailProvider) != null) {
+ findViewById(R.id.buttonOtherEmailProvider).setOnClickListener(this);
+ }
- private void resetAppComponents() throws IOException {
- stopService(new Intent(this, EmailSyncService.class));
- if (accountDao != null) {
- getContentResolver().delete(Uri.parse(FlowcryptContract.AUTHORITY_URI + "/"
- + FlowcryptContract.CLEAN_DATABASE), null, new String[]{accountDao.getEmail()});
+ if (findViewById(R.id.buttonPrivacy) != null) {
+ findViewById(R.id.buttonPrivacy).setOnClickListener(this);
}
- }
- /**
- * In this method we check GoogleSignResult. If the user's cached credentials are valid the
- * OptionalPendingResult will be "done" and the GoogleSignInResult will be available
- * instantly. If the user has not previously signed in on this device or the sign-in has
- * expired, this asynchronous branch will attempt to sign in the user silently. Cross-device
- * single sign-on will occur in this branch.
- */
- private void checkGoogleSignResult() {
- OptionalPendingResult optionalPendingResult
- = Auth.GoogleSignInApi.silentSignIn(googleApiClient);
- if (optionalPendingResult.isDone()) {
- GoogleSignInResult googleSignInResult = optionalPendingResult.get();
- handleSignInResult(googleSignInResult);
- } else {
- UIUtil.exchangeViewVisibility(this, true, splashView, signInView);
- optionalPendingResult.setResultCallback(new ResultCallback() {
- @Override
- public void onResult(@NonNull GoogleSignInResult googleSignInResult) {
- UIUtil.exchangeViewVisibility(SplashActivity.this, false, splashView, signInView);
- handleSignInResult(googleSignInResult);
- }
- });
+ if (findViewById(R.id.buttonTerms) != null) {
+ findViewById(R.id.buttonTerms).setOnClickListener(this);
+ }
+
+ if (findViewById(R.id.buttonSecurity) != null) {
+ findViewById(R.id.buttonSecurity).setOnClickListener(this);
}
}
}
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/base/BaseActivity.java b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/base/BaseActivity.java
index 264e196c98..7981647e87 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/base/BaseActivity.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/base/BaseActivity.java
@@ -65,7 +65,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
setContentView(getContentViewResourceId());
- initViews();
+ initScreenViews();
}
@Override
@@ -75,19 +75,19 @@ public void onStart() {
}
@Override
- protected void onResume() {
+ public void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
@Override
- protected void onStop() {
+ public void onStop() {
super.onStop();
Log.d(TAG, "onStop");
}
@Override
- protected void onDestroy() {
+ public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
@@ -202,13 +202,13 @@ public void handleSuccessLoaderResult(int loaderId, Object result) {
}
- private void initViews() {
- appBarLayout = (AppBarLayout) findViewById(R.id.appBarLayout);
+ private void initScreenViews() {
+ appBarLayout = findViewById(R.id.appBarLayout);
setupToolbarIfItExists();
}
private void setupToolbarIfItExists() {
- toolbar = (Toolbar) findViewById(R.id.toolbar);
+ toolbar = findViewById(R.id.toolbar);
if (toolbar != null) {
setSupportActionBar(toolbar);
}
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/base/BaseSignInActivity.java b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/base/BaseSignInActivity.java
new file mode 100644
index 0000000000..7f3f20f58e
--- /dev/null
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/base/BaseSignInActivity.java
@@ -0,0 +1,104 @@
+/*
+ * Business Source License 1.0 © 2017 FlowCrypt Limited (tom@cryptup.org).
+ * Use limitations apply. See https://github.com/FlowCrypt/flowcrypt-android/blob/master/LICENSE
+ * Contributors: DenBond7
+ */
+
+package com.flowcrypt.email.ui.activity.base;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+
+import com.flowcrypt.email.R;
+import com.flowcrypt.email.ui.activity.AddNewAccountManuallyActivity;
+import com.flowcrypt.email.util.GeneralUtil;
+import com.flowcrypt.email.util.UIUtil;
+import com.flowcrypt.email.util.google.GoogleApiClientHelper;
+import com.google.android.gms.auth.api.Auth;
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+
+/**
+ * This activity will be a common point of a sign-in logic.
+ *
+ * @author Denis Bondarenko
+ * Date: 06.10.2017
+ * Time: 10:38
+ * E-mail: DenBond7@gmail.com
+ */
+
+public abstract class BaseSignInActivity extends BaseActivity implements View.OnClickListener,
+ GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks {
+ protected static final int REQUEST_CODE_SIGN_IN = 10;
+ protected static final int REQUEST_CODE_ADD_OTHER_ACCOUNT = 11;
+
+ /**
+ * The main entry point for Google Play services integration.
+ */
+ protected GoogleApiClient googleApiClient;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ initGoogleApiClient();
+ initViews();
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.buttonSignInWithGmail:
+ if (GeneralUtil.isInternetConnectionAvailable(this)) {
+ googleApiClient.clearDefaultAccountAndReconnect();
+ Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient);
+ startActivityForResult(signInIntent, REQUEST_CODE_SIGN_IN);
+ } else {
+ UIUtil.showInfoSnackbar(getRootView(),
+ getString(R.string.internet_connection_is_not_available));
+ }
+ break;
+
+ case R.id.buttonOtherEmailProvider:
+ if (GeneralUtil.isInternetConnectionAvailable(this)) {
+ startActivityForResult(new Intent(this, AddNewAccountManuallyActivity.class),
+ REQUEST_CODE_ADD_OTHER_ACCOUNT);
+ } else {
+ UIUtil.showInfoSnackbar(getRootView(), getString(R.string.internet_connection_is_not_available));
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void onConnected(@Nullable Bundle bundle) {
+
+ }
+
+ @Override
+ public void onConnectionSuspended(int i) {
+
+ }
+
+ @Override
+ public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
+ UIUtil.showInfoSnackbar(getRootView(), connectionResult.getErrorMessage());
+ }
+
+ protected void initGoogleApiClient() {
+ googleApiClient = GoogleApiClientHelper.generateGoogleApiClient(this, this, this, this, GoogleApiClientHelper
+ .generateGoogleSignInOptions());
+ }
+
+ private void initViews() {
+ if (findViewById(R.id.buttonSignInWithGmail) != null) {
+ findViewById(R.id.buttonSignInWithGmail).setOnClickListener(this);
+ }
+
+ if (findViewById(R.id.buttonOtherEmailProvider) != null) {
+ findViewById(R.id.buttonOtherEmailProvider).setOnClickListener(this);
+ }
+ }
+}
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/base/BaseSyncActivity.java b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/base/BaseSyncActivity.java
index a8e5ba4729..04045afa08 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/base/BaseSyncActivity.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/base/BaseSyncActivity.java
@@ -84,7 +84,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
}
@Override
- protected void onDestroy() {
+ public void onDestroy() {
super.onDestroy();
// Unbind from the service
unbindFromService();
@@ -108,7 +108,7 @@ public void onServiceDisconnected(ComponentName name) {
}
public String getReplyMessengerName() {
- return getClass().getSimpleName();
+ return getClass().getSimpleName() + "_" + hashCode();
}
/**
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/SplashActivityFragment.java b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/SplashActivityFragment.java
deleted file mode 100644
index 9a775bc5a9..0000000000
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/SplashActivityFragment.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Business Source License 1.0 © 2017 FlowCrypt Limited (tom@cryptup.org).
- * Use limitations apply. See https://github.com/FlowCrypt/flowcrypt-android/blob/master/LICENSE
- * Contributors: DenBond7
- */
-
-package com.flowcrypt.email.ui.activity.fragment;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.flowcrypt.email.R;
-import com.flowcrypt.email.model.SignInType;
-import com.flowcrypt.email.ui.activity.AddNewAccountActivity;
-import com.flowcrypt.email.ui.activity.HtmlViewFromAssetsRawActivity;
-import com.flowcrypt.email.util.GeneralUtil;
-import com.flowcrypt.email.util.UIUtil;
-
-/**
- * This fragment containing a splash view. Also in this fragment, the implementation of Sign In
- * functions.
- */
-public class SplashActivityFragment extends Fragment implements View.OnClickListener {
-
- private static final int REQUEST_CODE_ADD_OTHER_ACCOUNT = 10;
- private OnSignInButtonClickListener onSignInButtonClickListener;
-
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
- if (context instanceof OnSignInButtonClickListener) {
- onSignInButtonClickListener = (OnSignInButtonClickListener) context;
- } else throw new IllegalArgumentException(context.toString() + " must implement " +
- OnSignInButtonClickListener.class.getSimpleName());
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fragment_splash, container, false);
- }
-
- @Override
- public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- initViews(view);
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case REQUEST_CODE_ADD_OTHER_ACCOUNT:
- switch (resultCode) {
- case Activity.RESULT_OK:
- getActivity().finish();
- break;
- }
- break;
-
- default:
- super.onActivityResult(requestCode, resultCode, data);
- }
- }
-
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.buttonSignInWithGmail:
- if (GeneralUtil.isInternetConnectionAvailable(getActivity())) {
- signInButtonWasClicked(SignInType.GMAIL);
- } else {
- UIUtil.showInfoSnackbar(getView(),
- getString(R.string.internet_connection_is_not_available));
- }
- break;
-
- case R.id.buttonOtherEmailProvider:
- if (GeneralUtil.isInternetConnectionAvailable(getActivity())) {
- startActivityForResult(new Intent(getContext(), AddNewAccountActivity.class),
- REQUEST_CODE_ADD_OTHER_ACCOUNT);
- } else {
- UIUtil.showInfoSnackbar(getView(),
- getString(R.string.internet_connection_is_not_available));
- }
- break;
-
- case R.id.buttonPrivacy:
- startActivity(HtmlViewFromAssetsRawActivity.newIntent(getContext(),
- getString(R.string.privacy), "html/privacy.htm"));
- break;
-
- case R.id.buttonTerms:
- startActivity(HtmlViewFromAssetsRawActivity.newIntent(getContext(),
- getString(R.string.terms), "html/terms.htm"));
- break;
-
- case R.id.buttonSecurity:
- startActivity(HtmlViewFromAssetsRawActivity.newIntent(getContext(),
- getString(R.string.security), "html/security.htm"));
- break;
- }
- }
-
- /**
- * In this method we init all used views.
- */
- private void initViews(View view) {
- if (view.findViewById(R.id.buttonSignInWithGmail) != null) {
- view.findViewById(R.id.buttonSignInWithGmail).setOnClickListener(this);
- }
-
- if (view.findViewById(R.id.buttonOtherEmailProvider) != null) {
- view.findViewById(R.id.buttonOtherEmailProvider).setOnClickListener(this);
- }
-
- if (view.findViewById(R.id.buttonPrivacy) != null) {
- view.findViewById(R.id.buttonPrivacy).setOnClickListener(this);
- }
-
- if (view.findViewById(R.id.buttonTerms) != null) {
- view.findViewById(R.id.buttonTerms).setOnClickListener(this);
- }
-
- if (view.findViewById(R.id.buttonSecurity) != null) {
- view.findViewById(R.id.buttonSecurity).setOnClickListener(this);
- }
- }
-
- /**
- * Handle a sign in button click.
- */
- private void signInButtonWasClicked(SignInType signInType) {
- if (onSignInButtonClickListener != null) {
- onSignInButtonClickListener.onSignInButtonClick(signInType);
- }
- }
-
- /**
- * Listener for sign in buttons.
- */
- public interface OnSignInButtonClickListener {
- void onSignInButtonClick(SignInType signInType);
- }
-}
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/base/CreateMessageFragment.java b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/base/CreateMessageFragment.java
index 5058c60384..127a28073d 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/base/CreateMessageFragment.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/base/CreateMessageFragment.java
@@ -31,6 +31,7 @@
import android.widget.TextView;
import android.widget.Toast;
+import com.flowcrypt.email.Constants;
import com.flowcrypt.email.R;
import com.flowcrypt.email.api.email.FoldersManager;
import com.flowcrypt.email.api.email.model.AttachmentInfo;
@@ -78,8 +79,6 @@ public class CreateMessageFragment extends BaseGmailFragment implements View.OnF
private static final int REQUEST_CODE_IMPORT_PUBLIC_KEY = 101;
private static final int REQUEST_CODE_GET_CONTENT_FOR_SENDING = 102;
- private static final int MAX_TOTAL_ATTACHMENT_SIZE_IN_BYTES = 1024 * 1024 * 3;
-
private Js js;
private OnMessageSendListener onMessageSendListener;
private OnChangeMessageEncryptedTypeListener onChangeMessageEncryptedTypeListener;
@@ -236,7 +235,8 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
} else {
showInfoSnackbar(getView(),
getString(R.string.template_warning_max_total_attachments_size,
- FileUtils.byteCountToDisplaySize(MAX_TOTAL_ATTACHMENT_SIZE_IN_BYTES)),
+ FileUtils.byteCountToDisplaySize(
+ Constants.MAX_TOTAL_ATTACHMENT_SIZE_IN_BYTES)),
Snackbar.LENGTH_LONG);
}
} else {
@@ -439,12 +439,10 @@ private OutgoingMessageInfo getOutgoingMessageInfo() {
}
+ List contacts = editTextRecipients.getChipValues();
if (onChangeMessageEncryptedTypeListener.getMessageEncryptionType() == MessageEncryptionType.ENCRYPTED) {
- pgpContacts = contactsDaoSource.getPgpContactsListFromDatabase(getContext(),
- editTextRecipients.getChipValues());
+ pgpContacts = contactsDaoSource.getPgpContactsListFromDatabase(getContext(), contacts);
} else {
- List contacts = editTextRecipients.getChipValues();
-
for (String s : contacts) {
pgpContacts.add(new PgpContact(s, null));
}
@@ -499,7 +497,7 @@ private void updateChips() {
}
private void initChipsView(View view) {
- editTextRecipients = (NachoTextView) view.findViewById(R.id.editTextRecipient);
+ editTextRecipients = view.findViewById(R.id.editTextRecipient);
editTextRecipients.setNachoValidator(new ChipifyingNachoValidator());
editTextRecipients.setIllegalCharacters(',');
editTextRecipients.setChipTokenizer(new SingleCharacterSpanChipTokenizer(getContext(),
@@ -585,12 +583,12 @@ private void removePgpContactFromRecipientsField(PgpContact deleteCandidatePgpCo
* @param view The root fragment view.
*/
private void initViews(View view) {
- layoutAttachments = (ViewGroup) view.findViewById(R.id.layoutAttachments);
+ layoutAttachments = view.findViewById(R.id.layoutAttachments);
initChipsView(view);
- editTextEmailSubject = (EditText) view.findViewById(R.id.editTextEmailSubject);
- editTextEmailMessage = (EditText) view.findViewById(R.id.editTextEmailMessage);
- textInputLayoutEmailMessage = (TextInputLayout) view.findViewById(R.id.textInputLayoutEmailMessage);
+ editTextEmailSubject = view.findViewById(R.id.editTextEmailSubject);
+ editTextEmailMessage = view.findViewById(R.id.editTextEmailMessage);
+ textInputLayoutEmailMessage = view.findViewById(R.id.textInputLayoutEmailMessage);
layoutContent = view.findViewById(R.id.scrollView);
progressBarCheckContactsDetails = view.findViewById(R.id.progressBarCheckContactsDetails);
@@ -679,7 +677,7 @@ private boolean isAttachmentCanBeAdded(AttachmentInfo newAttachmentInfo) {
totalSizeOfAttachments += newAttachmentInfo.getEncodedSize();
- return totalSizeOfAttachments < MAX_TOTAL_ATTACHMENT_SIZE_IN_BYTES;
+ return totalSizeOfAttachments < Constants.MAX_TOTAL_ATTACHMENT_SIZE_IN_BYTES;
}
/**
@@ -754,10 +752,10 @@ private void showAttachments() {
for (final AttachmentInfo attachmentInfo : attachmentInfoList) {
final View rootView = layoutInflater.inflate(R.layout.attachment_item, layoutAttachments, false);
- TextView textViewAttachmentName = (TextView) rootView.findViewById(R.id.textViewAttchmentName);
+ TextView textViewAttachmentName = rootView.findViewById(R.id.textViewAttchmentName);
textViewAttachmentName.setText(attachmentInfo.getName());
- TextView textViewAttachmentSize = (TextView) rootView.findViewById(R.id.textViewAttachmentSize);
+ TextView textViewAttachmentSize = rootView.findViewById(R.id.textViewAttachmentSize);
textViewAttachmentSize.setText(Formatter.formatFileSize(getContext(), attachmentInfo.getEncodedSize()));
View imageButtonDownloadAttachment = rootView.findViewById(R.id.imageButtonDownloadAttachment);
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/loader/DecryptMessageAsyncTaskLoader.java b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/loader/DecryptMessageAsyncTaskLoader.java
index 9541cbe699..abf74c06ee 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/loader/DecryptMessageAsyncTaskLoader.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/loader/DecryptMessageAsyncTaskLoader.java
@@ -179,7 +179,7 @@ private String decryptText(Js js, String encryptedText) {
if (encryptedText != null) {
PgpDecrypted pgpDecrypted = js.crypto_message_decrypt(encryptedText);
try {
- return pgpDecrypted != null ? pgpDecrypted.getContent() : "";
+ return pgpDecrypted != null ? pgpDecrypted.getString() : "";
} catch (Exception e) {
e.printStackTrace();
return encryptedText;
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/loader/EncryptAndSavePrivateKeysAsyncTaskLoader.java b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/loader/EncryptAndSavePrivateKeysAsyncTaskLoader.java
index b1fa009a47..380dbb56a6 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/loader/EncryptAndSavePrivateKeysAsyncTaskLoader.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/loader/EncryptAndSavePrivateKeysAsyncTaskLoader.java
@@ -14,8 +14,10 @@
import com.eclipsesource.v8.V8Object;
import com.flowcrypt.email.R;
import com.flowcrypt.email.database.dao.KeysDao;
+import com.flowcrypt.email.database.dao.source.ContactsDaoSource;
import com.flowcrypt.email.database.dao.source.KeysDaoSource;
import com.flowcrypt.email.js.Js;
+import com.flowcrypt.email.js.PgpContact;
import com.flowcrypt.email.js.PgpKey;
import com.flowcrypt.email.model.KeyDetails;
import com.flowcrypt.email.model.results.LoaderResult;
@@ -93,6 +95,12 @@ public LoaderResult loadInBackground() {
if (!keysDaoSource.isKeyExist(getContext(), pgpKey.getLongid())) {
Uri uri = saveKeyToDatabase(keyStoreCryptoManager, keyDetails,
pgpKey, passphrase);
+ PgpContact pgpContact = pgpKey.getPrimaryUserId();
+ PgpKey publicKey = pgpKey.toPublic();
+ if (pgpContact != null) {
+ pgpContact.setPubkey(publicKey.armor());
+ new ContactsDaoSource().addRow(getContext(), pgpContact);
+ }
isOneOrMoreKeySaved = uri != null;
} else if (isThrowErrorIfDuplicateFound) {
return new LoaderResult(null, new Exception(getContext().getString(R
@@ -126,8 +134,8 @@ public void onStopLoading() {
* for generate an algorithm parameter spec String.
*
* @param keyStoreCryptoManager A {@link KeyStoreCryptoManager} which will bu used to encrypt
- * information about a key;
- * @param keyDetails The private key details
+ * information about a key;
+ * @param keyDetails The private key details
* @param pgpKey A normalized key;
* @param passphrase A passphrase which user entered;
*/
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/loader/LoadPrivateKeysFromMailAsyncTaskLoader.java b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/loader/LoadPrivateKeysFromMailAsyncTaskLoader.java
index 6824e43cf4..7c59b41b83 100644
--- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/loader/LoadPrivateKeysFromMailAsyncTaskLoader.java
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/loader/LoadPrivateKeysFromMailAsyncTaskLoader.java
@@ -6,7 +6,6 @@
package com.flowcrypt.email.ui.loader;
-import android.accounts.Account;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
import android.text.TextUtils;
@@ -14,11 +13,10 @@
import com.flowcrypt.email.api.email.EmailUtil;
import com.flowcrypt.email.api.email.JavaEmailConstants;
import com.flowcrypt.email.api.email.SearchBackupsUtil;
-import com.flowcrypt.email.api.email.gmail.GmailConstants;
import com.flowcrypt.email.api.email.protocol.OpenStoreHelper;
+import com.flowcrypt.email.database.dao.source.AccountDao;
import com.flowcrypt.email.model.KeyDetails;
import com.flowcrypt.email.model.results.LoaderResult;
-import com.google.android.gms.auth.GoogleAuthUtil;
import com.sun.mail.imap.IMAPFolder;
import org.apache.commons.io.IOUtils;
@@ -33,6 +31,7 @@
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
+import javax.mail.Session;
import javax.mail.Store;
import javax.mail.internet.MimeBodyPart;
@@ -49,11 +48,11 @@ public class LoadPrivateKeysFromMailAsyncTaskLoader extends AsyncTaskLoader privateKeyDetailsList = new ArrayList<>();
try {
- String token = GoogleAuthUtil.getToken(getContext(), account,
- JavaEmailConstants.OAUTH2 + GmailConstants.SCOPE_MAIL_GOOGLE_COM);
+ Session session = OpenStoreHelper.getSessionForAccountDao(accountDao);
+ Store store = OpenStoreHelper.openAndConnectToStore(getContext(), accountDao, session);
- Store store = OpenStoreHelper.openAndConnectToGimapsStore(token, account.name);
Folder[] folders = store.getDefaultFolder().list("*");
for (Folder folder : folders) {
if (!EmailUtil.isFolderHasNoSelectAttribute((IMAPFolder) folder)) {
folder.open(Folder.READ_ONLY);
- Message[] foundMessages = folder.search(SearchBackupsUtil.generateSearchTerms(account.name));
+ Message[] foundMessages = folder.search(
+ SearchBackupsUtil.generateSearchTerms(accountDao.getEmail()));
for (Message message : foundMessages) {
String key = getKeyFromMessageIfItExists(message);
diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/util/google/GoogleApiClientHelper.java b/FlowCrypt/src/main/java/com/flowcrypt/email/util/google/GoogleApiClientHelper.java
new file mode 100644
index 0000000000..a572c38d6b
--- /dev/null
+++ b/FlowCrypt/src/main/java/com/flowcrypt/email/util/google/GoogleApiClientHelper.java
@@ -0,0 +1,67 @@
+/*
+ * Business Source License 1.0 © 2017 FlowCrypt Limited (tom@cryptup.org).
+ * Use limitations apply. See https://github.com/FlowCrypt/flowcrypt-android/blob/master/LICENSE
+ * Contributors: DenBond7
+ */
+
+package com.flowcrypt.email.util.google;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.v4.app.FragmentActivity;
+import android.widget.Toast;
+
+import com.flowcrypt.email.Constants;
+import com.flowcrypt.email.R;
+import com.google.android.gms.auth.api.Auth;
+import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.common.api.Scope;
+import com.google.android.gms.common.api.Status;
+
+/**
+ * This class describes methods which can be used to work with {@link GoogleApiClient}.
+ *
+ * @author Denis Bondarenko
+ * Date: 09.10.2017
+ * Time: 12:25
+ * E-mail: DenBond7@gmail.com
+ */
+
+public class GoogleApiClientHelper {
+ public static GoogleSignInOptions generateGoogleSignInOptions() {
+ return new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
+ .requestScopes(new Scope(Constants.SCOPE_MAIL_GOOGLE_COM))
+ .requestEmail()
+ .build();
+ }
+
+ public static GoogleApiClient generateGoogleApiClient(Context context, FragmentActivity fragmentActivity,
+ GoogleApiClient.OnConnectionFailedListener
+ onConnectionFailedListener,
+ GoogleApiClient.ConnectionCallbacks connectionCallbacks,
+ GoogleSignInOptions googleSignInOptions) {
+ return new GoogleApiClient.Builder(context)
+ .enableAutoManage(fragmentActivity, onConnectionFailedListener)
+ .addConnectionCallbacks(connectionCallbacks)
+ .addApi(Auth.GOOGLE_SIGN_IN_API, googleSignInOptions)
+ .build();
+ }
+
+ /**
+ * Sign out from the Google account.
+ */
+ public static void signOutFromGoogleAccount(final Context context, GoogleApiClient googleApiClient) {
+ Auth.GoogleSignInApi.signOut(googleApiClient).setResultCallback(
+ new ResultCallback() {
+ @Override
+ public void onResult(@NonNull Status status) {
+ if (!status.isSuccess()) {
+ Toast.makeText(context,
+ R.string.error_occurred_while_this_action_running, Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ }
+}
diff --git a/FlowCrypt/src/main/res/layout/activity_add_new_account.xml b/FlowCrypt/src/main/res/layout/activity_add_new_account.xml
index eaf759f16e..1ee3ed4a60 100644
--- a/FlowCrypt/src/main/res/layout/activity_add_new_account.xml
+++ b/FlowCrypt/src/main/res/layout/activity_add_new_account.xml
@@ -27,7 +27,7 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FlowCrypt/src/main/res/layout/activity_check_keys.xml b/FlowCrypt/src/main/res/layout/activity_check_keys.xml
index 6a8822383e..25522bce2b 100644
--- a/FlowCrypt/src/main/res/layout/activity_check_keys.xml
+++ b/FlowCrypt/src/main/res/layout/activity_check_keys.xml
@@ -4,105 +4,125 @@
~ Contributors: DenBond7
-->
-
-
+
-
+
-
+
-
+
-
+ android:layout_marginTop="@dimen/margin_top_key_password"
+ app:layout_constraintHorizontal_bias="0.0"
+ app:layout_constraintLeft_toLeftOf="@+id/guidelineLeft"
+ app:layout_constraintRight_toLeftOf="@+id/guidelineRight"
+ app:layout_constraintTop_toBottomOf="@+id/textViewCheckKeysTitle"
+ app:passwordToggleContentDescription="description"
+ app:passwordToggleEnabled="true">
+
+
+
+
-
+
-
+
-
+
-
+
+
+
-
+
-
+
diff --git a/FlowCrypt/src/main/res/layout/activity_create_or_import_key.xml b/FlowCrypt/src/main/res/layout/activity_create_or_import_key.xml
index 779ed35407..6d33d763ad 100644
--- a/FlowCrypt/src/main/res/layout/activity_create_or_import_key.xml
+++ b/FlowCrypt/src/main/res/layout/activity_create_or_import_key.xml
@@ -65,7 +65,7 @@
style="@style/AppWidget.Button"
android:layout_width="0dp"
android:layout_marginTop="@dimen/margin_between_buttons"
- android:text="@string/skip_setup"
+ android:text="@string/use_existing_keys"
android:theme="@style/AppWidget.Button.White"
android:visibility="gone"
app:layout_constraintHorizontal_bias="0.0"
diff --git a/FlowCrypt/src/main/res/layout/activity_splash.xml b/FlowCrypt/src/main/res/layout/activity_splash.xml
index b61322f21f..2e1859d1ed 100644
--- a/FlowCrypt/src/main/res/layout/activity_splash.xml
+++ b/FlowCrypt/src/main/res/layout/activity_splash.xml
@@ -12,11 +12,11 @@
+ layout="@layout/content_splash" />
+ layout="@layout/splash_after_auth"
+ android:visibility="gone" />
diff --git a/FlowCrypt/src/main/res/layout/add_account.xml b/FlowCrypt/src/main/res/layout/add_account.xml
new file mode 100644
index 0000000000..a30efa7d32
--- /dev/null
+++ b/FlowCrypt/src/main/res/layout/add_account.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FlowCrypt/src/main/res/layout/content_add_new_account.xml b/FlowCrypt/src/main/res/layout/content_add_new_account.xml
index ee3c9647d1..091ad3bb79 100644
--- a/FlowCrypt/src/main/res/layout/content_add_new_account.xml
+++ b/FlowCrypt/src/main/res/layout/content_add_new_account.xml
@@ -4,328 +4,53 @@
~ Contributors: DenBond7
-->
-
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context="com.flowcrypt.email.ui.activity.AddNewAccountActivity">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+ android:layout_marginTop="@dimen/default_margin_huge"
+ android:contentDescription="@string/app_name"
+ android:drawablePadding="@dimen/default_margin_medium"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toBottomOf="parent"
+ app:srcCompat="@mipmap/logo_flowcrypt" />
+
+
+
+
+
+
diff --git a/FlowCrypt/src/main/res/layout/content_add_new_account_manually.xml b/FlowCrypt/src/main/res/layout/content_add_new_account_manually.xml
new file mode 100644
index 0000000000..ee3c9647d1
--- /dev/null
+++ b/FlowCrypt/src/main/res/layout/content_add_new_account_manually.xml
@@ -0,0 +1,331 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FlowCrypt/src/main/res/layout/content_splash.xml b/FlowCrypt/src/main/res/layout/content_splash.xml
index 39d3acb5c3..7c2e8bc993 100644
--- a/FlowCrypt/src/main/res/layout/content_splash.xml
+++ b/FlowCrypt/src/main/res/layout/content_splash.xml
@@ -1,15 +1,100 @@
-
-
+ android:layout_height="match_parent">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FlowCrypt/src/main/res/layout/nav_header_email_manager.xml b/FlowCrypt/src/main/res/layout/nav_header_email_manager.xml
index bf82cbce27..63c8e2c8bf 100644
--- a/FlowCrypt/src/main/res/layout/nav_header_email_manager.xml
+++ b/FlowCrypt/src/main/res/layout/nav_header_email_manager.xml
@@ -4,38 +4,73 @@
~ Contributors: DenBond7
-->
-
+ android:src="@mipmap/ic_account_default_photo"
+ app:layout_constraintBottom_toTopOf="@+id/layoutUserDetails"
+ app:layout_constraintLeft_toLeftOf="parent" />
-
+ android:background="?android:attr/selectableItemBackground"
+ android:clickable="true"
+ android:paddingBottom="@dimen/default_margin_content_small"
+ android:paddingLeft="@dimen/default_margin_content"
+ android:paddingRight="@dimen/default_margin_content"
+ android:paddingTop="@dimen/default_margin_content_small"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent">
+
+
+
+
+
+
+
+
+
+
+
-
-
+
diff --git a/FlowCrypt/src/main/res/layout/nav_menu_account_item.xml b/FlowCrypt/src/main/res/layout/nav_menu_account_item.xml
new file mode 100644
index 0000000000..4f3de77500
--- /dev/null
+++ b/FlowCrypt/src/main/res/layout/nav_menu_account_item.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FlowCrypt/src/main/res/menu/activity_email_manager_drawer.xml b/FlowCrypt/src/main/res/menu/activity_email_manager_drawer.xml
index 4cad679c46..fceaecaed4 100644
--- a/FlowCrypt/src/main/res/menu/activity_email_manager_drawer.xml
+++ b/FlowCrypt/src/main/res/menu/activity_email_manager_drawer.xml
@@ -20,11 +20,6 @@
android:id="@+id/navigationMenuLogOut"
android:icon="@mipmap/ic_sign_out"
android:title="@string/log_out" />
-
-
diff --git a/FlowCrypt/src/main/res/mipmap-hdpi/ic_arrow_drop_down.png b/FlowCrypt/src/main/res/mipmap-hdpi/ic_arrow_drop_down.png
new file mode 100644
index 0000000000..adea314756
Binary files /dev/null and b/FlowCrypt/src/main/res/mipmap-hdpi/ic_arrow_drop_down.png differ
diff --git a/FlowCrypt/src/main/res/mipmap-hdpi/ic_arrow_drop_up.png b/FlowCrypt/src/main/res/mipmap-hdpi/ic_arrow_drop_up.png
new file mode 100644
index 0000000000..25c3f81a05
Binary files /dev/null and b/FlowCrypt/src/main/res/mipmap-hdpi/ic_arrow_drop_up.png differ
diff --git a/FlowCrypt/src/main/res/mipmap-mdpi/ic_arrow_drop_down.png b/FlowCrypt/src/main/res/mipmap-mdpi/ic_arrow_drop_down.png
new file mode 100644
index 0000000000..d15d811ef5
Binary files /dev/null and b/FlowCrypt/src/main/res/mipmap-mdpi/ic_arrow_drop_down.png differ
diff --git a/FlowCrypt/src/main/res/mipmap-mdpi/ic_arrow_drop_up.png b/FlowCrypt/src/main/res/mipmap-mdpi/ic_arrow_drop_up.png
new file mode 100644
index 0000000000..5ad4406f91
Binary files /dev/null and b/FlowCrypt/src/main/res/mipmap-mdpi/ic_arrow_drop_up.png differ
diff --git a/FlowCrypt/src/main/res/mipmap-xhdpi/ic_add.png b/FlowCrypt/src/main/res/mipmap-xhdpi/ic_add.png
new file mode 100644
index 0000000000..48959d02d5
Binary files /dev/null and b/FlowCrypt/src/main/res/mipmap-xhdpi/ic_add.png differ
diff --git a/FlowCrypt/src/main/res/mipmap-xhdpi/ic_arrow_drop_down.png b/FlowCrypt/src/main/res/mipmap-xhdpi/ic_arrow_drop_down.png
new file mode 100644
index 0000000000..5243b57a4b
Binary files /dev/null and b/FlowCrypt/src/main/res/mipmap-xhdpi/ic_arrow_drop_down.png differ
diff --git a/FlowCrypt/src/main/res/mipmap-xhdpi/ic_arrow_drop_up.png b/FlowCrypt/src/main/res/mipmap-xhdpi/ic_arrow_drop_up.png
new file mode 100644
index 0000000000..51a5b01fda
Binary files /dev/null and b/FlowCrypt/src/main/res/mipmap-xhdpi/ic_arrow_drop_up.png differ
diff --git a/FlowCrypt/src/main/res/mipmap-xxhdpi/ic_arrow_drop_down.png b/FlowCrypt/src/main/res/mipmap-xxhdpi/ic_arrow_drop_down.png
new file mode 100644
index 0000000000..e53e26fa28
Binary files /dev/null and b/FlowCrypt/src/main/res/mipmap-xxhdpi/ic_arrow_drop_down.png differ
diff --git a/FlowCrypt/src/main/res/mipmap-xxhdpi/ic_arrow_drop_up.png b/FlowCrypt/src/main/res/mipmap-xxhdpi/ic_arrow_drop_up.png
new file mode 100644
index 0000000000..262146dfde
Binary files /dev/null and b/FlowCrypt/src/main/res/mipmap-xxhdpi/ic_arrow_drop_up.png differ
diff --git a/FlowCrypt/src/main/res/values/dimens.xml b/FlowCrypt/src/main/res/values/dimens.xml
index d896eb9b14..759613d918 100644
--- a/FlowCrypt/src/main/res/values/dimens.xml
+++ b/FlowCrypt/src/main/res/values/dimens.xml
@@ -41,9 +41,9 @@
12dp
80dp
32sp
- 60dp
+ 40dp
32dp
- 80dp
+ 60dp
32dp
32dp
24dp
diff --git a/FlowCrypt/src/main/res/values/ids.xml b/FlowCrypt/src/main/res/values/ids.xml
index 35b6fb1a59..ded3575a2c 100644
--- a/FlowCrypt/src/main/res/values/ids.xml
+++ b/FlowCrypt/src/main/res/values/ids.xml
@@ -6,7 +6,7 @@
-
+
@@ -34,5 +34,6 @@
+
\ No newline at end of file
diff --git a/FlowCrypt/src/main/res/values/strings.xml b/FlowCrypt/src/main/res/values/strings.xml
index e454a51227..5ebc806473 100644
--- a/FlowCrypt/src/main/res/values/strings.xml
+++ b/FlowCrypt/src/main/res/values/strings.xml
@@ -192,9 +192,14 @@
STARTLS
The email \"%1$s\" already added!
The settings is not valid
- Use existing keys
+ Expand an account management
+ Add account
+ Use existing keys
Crash the App
Experimental
Experimental Settings
Simulate app crash
+ Attachments
+ The download attachments notification channel
+ The app cannot decrypt an attachment greater than %1$s