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"> + + + + - +