diff --git a/app/src/main/java/eu/faircode/email/AdapterAttachment.java b/app/src/main/java/eu/faircode/email/AdapterAttachment.java index ffa43e84c8..1297aeaa4c 100644 --- a/app/src/main/java/eu/faircode/email/AdapterAttachment.java +++ b/app/src/main/java/eu/faircode/email/AdapterAttachment.java @@ -150,138 +150,151 @@ public class AdapterAttachment extends RecyclerView.Adapter() { - @Override - protected Void onExecute(Context context, Bundle args) { - DB.getInstance(context).attachment().deleteAttachment(attachment.id); - EntityAttachment.getFile(context, attachment.id).delete(); - return null; - } + private void onDelete(final EntityAttachment attachment) { + Bundle args = new Bundle(); + args.putLong("id", attachment.id); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) { + DB.getInstance(context).attachment().deleteAttachment(attachment.id); + EntityAttachment.getFile(context, attachment.id).delete(); + return null; + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(context, owner, ex); + } + }.execute(context, owner, args, "attachment:delete"); + } + + private void onSave(EntityAttachment attachment) { + LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context); + lbm.sendBroadcast( + new Intent(ActivityView.ACTION_STORE_ATTACHMENT) + .putExtra("id", attachment.id) + .putExtra("name", attachment.name) + .putExtra("type", attachment.type)); + } + + private void onShare(EntityAttachment attachment) { + // Build file name + File file = EntityAttachment.getFile(context, attachment.id); + + // https://developer.android.com/reference/android/support/v4/content/FileProvider + final Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID, file); + Log.i("uri=" + uri); + + // Build intent + final Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(uri, attachment.type); + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + if (!TextUtils.isEmpty(attachment.name)) + intent.putExtra(Intent.EXTRA_TITLE, attachment.name); + Log.i("Sharing " + file + " type=" + attachment.type); + Log.i("Intent=" + intent); + + // Get targets + PackageManager pm = context.getPackageManager(); + List targets = new ArrayList<>(); + List ris = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); + for (ResolveInfo ri : ris) { + if ("com.adobe.reader".equals(ri.activityInfo.packageName)) + Toast.makeText(context, R.string.title_no_adobe, Toast.LENGTH_LONG).show(); + Log.i("Target=" + ri); + context.grantUriPermission(ri.activityInfo.packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); + targets.add(new NameResolveInfo( + pm.getApplicationIcon(ri.activityInfo.applicationInfo), + pm.getApplicationLabel(ri.activityInfo.applicationInfo).toString(), + ri)); + } + + // Check if viewer available + if (ris.size() == 0) { + Toast.makeText(context, context.getString(R.string.title_no_viewer, attachment.type), Toast.LENGTH_LONG).show(); + return; + } + if (confirm) { + View dview = LayoutInflater.from(context).inflate(R.layout.dialog_attachment, null); + final AlertDialog dialog = new DialogBuilderLifecycle(context, owner) + .setView(dview) + .setNegativeButton(android.R.string.cancel, null) + .create(); + + TextView tvName = dview.findViewById(R.id.tvName); + TextView tvType = dview.findViewById(R.id.tvType); + ListView lvApp = dview.findViewById(R.id.lvApp); + + tvName.setText(attachment.name); + tvType.setText(attachment.type); + + lvApp.setAdapter(new TargetAdapter(context, R.layout.item_target, targets)); + lvApp.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(context, owner, ex); + public void onItemClick(AdapterView parent, View view, int position, long id) { + NameResolveInfo selected = (NameResolveInfo) parent.getItemAtPosition(position); + intent.setPackage(selected.info.activityInfo.packageName); + context.startActivity(intent); + dialog.dismiss(); } - }.execute(context, owner, args, "attachment:delete"); + }); - } else if (view.getId() == R.id.ivSave) { - LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context); - lbm.sendBroadcast( - new Intent(ActivityView.ACTION_STORE_ATTACHMENT) - .putExtra("id", attachment.id) - .putExtra("name", attachment.name) - .putExtra("type", attachment.type)); + dialog.show(); + } else + context.startActivity(intent); + } - } else { - if (attachment.available) { - // Build file name - File file = EntityAttachment.getFile(context, attachment.id); - - // https://developer.android.com/reference/android/support/v4/content/FileProvider - final Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID, file); - Log.i("uri=" + uri); - - // Build intent - final Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(uri, attachment.type); - intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - if (!TextUtils.isEmpty(attachment.name)) - intent.putExtra(Intent.EXTRA_TITLE, attachment.name); - Log.i("Sharing " + file + " type=" + attachment.type); - Log.i("Intent=" + intent); - - // Get targets - PackageManager pm = context.getPackageManager(); - List targets = new ArrayList<>(); - List ris = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); - for (ResolveInfo ri : ris) { - if ("com.adobe.reader".equals(ri.activityInfo.packageName)) - Toast.makeText(context, R.string.title_no_adobe, Toast.LENGTH_LONG).show(); - Log.i("Target=" + ri); - context.grantUriPermission(ri.activityInfo.packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); - targets.add(new NameResolveInfo( - pm.getApplicationIcon(ri.activityInfo.applicationInfo), - pm.getApplicationLabel(ri.activityInfo.applicationInfo).toString(), - ri)); - } + private void onDownload(EntityAttachment attachment) { + Bundle args = new Bundle(); + args.putLong("id", attachment.id); + args.putLong("message", attachment.message); + args.putInt("sequence", attachment.sequence); - // Check if viewer available - if (ris.size() == 0) { - Toast.makeText(context, context.getString(R.string.title_no_viewer, attachment.type), Toast.LENGTH_LONG).show(); - return; - } + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) { + long id = args.getLong("id"); + long message = args.getLong("message"); + long sequence = args.getInt("sequence"); - if (confirm) { - View dview = LayoutInflater.from(context).inflate(R.layout.dialog_attachment, null); - final AlertDialog dialog = new DialogBuilderLifecycle(context, owner) - .setView(dview) - .setNegativeButton(android.R.string.cancel, null) - .create(); - - TextView tvName = dview.findViewById(R.id.tvName); - TextView tvType = dview.findViewById(R.id.tvType); - ListView lvApp = dview.findViewById(R.id.lvApp); - - tvName.setText(attachment.name); - tvType.setText(attachment.type); - - lvApp.setAdapter(new TargetAdapter(context, R.layout.item_target, targets)); - lvApp.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - NameResolveInfo selected = (NameResolveInfo) parent.getItemAtPosition(position); - intent.setPackage(selected.info.activityInfo.packageName); - context.startActivity(intent); - dialog.dismiss(); - } - }); - - dialog.show(); - } else - context.startActivity(intent); - } else { - if (attachment.progress == null) { - Bundle args = new Bundle(); - args.putLong("id", attachment.id); - args.putLong("message", attachment.message); - args.putInt("sequence", attachment.sequence); - - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) { - long id = args.getLong("id"); - long message = args.getLong("message"); - long sequence = args.getInt("sequence"); - - DB db = DB.getInstance(context); - try { - db.beginTransaction(); - - db.attachment().setProgress(id, 0); - - EntityMessage msg = db.message().getMessage(message); - EntityOperation.queue(context, db, msg, EntityOperation.ATTACHMENT, sequence); - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - - return null; - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(context, owner, ex); - } - }.execute(context, owner, args, "attachment:fetch"); + DB db = DB.getInstance(context); + try { + db.beginTransaction(); + + db.attachment().setProgress(id, 0); + + EntityMessage msg = db.message().getMessage(message); + EntityOperation.queue(context, db, msg, EntityOperation.ATTACHMENT, sequence); + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); } + + return null; } - } + + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(context, owner, ex); + } + }.execute(context, owner, args, "attachment:fetch"); } private class NameResolveInfo { diff --git a/app/src/main/java/eu/faircode/email/FragmentAccount.java b/app/src/main/java/eu/faircode/email/FragmentAccount.java index afb69993d0..d51160c3c1 100644 --- a/app/src/main/java/eu/faircode/email/FragmentAccount.java +++ b/app/src/main/java/eu/faircode/email/FragmentAccount.java @@ -267,43 +267,7 @@ public class FragmentAccount extends FragmentBase { btnAutoConfig.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - Bundle args = new Bundle(); - args.putString("domain", etDomain.getText().toString()); - - new SimpleTask() { - @Override - protected void onPreExecute(Bundle args) { - etDomain.setEnabled(false); - btnAutoConfig.setEnabled(false); - } - - @Override - protected void onPostExecute(Bundle args) { - etDomain.setEnabled(true); - btnAutoConfig.setEnabled(true); - } - - @Override - protected EmailProvider onExecute(Context context, Bundle args) throws Throwable { - String domain = args.getString("domain"); - return EmailProvider.fromDomain(context, domain); - } - - @Override - protected void onExecuted(Bundle args, EmailProvider provider) { - etHost.setText(provider.imap_host); - etPort.setText(Integer.toString(provider.imap_port)); - cbStartTls.setChecked(provider.imap_starttls); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - if (ex instanceof IllegalArgumentException || ex instanceof UnknownHostException) - Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); - else - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); - } - }.execute(FragmentAccount.this, args, "account:config"); + onAutoConfig(); } }); @@ -418,565 +382,613 @@ public class FragmentAccount extends FragmentBase { btnCheck.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - EmailProvider provider = (EmailProvider) spProvider.getSelectedItem(); + onCheck(); + } + }); - Bundle args = new Bundle(); - args.putLong("id", id); - args.putString("host", etHost.getText().toString()); - args.putBoolean("starttls", cbStartTls.isChecked()); - args.putBoolean("insecure", cbInsecure.isChecked()); - args.putString("port", etPort.getText().toString()); - args.putString("user", etUser.getText().toString()); - args.putString("password", tilPassword.getEditText().getText().toString()); - args.putString("realm", etRealm.getText().toString()); - args.putInt("auth_type", authorized == null ? Helper.AUTH_TYPE_PASSWORD : provider.getAuthType()); - - new SimpleTask() { - @Override - protected void onPreExecute(Bundle args) { - Helper.setViewsEnabled(view, false); - btnAuthorize.setEnabled(false); - btnCheck.setEnabled(false); - pbCheck.setVisibility(View.VISIBLE); - tvIdle.setVisibility(View.GONE); - grpFolders.setVisibility(View.GONE); - btnSave.setVisibility(View.GONE); - tvError.setVisibility(View.GONE); - } + btnSave.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onSave(); + } + }); - @Override - protected void onPostExecute(Bundle args) { - Helper.setViewsEnabled(view, true); - btnAuthorize.setEnabled(true); - btnCheck.setEnabled(true); - pbCheck.setVisibility(View.GONE); - } + adapter = new ArrayAdapter<>(getContext(), R.layout.spinner_item1, android.R.id.text1, new ArrayList()); + adapter.setDropDownViewResource(R.layout.spinner_item1_dropdown); - @Override - protected CheckResult onExecute(Context context, Bundle args) throws Throwable { - long id = args.getLong("id"); - String host = args.getString("host"); - boolean starttls = args.getBoolean("starttls"); - boolean insecure = args.getBoolean("insecure"); - String port = args.getString("port"); - String user = args.getString("user"); - String password = args.getString("password"); - String realm = args.getString("realm"); - int auth_type = args.getInt("auth_type"); - - if (TextUtils.isEmpty(host)) - throw new IllegalArgumentException(context.getString(R.string.title_no_host)); - if (TextUtils.isEmpty(port)) - port = (starttls ? "143" : "993"); - if (TextUtils.isEmpty(user)) - throw new IllegalArgumentException(context.getString(R.string.title_no_user)); - if (TextUtils.isEmpty(password) && !insecure) - throw new IllegalArgumentException(context.getString(R.string.title_no_password)); - - if (TextUtils.isEmpty(realm)) - realm = null; - - CheckResult result = new CheckResult(); - result.folders = new ArrayList<>(); - - // Check IMAP server / get folders - Properties props = MessageHelper.getSessionProperties(auth_type, realm, insecure); - Session isession = Session.getInstance(props, null); - isession.setDebug(true); - IMAPStore istore = null; - try { - istore = (IMAPStore) isession.getStore(starttls ? "imap" : "imaps"); - try { - istore.connect(host, Integer.parseInt(port), user, password); - } catch (AuthenticationFailedException ex) { - if (auth_type == Helper.AUTH_TYPE_GMAIL) { - password = Helper.refreshToken(context, "com.google", user, password); - istore.connect(host, Integer.parseInt(port), user, password); - } else - throw ex; - } + spDrafts.setAdapter(adapter); + spSent.setAdapter(adapter); + spAll.setAdapter(adapter); + spTrash.setAdapter(adapter); + spJunk.setAdapter(adapter); - result.idle = istore.hasCapability("IDLE"); - - boolean archive = false; - boolean drafts = false; - boolean trash = false; - boolean sent = false; - boolean junk = false; - EntityFolder altArchive = null; - EntityFolder altDrafts = null; - EntityFolder altTrash = null; - EntityFolder altSent = null; - EntityFolder altJunk = null; - - for (Folder ifolder : istore.getDefaultFolder().list("*")) { - // Check folder attributes - String type = null; - boolean selectable = true; - String[] attrs = ((IMAPFolder) ifolder).getAttributes(); - Log.i(ifolder.getFullName() + " attrs=" + TextUtils.join(" ", attrs)); - for (String attr : attrs) { - if ("\\Noselect".equals(attr) || "\\NonExistent".equals(attr)) - selectable = false; - if (attr.startsWith("\\")) { - int index = EntityFolder.SYSTEM_FOLDER_ATTR.indexOf(attr.substring(1)); - if (index >= 0) { - type = EntityFolder.SYSTEM_FOLDER_TYPE.get(index); - break; - } - } - } + // Initialize + Helper.setViewsEnabled(view, false); - if (selectable) { - // Create entry - DB db = DB.getInstance(context); - EntityFolder folder = db.folder().getFolderByName(id, ifolder.getFullName()); - if (folder == null) { - int sync = EntityFolder.SYSTEM_FOLDER_SYNC.indexOf(type); - folder = new EntityFolder(); - folder.name = ifolder.getFullName(); - folder.type = (type == null ? EntityFolder.USER : type); - folder.synchronize = (sync >= 0); - folder.download = (sync < 0 ? true : EntityFolder.SYSTEM_FOLDER_DOWNLOAD.get(sync)); - folder.sync_days = EntityFolder.DEFAULT_SYNC; - folder.keep_days = EntityFolder.DEFAULT_KEEP; - } - result.folders.add(folder); - - if (type == null) { - if (folder.name.toLowerCase().contains("archive")) - altArchive = folder; - if (folder.name.toLowerCase().contains("draft")) - altDrafts = folder; - if (folder.name.toLowerCase().contains("trash")) - altTrash = folder; - if (folder.name.toLowerCase().contains("sent")) - altSent = folder; - if (folder.name.toLowerCase().contains("junk")) - altJunk = folder; - } else { - if (EntityFolder.ARCHIVE.equals(type)) - archive = true; - else if (EntityFolder.DRAFTS.equals(type)) - drafts = true; - else if (EntityFolder.TRASH.equals(type)) - trash = true; - else if (EntityFolder.SENT.equals(type)) - sent = true; - else if (EntityFolder.JUNK.equals(type)) - junk = true; - } + btnAutoConfig.setEnabled(false); - Log.i(folder.name + " id=" + folder.id + - " type=" + folder.type + " attr=" + TextUtils.join(",", attrs)); - } - } + btnAuthorize.setVisibility(View.GONE); + cbStartTls.setVisibility(View.GONE); + cbInsecure.setVisibility(View.GONE); + tilPassword.setPasswordVisibilityToggleEnabled(id < 0); - if (!archive && altArchive != null) - altArchive.type = EntityFolder.ARCHIVE; - if (!drafts && altDrafts != null) - altDrafts.type = EntityFolder.DRAFTS; - if (!trash && altTrash != null) - altTrash.type = EntityFolder.TRASH; - if (!sent && altSent != null) - altSent.type = EntityFolder.SENT; - if (!junk && altJunk != null) - altJunk.type = EntityFolder.JUNK; - - } finally { - if (istore != null) - istore.close(); - } + btnAdvanced.setVisibility(View.GONE); - return result; - } + tvIdle.setVisibility(View.GONE); - @Override - protected void onExecuted(Bundle args, CheckResult result) { - tvIdle.setVisibility(result.idle ? View.GONE : View.VISIBLE); + btnCheck.setVisibility(View.GONE); + pbCheck.setVisibility(View.GONE); - setFolders(result.folders); + btnSave.setVisibility(View.GONE); + pbSave.setVisibility(View.GONE); + tvError.setVisibility(View.GONE); - new Handler().post(new Runnable() { - @Override - public void run() { - ((ScrollView) view).smoothScrollTo(0, btnSave.getBottom()); - } - }); + grpServer.setVisibility(View.GONE); + grpAuthorize.setVisibility(View.GONE); + grpAdvanced.setVisibility(View.GONE); + grpFolders.setVisibility(View.GONE); + + return view; + } + + private void onAutoConfig() { + Bundle args = new Bundle(); + args.putString("domain", etDomain.getText().toString()); + + new SimpleTask() { + @Override + protected void onPreExecute(Bundle args) { + etDomain.setEnabled(false); + btnAutoConfig.setEnabled(false); + } + + @Override + protected void onPostExecute(Bundle args) { + etDomain.setEnabled(true); + btnAutoConfig.setEnabled(true); + } + + @Override + protected EmailProvider onExecute(Context context, Bundle args) throws Throwable { + String domain = args.getString("domain"); + return EmailProvider.fromDomain(context, domain); + } + + @Override + protected void onExecuted(Bundle args, EmailProvider provider) { + etHost.setText(provider.imap_host); + etPort.setText(Integer.toString(provider.imap_port)); + cbStartTls.setChecked(provider.imap_starttls); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + if (ex instanceof IllegalArgumentException || ex instanceof UnknownHostException) + Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); + else + Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + } + }.execute(FragmentAccount.this, args, "account:config"); + } + + private void onCheck() { + EmailProvider provider = (EmailProvider) spProvider.getSelectedItem(); + + Bundle args = new Bundle(); + args.putLong("id", id); + args.putString("host", etHost.getText().toString()); + args.putBoolean("starttls", cbStartTls.isChecked()); + args.putBoolean("insecure", cbInsecure.isChecked()); + args.putString("port", etPort.getText().toString()); + args.putString("user", etUser.getText().toString()); + args.putString("password", tilPassword.getEditText().getText().toString()); + args.putString("realm", etRealm.getText().toString()); + args.putInt("auth_type", authorized == null ? Helper.AUTH_TYPE_PASSWORD : provider.getAuthType()); + + new SimpleTask() { + @Override + protected void onPreExecute(Bundle args) { + Helper.setViewsEnabled(view, false); + btnAuthorize.setEnabled(false); + btnCheck.setEnabled(false); + pbCheck.setVisibility(View.VISIBLE); + tvIdle.setVisibility(View.GONE); + grpFolders.setVisibility(View.GONE); + btnSave.setVisibility(View.GONE); + tvError.setVisibility(View.GONE); + } + + @Override + protected void onPostExecute(Bundle args) { + Helper.setViewsEnabled(view, true); + btnAuthorize.setEnabled(true); + btnCheck.setEnabled(true); + pbCheck.setVisibility(View.GONE); + } + + @Override + protected CheckResult onExecute(Context context, Bundle args) throws Throwable { + long id = args.getLong("id"); + String host = args.getString("host"); + boolean starttls = args.getBoolean("starttls"); + boolean insecure = args.getBoolean("insecure"); + String port = args.getString("port"); + String user = args.getString("user"); + String password = args.getString("password"); + String realm = args.getString("realm"); + int auth_type = args.getInt("auth_type"); + + if (TextUtils.isEmpty(host)) + throw new IllegalArgumentException(context.getString(R.string.title_no_host)); + if (TextUtils.isEmpty(port)) + port = (starttls ? "143" : "993"); + if (TextUtils.isEmpty(user)) + throw new IllegalArgumentException(context.getString(R.string.title_no_user)); + if (TextUtils.isEmpty(password) && !insecure) + throw new IllegalArgumentException(context.getString(R.string.title_no_password)); + + if (TextUtils.isEmpty(realm)) + realm = null; + + CheckResult result = new CheckResult(); + result.folders = new ArrayList<>(); + + // Check IMAP server / get folders + Properties props = MessageHelper.getSessionProperties(auth_type, realm, insecure); + Session isession = Session.getInstance(props, null); + isession.setDebug(true); + IMAPStore istore = null; + try { + istore = (IMAPStore) isession.getStore(starttls ? "imap" : "imaps"); + try { + istore.connect(host, Integer.parseInt(port), user, password); + } catch (AuthenticationFailedException ex) { + if (auth_type == Helper.AUTH_TYPE_GMAIL) { + password = Helper.refreshToken(context, "com.google", user, password); + istore.connect(host, Integer.parseInt(port), user, password); + } else + throw ex; } - @Override - protected void onException(Bundle args, Throwable ex) { - grpFolders.setVisibility(View.GONE); - btnSave.setVisibility(View.GONE); - - if (ex instanceof IllegalArgumentException) - Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); - else { - tvError.setText(Helper.formatThrowable(ex)); - tvError.setVisibility(View.VISIBLE); - new Handler().post(new Runnable() { - @Override - public void run() { - ((ScrollView) view).smoothScrollTo(0, tvError.getBottom()); + result.idle = istore.hasCapability("IDLE"); + + boolean archive = false; + boolean drafts = false; + boolean trash = false; + boolean sent = false; + boolean junk = false; + EntityFolder altArchive = null; + EntityFolder altDrafts = null; + EntityFolder altTrash = null; + EntityFolder altSent = null; + EntityFolder altJunk = null; + + for (Folder ifolder : istore.getDefaultFolder().list("*")) { + // Check folder attributes + String type = null; + boolean selectable = true; + String[] attrs = ((IMAPFolder) ifolder).getAttributes(); + Log.i(ifolder.getFullName() + " attrs=" + TextUtils.join(" ", attrs)); + for (String attr : attrs) { + if ("\\Noselect".equals(attr) || "\\NonExistent".equals(attr)) + selectable = false; + if (attr.startsWith("\\")) { + int index = EntityFolder.SYSTEM_FOLDER_ATTR.indexOf(attr.substring(1)); + if (index >= 0) { + type = EntityFolder.SYSTEM_FOLDER_TYPE.get(index); + break; } - }); + } + } + + if (selectable) { + // Create entry + DB db = DB.getInstance(context); + EntityFolder folder = db.folder().getFolderByName(id, ifolder.getFullName()); + if (folder == null) { + int sync = EntityFolder.SYSTEM_FOLDER_SYNC.indexOf(type); + folder = new EntityFolder(); + folder.name = ifolder.getFullName(); + folder.type = (type == null ? EntityFolder.USER : type); + folder.synchronize = (sync >= 0); + folder.download = (sync < 0 ? true : EntityFolder.SYSTEM_FOLDER_DOWNLOAD.get(sync)); + folder.sync_days = EntityFolder.DEFAULT_SYNC; + folder.keep_days = EntityFolder.DEFAULT_KEEP; + } + result.folders.add(folder); + + if (type == null) { + if (folder.name.toLowerCase().contains("archive")) + altArchive = folder; + if (folder.name.toLowerCase().contains("draft")) + altDrafts = folder; + if (folder.name.toLowerCase().contains("trash")) + altTrash = folder; + if (folder.name.toLowerCase().contains("sent")) + altSent = folder; + if (folder.name.toLowerCase().contains("junk")) + altJunk = folder; + } else { + if (EntityFolder.ARCHIVE.equals(type)) + archive = true; + else if (EntityFolder.DRAFTS.equals(type)) + drafts = true; + else if (EntityFolder.TRASH.equals(type)) + trash = true; + else if (EntityFolder.SENT.equals(type)) + sent = true; + else if (EntityFolder.JUNK.equals(type)) + junk = true; + } + + Log.i(folder.name + " id=" + folder.id + + " type=" + folder.type + " attr=" + TextUtils.join(",", attrs)); } } - }.execute(FragmentAccount.this, args, "account:check"); + + if (!archive && altArchive != null) + altArchive.type = EntityFolder.ARCHIVE; + if (!drafts && altDrafts != null) + altDrafts.type = EntityFolder.DRAFTS; + if (!trash && altTrash != null) + altTrash.type = EntityFolder.TRASH; + if (!sent && altSent != null) + altSent.type = EntityFolder.SENT; + if (!junk && altJunk != null) + altJunk.type = EntityFolder.JUNK; + + } finally { + if (istore != null) + istore.close(); + } + + return result; } - }); - btnSave.setOnClickListener(new View.OnClickListener() { @Override - public void onClick(View v) { - EmailProvider provider = (EmailProvider) spProvider.getSelectedItem(); + protected void onExecuted(Bundle args, CheckResult result) { + tvIdle.setVisibility(result.idle ? View.GONE : View.VISIBLE); - EntityFolder drafts = (EntityFolder) spDrafts.getSelectedItem(); - EntityFolder sent = (EntityFolder) spSent.getSelectedItem(); - EntityFolder all = (EntityFolder) spAll.getSelectedItem(); - EntityFolder trash = (EntityFolder) spTrash.getSelectedItem(); - EntityFolder junk = (EntityFolder) spJunk.getSelectedItem(); - - if (drafts != null && drafts.type == null) - drafts = null; - if (sent != null && sent.type == null) - sent = null; - if (all != null && all.type == null) - all = null; - if (trash != null && trash.type == null) - trash = null; - if (junk != null && junk.type == null) - junk = null; - - Bundle args = new Bundle(); - args.putLong("id", id); - - args.putInt("auth_type", authorized == null ? Helper.AUTH_TYPE_PASSWORD : provider.getAuthType()); - args.putString("host", etHost.getText().toString()); - args.putBoolean("starttls", cbStartTls.isChecked()); - args.putBoolean("insecure", cbInsecure.isChecked()); - args.putString("port", etPort.getText().toString()); - args.putString("user", etUser.getText().toString()); - args.putString("password", tilPassword.getEditText().getText().toString()); - args.putString("realm", etRealm.getText().toString()); - - args.putString("name", etName.getText().toString()); - args.putInt("color", color); - - args.putBoolean("synchronize", cbSynchronize.isChecked()); - args.putBoolean("primary", cbPrimary.isChecked()); - args.putBoolean("notify", cbNotify.isChecked()); - args.putBoolean("browse", cbBrowse.isChecked()); - args.putString("interval", etInterval.getText().toString()); - args.putString("prefix", etPrefix.getText().toString()); - - args.putSerializable("drafts", drafts); - args.putSerializable("sent", sent); - args.putSerializable("all", all); - args.putSerializable("trash", trash); - args.putSerializable("junk", junk); - - new SimpleTask() { - @Override - protected void onPreExecute(Bundle args) { - Helper.setViewsEnabled(view, false); - btnAuthorize.setEnabled(false); - btnCheck.setEnabled(false); - btnSave.setEnabled(false); - pbSave.setVisibility(View.VISIBLE); - tvError.setVisibility(View.GONE); - } + setFolders(result.folders); + new Handler().post(new Runnable() { @Override - protected void onPostExecute(Bundle args) { - Helper.setViewsEnabled(view, true); - btnAuthorize.setEnabled(true); - btnCheck.setEnabled(true); - btnSave.setEnabled(true); - pbSave.setVisibility(View.GONE); + public void run() { + ((ScrollView) view).smoothScrollTo(0, btnSave.getBottom()); } + }); + } - @Override - protected Void onExecute(Context context, Bundle args) throws Throwable { - long id = args.getLong("id"); - - int auth_type = args.getInt("auth_type"); - String host = args.getString("host"); - boolean starttls = args.getBoolean("starttls"); - boolean insecure = args.getBoolean("insecure"); - String port = args.getString("port"); - String user = args.getString("user"); - String password = args.getString("password"); - String realm = args.getString("realm"); - - String name = args.getString("name"); - Integer color = args.getInt("color"); - - boolean synchronize = args.getBoolean("synchronize"); - boolean primary = args.getBoolean("primary"); - boolean notify = args.getBoolean("notify"); - boolean browse = args.getBoolean("browse"); - String interval = args.getString("interval"); - String prefix = args.getString("prefix"); - - EntityFolder drafts = (EntityFolder) args.getSerializable("drafts"); - EntityFolder sent = (EntityFolder) args.getSerializable("sent"); - EntityFolder all = (EntityFolder) args.getSerializable("all"); - EntityFolder trash = (EntityFolder) args.getSerializable("trash"); - EntityFolder junk = (EntityFolder) args.getSerializable("junk"); - - if (TextUtils.isEmpty(host)) - throw new IllegalArgumentException(context.getString(R.string.title_no_host)); - if (TextUtils.isEmpty(port)) - port = (starttls ? "143" : "993"); - if (TextUtils.isEmpty(user)) - throw new IllegalArgumentException(context.getString(R.string.title_no_user)); - if (synchronize && TextUtils.isEmpty(password) && !insecure) - throw new IllegalArgumentException(context.getString(R.string.title_no_password)); - if (TextUtils.isEmpty(interval)) - interval = "19"; - if (synchronize && drafts == null) - throw new IllegalArgumentException(context.getString(R.string.title_no_drafts)); - - if (TextUtils.isEmpty(realm)) - realm = null; - - if (Color.TRANSPARENT == color) - color = null; - if (TextUtils.isEmpty(prefix)) - prefix = null; - - Character separator = null; - long now = new Date().getTime(); - - DB db = DB.getInstance(context); - EntityAccount account = db.account().getAccount(id); - - String accountRealm = (account == null ? null : account.realm); - - boolean check = (synchronize && (account == null || - !host.equals(account.host) || Integer.parseInt(port) != account.port || - !user.equals(account.user) || !password.equals(account.password) || - (realm == null ? accountRealm != null : !realm.equals(accountRealm)))); - boolean reload = (check || account == null || - (account.prefix == null ? prefix != null : !account.prefix.equals(prefix)) || - account.synchronize != synchronize || - !account.poll_interval.equals(Integer.parseInt(interval))); - - // Check IMAP server - if (check) { - Properties props = MessageHelper.getSessionProperties(auth_type, realm, insecure); - Session isession = Session.getInstance(props, null); - isession.setDebug(true); - - IMAPStore istore = null; - try { - istore = (IMAPStore) isession.getStore(starttls ? "imap" : "imaps"); - try { - istore.connect(host, Integer.parseInt(port), user, password); - } catch (AuthenticationFailedException ex) { - if (auth_type == Helper.AUTH_TYPE_GMAIL) { - password = Helper.refreshToken(context, "com.google", user, password); - istore.connect(host, Integer.parseInt(port), user, password); - } else - throw ex; - } - separator = istore.getDefaultFolder().getSeparator(); - } finally { - if (istore != null) - istore.close(); - } + @Override + protected void onException(Bundle args, Throwable ex) { + grpFolders.setVisibility(View.GONE); + btnSave.setVisibility(View.GONE); + + if (ex instanceof IllegalArgumentException) + Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); + else { + tvError.setText(Helper.formatThrowable(ex)); + tvError.setVisibility(View.VISIBLE); + new Handler().post(new Runnable() { + @Override + public void run() { + ((ScrollView) view).smoothScrollTo(0, tvError.getBottom()); } + }); + } + } + }.execute(FragmentAccount.this, args, "account:check"); + } - if (TextUtils.isEmpty(name)) - name = user; + private void onSave() { + EmailProvider provider = (EmailProvider) spProvider.getSelectedItem(); - try { - db.beginTransaction(); + EntityFolder drafts = (EntityFolder) spDrafts.getSelectedItem(); + EntityFolder sent = (EntityFolder) spSent.getSelectedItem(); + EntityFolder all = (EntityFolder) spAll.getSelectedItem(); + EntityFolder trash = (EntityFolder) spTrash.getSelectedItem(); + EntityFolder junk = (EntityFolder) spJunk.getSelectedItem(); + + if (drafts != null && drafts.type == null) + drafts = null; + if (sent != null && sent.type == null) + sent = null; + if (all != null && all.type == null) + all = null; + if (trash != null && trash.type == null) + trash = null; + if (junk != null && junk.type == null) + junk = null; - boolean update = (account != null); - if (account == null) - account = new EntityAccount(); + Bundle args = new Bundle(); + args.putLong("id", id); - account.auth_type = auth_type; - account.host = host; - account.starttls = starttls; - account.insecure = insecure; - account.port = Integer.parseInt(port); - account.user = user; - account.password = password; - account.realm = realm; + args.putInt("auth_type", authorized == null ? Helper.AUTH_TYPE_PASSWORD : provider.getAuthType()); + args.putString("host", etHost.getText().toString()); + args.putBoolean("starttls", cbStartTls.isChecked()); + args.putBoolean("insecure", cbInsecure.isChecked()); + args.putString("port", etPort.getText().toString()); + args.putString("user", etUser.getText().toString()); + args.putString("password", tilPassword.getEditText().getText().toString()); + args.putString("realm", etRealm.getText().toString()); + + args.putString("name", etName.getText().toString()); + args.putInt("color", color); + + args.putBoolean("synchronize", cbSynchronize.isChecked()); + args.putBoolean("primary", cbPrimary.isChecked()); + args.putBoolean("notify", cbNotify.isChecked()); + args.putBoolean("browse", cbBrowse.isChecked()); + args.putString("interval", etInterval.getText().toString()); + args.putString("prefix", etPrefix.getText().toString()); + + args.putSerializable("drafts", drafts); + args.putSerializable("sent", sent); + args.putSerializable("all", all); + args.putSerializable("trash", trash); + args.putSerializable("junk", junk); + + new SimpleTask() { + @Override + protected void onPreExecute(Bundle args) { + Helper.setViewsEnabled(view, false); + btnAuthorize.setEnabled(false); + btnCheck.setEnabled(false); + btnSave.setEnabled(false); + pbSave.setVisibility(View.VISIBLE); + tvError.setVisibility(View.GONE); + } - account.name = name; - account.color = color; + @Override + protected void onPostExecute(Bundle args) { + Helper.setViewsEnabled(view, true); + btnAuthorize.setEnabled(true); + btnCheck.setEnabled(true); + btnSave.setEnabled(true); + pbSave.setVisibility(View.GONE); + } - account.synchronize = synchronize; - account.primary = (account.synchronize && primary); - account.notify = notify; - account.browse = browse; - account.poll_interval = Integer.parseInt(interval); - account.prefix = prefix; + @Override + protected Void onExecute(Context context, Bundle args) throws Throwable { + long id = args.getLong("id"); - if (!update) - account.created = now; + int auth_type = args.getInt("auth_type"); + String host = args.getString("host"); + boolean starttls = args.getBoolean("starttls"); + boolean insecure = args.getBoolean("insecure"); + String port = args.getString("port"); + String user = args.getString("user"); + String password = args.getString("password"); + String realm = args.getString("realm"); + + String name = args.getString("name"); + Integer color = args.getInt("color"); + + boolean synchronize = args.getBoolean("synchronize"); + boolean primary = args.getBoolean("primary"); + boolean notify = args.getBoolean("notify"); + boolean browse = args.getBoolean("browse"); + String interval = args.getString("interval"); + String prefix = args.getString("prefix"); + + EntityFolder drafts = (EntityFolder) args.getSerializable("drafts"); + EntityFolder sent = (EntityFolder) args.getSerializable("sent"); + EntityFolder all = (EntityFolder) args.getSerializable("all"); + EntityFolder trash = (EntityFolder) args.getSerializable("trash"); + EntityFolder junk = (EntityFolder) args.getSerializable("junk"); + + if (TextUtils.isEmpty(host)) + throw new IllegalArgumentException(context.getString(R.string.title_no_host)); + if (TextUtils.isEmpty(port)) + port = (starttls ? "143" : "993"); + if (TextUtils.isEmpty(user)) + throw new IllegalArgumentException(context.getString(R.string.title_no_user)); + if (synchronize && TextUtils.isEmpty(password) && !insecure) + throw new IllegalArgumentException(context.getString(R.string.title_no_password)); + if (TextUtils.isEmpty(interval)) + interval = "19"; + if (synchronize && drafts == null) + throw new IllegalArgumentException(context.getString(R.string.title_no_drafts)); + + if (TextUtils.isEmpty(realm)) + realm = null; + + if (Color.TRANSPARENT == color) + color = null; + if (TextUtils.isEmpty(prefix)) + prefix = null; + + Character separator = null; + long now = new Date().getTime(); + + DB db = DB.getInstance(context); + EntityAccount account = db.account().getAccount(id); + + String accountRealm = (account == null ? null : account.realm); + + boolean check = (synchronize && (account == null || + !host.equals(account.host) || Integer.parseInt(port) != account.port || + !user.equals(account.user) || !password.equals(account.password) || + (realm == null ? accountRealm != null : !realm.equals(accountRealm)))); + boolean reload = (check || account == null || + (account.prefix == null ? prefix != null : !account.prefix.equals(prefix)) || + account.synchronize != synchronize || + !account.poll_interval.equals(Integer.parseInt(interval))); + + // Check IMAP server + if (check) { + Properties props = MessageHelper.getSessionProperties(auth_type, realm, insecure); + Session isession = Session.getInstance(props, null); + isession.setDebug(true); + + IMAPStore istore = null; + try { + istore = (IMAPStore) isession.getStore(starttls ? "imap" : "imaps"); + try { + istore.connect(host, Integer.parseInt(port), user, password); + } catch (AuthenticationFailedException ex) { + if (auth_type == Helper.AUTH_TYPE_GMAIL) { + password = Helper.refreshToken(context, "com.google", user, password); + istore.connect(host, Integer.parseInt(port), user, password); + } else + throw ex; + } + separator = istore.getDefaultFolder().getSeparator(); + } finally { + if (istore != null) + istore.close(); + } + } - account.error = null; + if (TextUtils.isEmpty(name)) + name = user; - if (synchronize) - account.last_connected = now; + try { + db.beginTransaction(); - if (account.primary) - db.account().resetPrimary(); + boolean update = (account != null); + if (account == null) + account = new EntityAccount(); - if (!Helper.isPro(context)) { - account.color = null; - account.notify = false; - } + account.auth_type = auth_type; + account.host = host; + account.starttls = starttls; + account.insecure = insecure; + account.port = Integer.parseInt(port); + account.user = user; + account.password = password; + account.realm = realm; - if (update) - db.account().updateAccount(account); - else - account.id = db.account().insertAccount(account); - - // Make sure the channel exists on commit - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) - if (account.notify) { - // Add or update notification channel - account.deleteNotificationChannel(context); - account.createNotificationChannel(context); - } else if (!account.synchronize) - account.deleteNotificationChannel(context); - - List folders = new ArrayList<>(); - - EntityFolder inbox = new EntityFolder(); - inbox.name = "INBOX"; - inbox.type = EntityFolder.INBOX; - inbox.synchronize = true; - inbox.unified = true; - inbox.notify = true; - inbox.sync_days = EntityFolder.DEFAULT_SYNC; - inbox.keep_days = EntityFolder.DEFAULT_KEEP; - - folders.add(inbox); - - if (drafts != null) { - drafts.type = EntityFolder.DRAFTS; - folders.add(drafts); - } + account.name = name; + account.color = color; - if (sent != null) { - sent.type = EntityFolder.SENT; - folders.add(sent); - } - if (all != null) { - all.type = EntityFolder.ARCHIVE; - folders.add(all); - } - if (trash != null) { - trash.type = EntityFolder.TRASH; - folders.add(trash); - } - if (junk != null) { - junk.type = EntityFolder.JUNK; - folders.add(junk); - } + account.synchronize = synchronize; + account.primary = (account.synchronize && primary); + account.notify = notify; + account.browse = browse; + account.poll_interval = Integer.parseInt(interval); + account.prefix = prefix; - db.folder().setFoldersUser(account.id); - - for (EntityFolder folder : folders) { - folder.level = EntityFolder.getLevel(separator, folder.name); - if (account.prefix != null && folder.name.startsWith(account.prefix + separator)) - folder.display = folder.name.substring(account.prefix.length() + 1); - - EntityFolder existing = db.folder().getFolderByName(account.id, folder.name); - if (existing == null) { - folder.account = account.id; - Log.i("Creating folder=" + folder.name + " (" + folder.type + ")"); - folder.id = db.folder().insertFolder(folder); - } else { - db.folder().setFolderType(existing.id, folder.type); - db.folder().setFolderLevel(existing.id, folder.level); - } - } + if (!update) + account.created = now; - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } + account.error = null; - if (reload) - ServiceSynchronize.reload(context, "save account"); + if (synchronize) + account.last_connected = now; - if (!synchronize) { - NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - nm.cancel("receive", account.id.intValue()); - } + if (account.primary) + db.account().resetPrimary(); - return null; + if (!Helper.isPro(context)) { + account.color = null; + account.notify = false; } - @Override - protected void onExecuted(Bundle args, Void data) { - getFragmentManager().popBackStack(); + if (update) + db.account().updateAccount(account); + else + account.id = db.account().insertAccount(account); + + // Make sure the channel exists on commit + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + if (account.notify) { + // Add or update notification channel + account.deleteNotificationChannel(context); + account.createNotificationChannel(context); + } else if (!account.synchronize) + account.deleteNotificationChannel(context); + + List folders = new ArrayList<>(); + + EntityFolder inbox = new EntityFolder(); + inbox.name = "INBOX"; + inbox.type = EntityFolder.INBOX; + inbox.synchronize = true; + inbox.unified = true; + inbox.notify = true; + inbox.sync_days = EntityFolder.DEFAULT_SYNC; + inbox.keep_days = EntityFolder.DEFAULT_KEEP; + + folders.add(inbox); + + if (drafts != null) { + drafts.type = EntityFolder.DRAFTS; + folders.add(drafts); } - @Override - protected void onException(Bundle args, Throwable ex) { - if (ex instanceof IllegalArgumentException) - Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); - else { - tvError.setText(Helper.formatThrowable(ex)); - tvError.setVisibility(View.VISIBLE); - new Handler().post(new Runnable() { - @Override - public void run() { - ((ScrollView) view).smoothScrollTo(0, tvError.getBottom()); - } - }); - } + if (sent != null) { + sent.type = EntityFolder.SENT; + folders.add(sent); + } + if (all != null) { + all.type = EntityFolder.ARCHIVE; + folders.add(all); + } + if (trash != null) { + trash.type = EntityFolder.TRASH; + folders.add(trash); + } + if (junk != null) { + junk.type = EntityFolder.JUNK; + folders.add(junk); } - }.execute(FragmentAccount.this, args, "account:save"); - } - }); - - adapter = new ArrayAdapter<>(getContext(), R.layout.spinner_item1, android.R.id.text1, new ArrayList()); - adapter.setDropDownViewResource(R.layout.spinner_item1_dropdown); - - spDrafts.setAdapter(adapter); - spSent.setAdapter(adapter); - spAll.setAdapter(adapter); - spTrash.setAdapter(adapter); - spJunk.setAdapter(adapter); - - // Initialize - Helper.setViewsEnabled(view, false); - - btnAutoConfig.setEnabled(false); - btnAuthorize.setVisibility(View.GONE); - cbStartTls.setVisibility(View.GONE); - cbInsecure.setVisibility(View.GONE); - tilPassword.setPasswordVisibilityToggleEnabled(id < 0); + db.folder().setFoldersUser(account.id); + + for (EntityFolder folder : folders) { + folder.level = EntityFolder.getLevel(separator, folder.name); + if (account.prefix != null && folder.name.startsWith(account.prefix + separator)) + folder.display = folder.name.substring(account.prefix.length() + 1); + + EntityFolder existing = db.folder().getFolderByName(account.id, folder.name); + if (existing == null) { + folder.account = account.id; + Log.i("Creating folder=" + folder.name + " (" + folder.type + ")"); + folder.id = db.folder().insertFolder(folder); + } else { + db.folder().setFolderType(existing.id, folder.type); + db.folder().setFolderLevel(existing.id, folder.level); + } + } - btnAdvanced.setVisibility(View.GONE); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } - tvIdle.setVisibility(View.GONE); + if (reload) + ServiceSynchronize.reload(context, "save account"); - btnCheck.setVisibility(View.GONE); - pbCheck.setVisibility(View.GONE); + if (!synchronize) { + NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + nm.cancel("receive", account.id.intValue()); + } - btnSave.setVisibility(View.GONE); - pbSave.setVisibility(View.GONE); - tvError.setVisibility(View.GONE); + return null; + } - grpServer.setVisibility(View.GONE); - grpAuthorize.setVisibility(View.GONE); - grpAdvanced.setVisibility(View.GONE); - grpFolders.setVisibility(View.GONE); + @Override + protected void onExecuted(Bundle args, Void data) { + getFragmentManager().popBackStack(); + } - return view; + @Override + protected void onException(Bundle args, Throwable ex) { + if (ex instanceof IllegalArgumentException) + Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); + else { + tvError.setText(Helper.formatThrowable(ex)); + tvError.setVisibility(View.VISIBLE); + new Handler().post(new Runnable() { + @Override + public void run() { + ((ScrollView) view).smoothScrollTo(0, tvError.getBottom()); + } + }); + } + } + }.execute(FragmentAccount.this, args, "account:save"); } @Override diff --git a/app/src/main/java/eu/faircode/email/FragmentFolder.java b/app/src/main/java/eu/faircode/email/FragmentFolder.java index e051a76a66..4b5041d489 100644 --- a/app/src/main/java/eu/faircode/email/FragmentFolder.java +++ b/app/src/main/java/eu/faircode/email/FragmentFolder.java @@ -126,150 +126,154 @@ public class FragmentFolder extends FragmentBase { btnSave.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - Bundle args = new Bundle(); - args.putLong("id", id); - args.putLong("account", account); - args.putString("name", etName.getText().toString()); - args.putString("display", etDisplay.getText().toString()); - args.putBoolean("hide", cbHide.isChecked()); - args.putBoolean("unified", cbUnified.isChecked()); - args.putBoolean("notify", cbNotify.getVisibility() == View.VISIBLE && cbNotify.isChecked()); - args.putBoolean("synchronize", cbSynchronize.isChecked()); - args.putBoolean("poll", cbPoll.isChecked()); - args.putBoolean("download", cbDownload.isChecked()); - args.putString("sync", etSyncDays.getText().toString()); - args.putString("keep", cbKeepAll.isChecked() - ? Integer.toString(Integer.MAX_VALUE) - : etKeepDays.getText().toString()); - - new SimpleTask() { - @Override - protected void onPreExecute(Bundle args) { - Helper.setViewsEnabled(view, false); - btnSave.setEnabled(false); - pbSave.setVisibility(View.VISIBLE); - } + onSave(); + } + }); - @Override - protected void onPostExecute(Bundle args) { - Helper.setViewsEnabled(view, true); - btnSave.setEnabled(true); - pbSave.setVisibility(View.GONE); - } + // Initialize + Helper.setViewsEnabled(view, false); + btnSave.setEnabled(false); + pbSave.setVisibility(View.GONE); + pbWait.setVisibility(View.VISIBLE); - @Override - protected Void onExecute(Context context, Bundle args) { - long id = args.getLong("id"); - long aid = args.getLong("account"); - String name = args.getString("name"); - String display = args.getString("display"); - boolean hide = args.getBoolean("hide"); - boolean unified = args.getBoolean("unified"); - boolean notify = args.getBoolean("notify"); - boolean synchronize = args.getBoolean("synchronize"); - boolean poll = args.getBoolean("poll"); - boolean download = args.getBoolean("download"); - String sync = args.getString("sync"); - String keep = args.getString("keep"); - - if (TextUtils.isEmpty(display) || display.equals(name)) - display = null; - int sync_days = (TextUtils.isEmpty(sync) ? EntityFolder.DEFAULT_SYNC : Integer.parseInt(sync)); - int keep_days = (TextUtils.isEmpty(keep) ? EntityFolder.DEFAULT_KEEP : Integer.parseInt(keep)); - if (keep_days < sync_days) - keep_days = sync_days; - - boolean reload; - DB db = DB.getInstance(context); - try { - db.beginTransaction(); - - EntityFolder folder = db.folder().getFolder(id); - - if (folder == null) { - reload = true; - Log.i("Creating folder=" + name); - - if (TextUtils.isEmpty(name)) - throw new IllegalArgumentException(getString(R.string.title_folder_name_missing)); - - EntityFolder create = new EntityFolder(); - create.account = aid; - create.name = name; - create.level = 0; - create.display = display; - create.hide = hide; - create.type = EntityFolder.USER; - create.unified = unified; - create.notify = notify; - create.synchronize = synchronize; - create.poll = poll; - create.download = download; - create.sync_days = sync_days; - create.keep_days = keep_days; - create.tbc = true; - db.folder().insertFolder(create); - } else { - reload = (!folder.synchronize.equals(synchronize) || - !folder.poll.equals(poll)); - - Calendar cal_keep = Calendar.getInstance(); - cal_keep.add(Calendar.DAY_OF_MONTH, -keep_days); - cal_keep.set(Calendar.HOUR_OF_DAY, 12); - cal_keep.set(Calendar.MINUTE, 0); - cal_keep.set(Calendar.SECOND, 0); - cal_keep.set(Calendar.MILLISECOND, 0); - - long keep_time = cal_keep.getTimeInMillis(); - if (keep_time < 0) - keep_time = 0; - - Log.i("Updating folder=" + name); - db.folder().setFolderProperties(id, - display, unified, notify, hide, - synchronize, poll, download, - sync_days, keep_days); - db.folder().setFolderError(id, null); - - db.message().deleteMessagesBefore(id, keep_time, true); - - EntityOperation.sync(db, folder.id); - } + return view; + } - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } + private void onSave() { + Bundle args = new Bundle(); + args.putLong("id", id); + args.putLong("account", account); + args.putString("name", etName.getText().toString()); + args.putString("display", etDisplay.getText().toString()); + args.putBoolean("hide", cbHide.isChecked()); + args.putBoolean("unified", cbUnified.isChecked()); + args.putBoolean("notify", cbNotify.getVisibility() == View.VISIBLE && cbNotify.isChecked()); + args.putBoolean("synchronize", cbSynchronize.isChecked()); + args.putBoolean("poll", cbPoll.isChecked()); + args.putBoolean("download", cbDownload.isChecked()); + args.putString("sync", etSyncDays.getText().toString()); + args.putString("keep", cbKeepAll.isChecked() + ? Integer.toString(Integer.MAX_VALUE) + : etKeepDays.getText().toString()); + + new SimpleTask() { + @Override + protected void onPreExecute(Bundle args) { + Helper.setViewsEnabled(view, false); + btnSave.setEnabled(false); + pbSave.setVisibility(View.VISIBLE); + } - if (reload) - ServiceSynchronize.reload(context, "save folder"); + @Override + protected void onPostExecute(Bundle args) { + Helper.setViewsEnabled(view, true); + btnSave.setEnabled(true); + pbSave.setVisibility(View.GONE); + } - return null; + @Override + protected Void onExecute(Context context, Bundle args) { + long id = args.getLong("id"); + long aid = args.getLong("account"); + String name = args.getString("name"); + String display = args.getString("display"); + boolean hide = args.getBoolean("hide"); + boolean unified = args.getBoolean("unified"); + boolean notify = args.getBoolean("notify"); + boolean synchronize = args.getBoolean("synchronize"); + boolean poll = args.getBoolean("poll"); + boolean download = args.getBoolean("download"); + String sync = args.getString("sync"); + String keep = args.getString("keep"); + + if (TextUtils.isEmpty(display) || display.equals(name)) + display = null; + int sync_days = (TextUtils.isEmpty(sync) ? EntityFolder.DEFAULT_SYNC : Integer.parseInt(sync)); + int keep_days = (TextUtils.isEmpty(keep) ? EntityFolder.DEFAULT_KEEP : Integer.parseInt(keep)); + if (keep_days < sync_days) + keep_days = sync_days; + + boolean reload; + DB db = DB.getInstance(context); + try { + db.beginTransaction(); + + EntityFolder folder = db.folder().getFolder(id); + + if (folder == null) { + reload = true; + Log.i("Creating folder=" + name); + + if (TextUtils.isEmpty(name)) + throw new IllegalArgumentException(getString(R.string.title_folder_name_missing)); + + EntityFolder create = new EntityFolder(); + create.account = aid; + create.name = name; + create.level = 0; + create.display = display; + create.hide = hide; + create.type = EntityFolder.USER; + create.unified = unified; + create.notify = notify; + create.synchronize = synchronize; + create.poll = poll; + create.download = download; + create.sync_days = sync_days; + create.keep_days = keep_days; + create.tbc = true; + db.folder().insertFolder(create); + } else { + reload = (!folder.synchronize.equals(synchronize) || + !folder.poll.equals(poll)); + + Calendar cal_keep = Calendar.getInstance(); + cal_keep.add(Calendar.DAY_OF_MONTH, -keep_days); + cal_keep.set(Calendar.HOUR_OF_DAY, 12); + cal_keep.set(Calendar.MINUTE, 0); + cal_keep.set(Calendar.SECOND, 0); + cal_keep.set(Calendar.MILLISECOND, 0); + + long keep_time = cal_keep.getTimeInMillis(); + if (keep_time < 0) + keep_time = 0; + + Log.i("Updating folder=" + name); + db.folder().setFolderProperties(id, + display, unified, notify, hide, + synchronize, poll, download, + sync_days, keep_days); + db.folder().setFolderError(id, null); + + db.message().deleteMessagesBefore(id, keep_time, true); + + EntityOperation.sync(db, folder.id); } - @Override - protected void onExecuted(Bundle args, Void data) { - getFragmentManager().popBackStack(); - } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } - @Override - protected void onException(Bundle args, Throwable ex) { - if (ex instanceof IllegalArgumentException) - Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); - else - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); - } - }.execute(FragmentFolder.this, args, "folder:save"); + if (reload) + ServiceSynchronize.reload(context, "save folder"); + + return null; } - }); - // Initialize - Helper.setViewsEnabled(view, false); - btnSave.setEnabled(false); - pbSave.setVisibility(View.GONE); - pbWait.setVisibility(View.VISIBLE); + @Override + protected void onExecuted(Bundle args, Void data) { + getFragmentManager().popBackStack(); + } - return view; + @Override + protected void onException(Bundle args, Throwable ex) { + if (ex instanceof IllegalArgumentException) + Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); + else + Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + } + }.execute(FragmentFolder.this, args, "folder:save"); } @Override diff --git a/app/src/main/java/eu/faircode/email/FragmentIdentity.java b/app/src/main/java/eu/faircode/email/FragmentIdentity.java index 5d78320ad3..800373a562 100644 --- a/app/src/main/java/eu/faircode/email/FragmentIdentity.java +++ b/app/src/main/java/eu/faircode/email/FragmentIdentity.java @@ -382,46 +382,7 @@ public class FragmentIdentity extends FragmentBase { btnAutoConfig.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - etDomain.setEnabled(false); - btnAutoConfig.setEnabled(false); - - Bundle args = new Bundle(); - args.putString("domain", etDomain.getText().toString()); - - new SimpleTask() { - @Override - protected void onPreExecute(Bundle args) { - etDomain.setEnabled(false); - btnAutoConfig.setEnabled(false); - } - - @Override - protected void onPostExecute(Bundle args) { - etDomain.setEnabled(true); - btnAutoConfig.setEnabled(true); - } - - @Override - protected EmailProvider onExecute(Context context, Bundle args) throws Throwable { - String domain = args.getString("domain"); - return EmailProvider.fromDomain(context, domain); - } - - @Override - protected void onExecuted(Bundle args, EmailProvider provider) { - etHost.setText(provider.smtp_host); - etPort.setText(Integer.toString(provider.smtp_port)); - cbStartTls.setChecked(provider.smtp_starttls); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - if (ex instanceof IllegalArgumentException || ex instanceof UnknownHostException) - Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); - else - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); - } - }.execute(FragmentIdentity.this, args, "identity:config"); + onAutoConfig(); } }); @@ -442,224 +403,7 @@ public class FragmentIdentity extends FragmentBase { btnSave.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - EntityAccount account = (EntityAccount) spAccount.getSelectedItem(); - - String name = etName.getText().toString(); - if (TextUtils.isEmpty(name)) { - CharSequence hint = etName.getHint(); - if (!TextUtils.isEmpty(hint)) - name = hint.toString(); - } - - Bundle args = new Bundle(); - args.putLong("id", id); - args.putString("name", name); - args.putString("email", etEmail.getText().toString().trim()); - args.putString("display", etDisplay.getText().toString()); - args.putString("replyto", etReplyTo.getText().toString().trim()); - args.putString("bcc", etBcc.getText().toString().trim()); - args.putBoolean("delivery_receipt", cbDeliveryReceipt.isChecked()); - args.putBoolean("read_receipt", cbReadReceipt.isChecked()); - args.putLong("account", account == null ? -1 : account.id); - args.putInt("auth_type", account == null || account.auth_type == null ? Helper.AUTH_TYPE_PASSWORD : account.auth_type); - args.putString("host", etHost.getText().toString()); - args.putBoolean("starttls", cbStartTls.isChecked()); - args.putBoolean("insecure", cbInsecure.isChecked()); - args.putString("port", etPort.getText().toString()); - args.putString("user", etUser.getText().toString()); - args.putString("password", tilPassword.getEditText().getText().toString()); - args.putString("realm", etRealm.getText().toString()); - args.putInt("color", color); - args.putString("signature", Html.toHtml(etSignature.getText())); - args.putBoolean("synchronize", cbSynchronize.isChecked()); - args.putBoolean("primary", cbPrimary.isChecked()); - args.putSerializable("sent", (EntityFolder) spSent.getSelectedItem()); - - new SimpleTask() { - @Override - protected void onPreExecute(Bundle args) { - Helper.setViewsEnabled(view, false); - btnSave.setEnabled(false); - pbSave.setVisibility(View.VISIBLE); - tvError.setVisibility(View.GONE); - } - - @Override - protected void onPostExecute(Bundle args) { - Helper.setViewsEnabled(view, true); - btnSave.setEnabled(true); - pbSave.setVisibility(View.GONE); - } - - @Override - protected Void onExecute(Context context, Bundle args) throws Throwable { - long id = args.getLong("id"); - String name = args.getString("name"); - String email = args.getString("email"); - long account = args.getLong("account"); - - String display = args.getString("display"); - Integer color = args.getInt("color"); - String signature = args.getString("signature"); - - int auth_type = args.getInt("auth_type"); - String host = args.getString("host"); - boolean starttls = args.getBoolean("starttls"); - boolean insecure = args.getBoolean("insecure"); - String port = args.getString("port"); - String user = args.getString("user"); - String password = args.getString("password"); - String realm = args.getString("realm"); - boolean synchronize = args.getBoolean("synchronize"); - boolean primary = args.getBoolean("primary"); - - String replyto = args.getString("replyto"); - String bcc = args.getString("bcc"); - boolean delivery_receipt = args.getBoolean("delivery_receipt"); - boolean read_receipt = args.getBoolean("read_receipt"); - EntityFolder sent = (EntityFolder) args.getSerializable("sent"); - - if (TextUtils.isEmpty(name)) - throw new IllegalArgumentException(context.getString(R.string.title_no_name)); - if (TextUtils.isEmpty(email)) - throw new IllegalArgumentException(context.getString(R.string.title_no_email)); - if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) - throw new IllegalArgumentException(context.getString(R.string.title_email_invalid)); - if (TextUtils.isEmpty(host)) - throw new IllegalArgumentException(context.getString(R.string.title_no_host)); - if (TextUtils.isEmpty(port)) - port = (starttls ? "587" : "465"); - if (TextUtils.isEmpty(user)) - throw new IllegalArgumentException(context.getString(R.string.title_no_user)); - if (synchronize && TextUtils.isEmpty(password) && !insecure) - throw new IllegalArgumentException(context.getString(R.string.title_no_password)); - - email = email.toLowerCase(); - - if (TextUtils.isEmpty(display)) - display = null; - - if (TextUtils.isEmpty(realm)) - realm = null; - - if (TextUtils.isEmpty(replyto)) - replyto = null; - else - replyto = replyto.toLowerCase(); - - if (TextUtils.isEmpty(bcc)) - bcc = null; - else - bcc = bcc.toLowerCase(); - - if (Color.TRANSPARENT == color) - color = null; - - DB db = DB.getInstance(context); - EntityIdentity identity = db.identity().getIdentity(id); - - String identityRealm = (identity == null ? null : identity.realm); - - boolean check = (synchronize && (identity == null || - !host.equals(identity.host) || Integer.parseInt(port) != identity.port || - !user.equals(identity.user) || !password.equals(identity.password) || - realm == null ? identityRealm != null : !realm.equals(identityRealm))); - boolean reload = (identity == null || identity.synchronize != synchronize || check); - - // Check SMTP server - if (check) { - String transportType = (starttls ? "smtp" : "smtps"); - Properties props = MessageHelper.getSessionProperties(auth_type, realm, insecure); - Session isession = Session.getInstance(props, null); - isession.setDebug(true); - Transport itransport = isession.getTransport(transportType); - try { - try { - itransport.connect(host, Integer.parseInt(port), user, password); - } catch (AuthenticationFailedException ex) { - if (auth_type == Helper.AUTH_TYPE_GMAIL) { - password = Helper.refreshToken(context, "com.google", user, password); - itransport.connect(host, Integer.parseInt(port), user, password); - } else - throw ex; - } - } finally { - itransport.close(); - } - } - - try { - db.beginTransaction(); - - boolean update = (identity != null); - if (identity == null) - identity = new EntityIdentity(); - identity.name = name; - identity.email = email; - identity.account = account; - identity.display = display; - identity.color = color; - identity.signature = signature; - - identity.auth_type = auth_type; - identity.host = host; - identity.starttls = starttls; - identity.insecure = insecure; - identity.port = Integer.parseInt(port); - identity.user = user; - identity.password = password; - identity.realm = realm; - identity.synchronize = synchronize; - identity.primary = (identity.synchronize && primary); - - identity.replyto = replyto; - identity.bcc = bcc; - identity.delivery_receipt = delivery_receipt; - identity.read_receipt = read_receipt; - identity.store_sent = false; - identity.sent_folder = (sent == null ? null : sent.id); - identity.error = null; - - if (identity.primary) - db.identity().resetPrimary(account); - - if (update) - db.identity().updateIdentity(identity); - else - identity.id = db.identity().insertIdentity(identity); - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - - if (reload) - ServiceSynchronize.reload(context, "save identity"); - - return null; - } - - @Override - protected void onExecuted(Bundle args, Void data) { - getFragmentManager().popBackStack(); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - if (ex instanceof IllegalArgumentException) - Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); - else { - tvError.setText(Helper.formatThrowable(ex)); - tvError.setVisibility(View.VISIBLE); - new Handler().post(new Runnable() { - @Override - public void run() { - ((ScrollView) view).smoothScrollTo(0, tvError.getBottom()); - } - }); - } - } - }.execute(FragmentIdentity.this, args, "identity:save"); + onSave(); } }); @@ -683,6 +427,270 @@ public class FragmentIdentity extends FragmentBase { return view; } + private void onAutoConfig() { + etDomain.setEnabled(false); + btnAutoConfig.setEnabled(false); + + Bundle args = new Bundle(); + args.putString("domain", etDomain.getText().toString()); + + new SimpleTask() { + @Override + protected void onPreExecute(Bundle args) { + etDomain.setEnabled(false); + btnAutoConfig.setEnabled(false); + } + + @Override + protected void onPostExecute(Bundle args) { + etDomain.setEnabled(true); + btnAutoConfig.setEnabled(true); + } + + @Override + protected EmailProvider onExecute(Context context, Bundle args) throws Throwable { + String domain = args.getString("domain"); + return EmailProvider.fromDomain(context, domain); + } + + @Override + protected void onExecuted(Bundle args, EmailProvider provider) { + etHost.setText(provider.smtp_host); + etPort.setText(Integer.toString(provider.smtp_port)); + cbStartTls.setChecked(provider.smtp_starttls); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + if (ex instanceof IllegalArgumentException || ex instanceof UnknownHostException) + Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); + else + Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + } + }.execute(FragmentIdentity.this, args, "identity:config"); + } + + private void onSave() { + EntityAccount account = (EntityAccount) spAccount.getSelectedItem(); + + String name = etName.getText().toString(); + if (TextUtils.isEmpty(name)) { + CharSequence hint = etName.getHint(); + if (!TextUtils.isEmpty(hint)) + name = hint.toString(); + } + + Bundle args = new Bundle(); + args.putLong("id", id); + args.putString("name", name); + args.putString("email", etEmail.getText().toString().trim()); + args.putString("display", etDisplay.getText().toString()); + args.putString("replyto", etReplyTo.getText().toString().trim()); + args.putString("bcc", etBcc.getText().toString().trim()); + args.putBoolean("delivery_receipt", cbDeliveryReceipt.isChecked()); + args.putBoolean("read_receipt", cbReadReceipt.isChecked()); + args.putLong("account", account == null ? -1 : account.id); + args.putInt("auth_type", account == null || account.auth_type == null ? Helper.AUTH_TYPE_PASSWORD : account.auth_type); + args.putString("host", etHost.getText().toString()); + args.putBoolean("starttls", cbStartTls.isChecked()); + args.putBoolean("insecure", cbInsecure.isChecked()); + args.putString("port", etPort.getText().toString()); + args.putString("user", etUser.getText().toString()); + args.putString("password", tilPassword.getEditText().getText().toString()); + args.putString("realm", etRealm.getText().toString()); + args.putInt("color", color); + args.putString("signature", Html.toHtml(etSignature.getText())); + args.putBoolean("synchronize", cbSynchronize.isChecked()); + args.putBoolean("primary", cbPrimary.isChecked()); + args.putSerializable("sent", (EntityFolder) spSent.getSelectedItem()); + + new SimpleTask() { + @Override + protected void onPreExecute(Bundle args) { + Helper.setViewsEnabled(view, false); + btnSave.setEnabled(false); + pbSave.setVisibility(View.VISIBLE); + tvError.setVisibility(View.GONE); + } + + @Override + protected void onPostExecute(Bundle args) { + Helper.setViewsEnabled(view, true); + btnSave.setEnabled(true); + pbSave.setVisibility(View.GONE); + } + + @Override + protected Void onExecute(Context context, Bundle args) throws Throwable { + long id = args.getLong("id"); + String name = args.getString("name"); + String email = args.getString("email"); + long account = args.getLong("account"); + + String display = args.getString("display"); + Integer color = args.getInt("color"); + String signature = args.getString("signature"); + + int auth_type = args.getInt("auth_type"); + String host = args.getString("host"); + boolean starttls = args.getBoolean("starttls"); + boolean insecure = args.getBoolean("insecure"); + String port = args.getString("port"); + String user = args.getString("user"); + String password = args.getString("password"); + String realm = args.getString("realm"); + boolean synchronize = args.getBoolean("synchronize"); + boolean primary = args.getBoolean("primary"); + + String replyto = args.getString("replyto"); + String bcc = args.getString("bcc"); + boolean delivery_receipt = args.getBoolean("delivery_receipt"); + boolean read_receipt = args.getBoolean("read_receipt"); + EntityFolder sent = (EntityFolder) args.getSerializable("sent"); + + if (TextUtils.isEmpty(name)) + throw new IllegalArgumentException(context.getString(R.string.title_no_name)); + if (TextUtils.isEmpty(email)) + throw new IllegalArgumentException(context.getString(R.string.title_no_email)); + if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) + throw new IllegalArgumentException(context.getString(R.string.title_email_invalid)); + if (TextUtils.isEmpty(host)) + throw new IllegalArgumentException(context.getString(R.string.title_no_host)); + if (TextUtils.isEmpty(port)) + port = (starttls ? "587" : "465"); + if (TextUtils.isEmpty(user)) + throw new IllegalArgumentException(context.getString(R.string.title_no_user)); + if (synchronize && TextUtils.isEmpty(password) && !insecure) + throw new IllegalArgumentException(context.getString(R.string.title_no_password)); + + email = email.toLowerCase(); + + if (TextUtils.isEmpty(display)) + display = null; + + if (TextUtils.isEmpty(realm)) + realm = null; + + if (TextUtils.isEmpty(replyto)) + replyto = null; + else + replyto = replyto.toLowerCase(); + + if (TextUtils.isEmpty(bcc)) + bcc = null; + else + bcc = bcc.toLowerCase(); + + if (Color.TRANSPARENT == color) + color = null; + + DB db = DB.getInstance(context); + EntityIdentity identity = db.identity().getIdentity(id); + + String identityRealm = (identity == null ? null : identity.realm); + + boolean check = (synchronize && (identity == null || + !host.equals(identity.host) || Integer.parseInt(port) != identity.port || + !user.equals(identity.user) || !password.equals(identity.password) || + realm == null ? identityRealm != null : !realm.equals(identityRealm))); + boolean reload = (identity == null || identity.synchronize != synchronize || check); + + // Check SMTP server + if (check) { + String transportType = (starttls ? "smtp" : "smtps"); + Properties props = MessageHelper.getSessionProperties(auth_type, realm, insecure); + Session isession = Session.getInstance(props, null); + isession.setDebug(true); + Transport itransport = isession.getTransport(transportType); + try { + try { + itransport.connect(host, Integer.parseInt(port), user, password); + } catch (AuthenticationFailedException ex) { + if (auth_type == Helper.AUTH_TYPE_GMAIL) { + password = Helper.refreshToken(context, "com.google", user, password); + itransport.connect(host, Integer.parseInt(port), user, password); + } else + throw ex; + } + } finally { + itransport.close(); + } + } + + try { + db.beginTransaction(); + + boolean update = (identity != null); + if (identity == null) + identity = new EntityIdentity(); + identity.name = name; + identity.email = email; + identity.account = account; + identity.display = display; + identity.color = color; + identity.signature = signature; + + identity.auth_type = auth_type; + identity.host = host; + identity.starttls = starttls; + identity.insecure = insecure; + identity.port = Integer.parseInt(port); + identity.user = user; + identity.password = password; + identity.realm = realm; + identity.synchronize = synchronize; + identity.primary = (identity.synchronize && primary); + + identity.replyto = replyto; + identity.bcc = bcc; + identity.delivery_receipt = delivery_receipt; + identity.read_receipt = read_receipt; + identity.store_sent = false; + identity.sent_folder = (sent == null ? null : sent.id); + identity.error = null; + + if (identity.primary) + db.identity().resetPrimary(account); + + if (update) + db.identity().updateIdentity(identity); + else + identity.id = db.identity().insertIdentity(identity); + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + if (reload) + ServiceSynchronize.reload(context, "save identity"); + + return null; + } + + @Override + protected void onExecuted(Bundle args, Void data) { + getFragmentManager().popBackStack(); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + if (ex instanceof IllegalArgumentException) + Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); + else { + tvError.setText(Helper.formatThrowable(ex)); + tvError.setVisibility(View.VISIBLE); + new Handler().post(new Runnable() { + @Override + public void run() { + ((ScrollView) view).smoothScrollTo(0, tvError.getBottom()); + } + }); + } + } + }.execute(FragmentIdentity.this, args, "identity:save"); + } + @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); diff --git a/app/src/main/java/eu/faircode/email/FragmentMessages.java b/app/src/main/java/eu/faircode/email/FragmentMessages.java index ee2e850aa3..b1727ee04d 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessages.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessages.java @@ -133,6 +133,17 @@ public class FragmentMessages extends FragmentBase { private ExecutorService executor = Executors.newSingleThreadExecutor(Helper.backgroundThreadFactory); + private final int action_seen = 1; + private final int action_unseen = 2; + private final int action_snooze = 3; + private final int action_flag = 4; + private final int action_unflag = 5; + private final int action_archive = 6; + private final int action_trash = 7; + private final int action_delete = 8; + private final int action_junk = 9; + private final int action_move = 10; + private static final int LOCAL_PAGE_SIZE = 100; private static final int REMOTE_PAGE_SIZE = 10; private static final int UNDO_TIMEOUT = 5000; // milliseconds @@ -211,65 +222,7 @@ public class FragmentMessages extends FragmentBase { swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { - Bundle args = new Bundle(); - args.putLong("folder", folder); - - new SimpleTask() { - @Override - protected Boolean onExecute(Context context, Bundle args) { - long fid = args.getLong("folder"); - - boolean connected = false; - - DB db = DB.getInstance(context); - try { - db.beginTransaction(); - - List folders = new ArrayList<>(); - if (fid < 0) { - List unified = db.folder().getFoldersSynchronizingUnified(); - if (unified != null) - folders.addAll(unified); - } else { - EntityFolder folder = db.folder().getFolder(fid); - if (folder != null) - folders.add(folder); - } - - for (EntityFolder folder : folders) { - EntityOperation.sync(db, folder.id); - - if (folder.account == null) { // outbox - if ("connected".equals(folder.state)) - connected = true; - } else { - EntityAccount account = db.account().getAccount(folder.account); - if ("connected".equals(account.state)) - connected = true; - } - } - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - - return connected; - } - - @Override - protected void onExecuted(Bundle args, Boolean connected) { - if (!connected) { - swipeRefresh.setRefreshing(false); - Snackbar.make(view, R.string.title_sync_queued, Snackbar.LENGTH_LONG).show(); - } - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); - } - }.execute(FragmentMessages.this, args, "messages:refresh"); + onSwipeRefresh(); } }); @@ -316,101 +269,7 @@ public class FragmentMessages extends FragmentBase { int zoom = prefs.getInt("zoom", compact ? 0 : 1); adapter = new AdapterMessage( getContext(), getViewLifecycleOwner(), getFragmentManager(), - viewType, outgoing, - compact, zoom, - new AdapterMessage.IProperties() { - @Override - public void setValue(String name, long id, boolean enabled) { - if (!values.containsKey(name)) - values.put(name, new ArrayList()); - if (enabled) { - values.get(name).add(id); - if ("expanded".equals(name)) - handleExpand(id); - } else - values.get(name).remove(id); - } - - @Override - public boolean getValue(String name, long id) { - if (values.containsKey(name)) - return values.get(name).contains(id); - else if ("addresses".equals(name)) - return !addresses; - return false; - } - - @Override - public void setBody(long id, Spanned body) { - if (body == null) - bodies.remove(id); - else - bodies.put(id, body); - } - - @Override - public Spanned getBody(long id) { - return bodies.get(id); - } - - @Override - public void scrollTo(final int pos, final int dy) { - new Handler().post(new Runnable() { - @Override - public void run() { - rvMessage.scrollToPosition(pos); - rvMessage.scrollBy(0, dy); - } - }); - } - - @Override - public void move(long id, String name, boolean type) { - Bundle args = new Bundle(); - args.putLong("id", id); - args.putString("name", name); - args.putBoolean("type", type); - - new SimpleTask() { - @Override - protected MessageTarget onExecute(Context context, Bundle args) { - long id = args.getLong("id"); - String name = args.getString("name"); - boolean type = args.getBoolean("type"); - - MessageTarget result = new MessageTarget(); - - DB db = DB.getInstance(context); - try { - db.beginTransaction(); - - EntityMessage message = db.message().getMessage(id); - if (type) - result.target = db.folder().getFolderByType(message.account, name); - else - result.target = db.folder().getFolderByName(message.account, name); - result.ids.add(message.id); - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - - return result; - } - - @Override - protected void onExecuted(Bundle args, MessageTarget result) { - moveAsk(result); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); - } - }.execute(FragmentMessages.this, args, "messages:move"); - } - }); + viewType, outgoing, compact, zoom, iProperties); rvMessage.setAdapter(adapter); @@ -456,158 +315,7 @@ public class FragmentMessages extends FragmentBase { }); } - new ItemTouchHelper(new ItemTouchHelper.Callback() { - @Override - public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { - if (!prefs.getBoolean("swipe", true)) - return 0; - - if (selectionTracker != null && selectionTracker.hasSelection()) - return 0; - - int pos = viewHolder.getAdapterPosition(); - if (pos == RecyclerView.NO_POSITION) - return 0; - - TupleMessageEx message = ((AdapterMessage) rvMessage.getAdapter()).getCurrentList().get(pos); - if (message == null || message.uid == null || - (values.containsKey("expanded") && values.get("expanded").contains(message.id)) || - EntityFolder.DRAFTS.equals(message.folderType) || - EntityFolder.OUTBOX.equals(message.folderType)) - return 0; - - int flags = 0; - if (archives.contains(message.account)) - flags |= ItemTouchHelper.RIGHT; - if (trashes.contains(message.account)) - flags |= ItemTouchHelper.LEFT; - - return makeMovementFlags(0, flags); - } - - @Override - public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { - return false; - } - - @Override - public void onChildDraw(Canvas canvas, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { - int pos = viewHolder.getAdapterPosition(); - if (pos == RecyclerView.NO_POSITION) - return; - - TupleMessageEx message = ((AdapterMessage) rvMessage.getAdapter()).getCurrentList().get(pos); - if (message == null) - return; - - boolean inbox = (EntityFolder.ARCHIVE.equals(message.folderType) || EntityFolder.TRASH.equals(message.folderType)); - - View itemView = viewHolder.itemView; - int margin = Helper.dp2pixels(getContext(), 12); - - if (dX > margin) { - // Right swipe - Drawable d = getResources().getDrawable( - inbox ? R.drawable.baseline_move_to_inbox_24 : R.drawable.baseline_archive_24, - getContext().getTheme()); - int padding = (itemView.getHeight() - d.getIntrinsicHeight()); - d.setBounds( - itemView.getLeft() + margin, - itemView.getTop() + padding / 2, - itemView.getLeft() + margin + d.getIntrinsicWidth(), - itemView.getTop() + padding / 2 + d.getIntrinsicHeight()); - d.draw(canvas); - } else if (dX < -margin) { - // Left swipe - Drawable d = getResources().getDrawable(inbox ? R.drawable.baseline_move_to_inbox_24 : R.drawable.baseline_delete_24, getContext().getTheme()); - int padding = (itemView.getHeight() - d.getIntrinsicHeight()); - d.setBounds( - itemView.getLeft() + itemView.getWidth() - d.getIntrinsicWidth() - margin, - itemView.getTop() + padding / 2, - itemView.getLeft() + itemView.getWidth() - margin, - itemView.getTop() + padding / 2 + d.getIntrinsicHeight()); - d.draw(canvas); - } - - super.onChildDraw(canvas, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); - } - - @Override - public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { - int pos = viewHolder.getAdapterPosition(); - if (pos == RecyclerView.NO_POSITION) - return; - - TupleMessageEx message = ((AdapterMessage) rvMessage.getAdapter()).getCurrentList().get(pos); - if (message == null) - return; - Log.i("Swiped dir=" + direction + " message=" + message.id); - - Bundle args = new Bundle(); - args.putLong("id", message.id); - args.putBoolean("thread", viewType != AdapterMessage.ViewType.THREAD); - args.putInt("direction", direction); - - new SimpleTask() { - @Override - protected MessageTarget onExecute(Context context, Bundle args) { - long id = args.getLong("id"); - boolean thread = args.getBoolean("thread"); - int direction = args.getInt("direction"); - - MessageTarget result = new MessageTarget(); - EntityFolder target = null; - - // Get target folder and hide message - DB db = DB.getInstance(context); - try { - db.beginTransaction(); - - EntityMessage message = db.message().getMessage(id); - - EntityFolder folder = db.folder().getFolder(message.folder); - if (EntityFolder.ARCHIVE.equals(folder.type) || EntityFolder.TRASH.equals(folder.type)) - target = db.folder().getFolderByType(message.account, EntityFolder.INBOX); - else { - if (direction == ItemTouchHelper.RIGHT) - target = db.folder().getFolderByType(message.account, EntityFolder.ARCHIVE); - if (direction == ItemTouchHelper.LEFT || target == null) - target = db.folder().getFolderByType(message.account, EntityFolder.TRASH); - if (target == null) - target = db.folder().getFolderByType(message.account, EntityFolder.INBOX); - } - - result.target = target; - - List messages = db.message().getMessageByThread( - message.account, message.thread, threading && thread ? null : id, message.folder); - for (EntityMessage threaded : messages) { - result.ids.add(threaded.id); - db.message().setMessageUiHide(threaded.id, true); - // Prevent new message notification on undo - db.message().setMessageUiIgnored(threaded.id, true); - } - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - - return result; - } - - @Override - protected void onExecuted(final Bundle args, final MessageTarget result) { - moveUndo(result); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); - } - }.execute(FragmentMessages.this, args, "messages:swipe"); - } - }).attachToRecyclerView(rvMessage); + new ItemTouchHelper(touchHelper).attachToRecyclerView(rvMessage); bottom_navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { @Override @@ -633,62 +341,6 @@ public class FragmentMessages extends FragmentBase { return false; } } - - private void onActionMove(String folderType) { - Bundle args = new Bundle(); - args.putLong("account", account); - args.putString("thread", thread); - args.putLong("id", id); - args.putString("folderType", folderType); - - new SimpleTask() { - @Override - protected MessageTarget onExecute(Context context, Bundle args) { - long account = args.getLong("account"); - String thread = args.getString("thread"); - long id = args.getLong("id"); - String folderType = args.getString("folderType"); - - MessageTarget result = new MessageTarget(); - - DB db = DB.getInstance(context); - try { - db.beginTransaction(); - - result.target = db.folder().getFolderByType(account, folderType); - - List messages = db.message().getMessageByThread( - account, thread, threading ? null : id, null); - for (EntityMessage threaded : messages) { - EntityFolder folder = db.folder().getFolder(threaded.folder); - if (!result.target.id.equals(threaded.folder) && - !EntityFolder.DRAFTS.equals(folder.type) && - !EntityFolder.OUTBOX.equals(folder.type) && - (!EntityFolder.SENT.equals(folder.type) || EntityFolder.TRASH.equals(result.target.type)) && - !EntityFolder.TRASH.equals(folder.type) && - !EntityFolder.JUNK.equals(folder.type)) - result.ids.add(threaded.id); - } - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - - return result; - } - - @Override - protected void onExecuted(Bundle args, MessageTarget result) { - moveAsk(result); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); - } - }.execute(FragmentMessages.this, args, "messages:move"); - } }); fab.setOnClickListener(new View.OnClickListener() { @@ -720,533 +372,892 @@ public class FragmentMessages extends FragmentBase { }); fabMore.setOnClickListener(new View.OnClickListener() { - private final int action_seen = 1; - private final int action_unseen = 2; - private final int action_snooze = 3; - private final int action_flag = 4; - private final int action_unflag = 5; - private final int action_archive = 6; - private final int action_trash = 7; - private final int action_delete = 8; - private final int action_junk = 9; - private final int action_move = 10; - @Override public void onClick(View v) { - Bundle args = new Bundle(); - args.putLong("folder", folder); - args.putLongArray("ids", getSelection()); + onMore(); + } + }); - new SimpleTask() { - @Override - protected Boolean[] onExecute(Context context, Bundle args) { - long fid = args.getLong("folder"); - long[] ids = args.getLongArray("ids"); + ((ActivityBase) getActivity()).addBackPressedListener(onBackPressedListener); - Boolean[] result = new Boolean[10]; - for (int i = 0; i < result.length; i++) - result[i] = false; + // Initialize + swipeRefresh.setEnabled(pull); + tvNoEmail.setVisibility(View.GONE); + bottom_navigation.setVisibility(View.GONE); + grpReady.setVisibility(View.GONE); + pbWait.setVisibility(View.VISIBLE); - DB db = DB.getInstance(context); + fab.hide(); + fabMore.hide(); - long account = -1; - for (long id : ids) { - EntityMessage message = db.message().getMessage(id); - if (message != null) { - account = message.account; - result[message.ui_seen ? 1 : 0] = true; - result[message.flagged ? 3 : 2] = true; - } - } + return view; + } + + private void onSwipeRefresh() { + Bundle args = new Bundle(); + args.putLong("folder", folder); - EntityFolder archive = db.folder().getFolderByType(account, EntityFolder.ARCHIVE); - EntityFolder trash = db.folder().getFolderByType(account, EntityFolder.TRASH); + new SimpleTask() { + @Override + protected Boolean onExecute(Context context, Bundle args) { + long fid = args.getLong("folder"); - result[4] = (archive != null); - result[5] = (trash != null); + boolean connected = false; - EntityFolder folder = db.folder().getFolder(fid); - if (folder != null) { - result[6] = EntityFolder.ARCHIVE.equals(folder.type); - result[7] = EntityFolder.TRASH.equals(folder.type); - result[8] = EntityFolder.JUNK.equals(folder.type); - result[9] = EntityFolder.DRAFTS.equals(folder.type); - } + DB db = DB.getInstance(context); + try { + db.beginTransaction(); - return result; + List folders = new ArrayList<>(); + if (fid < 0) { + List unified = db.folder().getFoldersSynchronizingUnified(); + if (unified != null) + folders.addAll(unified); + } else { + EntityFolder folder = db.folder().getFolder(fid); + if (folder != null) + folders.add(folder); } - @Override - protected void onExecuted(Bundle args, final Boolean[] result) { - PopupMenu popupMenu = new PopupMenu(getContext(), fabMore); + for (EntityFolder folder : folders) { + EntityOperation.sync(db, folder.id); - if (result[0] && !result[9]) - popupMenu.getMenu().add(Menu.NONE, action_seen, 1, R.string.title_seen); - if (result[1] && !result[9]) - popupMenu.getMenu().add(Menu.NONE, action_unseen, 2, R.string.title_unseen); + if (folder.account == null) { // outbox + if ("connected".equals(folder.state)) + connected = true; + } else { + EntityAccount account = db.account().getAccount(folder.account); + if ("connected".equals(account.state)) + connected = true; + } + } - popupMenu.getMenu().add(Menu.NONE, action_snooze, 3, R.string.title_snooze); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } - if (result[2]) - popupMenu.getMenu().add(Menu.NONE, action_flag, 4, R.string.title_flag); - if (result[3]) - popupMenu.getMenu().add(Menu.NONE, action_unflag, 5, R.string.title_unflag); + return connected; + } - if (result[4] && !result[6] && !result[9]) // has archive and not is archive - popupMenu.getMenu().add(Menu.NONE, action_archive, 6, R.string.title_archive); + @Override + protected void onExecuted(Bundle args, Boolean connected) { + if (!connected) { + swipeRefresh.setRefreshing(false); + Snackbar.make(view, R.string.title_sync_queued, Snackbar.LENGTH_LONG).show(); + } + } - if (result[7]) // is trash - popupMenu.getMenu().add(Menu.NONE, action_delete, 7, R.string.title_delete); - else if (result[5]) // has trash - popupMenu.getMenu().add(Menu.NONE, action_trash, 8, R.string.title_trash); + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + } + }.execute(FragmentMessages.this, args, "messages:refresh"); + } - if (!result[8] && !result[9]) - popupMenu.getMenu().add(Menu.NONE, action_junk, 9, R.string.title_spam); + private AdapterMessage.IProperties iProperties = new AdapterMessage.IProperties() { + @Override + public void setValue(String name, long id, boolean enabled) { + if (!values.containsKey(name)) + values.put(name, new ArrayList()); + if (enabled) { + values.get(name).add(id); + if ("expanded".equals(name)) + handleExpand(id); + } else + values.get(name).remove(id); + } - if (!result[9]) - popupMenu.getMenu().add(Menu.NONE, action_move, 10, R.string.title_move); + @Override + public boolean getValue(String name, long id) { + if (values.containsKey(name)) + return values.get(name).contains(id); + else if ("addresses".equals(name)) + return !addresses; + return false; + } - popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem target) { - switch (target.getItemId()) { - case action_seen: - onActionSeen(true); - return true; - case action_unseen: - onActionSeen(false); - return true; - case action_snooze: - onActionSnooze(); - return true; - case action_flag: - onActionFlag(true); - return true; - case action_unflag: - onActionFlag(false); - return true; - case action_archive: - onActionMove(EntityFolder.ARCHIVE); - return true; - case action_trash: - onActionMove(EntityFolder.TRASH); - return true; - case action_delete: - onActionDelete(); - return true; - case action_junk: - onActionJunk(); - return true; - case action_move: - onActionMove(); - return true; - default: - return false; - } - } - }); + @Override + public void setBody(long id, Spanned body) { + if (body == null) + bodies.remove(id); + else + bodies.put(id, body); + } - popupMenu.show(); - } + @Override + public Spanned getBody(long id) { + return bodies.get(id); + } - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); - } - }.execute(FragmentMessages.this, args, "messages:more"); - } + @Override + public void scrollTo(final int pos, final int dy) { + new Handler().post(new Runnable() { + @Override + public void run() { + rvMessage.scrollToPosition(pos); + rvMessage.scrollBy(0, dy); + } + }); + } - private long[] getSelection() { - Selection selection = selectionTracker.getSelection(); + @Override + public void move(long id, String name, boolean type) { + Bundle args = new Bundle(); + args.putLong("id", id); + args.putString("name", name); + args.putBoolean("type", type); - long[] ids = new long[selection.size()]; - int i = 0; - for (Long id : selection) - ids[i++] = id; + new SimpleTask() { + @Override + protected MessageTarget onExecute(Context context, Bundle args) { + long id = args.getLong("id"); + String name = args.getString("name"); + boolean type = args.getBoolean("type"); - return ids; - } + MessageTarget result = new MessageTarget(); - private void onActionSeen(boolean seen) { - Bundle args = new Bundle(); - args.putLongArray("ids", getSelection()); - args.putBoolean("seen", seen); + DB db = DB.getInstance(context); + try { + db.beginTransaction(); - selectionTracker.clearSelection(); + EntityMessage message = db.message().getMessage(id); + if (type) + result.target = db.folder().getFolderByType(message.account, name); + else + result.target = db.folder().getFolderByName(message.account, name); + result.ids.add(message.id); - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) { - long[] ids = args.getLongArray("ids"); - boolean seen = args.getBoolean("seen"); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } - DB db = DB.getInstance(context); - try { - db.beginTransaction(); + return result; + } - for (long id : ids) { - EntityMessage message = db.message().getMessage(id); - if (message != null && message.ui_seen != seen) { - List messages = db.message().getMessageByThread( - message.account, message.thread, threading ? null : id, message.folder); - for (EntityMessage threaded : messages) - EntityOperation.queue(context, db, threaded, EntityOperation.SEEN, seen); - } - } + @Override + protected void onExecuted(Bundle args, MessageTarget result) { + moveAsk(result); + } - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + } + }.execute(FragmentMessages.this, args, "messages:move"); + } + }; - return null; - } + private ItemTouchHelper.Callback touchHelper = new ItemTouchHelper.Callback() { + @Override + public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + if (!prefs.getBoolean("swipe", true)) + return 0; + + if (selectionTracker != null && selectionTracker.hasSelection()) + return 0; + + int pos = viewHolder.getAdapterPosition(); + if (pos == RecyclerView.NO_POSITION) + return 0; + + TupleMessageEx message = ((AdapterMessage) rvMessage.getAdapter()).getCurrentList().get(pos); + if (message == null || message.uid == null || + (values.containsKey("expanded") && values.get("expanded").contains(message.id)) || + EntityFolder.DRAFTS.equals(message.folderType) || + EntityFolder.OUTBOX.equals(message.folderType)) + return 0; + + int flags = 0; + if (archives.contains(message.account)) + flags |= ItemTouchHelper.RIGHT; + if (trashes.contains(message.account)) + flags |= ItemTouchHelper.LEFT; + + return makeMovementFlags(0, flags); + } - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); - } - }.execute(FragmentMessages.this, args, "messages:seen"); + @Override + public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { + return false; + } + + @Override + public void onChildDraw(Canvas canvas, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { + int pos = viewHolder.getAdapterPosition(); + if (pos == RecyclerView.NO_POSITION) + return; + + TupleMessageEx message = ((AdapterMessage) rvMessage.getAdapter()).getCurrentList().get(pos); + if (message == null) + return; + + boolean inbox = (EntityFolder.ARCHIVE.equals(message.folderType) || EntityFolder.TRASH.equals(message.folderType)); + + View itemView = viewHolder.itemView; + int margin = Helper.dp2pixels(getContext(), 12); + + if (dX > margin) { + // Right swipe + Drawable d = getResources().getDrawable( + inbox ? R.drawable.baseline_move_to_inbox_24 : R.drawable.baseline_archive_24, + getContext().getTheme()); + int padding = (itemView.getHeight() - d.getIntrinsicHeight()); + d.setBounds( + itemView.getLeft() + margin, + itemView.getTop() + padding / 2, + itemView.getLeft() + margin + d.getIntrinsicWidth(), + itemView.getTop() + padding / 2 + d.getIntrinsicHeight()); + d.draw(canvas); + } else if (dX < -margin) { + // Left swipe + Drawable d = getResources().getDrawable(inbox ? R.drawable.baseline_move_to_inbox_24 : R.drawable.baseline_delete_24, getContext().getTheme()); + int padding = (itemView.getHeight() - d.getIntrinsicHeight()); + d.setBounds( + itemView.getLeft() + itemView.getWidth() - d.getIntrinsicWidth() - margin, + itemView.getTop() + padding / 2, + itemView.getLeft() + itemView.getWidth() - margin, + itemView.getTop() + padding / 2 + d.getIntrinsicHeight()); + d.draw(canvas); } - private void onActionSnooze() { - DialogDuration.show(getContext(), getViewLifecycleOwner(), R.string.title_snooze, - new DialogDuration.IDialogDuration() { - @Override - public void onDurationSelected(long duration, long time) { - if (Helper.isPro(getContext())) { - Bundle args = new Bundle(); - args.putLongArray("ids", getSelection()); - args.putLong("wakeup", duration == 0 ? -1 : time); - - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) { - long[] ids = args.getLongArray("ids"); - Long wakeup = args.getLong("wakeup"); - if (wakeup < 0) - wakeup = null; - - DB db = DB.getInstance(context); - for (long id : ids) { - EntityMessage message = db.message().getMessage(id); - if (message != null) { - List messages = db.message().getMessageByThread( - message.account, message.thread, threading ? null : id, message.folder); - for (EntityMessage threaded : messages) { - db.message().setMessageSnoozed(threaded.id, wakeup); - EntityMessage.snooze(context, threaded.id, wakeup); - } - } - } + super.onChildDraw(canvas, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); + } - return null; - } + @Override + public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { + int pos = viewHolder.getAdapterPosition(); + if (pos == RecyclerView.NO_POSITION) + return; - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); - } - }.execute(FragmentMessages.this, args, "messages:snooze"); - } else { - FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); - fragmentTransaction.replace(R.id.content_frame, new FragmentPro()).addToBackStack("pro"); - fragmentTransaction.commit(); - } - } + TupleMessageEx message = ((AdapterMessage) rvMessage.getAdapter()).getCurrentList().get(pos); + if (message == null) + return; + Log.i("Swiped dir=" + direction + " message=" + message.id); - @Override - public void onDismiss() { - selectionTracker.clearSelection(); - } - }); - } + Bundle args = new Bundle(); + args.putLong("id", message.id); + args.putBoolean("thread", viewType != AdapterMessage.ViewType.THREAD); + args.putInt("direction", direction); - private void onActionFlag(boolean flagged) { - Bundle args = new Bundle(); - args.putLongArray("ids", getSelection()); - args.putBoolean("flagged", flagged); + new SimpleTask() { + @Override + protected MessageTarget onExecute(Context context, Bundle args) { + long id = args.getLong("id"); + boolean thread = args.getBoolean("thread"); + int direction = args.getInt("direction"); - selectionTracker.clearSelection(); + MessageTarget result = new MessageTarget(); + EntityFolder target = null; - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) { - long[] ids = args.getLongArray("ids"); - boolean flagged = args.getBoolean("flagged"); + // Get target folder and hide message + DB db = DB.getInstance(context); + try { + db.beginTransaction(); - DB db = DB.getInstance(context); - try { - db.beginTransaction(); + EntityMessage message = db.message().getMessage(id); - for (long id : ids) { - EntityMessage message = db.message().getMessage(id); - if (message != null && message.ui_flagged != flagged) { - List messages = db.message().getMessageByThread( - message.account, message.thread, threading ? null : id, message.folder); - for (EntityMessage threaded : messages) - EntityOperation.queue(context, db, threaded, EntityOperation.FLAG, flagged); - } - } + EntityFolder folder = db.folder().getFolder(message.folder); + if (EntityFolder.ARCHIVE.equals(folder.type) || EntityFolder.TRASH.equals(folder.type)) + target = db.folder().getFolderByType(message.account, EntityFolder.INBOX); + else { + if (direction == ItemTouchHelper.RIGHT) + target = db.folder().getFolderByType(message.account, EntityFolder.ARCHIVE); + if (direction == ItemTouchHelper.LEFT || target == null) + target = db.folder().getFolderByType(message.account, EntityFolder.TRASH); + if (target == null) + target = db.folder().getFolderByType(message.account, EntityFolder.INBOX); + } - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); + result.target = target; + + List messages = db.message().getMessageByThread( + message.account, message.thread, threading && thread ? null : id, message.folder); + for (EntityMessage threaded : messages) { + result.ids.add(threaded.id); + db.message().setMessageUiHide(threaded.id, true); + // Prevent new message notification on undo + db.message().setMessageUiIgnored(threaded.id, true); } - return null; + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); } - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + return result; + } + + @Override + protected void onExecuted(final Bundle args, final MessageTarget result) { + moveUndo(result); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + } + }.execute(FragmentMessages.this, args, "messages:swipe"); + } + }; + + private void onActionMove(String folderType) { + Bundle args = new Bundle(); + args.putLong("account", account); + args.putString("thread", thread); + args.putLong("id", id); + args.putString("folderType", folderType); + + new SimpleTask() { + @Override + protected MessageTarget onExecute(Context context, Bundle args) { + long account = args.getLong("account"); + String thread = args.getString("thread"); + long id = args.getLong("id"); + String folderType = args.getString("folderType"); + + MessageTarget result = new MessageTarget(); + + DB db = DB.getInstance(context); + try { + db.beginTransaction(); + + result.target = db.folder().getFolderByType(account, folderType); + + List messages = db.message().getMessageByThread( + account, thread, threading ? null : id, null); + for (EntityMessage threaded : messages) { + EntityFolder folder = db.folder().getFolder(threaded.folder); + if (!result.target.id.equals(threaded.folder) && + !EntityFolder.DRAFTS.equals(folder.type) && + !EntityFolder.OUTBOX.equals(folder.type) && + (!EntityFolder.SENT.equals(folder.type) || EntityFolder.TRASH.equals(result.target.type)) && + !EntityFolder.TRASH.equals(folder.type) && + !EntityFolder.JUNK.equals(folder.type)) + result.ids.add(threaded.id); } - }.execute(FragmentMessages.this, args, "messages:flag"); + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + return result; } - private void onActionJunk() { - new DialogBuilderLifecycle(getContext(), getViewLifecycleOwner()) - .setMessage(R.string.title_ask_spam) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - onActionMove(EntityFolder.JUNK); - } - }) - .setNegativeButton(android.R.string.cancel, null) - .show(); + @Override + protected void onExecuted(Bundle args, MessageTarget result) { + moveAsk(result); } - private void onActionDelete() { - new DialogBuilderLifecycle(getContext(), getViewLifecycleOwner()) - .setMessage(R.string.title_ask_delete_selected) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - Bundle args = new Bundle(); - args.putLongArray("ids", getSelection()); - - selectionTracker.clearSelection(); - - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) { - long[] ids = args.getLongArray("ids"); - - DB db = DB.getInstance(context); - try { - db.beginTransaction(); - - for (long id : ids) { - EntityMessage message = db.message().getMessage(id); - if (message != null) { - List messages = db.message().getMessageByThread( - message.account, message.thread, threading ? null : id, message.folder); - for (EntityMessage threaded : messages) - if (threaded.uid != null) - EntityOperation.queue(context, db, threaded, EntityOperation.DELETE); - } - } + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + } + }.execute(FragmentMessages.this, args, "messages:move"); + } - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } + private void onMore() { + Bundle args = new Bundle(); + args.putLong("folder", folder); + args.putLongArray("ids", getSelection()); - return null; - } + new SimpleTask() { + @Override + protected Boolean[] onExecute(Context context, Bundle args) { + long fid = args.getLong("folder"); + long[] ids = args.getLongArray("ids"); - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); - } - }.execute(FragmentMessages.this, args, "messages:delete"); - } - }) - .setNegativeButton(android.R.string.cancel, null) - .show(); + Boolean[] result = new Boolean[10]; + for (int i = 0; i < result.length; i++) + result[i] = false; + + DB db = DB.getInstance(context); + + long account = -1; + for (long id : ids) { + EntityMessage message = db.message().getMessage(id); + if (message != null) { + account = message.account; + result[message.ui_seen ? 1 : 0] = true; + result[message.flagged ? 3 : 2] = true; + } + } + + EntityFolder archive = db.folder().getFolderByType(account, EntityFolder.ARCHIVE); + EntityFolder trash = db.folder().getFolderByType(account, EntityFolder.TRASH); + + result[4] = (archive != null); + result[5] = (trash != null); + + EntityFolder folder = db.folder().getFolder(fid); + if (folder != null) { + result[6] = EntityFolder.ARCHIVE.equals(folder.type); + result[7] = EntityFolder.TRASH.equals(folder.type); + result[8] = EntityFolder.JUNK.equals(folder.type); + result[9] = EntityFolder.DRAFTS.equals(folder.type); + } + + return result; } - private void onActionMove(String type) { - Bundle args = new Bundle(); - args.putString("type", type); - args.putLongArray("ids", getSelection()); + @Override + protected void onExecuted(Bundle args, final Boolean[] result) { + PopupMenu popupMenu = new PopupMenu(getContext(), fabMore); - selectionTracker.clearSelection(); + if (result[0] && !result[9]) + popupMenu.getMenu().add(Menu.NONE, action_seen, 1, R.string.title_seen); + if (result[1] && !result[9]) + popupMenu.getMenu().add(Menu.NONE, action_unseen, 2, R.string.title_unseen); - new SimpleTask() { - @Override - protected MessageTarget onExecute(Context context, Bundle args) { - String type = args.getString("type"); - long[] ids = args.getLongArray("ids"); + popupMenu.getMenu().add(Menu.NONE, action_snooze, 3, R.string.title_snooze); - MessageTarget result = new MessageTarget(); + if (result[2]) + popupMenu.getMenu().add(Menu.NONE, action_flag, 4, R.string.title_flag); + if (result[3]) + popupMenu.getMenu().add(Menu.NONE, action_unflag, 5, R.string.title_unflag); - DB db = DB.getInstance(context); - try { - db.beginTransaction(); + if (result[4] && !result[6] && !result[9]) // has archive and not is archive + popupMenu.getMenu().add(Menu.NONE, action_archive, 6, R.string.title_archive); - long account = -1; - for (long id : ids) { - EntityMessage message = db.message().getMessage(id); - if (message != null) { - account = message.account; - List messages = db.message().getMessageByThread( - message.account, message.thread, threading ? null : id, message.folder); - for (EntityMessage threaded : messages) - result.ids.add(threaded.id); - } - } + if (result[7]) // is trash + popupMenu.getMenu().add(Menu.NONE, action_delete, 7, R.string.title_delete); + else if (result[5]) // has trash + popupMenu.getMenu().add(Menu.NONE, action_trash, 8, R.string.title_trash); - result.target = db.folder().getFolderByType(account, type); + if (!result[8] && !result[9]) + popupMenu.getMenu().add(Menu.NONE, action_junk, 9, R.string.title_spam); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); + if (!result[9]) + popupMenu.getMenu().add(Menu.NONE, action_move, 10, R.string.title_move); + + popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem target) { + switch (target.getItemId()) { + case action_seen: + onActionSeenSelection(true); + return true; + case action_unseen: + onActionSeenSelection(false); + return true; + case action_snooze: + onActionSnoozeSelection(); + return true; + case action_flag: + onActionFlagSelection(true); + return true; + case action_unflag: + onActionFlagSelection(false); + return true; + case action_archive: + onActionMoveSelection(EntityFolder.ARCHIVE); + return true; + case action_trash: + onActionMoveSelection(EntityFolder.TRASH); + return true; + case action_delete: + onActionDeleteSelection(); + return true; + case action_junk: + onActionJunkSelection(); + return true; + case action_move: + onActionMoveSelection(); + return true; + default: + return false; } + } + }); + + popupMenu.show(); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + } + }.execute(FragmentMessages.this, args, "messages:more"); + } + + private long[] getSelection() { + Selection selection = selectionTracker.getSelection(); + + long[] ids = new long[selection.size()]; + int i = 0; + for (Long id : selection) + ids[i++] = id; + + return ids; + } + + private void onActionSeenSelection(boolean seen) { + Bundle args = new Bundle(); + args.putLongArray("ids", getSelection()); + args.putBoolean("seen", seen); + + selectionTracker.clearSelection(); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) { + long[] ids = args.getLongArray("ids"); + boolean seen = args.getBoolean("seen"); + + DB db = DB.getInstance(context); + try { + db.beginTransaction(); - return result; + for (long id : ids) { + EntityMessage message = db.message().getMessage(id); + if (message != null && message.ui_seen != seen) { + List messages = db.message().getMessageByThread( + message.account, message.thread, threading ? null : id, message.folder); + for (EntityMessage threaded : messages) + EntityOperation.queue(context, db, threaded, EntityOperation.SEEN, seen); + } } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + return null; + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + } + }.execute(FragmentMessages.this, args, "messages:seen"); + } + + private void onActionSnoozeSelection() { + DialogDuration.show(getContext(), getViewLifecycleOwner(), R.string.title_snooze, + new DialogDuration.IDialogDuration() { @Override - protected void onExecuted(Bundle args, MessageTarget result) { - if (EntityFolder.JUNK.equals(result.target.type)) - moveAskConfirmed(result); - else - moveAsk(result); + public void onDurationSelected(long duration, long time) { + if (Helper.isPro(getContext())) { + Bundle args = new Bundle(); + args.putLongArray("ids", getSelection()); + args.putLong("wakeup", duration == 0 ? -1 : time); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) { + long[] ids = args.getLongArray("ids"); + Long wakeup = args.getLong("wakeup"); + if (wakeup < 0) + wakeup = null; + + DB db = DB.getInstance(context); + for (long id : ids) { + EntityMessage message = db.message().getMessage(id); + if (message != null) { + List messages = db.message().getMessageByThread( + message.account, message.thread, threading ? null : id, message.folder); + for (EntityMessage threaded : messages) { + db.message().setMessageSnoozed(threaded.id, wakeup); + EntityMessage.snooze(context, threaded.id, wakeup); + } + } + } + + return null; + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + } + }.execute(FragmentMessages.this, args, "messages:snooze"); + } else { + FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); + fragmentTransaction.replace(R.id.content_frame, new FragmentPro()).addToBackStack("pro"); + fragmentTransaction.commit(); + } } @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + public void onDismiss() { + selectionTracker.clearSelection(); } - }.execute(FragmentMessages.this, args, "messages:move"); + }); + } + + private void onActionFlagSelection(boolean flagged) { + Bundle args = new Bundle(); + args.putLongArray("ids", getSelection()); + args.putBoolean("flagged", flagged); + + selectionTracker.clearSelection(); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) { + long[] ids = args.getLongArray("ids"); + boolean flagged = args.getBoolean("flagged"); + + DB db = DB.getInstance(context); + try { + db.beginTransaction(); + + for (long id : ids) { + EntityMessage message = db.message().getMessage(id); + if (message != null && message.ui_flagged != flagged) { + List messages = db.message().getMessageByThread( + message.account, message.thread, threading ? null : id, message.folder); + for (EntityMessage threaded : messages) + EntityOperation.queue(context, db, threaded, EntityOperation.FLAG, flagged); + } + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + return null; } - private void onActionMove() { - Bundle args = new Bundle(); - args.putLong("folder", folder); - args.putLongArray("ids", getSelection()); + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + } + }.execute(FragmentMessages.this, args, "messages:flag"); + } - new SimpleTask>() { + private void onActionDeleteSelection() { + new DialogBuilderLifecycle(getContext(), getViewLifecycleOwner()) + .setMessage(R.string.title_ask_delete_selected) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override - protected List onExecute(Context context, Bundle args) { - long fid = args.getLong("folder"); - long[] ids = args.getLongArray("ids"); + public void onClick(DialogInterface dialog, int which) { + Bundle args = new Bundle(); + args.putLongArray("ids", getSelection()); - DB db = DB.getInstance(context); + selectionTracker.clearSelection(); - long account = -1; - for (long id : ids) { - EntityMessage message = db.message().getMessage(id); - if (message != null) { - account = message.account; - break; - } - } + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) { + long[] ids = args.getLongArray("ids"); - List folders = db.folder().getFolders(account); + DB db = DB.getInstance(context); + try { + db.beginTransaction(); - List targets = new ArrayList<>(); - for (EntityFolder folder : folders) - if (!folder.hide && - !EntityFolder.ARCHIVE.equals(folder.type) && - !EntityFolder.TRASH.equals(folder.type) && - !EntityFolder.JUNK.equals(folder.type) && - (fid < 0 ? !folder.unified : !folder.id.equals(fid))) - targets.add(folder); + for (long id : ids) { + EntityMessage message = db.message().getMessage(id); + if (message != null) { + List messages = db.message().getMessageByThread( + message.account, message.thread, threading ? null : id, message.folder); + for (EntityMessage threaded : messages) + if (threaded.uid != null) + EntityOperation.queue(context, db, threaded, EntityOperation.DELETE); + } + } - EntityFolder.sort(context, targets); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + return null; + } - return targets; + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + } + }.execute(FragmentMessages.this, args, "messages:delete"); } + }) + .setNegativeButton(android.R.string.cancel, null) + .show(); + } + private void onActionJunkSelection() { + new DialogBuilderLifecycle(getContext(), getViewLifecycleOwner()) + .setMessage(R.string.title_ask_spam) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override - protected void onExecuted(final Bundle args, List folders) { - PopupMenu popupMenu = new PopupMenu(getContext(), popupAnchor); + public void onClick(DialogInterface dialog, int which) { + onActionMoveSelection(EntityFolder.JUNK); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .show(); + } - int order = 0; - for (EntityFolder folder : folders) - popupMenu.getMenu().add(Menu.NONE, folder.id.intValue(), order++, folder.getDisplayName(getContext())); + private void onActionMoveSelection(String type) { + Bundle args = new Bundle(); + args.putString("type", type); + args.putLongArray("ids", getSelection()); - popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(final MenuItem target) { - args.putLong("target", target.getItemId()); + selectionTracker.clearSelection(); - selectionTracker.clearSelection(); + new SimpleTask() { + @Override + protected MessageTarget onExecute(Context context, Bundle args) { + String type = args.getString("type"); + long[] ids = args.getLongArray("ids"); - new SimpleTask() { - @Override - protected MessageTarget onExecute(Context context, Bundle args) { - long[] ids = args.getLongArray("ids"); - long target = args.getLong("target"); + MessageTarget result = new MessageTarget(); - MessageTarget result = new MessageTarget(); + DB db = DB.getInstance(context); + try { + db.beginTransaction(); - DB db = DB.getInstance(context); - try { - db.beginTransaction(); + long account = -1; + for (long id : ids) { + EntityMessage message = db.message().getMessage(id); + if (message != null) { + account = message.account; + List messages = db.message().getMessageByThread( + message.account, message.thread, threading ? null : id, message.folder); + for (EntityMessage threaded : messages) + result.ids.add(threaded.id); + } + } - result.target = db.folder().getFolder(target); + result.target = db.folder().getFolderByType(account, type); - for (long id : ids) { - EntityMessage message = db.message().getMessage(id); - if (message != null) { - List messages = db.message().getMessageByThread( - message.account, message.thread, threading ? null : id, message.folder); - for (EntityMessage threaded : messages) - result.ids.add(threaded.id); - } - } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } + return result; + } - return result; - } + @Override + protected void onExecuted(Bundle args, MessageTarget result) { + if (EntityFolder.JUNK.equals(result.target.type)) + moveAskConfirmed(result); + else + moveAsk(result); + } - @Override - protected void onExecuted(Bundle args, MessageTarget result) { - moveAsk(result); - } + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + } + }.execute(FragmentMessages.this, args, "messages:move"); + } - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); - } - }.execute(FragmentMessages.this, args, "messages:move"); + private void onActionMoveSelection() { + Bundle args = new Bundle(); + args.putLong("folder", folder); + args.putLongArray("ids", getSelection()); - return true; - } - }); + new SimpleTask>() { + @Override + protected List onExecute(Context context, Bundle args) { + long fid = args.getLong("folder"); + long[] ids = args.getLongArray("ids"); - popupMenu.show(); - } + DB db = DB.getInstance(context); - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + long account = -1; + for (long id : ids) { + EntityMessage message = db.message().getMessage(id); + if (message != null) { + account = message.account; + break; } - }.execute(FragmentMessages.this, args, "messages:move"); + } + + List folders = db.folder().getFolders(account); + + List targets = new ArrayList<>(); + for (EntityFolder folder : folders) + if (!folder.hide && + !EntityFolder.ARCHIVE.equals(folder.type) && + !EntityFolder.TRASH.equals(folder.type) && + !EntityFolder.JUNK.equals(folder.type) && + (fid < 0 ? !folder.unified : !folder.id.equals(fid))) + targets.add(folder); + + EntityFolder.sort(context, targets); + + return targets; } - }); - ((ActivityBase) getActivity()).addBackPressedListener(onBackPressedListener); + @Override + protected void onExecuted(final Bundle args, List folders) { + PopupMenu popupMenu = new PopupMenu(getContext(), popupAnchor); - // Initialize - swipeRefresh.setEnabled(pull); - tvNoEmail.setVisibility(View.GONE); - bottom_navigation.setVisibility(View.GONE); - grpReady.setVisibility(View.GONE); - pbWait.setVisibility(View.VISIBLE); + int order = 0; + for (EntityFolder folder : folders) + popupMenu.getMenu().add(Menu.NONE, folder.id.intValue(), order++, folder.getDisplayName(getContext())); - fab.hide(); - fabMore.hide(); + popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(final MenuItem target) { + args.putLong("target", target.getItemId()); - return view; + selectionTracker.clearSelection(); + + new SimpleTask() { + @Override + protected MessageTarget onExecute(Context context, Bundle args) { + long[] ids = args.getLongArray("ids"); + long target = args.getLong("target"); + + MessageTarget result = new MessageTarget(); + + DB db = DB.getInstance(context); + try { + db.beginTransaction(); + + result.target = db.folder().getFolder(target); + + for (long id : ids) { + EntityMessage message = db.message().getMessage(id); + if (message != null) { + List messages = db.message().getMessageByThread( + message.account, message.thread, threading ? null : id, message.folder); + for (EntityMessage threaded : messages) + result.ids.add(threaded.id); + } + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + return result; + } + + @Override + protected void onExecuted(Bundle args, MessageTarget result) { + moveAsk(result); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + } + }.execute(FragmentMessages.this, args, "messages:move"); + + return true; + } + }); + + popupMenu.show(); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex); + } + }.execute(FragmentMessages.this, args, "messages:move"); } @Override diff --git a/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java b/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java index 00f645ffc0..747d4eecba 100644 --- a/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java +++ b/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java @@ -135,259 +135,263 @@ public class FragmentQuickSetup extends FragmentBase { btnCheck.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - Bundle args = new Bundle(); - args.putString("name", etName.getText().toString()); - args.putString("email", etEmail.getText().toString().trim()); - args.putString("password", tilPassword.getEditText().getText().toString()); - args.putInt("auth_type", auth_type); - - new SimpleTask() { - @Override - protected void onPreExecute(Bundle args) { - etName.setEnabled(false); - etEmail.setEnabled(false); - tilPassword.setEnabled(false); - btnAuthorize.setEnabled(false); - btnCheck.setEnabled(false); - tvError.setVisibility(View.GONE); - tvInstructions.setVisibility(View.GONE); - } + onCheck(); + } + }); - @Override - protected void onPostExecute(Bundle args) { - etName.setEnabled(true); - etEmail.setEnabled(true); - tilPassword.setEnabled(true); - btnAuthorize.setEnabled(true); - btnCheck.setEnabled(true); - } + // Initialize + tvError.setVisibility(View.GONE); + tvInstructions.setVisibility(View.GONE); + tvInstructions.setMovementMethod(LinkMovementMethod.getInstance()); - @Override - protected Void onExecute(Context context, Bundle args) throws Throwable { - String name = args.getString("name"); - String email = args.getString("email"); - String password = args.getString("password"); - int auth_type = args.getInt("auth_type"); - - if (TextUtils.isEmpty(name)) - throw new IllegalArgumentException(context.getString(R.string.title_no_name)); - if (TextUtils.isEmpty(email)) - throw new IllegalArgumentException(context.getString(R.string.title_no_email)); - if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) - throw new IllegalArgumentException(context.getString(R.string.title_email_invalid)); - - String[] dparts = email.split("@"); - EmailProvider provider = EmailProvider.fromDomain(context, dparts[1]); - - if (provider.documentation != null) - args.putString("documentation", provider.documentation.toString()); - - String user = (provider.user == EmailProvider.UserType.EMAIL ? email : dparts[0]); - - Character separator; - long now = new Date().getTime(); - - List folders = new ArrayList<>(); - - EntityFolder inbox = new EntityFolder(); - inbox.name = "INBOX"; - inbox.type = EntityFolder.INBOX; - inbox.level = 0; - inbox.synchronize = true; - inbox.unified = true; - inbox.notify = true; - inbox.sync_days = EntityFolder.DEFAULT_SYNC; - inbox.keep_days = EntityFolder.DEFAULT_KEEP; - folders.add(inbox); - - { - Properties props = MessageHelper.getSessionProperties(auth_type, null, false); - Session isession = Session.getInstance(props, null); - isession.setDebug(true); - IMAPStore istore = null; - try { - istore = (IMAPStore) isession.getStore(provider.imap_starttls ? "imap" : "imaps"); - istore.connect(provider.imap_host, provider.imap_port, user, password); - - separator = istore.getDefaultFolder().getSeparator(); - - boolean drafts = false; - for (Folder ifolder : istore.getDefaultFolder().list("*")) { - String type = null; - boolean selectable = true; - String[] attrs = ((IMAPFolder) ifolder).getAttributes(); - Log.i(ifolder.getFullName() + " attrs=" + TextUtils.join(" ", attrs)); - for (String attr : attrs) { - if ("\\Noselect".equals(attr) || "\\NonExistent".equals(attr)) - selectable = false; - if (attr.startsWith("\\")) { - int index = EntityFolder.SYSTEM_FOLDER_ATTR.indexOf(attr.substring(1)); - if (index >= 0) { - type = EntityFolder.SYSTEM_FOLDER_TYPE.get(index); - break; - } - } - } + return view; + } - if (selectable && type != null) { - int sync = EntityFolder.SYSTEM_FOLDER_SYNC.indexOf(type); - EntityFolder folder = new EntityFolder(); - folder.name = ifolder.getFullName(); - folder.type = type; - folder.level = EntityFolder.getLevel(separator, folder.name); - folder.synchronize = (sync >= 0); - folder.download = (sync < 0 ? true : EntityFolder.SYSTEM_FOLDER_DOWNLOAD.get(sync)); - folder.sync_days = EntityFolder.DEFAULT_SYNC; - folder.keep_days = EntityFolder.DEFAULT_KEEP; - folders.add(folder); - - if (EntityFolder.DRAFTS.equals(type)) - drafts = true; - } - } + private void onCheck() { + Bundle args = new Bundle(); + args.putString("name", etName.getText().toString()); + args.putString("email", etEmail.getText().toString().trim()); + args.putString("password", tilPassword.getEditText().getText().toString()); + args.putInt("auth_type", auth_type); - if (!drafts) - throw new IllegalArgumentException( - context.getString(R.string.title_setup_no_settings, dparts[1])); - } finally { - if (istore != null) - istore.close(); - } - } + new SimpleTask() { + @Override + protected void onPreExecute(Bundle args) { + etName.setEnabled(false); + etEmail.setEnabled(false); + tilPassword.setEnabled(false); + btnAuthorize.setEnabled(false); + btnCheck.setEnabled(false); + tvError.setVisibility(View.GONE); + tvInstructions.setVisibility(View.GONE); + } - { - Properties props = MessageHelper.getSessionProperties(auth_type, null, false); - Session isession = Session.getInstance(props, null); - isession.setDebug(true); - Transport itransport = isession.getTransport(provider.smtp_starttls ? "smtp" : "smtps"); - try { - itransport.connect(provider.smtp_host, provider.smtp_port, user, password); - } finally { - itransport.close(); - } - } + @Override + protected void onPostExecute(Bundle args) { + etName.setEnabled(true); + etEmail.setEnabled(true); + tilPassword.setEnabled(true); + btnAuthorize.setEnabled(true); + btnCheck.setEnabled(true); + } - DB db = DB.getInstance(context); - try { - db.beginTransaction(); - EntityAccount primary = db.account().getPrimaryAccount(); - - // Create account - EntityAccount account = new EntityAccount(); - - account.auth_type = auth_type; - account.host = provider.imap_host; - account.starttls = provider.imap_starttls; - account.insecure = false; - account.port = provider.imap_port; - account.user = user; - account.password = password; - - account.name = provider.name; - account.color = null; - - account.synchronize = true; - account.primary = (primary == null); - account.notify = false; - account.browse = true; - account.poll_interval = 19; - account.prefix = provider.prefix; - - account.created = now; - account.error = null; - account.last_connected = now; - - account.id = db.account().insertAccount(account); - - // Create folders - for (EntityFolder folder : folders) { - folder.account = account.id; - folder.id = db.folder().insertFolder(folder); + @Override + protected Void onExecute(Context context, Bundle args) throws Throwable { + String name = args.getString("name"); + String email = args.getString("email"); + String password = args.getString("password"); + int auth_type = args.getInt("auth_type"); + + if (TextUtils.isEmpty(name)) + throw new IllegalArgumentException(context.getString(R.string.title_no_name)); + if (TextUtils.isEmpty(email)) + throw new IllegalArgumentException(context.getString(R.string.title_no_email)); + if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) + throw new IllegalArgumentException(context.getString(R.string.title_email_invalid)); + + String[] dparts = email.split("@"); + EmailProvider provider = EmailProvider.fromDomain(context, dparts[1]); + + if (provider.documentation != null) + args.putString("documentation", provider.documentation.toString()); + + String user = (provider.user == EmailProvider.UserType.EMAIL ? email : dparts[0]); + + Character separator; + long now = new Date().getTime(); + + List folders = new ArrayList<>(); + + EntityFolder inbox = new EntityFolder(); + inbox.name = "INBOX"; + inbox.type = EntityFolder.INBOX; + inbox.level = 0; + inbox.synchronize = true; + inbox.unified = true; + inbox.notify = true; + inbox.sync_days = EntityFolder.DEFAULT_SYNC; + inbox.keep_days = EntityFolder.DEFAULT_KEEP; + folders.add(inbox); + + { + Properties props = MessageHelper.getSessionProperties(auth_type, null, false); + Session isession = Session.getInstance(props, null); + isession.setDebug(true); + IMAPStore istore = null; + try { + istore = (IMAPStore) isession.getStore(provider.imap_starttls ? "imap" : "imaps"); + istore.connect(provider.imap_host, provider.imap_port, user, password); + + separator = istore.getDefaultFolder().getSeparator(); + + boolean drafts = false; + for (Folder ifolder : istore.getDefaultFolder().list("*")) { + String type = null; + boolean selectable = true; + String[] attrs = ((IMAPFolder) ifolder).getAttributes(); + Log.i(ifolder.getFullName() + " attrs=" + TextUtils.join(" ", attrs)); + for (String attr : attrs) { + if ("\\Noselect".equals(attr) || "\\NonExistent".equals(attr)) + selectable = false; + if (attr.startsWith("\\")) { + int index = EntityFolder.SYSTEM_FOLDER_ATTR.indexOf(attr.substring(1)); + if (index >= 0) { + type = EntityFolder.SYSTEM_FOLDER_TYPE.get(index); + break; + } + } } - // Create identity - EntityIdentity identity = new EntityIdentity(); - identity.name = name; - identity.email = email; - identity.account = account.id; - - identity.display = null; - identity.color = null; - identity.signature = null; - - identity.auth_type = auth_type; - identity.host = provider.smtp_host; - identity.starttls = provider.smtp_starttls; - identity.insecure = false; - identity.port = provider.smtp_port; - identity.user = user; - identity.password = password; - identity.synchronize = true; - identity.primary = true; - - identity.replyto = null; - identity.bcc = null; - identity.delivery_receipt = false; - identity.read_receipt = false; - identity.store_sent = false; - identity.sent_folder = null; - identity.error = null; - - identity.id = db.identity().insertIdentity(identity); - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); + if (selectable && type != null) { + int sync = EntityFolder.SYSTEM_FOLDER_SYNC.indexOf(type); + EntityFolder folder = new EntityFolder(); + folder.name = ifolder.getFullName(); + folder.type = type; + folder.level = EntityFolder.getLevel(separator, folder.name); + folder.synchronize = (sync >= 0); + folder.download = (sync < 0 ? true : EntityFolder.SYSTEM_FOLDER_DOWNLOAD.get(sync)); + folder.sync_days = EntityFolder.DEFAULT_SYNC; + folder.keep_days = EntityFolder.DEFAULT_KEEP; + folders.add(folder); + + if (EntityFolder.DRAFTS.equals(type)) + drafts = true; + } } - ServiceSynchronize.reload(context, "quick setup"); - - return null; + if (!drafts) + throw new IllegalArgumentException( + context.getString(R.string.title_setup_no_settings, dparts[1])); + } finally { + if (istore != null) + istore.close(); } - - @Override - protected void onExecuted(Bundle args, Void data) { - etName.setText(null); - etEmail.setText(null); - tilPassword.getEditText().setText(null); - - new DialogBuilderLifecycle(getContext(), getViewLifecycleOwner()) - .setMessage(R.string.title_setup_quick_success) - .setPositiveButton(android.R.string.ok, null) - .setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialog) { - finish(); - } - }) - .create() - .show(); + } + + { + Properties props = MessageHelper.getSessionProperties(auth_type, null, false); + Session isession = Session.getInstance(props, null); + isession.setDebug(true); + Transport itransport = isession.getTransport(provider.smtp_starttls ? "smtp" : "smtps"); + try { + itransport.connect(provider.smtp_host, provider.smtp_port, user, password); + } finally { + itransport.close(); } - - @Override - protected void onException(Bundle args, Throwable ex) { - if (args.containsKey("documentation")) { - tvInstructions.setText(Html.fromHtml(args.getString("documentation"))); - tvInstructions.setVisibility(View.VISIBLE); - } - - if (ex instanceof IllegalArgumentException) - Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); - else { - tvError.setText(Helper.formatThrowable(ex)); - tvError.setVisibility(View.VISIBLE); - } + } + + DB db = DB.getInstance(context); + try { + db.beginTransaction(); + EntityAccount primary = db.account().getPrimaryAccount(); + + // Create account + EntityAccount account = new EntityAccount(); + + account.auth_type = auth_type; + account.host = provider.imap_host; + account.starttls = provider.imap_starttls; + account.insecure = false; + account.port = provider.imap_port; + account.user = user; + account.password = password; + + account.name = provider.name; + account.color = null; + + account.synchronize = true; + account.primary = (primary == null); + account.notify = false; + account.browse = true; + account.poll_interval = 19; + account.prefix = provider.prefix; + + account.created = now; + account.error = null; + account.last_connected = now; + + account.id = db.account().insertAccount(account); + + // Create folders + for (EntityFolder folder : folders) { + folder.account = account.id; + folder.id = db.folder().insertFolder(folder); } - }.execute(FragmentQuickSetup.this, args, "setup:quick"); + + // Create identity + EntityIdentity identity = new EntityIdentity(); + identity.name = name; + identity.email = email; + identity.account = account.id; + + identity.display = null; + identity.color = null; + identity.signature = null; + + identity.auth_type = auth_type; + identity.host = provider.smtp_host; + identity.starttls = provider.smtp_starttls; + identity.insecure = false; + identity.port = provider.smtp_port; + identity.user = user; + identity.password = password; + identity.synchronize = true; + identity.primary = true; + + identity.replyto = null; + identity.bcc = null; + identity.delivery_receipt = false; + identity.read_receipt = false; + identity.store_sent = false; + identity.sent_folder = null; + identity.error = null; + + identity.id = db.identity().insertIdentity(identity); + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + ServiceSynchronize.reload(context, "quick setup"); + + return null; } - }); - // Initialize - tvError.setVisibility(View.GONE); - tvInstructions.setVisibility(View.GONE); - tvInstructions.setMovementMethod(LinkMovementMethod.getInstance()); + @Override + protected void onExecuted(Bundle args, Void data) { + etName.setText(null); + etEmail.setText(null); + tilPassword.getEditText().setText(null); + + new DialogBuilderLifecycle(getContext(), getViewLifecycleOwner()) + .setMessage(R.string.title_setup_quick_success) + .setPositiveButton(android.R.string.ok, null) + .setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + finish(); + } + }) + .create() + .show(); + } - return view; + @Override + protected void onException(Bundle args, Throwable ex) { + if (args.containsKey("documentation")) { + tvInstructions.setText(Html.fromHtml(args.getString("documentation"))); + tvInstructions.setVisibility(View.VISIBLE); + } + + if (ex instanceof IllegalArgumentException) + Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); + else { + tvError.setText(Helper.formatThrowable(ex)); + tvError.setVisibility(View.VISIBLE); + } + } + }.execute(FragmentQuickSetup.this, args, "setup:quick"); } @Override diff --git a/app/src/main/java/eu/faircode/email/FragmentWebView.java b/app/src/main/java/eu/faircode/email/FragmentWebView.java index 422623a63a..e6990395d1 100644 --- a/app/src/main/java/eu/faircode/email/FragmentWebView.java +++ b/app/src/main/java/eu/faircode/email/FragmentWebView.java @@ -122,6 +122,14 @@ public class FragmentWebView extends FragmentBase { registerForContextMenu(webview); + onLoad(); + + ((ActivityBase) getActivity()).addBackPressedListener(onBackPressedListener); + + return view; + } + + private void onLoad() { Bundle args = getArguments(); if (args.containsKey("url")) { String url = args.getString("url"); @@ -188,10 +196,6 @@ public class FragmentWebView extends FragmentBase { } }.execute(this, args, "webview:format"); } - - ((ActivityBase) getActivity()).addBackPressedListener(onBackPressedListener); - - return view; } @Override