diff --git a/app/src/main/java/eu/faircode/email/DaoFolder.java b/app/src/main/java/eu/faircode/email/DaoFolder.java index cda003f0..83b843bc 100644 --- a/app/src/main/java/eu/faircode/email/DaoFolder.java +++ b/app/src/main/java/eu/faircode/email/DaoFolder.java @@ -114,12 +114,15 @@ public interface DaoFolder { " AND type = :type") int setFolderUser(long account, String type); - @Query("UPDATE folder SET synchronize = :synchronize, unified = :unified, after = :after WHERE id = :id") - int setFolderProperties(long id, boolean synchronize, boolean unified, int after); + @Query("UPDATE folder SET name = :name, synchronize = :synchronize, unified = :unified, after = :after WHERE id = :id") + int setFolderProperties(long id, String name, boolean synchronize, boolean unified, int after); @Query("UPDATE folder SET name = :name WHERE account = :account AND name = :old") int renameFolder(long account, String old, String name); + @Query("DELETE FROM folder WHERE id = :id") + void deleteFolder(long id); + @Query("DELETE FROM folder WHERE account= :account AND name = :name") - void deleteFolder(Long account, String name); + void deleteFolder(long account, String name); } diff --git a/app/src/main/java/eu/faircode/email/FragmentFolder.java b/app/src/main/java/eu/faircode/email/FragmentFolder.java index 5735409b..13662ce4 100644 --- a/app/src/main/java/eu/faircode/email/FragmentFolder.java +++ b/app/src/main/java/eu/faircode/email/FragmentFolder.java @@ -20,31 +20,47 @@ package eu.faircode.email; */ import android.content.Context; +import android.content.DialogInterface; import android.os.Bundle; import android.text.TextUtils; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; +import android.widget.ImageButton; import android.widget.ProgressBar; import android.widget.Toast; +import com.google.android.material.snackbar.Snackbar; +import com.sun.mail.imap.IMAPFolder; +import com.sun.mail.imap.IMAPStore; + +import java.util.Properties; + +import javax.mail.Folder; +import javax.mail.Session; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; import androidx.lifecycle.Observer; public class FragmentFolder extends FragmentEx { private ViewGroup view; + private EditText etRename; private CheckBox cbSynchronize; private CheckBox cbUnified; private EditText etAfter; private Button btnSave; + private ImageButton ibDelete; private ProgressBar pbSave; private ProgressBar pbWait; private long id = -1; + private long account = -1; @Override public void onCreate(Bundle savedInstanceState) { @@ -53,6 +69,7 @@ public class FragmentFolder extends FragmentEx { // Get arguments Bundle args = getArguments(); id = (args == null ? -1 : args.getLong("id")); + account = (args == null ? -1 : args.getLong("account")); } @Override @@ -63,11 +80,13 @@ public class FragmentFolder extends FragmentEx { view = (ViewGroup) inflater.inflate(R.layout.fragment_folder, container, false); // Get controls + etRename = view.findViewById(R.id.etRename); cbSynchronize = view.findViewById(R.id.cbSynchronize); cbUnified = view.findViewById(R.id.cbUnified); etAfter = view.findViewById(R.id.etAfter); - pbSave = view.findViewById(R.id.pbSave); btnSave = view.findViewById(R.id.btnSave); + ibDelete = view.findViewById(R.id.ibDelete); + pbSave = view.findViewById(R.id.pbSave); pbWait = view.findViewById(R.id.pbWait); btnSave.setOnClickListener(new View.OnClickListener() { @@ -75,34 +94,83 @@ public class FragmentFolder extends FragmentEx { public void onClick(View v) { Helper.setViewsEnabled(view, false); btnSave.setEnabled(false); + ibDelete.setEnabled(false); pbSave.setVisibility(View.VISIBLE); Bundle args = new Bundle(); args.putLong("id", id); + args.putLong("account", account); + args.putString("name", etRename.getText().toString()); args.putBoolean("synchronize", cbSynchronize.isChecked()); args.putBoolean("unified", cbUnified.isChecked()); args.putString("after", etAfter.getText().toString()); new SimpleTask() { @Override - protected Void onLoad(Context context, Bundle args) { + protected Void onLoad(Context context, Bundle args) throws Throwable { long id = args.getLong("id"); + long aid = args.getLong("account"); + String name = args.getString("name"); boolean synchronize = args.getBoolean("synchronize"); boolean unified = args.getBoolean("unified"); String after = args.getString("after"); - int days = (TextUtils.isEmpty(after) ? 7 : Integer.parseInt(after)); + int days = (TextUtils.isEmpty(after) ? EntityFolder.DEFAULT_USER_SYNC : Integer.parseInt(after)); + IMAPStore istore = null; DB db = DB.getInstance(getContext()); try { db.beginTransaction(); - db.folder().setFolderProperties(id, synchronize, unified, days); - if (!synchronize) - db.folder().setFolderError(id, null); + EntityFolder folder = db.folder().getFolder(id); + + if (folder == null || !folder.name.equals(name)) { + EntityAccount account = db.account().getAccount(folder == null ? aid : folder.account); + + Properties props = MessageHelper.getSessionProperties(context, account.auth_type); + Session isession = Session.getInstance(props, null); + istore = (IMAPStore) isession.getStore("imaps"); + istore.connect(account.host, account.port, account.user, account.password); + + if (folder == null) { + Log.i(Helper.TAG, "Creating folder=" + name); + + IMAPFolder ifolder = (IMAPFolder) istore.getFolder(name); + if (ifolder.exists()) + throw new IllegalArgumentException(getString(R.string.title_folder_exists, name)); + ifolder.create(Folder.HOLDS_MESSAGES); + + EntityFolder create = new EntityFolder(); + create.account = aid; + create.name = name; + create.type = EntityFolder.USER; + create.unified = unified; + create.synchronize = synchronize; + create.after = days; + db.folder().insertFolder(create); + } else { + Log.i(Helper.TAG, "Renaming folder=" + name); + + IMAPFolder iold = (IMAPFolder) istore.getFolder(folder.name); + IMAPFolder ifolder = (IMAPFolder) istore.getFolder(name); + if (ifolder.exists()) + throw new IllegalArgumentException(getString(R.string.title_folder_exists, name)); + iold.renameTo(ifolder); + } + } + + if (folder != null) { + Log.i(Helper.TAG, "Updating folder=" + name); + db.folder().setFolderProperties(id, name, synchronize, unified, days); + if (!synchronize) + db.folder().setFolderError(id, null); + } db.setTransactionSuccessful(); } finally { db.endTransaction(); + + if (istore != null) + istore.close(); } ServiceSynchronize.reload(getContext(), "save folder"); @@ -119,17 +187,100 @@ public class FragmentFolder extends FragmentEx { protected void onException(Bundle args, Throwable ex) { Helper.setViewsEnabled(view, true); btnSave.setEnabled(true); + ibDelete.setEnabled(true); pbSave.setVisibility(View.GONE); - Toast.makeText(getContext(), ex.toString(), Toast.LENGTH_LONG).show(); + if (ex instanceof IllegalArgumentException) + Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); + else + Toast.makeText(getContext(), ex.toString(), Toast.LENGTH_LONG).show(); } }.load(FragmentFolder.this, args); } }); + ibDelete.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + new AlertDialog.Builder(getContext()) + .setMessage(R.string.title_folder_delete) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Helper.setViewsEnabled(view, false); + btnSave.setEnabled(false); + ibDelete.setEnabled(false); + pbSave.setVisibility(View.VISIBLE); + + Bundle args = new Bundle(); + args.putLong("id", id); + + new SimpleTask() { + @Override + protected Void onLoad(Context context, Bundle args) throws Throwable { + long id = args.getLong("id"); + + IMAPStore istore = null; + DB db = DB.getInstance(getContext()); + try { + db.beginTransaction(); + + EntityFolder folder = db.folder().getFolder(id); + EntityAccount account = db.account().getAccount(folder.account); + + Properties props = MessageHelper.getSessionProperties(context, account.auth_type); + Session isession = Session.getInstance(props, null); + istore = (IMAPStore) isession.getStore("imaps"); + istore.connect(account.host, account.port, account.user, account.password); + + IMAPFolder ifolder = (IMAPFolder) istore.getFolder(folder.name); + ifolder.delete(false); + + db.folder().deleteFolder(id); + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + + if (istore != null) + istore.close(); + } + + ServiceSynchronize.reload(getContext(), "delete folder"); + + return null; + } + + @Override + protected void onLoaded(Bundle args, Void data) { + getFragmentManager().popBackStack(); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.setViewsEnabled(view, true); + btnSave.setEnabled(true); + ibDelete.setEnabled(true); + pbSave.setVisibility(View.GONE); + + if (ex instanceof IllegalArgumentException) + Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show(); + else + Toast.makeText(getContext(), ex.toString(), Toast.LENGTH_LONG).show(); + } + }.load(FragmentFolder.this, args); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .show(); + } + }); + // Initialize Helper.setViewsEnabled(view, false); btnSave.setEnabled(false); + ibDelete.setEnabled(false); + ibDelete.setVisibility(View.GONE); pbSave.setVisibility(View.GONE); pbWait.setVisibility(View.VISIBLE); @@ -146,25 +297,23 @@ public class FragmentFolder extends FragmentEx { @Override public void onChanged(@Nullable EntityFolder folder) { - if (folder == null) { - finish(); - return; - } - if (once) return; once = true; if (savedInstanceState == null) { - cbSynchronize.setChecked(folder.synchronize); - cbUnified.setChecked(folder.unified); - etAfter.setText(Integer.toString(folder.after)); + etRename.setText(folder == null ? null : folder.name); + cbSynchronize.setChecked(folder == null ? true : folder.synchronize); + cbUnified.setChecked(folder == null ? false : folder.unified); + etAfter.setText(Integer.toString(folder == null ? EntityFolder.DEFAULT_USER_SYNC : folder.after)); } // Consider previous save as cancelled pbWait.setVisibility(View.GONE); Helper.setViewsEnabled(view, true); btnSave.setEnabled(true); + ibDelete.setEnabled(true); + ibDelete.setVisibility(folder == null ? View.GONE : View.VISIBLE); } }); } diff --git a/app/src/main/java/eu/faircode/email/FragmentFolders.java b/app/src/main/java/eu/faircode/email/FragmentFolders.java index a7c5272f..50ce9389 100644 --- a/app/src/main/java/eu/faircode/email/FragmentFolders.java +++ b/app/src/main/java/eu/faircode/email/FragmentFolders.java @@ -19,7 +19,6 @@ package eu.faircode.email; Copyright 2018 by Marcel Bokhorst (M66B) */ -import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; @@ -36,6 +35,7 @@ import java.util.List; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.constraintlayout.widget.Group; +import androidx.fragment.app.FragmentTransaction; import androidx.lifecycle.Observer; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -94,13 +94,13 @@ public class FragmentFolders extends FragmentEx { fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - Bundle args = getArguments(); - long account = (args == null ? -1 : args.getLong("account")); - - startActivity(new Intent(getContext(), ActivityCompose.class) - .putExtra("action", "new") - .putExtra("account", account) - ); + Bundle args = new Bundle(); + args.putLong("account", account); + FragmentFolder fragment = new FragmentFolder(); + fragment.setArguments(args); + FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); + fragmentTransaction.replace(R.id.content_frame, fragment).addToBackStack("folder"); + fragmentTransaction.commit(); } }); diff --git a/app/src/main/res/layout/fragment_folder.xml b/app/src/main/res/layout/fragment_folder.xml index 679d2b68..b439d1f6 100644 --- a/app/src/main/res/layout/fragment_folder.xml +++ b/app/src/main/res/layout/fragment_folder.xml @@ -13,6 +13,26 @@ android:layout_height="match_parent" android:padding="12dp"> + + + + + app:layout_constraintTop_toBottomOf="@id/etRename" /> + + Delete local messages Edit properties + Folder name Synchronize (receive messages) Show in unified inbox Synchronize (days) + Folder %1$s exists + Delete folder permanently? Unified inbox Inbox