From e8027bde3828789824105429cdf3d4d63ca51c89 Mon Sep 17 00:00:00 2001 From: M66B Date: Tue, 14 Mar 2023 08:22:43 +0100 Subject: [PATCH] Dialog refactoring --- .../java/eu/faircode/email/ActivityView.java | 60 -- .../eu/faircode/email/AdapterAttachment.java | 213 ----- .../eu/faircode/email/AdapterContact.java | 2 +- .../eu/faircode/email/FragmentCompose.java | 902 +----------------- .../eu/faircode/email/FragmentContacts.java | 109 +-- .../email/FragmentDialogAddImage.java | 164 ++++ .../faircode/email/FragmentDialogAskSpam.java | 72 ++ .../email/FragmentDialogBoundaryError.java | 63 ++ .../email/FragmentDialogContactDelete.java | 69 ++ .../email/FragmentDialogContactEdit.java | 96 ++ .../email/FragmentDialogContactGroup.java | 184 ++++ .../eu/faircode/email/FragmentDialogDoze.java | 53 + .../faircode/email/FragmentDialogExport.java | 136 +++ .../faircode/email/FragmentDialogFirst.java | 71 ++ .../email/FragmentDialogFoldersApply.java | 149 +++ .../faircode/email/FragmentDialogImport.java | 136 +++ .../email/FragmentDialogOperationsDelete.java | 176 ++++ .../eu/faircode/email/FragmentDialogPin.java | 115 +++ .../email/FragmentDialogQuickActions.java | 118 +++ .../eu/faircode/email/FragmentDialogRate.java | 50 + .../email/FragmentDialogReporting.java | 68 ++ .../faircode/email/FragmentDialogReview.java | 93 ++ .../email/FragmentDialogRuleCheck.java | 213 +++++ .../email/FragmentDialogSaveSearch.java | 125 +++ .../eu/faircode/email/FragmentDialogSend.java | 696 ++++++++++++++ .../faircode/email/FragmentDialogSwipes.java | 158 +++ .../email/FragmentDialogVirusTotal.java | 248 +++++ .../faircode/email/FragmentDialogWeekend.java | 70 ++ .../eu/faircode/email/FragmentFolders.java | 120 +-- .../eu/faircode/email/FragmentMessages.java | 512 +--------- .../eu/faircode/email/FragmentMoveAsk.java | 194 ++++ .../eu/faircode/email/FragmentOperations.java | 148 +-- .../faircode/email/FragmentOptionsBackup.java | 185 ---- .../email/FragmentOptionsBehavior.java | 128 --- .../email/FragmentOptionsPrivacy.java | 83 -- .../email/FragmentOptionsSynchronize.java | 38 - .../java/eu/faircode/email/FragmentRule.java | 180 +--- .../java/eu/faircode/email/FragmentSetup.java | 24 - 38 files changed, 3542 insertions(+), 2679 deletions(-) create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogAddImage.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogAskSpam.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogBoundaryError.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogContactDelete.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogContactEdit.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogContactGroup.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogDoze.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogExport.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogFirst.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogFoldersApply.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogImport.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogOperationsDelete.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogPin.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogQuickActions.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogRate.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogReporting.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogReview.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogRuleCheck.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogSaveSearch.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogSend.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogSwipes.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogVirusTotal.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentDialogWeekend.java create mode 100644 app/src/main/java/eu/faircode/email/FragmentMoveAsk.java diff --git a/app/src/main/java/eu/faircode/email/ActivityView.java b/app/src/main/java/eu/faircode/email/ActivityView.java index f7db17fcbf..c8cc3ee6fb 100644 --- a/app/src/main/java/eu/faircode/email/ActivityView.java +++ b/app/src/main/java/eu/faircode/email/ActivityView.java @@ -25,7 +25,6 @@ import static androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_UNLOCKED; import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; import android.annotation.SuppressLint; -import android.app.Dialog; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -2487,65 +2486,6 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB } } - public static class FragmentDialogFirst extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - LayoutInflater inflater = LayoutInflater.from(getContext()); - View dview = inflater.inflate(R.layout.dialog_first, null); - ImageButton ibBatteryInfo = dview.findViewById(R.id.ibBatteryInfo); - ImageButton ibReformatInfo = dview.findViewById(R.id.ibReformatInfo); - - ibBatteryInfo.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Helper.viewFAQ(v.getContext(), 39); - } - }); - - ibReformatInfo.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Helper.viewFAQ(v.getContext(), 35); - } - }); - - return new AlertDialog.Builder(getContext()) - .setView(dview) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - prefs.edit().putBoolean("first", false).apply(); - } - }) - .setNegativeButton(android.R.string.cancel, null) - .create(); - } - } - - public static class FragmentDialogRate extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - return new AlertDialog.Builder(getContext()) - .setMessage(R.string.title_issue) - .setPositiveButton(R.string.title_yes, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - Helper.viewFAQ(getContext(), 0); - } - }) - .setNegativeButton(R.string.title_no, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - Helper.view(getContext(), Helper.getIntentRate(getContext())); - } - }) - .create(); - } - } - private final Consumer layoutStateChangeCallback = new Consumer() { @Override public void accept(WindowLayoutInfo info) { diff --git a/app/src/main/java/eu/faircode/email/AdapterAttachment.java b/app/src/main/java/eu/faircode/email/AdapterAttachment.java index 7d674f9ed1..917eb5effc 100644 --- a/app/src/main/java/eu/faircode/email/AdapterAttachment.java +++ b/app/src/main/java/eu/faircode/email/AdapterAttachment.java @@ -19,12 +19,10 @@ package eu.faircode.email; Copyright 2018-2023 by Marcel Bokhorst (M66B) */ -import android.app.Dialog; import android.content.Context; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.Typeface; -import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import android.view.LayoutInflater; @@ -32,7 +30,6 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ProgressBar; @@ -40,8 +37,6 @@ import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.PopupMenu; import androidx.core.app.ShareCompat; import androidx.fragment.app.Fragment; @@ -51,12 +46,9 @@ import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.OnLifecycleEvent; import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.DiffUtil; -import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.ListUpdateCallback; import androidx.recyclerview.widget.RecyclerView; -import java.io.File; -import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -597,209 +589,4 @@ public class AdapterAttachment extends RecyclerView.Adapter taskLookup = new SimpleTask() { - @Override - protected void onPreExecute(Bundle args) { - tvError.setVisibility(View.GONE); - pbWait.setVisibility(View.VISIBLE); - } - - @Override - protected void onPostExecute(Bundle args) { - pbWait.setVisibility(View.GONE); - } - - @Override - protected Bundle onExecute(Context context, Bundle args) throws Throwable { - String apiKey = args.getString("apiKey"); - File file = (File) args.getSerializable("file"); - return VirusTotal.lookup(context, file, apiKey); - } - - @Override - protected void onExecuted(Bundle args, Bundle result) { - List scans = result.getParcelableArrayList("scans"); - String label = result.getString("label"); - String analysis = args.getString("analysis"); - - int malicious = 0; - if (scans != null) - for (VirusTotal.ScanResult scan : scans) - if ("malicious".equals(scan.category)) - malicious++; - - NumberFormat NF = NumberFormat.getNumberInstance(); - - tvUnknown.setVisibility(scans == null ? View.VISIBLE : View.GONE); - tvSummary.setText(getString(R.string.title_vt_summary, NF.format(malicious))); - tvSummary.setTextColor(Helper.resolveColor(context, - malicious == 0 ? android.R.attr.textColorPrimary : R.attr.colorWarning)); - tvSummary.setTypeface(malicious == 0 ? Typeface.DEFAULT : Typeface.DEFAULT_BOLD); - tvSummary.setVisibility(scans == null ? View.GONE : View.VISIBLE); - tvLabel.setText(label); - tvReport.setVisibility(scans == null ? View.GONE : View.VISIBLE); - adapter.set(scans == null ? new ArrayList<>() : scans); - rvScan.setVisibility(scans == null ? View.GONE : View.VISIBLE); - btnUpload.setVisibility(scans == null && !TextUtils.isEmpty(apiKey) ? View.VISIBLE : View.GONE); - tvPrivacy.setVisibility(btnUpload.getVisibility()); - - if (analysis != null && args.getBoolean("init")) { - args.remove("init"); - btnUpload.callOnClick(); - } - } - - @Override - protected void onException(Bundle args, Throwable ex) { - tvError.setText(Log.formatThrowable(ex, false)); - tvError.setVisibility(View.VISIBLE); - } - }; - - final SimpleTask taskUpload = new SimpleTask() { - @Override - protected void onPreExecute(Bundle args) { - btnUpload.setEnabled(false); - pbUpload.setVisibility(View.VISIBLE); - } - - @Override - protected void onPostExecute(Bundle args) { - btnUpload.setEnabled(true); - tvAnalyzing.setVisibility(View.GONE); - pbUpload.setVisibility(View.GONE); - } - - @Override - protected Void onExecute(Context context, Bundle args) throws Throwable { - String apiKey = args.getString("apiKey"); - File file = (File) args.getSerializable("file"); - - String analysis = args.getString("analysis"); - if (analysis == null) { - analysis = VirusTotal.upload(context, file, apiKey); - args.putString("analysis", analysis); - } - postProgress(analysis); - VirusTotal.waitForAnalysis(context, analysis, apiKey); - return null; - } - - @Override - protected void onProgress(CharSequence status, Bundle data) { - tvAnalyzing.setVisibility(View.VISIBLE); - } - - @Override - protected void onExecuted(Bundle args, Void data) { - taskLookup.execute(FragmentDialogVirusTotal.this, args, "attachment:lookup"); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(getParentFragmentManager(), ex); - } - }; - - final SimpleTask taskUrl = new SimpleTask() { - @Override - protected String onExecute(Context context, Bundle args) throws Throwable { - File file = (File) args.getSerializable("file"); - return VirusTotal.getUrl(file); - } - - @Override - protected void onExecuted(Bundle args, String uri) { - Helper.view(context, Uri.parse(uri), true); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(getParentFragmentManager(), ex); - } - }; - - tvReport.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - taskUrl.execute(FragmentDialogVirusTotal.this, args, "attachment:report"); - } - }); - - btnUpload.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - taskUpload.execute(FragmentDialogVirusTotal.this, args, "attachment:upload"); - } - }); - - tvPrivacy.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Helper.view(v.getContext(), Uri.parse(VirusTotal.URI_PRIVACY), true); - } - }); - - if (TextUtils.isEmpty(apiKey)) - pbWait.setVisibility(View.GONE); - else { - args.putBoolean("init", true); - taskLookup.execute(this, args, "attachment:lookup"); - } - - return new AlertDialog.Builder(context) - .setView(view) - .setNegativeButton(R.string.title_setup_done, null) - .create(); - } - } } diff --git a/app/src/main/java/eu/faircode/email/AdapterContact.java b/app/src/main/java/eu/faircode/email/AdapterContact.java index c79d08f7a6..18bb61f3ea 100644 --- a/app/src/main/java/eu/faircode/email/AdapterContact.java +++ b/app/src/main/java/eu/faircode/email/AdapterContact.java @@ -325,7 +325,7 @@ public class AdapterContact extends RecyclerView.Adapter RECIPIENTS_WARNING || + recipients > FragmentDialogSend.RECIPIENTS_WARNING || (styled && draft.isPlainOnly()) || (send_reminders && (remind_extra || remind_subject || remind_text || @@ -7483,892 +7471,6 @@ public class FragmentCompose extends FragmentBase { } }; - public static class FragmentDialogContactGroup extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - final Bundle args = getArguments(); - final long working = args.getLong("working"); - int focussed = args.getInt("focussed"); - - final Context context = getContext(); - View dview = LayoutInflater.from(context).inflate(R.layout.dialog_contact_group, null); - final ImageButton ibInfo = dview.findViewById(R.id.ibInfo); - final Spinner spGroup = dview.findViewById(R.id.spGroup); - final Spinner spTarget = dview.findViewById(R.id.spTarget); - final Spinner spType = dview.findViewById(R.id.spType); - - ibInfo.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Helper.view(v.getContext(), Uri.parse(Helper.URI_SUPPORT_CONTACT_GROUP), true); - } - }); - - new SimpleTask() { - @Override - protected Cursor onExecute(Context context, Bundle args) { - final String[] projection = new String[]{ - ContactsContract.Groups._ID, - ContactsContract.Groups.TITLE, - ContactsContract.Groups.SUMMARY_COUNT, - ContactsContract.Groups.ACCOUNT_NAME, - ContactsContract.Groups.ACCOUNT_TYPE, - }; - - Cursor contacts = new MatrixCursor(projection); - if (Helper.hasPermission(context, Manifest.permission.READ_CONTACTS)) - try { - ContentResolver resolver = context.getContentResolver(); - contacts = resolver.query( - ContactsContract.Groups.CONTENT_SUMMARY_URI, - projection, - // ContactsContract.Groups.GROUP_VISIBLE + " = 1" + " AND " + - ContactsContract.Groups.DELETED + " = 0" + - " AND " + ContactsContract.Groups.SUMMARY_COUNT + " > 0", - null, - ContactsContract.Groups.TITLE - ); - } catch (SecurityException ex) { - Log.w(ex); - } - - DB db = DB.getInstance(context); - Cursor local = db.contact().getGroups( - null, - context.getString(R.string.app_name), - BuildConfig.APPLICATION_ID); - - return new MergeCursor(new Cursor[]{contacts, local}); - } - - @Override - protected void onExecuted(Bundle args, Cursor cursor) { - SimpleCursorAdapter adapter = new SimpleCursorAdapter( - context, - R.layout.spinner_contact_group, - cursor, - new String[]{ContactsContract.Groups.TITLE, ContactsContract.Groups.ACCOUNT_NAME}, - new int[]{R.id.tvGroup, R.id.tvAccount}, - 0); - - final NumberFormat NF = NumberFormat.getInstance(); - - adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() { - @Override - public boolean setViewValue(View view, Cursor cursor, int columnIndex) { - if (view.getId() == R.id.tvGroup) { - String title = cursor.getString(1); - if (TextUtils.isEmpty(title)) - title = "-"; - int count = cursor.getInt(2); - ((TextView) view).setText(context.getString(R.string.title_name_count, title, NF.format(count))); - return true; - } else if (view.getId() == R.id.tvAccount) { - String account = cursor.getString(3); - String type = cursor.getString(4); - ((TextView) view).setText(account + (BuildConfig.DEBUG ? "/" + type : "")); - return true; - } else - return false; - } - }); - - spGroup.setAdapter(adapter); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(getParentFragmentManager(), ex); - } - }.execute(this, new Bundle(), "compose:groups"); - - spTarget.setSelection(focussed); - - return new AlertDialog.Builder(context) - .setView(dview) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - try { - int target = spTarget.getSelectedItemPosition(); - Cursor cursor = (Cursor) spGroup.getSelectedItem(); - if (target != INVALID_POSITION && - cursor != null && cursor.getCount() > 0) { - long group = cursor.getLong(0); - String name = cursor.getString(1); - - Bundle args = getArguments(); - args.putLong("id", working); - args.putInt("target", target); - args.putLong("group", group); - args.putString("name", name); - args.putInt("type", spType.getSelectedItemPosition()); - - sendResult(RESULT_OK); - } else - sendResult(RESULT_CANCELED); - } catch (Throwable ex) { - Log.e(ex); - } - } - }) - .setNegativeButton(android.R.string.cancel, null) - .create(); - } - } - - public static class FragmentDialogAddImage extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - int title = getArguments().getInt("title"); - - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - boolean add_inline = prefs.getBoolean("add_inline", true); - boolean resize_images = prefs.getBoolean("resize_images", true); - int resize = prefs.getInt("resize", FragmentCompose.REDUCED_IMAGE_SIZE); - boolean privacy_images = prefs.getBoolean("privacy_images", false); - boolean image_dialog = prefs.getBoolean("image_dialog", true); - - final ViewGroup dview = (ViewGroup) LayoutInflater.from(getContext()).inflate(R.layout.dialog_add_image, null); - final ImageView ivType = dview.findViewById(R.id.ivType); - final RadioGroup rgAction = dview.findViewById(R.id.rgAction); - final ImageButton ibSettings = dview.findViewById(R.id.ibSettings); - final CheckBox cbResize = dview.findViewById(R.id.cbResize); - final ImageButton ibResize = dview.findViewById(R.id.ibResize); - final Spinner spResize = dview.findViewById(R.id.spResize); - final TextView tvResize = dview.findViewById(R.id.tvResize); - final CheckBox cbPrivacy = dview.findViewById(R.id.cbPrivacy); - final CheckBox cbNotAgain = dview.findViewById(R.id.cbNotAgain); - final TextView tvNotAgain = dview.findViewById(R.id.tvNotAgain); - - ivType.setImageResource(title == R.string.title_attachment_photo - ? R.drawable.twotone_photo_camera_24 : R.drawable.twotone_image_24); - rgAction.check(add_inline ? R.id.rbInline : R.id.rbAttach); - cbResize.setChecked(resize_images); - spResize.setEnabled(resize_images); - cbPrivacy.setChecked(privacy_images); - - final int[] resizeValues = getResources().getIntArray(R.array.resizeValues); - for (int pos = 0; pos < resizeValues.length; pos++) - if (resizeValues[pos] == resize) { - spResize.setSelection(pos); - tvResize.setText(getString(R.string.title_add_resize_pixels, resizeValues[pos])); - break; - } - - rgAction.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(RadioGroup group, int checkedId) { - prefs.edit().putBoolean("add_inline", checkedId == R.id.rbInline).apply(); - } - }); - - // https://developer.android.com/reference/android/provider/MediaStore#ACTION_PICK_IMAGES_SETTINGS - PackageManager pm = getContext().getPackageManager(); - Intent settings = new Intent(MediaStore.ACTION_PICK_IMAGES_SETTINGS); - ibSettings.setVisibility(settings.resolveActivity(pm) == null ? View.GONE : View.VISIBLE); - ibSettings.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - v.getContext().startActivity(settings); - } - }); - - cbResize.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - prefs.edit().putBoolean("resize_images", isChecked).apply(); - spResize.setEnabled(isChecked); - } - }); - - ibResize.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Helper.viewFAQ(v.getContext(), 63); - } - }); - - spResize.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView adapterView, View view, int position, long id) { - prefs.edit().putInt("resize", resizeValues[position]).apply(); - tvResize.setText(getString(R.string.title_add_resize_pixels, resizeValues[position])); - } - - @Override - public void onNothingSelected(AdapterView parent) { - prefs.edit().remove("resize").apply(); - } - }); - - cbPrivacy.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - prefs.edit().putBoolean("privacy_images", isChecked).apply(); - } - }); - - cbNotAgain.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - prefs.edit().putBoolean("image_dialog", !isChecked).apply(); - tvNotAgain.setVisibility(isChecked ? View.VISIBLE : View.GONE); - } - }); - - cbNotAgain.setChecked(!image_dialog); - tvNotAgain.setVisibility(cbNotAgain.isChecked() ? View.VISIBLE : View.GONE); - - return new AlertDialog.Builder(getContext()) - .setView(dview) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(title, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - sendResult(RESULT_OK); - } - }) - .create(); - } - } - - public static class FragmentDialogSend extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - Bundle args = getArguments(); - long id = args.getLong("id"); - final boolean sent_missing = args.getBoolean("sent_missing", false); - final String address_error = args.getString("address_error"); - final String mx_error = args.getString("mx_error"); - final boolean remind_dsn = args.getBoolean("remind_dsn", false); - final boolean remind_size = args.getBoolean("remind_size", false); - final boolean remind_pgp = args.getBoolean("remind_pgp", false); - final boolean remind_smime = args.getBoolean("remind_smime", false); - final boolean remind_to = args.getBoolean("remind_to", false); - final boolean remind_extra = args.getBoolean("remind_extra", false); - final boolean remind_noreply = args.getBoolean("remind_noreply", false); - final boolean remind_external = args.getBoolean("remind_external", false); - final boolean remind_subject = args.getBoolean("remind_subject", false); - final boolean remind_text = args.getBoolean("remind_text", false); - final boolean remind_attachment = args.getBoolean("remind_attachment", false); - final String remind_extension = args.getString("remind_extension"); - final boolean remind_internet = args.getBoolean("remind_internet", false); - final boolean styled = args.getBoolean("styled", false); - final long size = args.getLong("size", -1); - final long max_size = args.getLong("max_size", -1); - - final Context context = getContext(); - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - final boolean send_reminders = prefs.getBoolean("send_reminders", true); - final int send_delayed = prefs.getInt("send_delayed", 0); - final boolean send_dialog = prefs.getBoolean("send_dialog", true); - final boolean send_archive = prefs.getBoolean("send_archive", false); - final MessageHelper.AddressFormat email_format = MessageHelper.getAddressFormat(getContext()); - - final int[] encryptValues = getResources().getIntArray(R.array.encryptValues); - final int[] sendDelayedValues = getResources().getIntArray(R.array.sendDelayedValues); - final String[] sendDelayedNames = getResources().getStringArray(R.array.sendDelayedNames); - - final ViewGroup dview = (ViewGroup) LayoutInflater.from(context).inflate(R.layout.dialog_send, null); - final Button btnFixSent = dview.findViewById(R.id.btnFixSent); - final TextView tvAddressError = dview.findViewById(R.id.tvAddressError); - final TextView tvRemindDsn = dview.findViewById(R.id.tvRemindDsn); - final TextView tvRemindSize = dview.findViewById(R.id.tvRemindSize); - final TextView tvRemindPgp = dview.findViewById(R.id.tvRemindPgp); - final TextView tvRemindSmime = dview.findViewById(R.id.tvRemindSmime); - final TextView tvRemindTo = dview.findViewById(R.id.tvRemindTo); - final TextView tvRemindExtra = dview.findViewById(R.id.tvRemindExtra); - final TextView tvRemindNoReply = dview.findViewById(R.id.tvRemindNoReply); - final TextView tvRemindExternal = dview.findViewById(R.id.tvRemindExternal); - final TextView tvRemindSubject = dview.findViewById(R.id.tvRemindSubject); - final TextView tvRemindText = dview.findViewById(R.id.tvRemindText); - final TextView tvRemindAttachment = dview.findViewById(R.id.tvRemindAttachment); - final TextView tvRemindExtension = dview.findViewById(R.id.tvRemindExtension); - final TextView tvRemindInternet = dview.findViewById(R.id.tvRemindInternet); - final SwitchCompat swSendReminders = dview.findViewById(R.id.swSendReminders); - final TextView tvSendRemindersHint = dview.findViewById(R.id.tvSendRemindersHint); - final TextView tvTo = dview.findViewById(R.id.tvTo); - final TextView tvViaTitle = dview.findViewById(R.id.tvViaTitle); - final TextView tvVia = dview.findViewById(R.id.tvVia); - final CheckBox cbPlainOnly = dview.findViewById(R.id.cbPlainOnly); - final TextView tvPlainHint = dview.findViewById(R.id.tvPlainHint); - final CheckBox cbReceipt = dview.findViewById(R.id.cbReceipt); - final TextView tvReceiptHint = dview.findViewById(R.id.tvReceiptHint); - final TextView tvEncrypt = dview.findViewById(R.id.tvEncrypt); - final Spinner spEncrypt = dview.findViewById(R.id.spEncrypt); - final ImageButton ibEncryption = dview.findViewById(R.id.ibEncryption); - final Spinner spPriority = dview.findViewById(R.id.spPriority); - final Spinner spSensitivity = dview.findViewById(R.id.spSensitivity); - final ImageButton ibSensitivity = dview.findViewById(R.id.ibSensitivity); - final TextView tvSendAt = dview.findViewById(R.id.tvSendAt); - final ImageButton ibSendAt = dview.findViewById(R.id.ibSendAt); - final CheckBox cbArchive = dview.findViewById(R.id.cbArchive); - final CheckBox cbNotAgain = dview.findViewById(R.id.cbNotAgain); - final TextView tvNotAgain = dview.findViewById(R.id.tvNotAgain); - final Group grpSentMissing = dview.findViewById(R.id.grpSentMissing); - final Group grpDsn = dview.findViewById(R.id.grpDsn); - - btnFixSent.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - v.getContext().startActivity(new Intent(v.getContext(), ActivitySetup.class) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) - .putExtra("target", "accounts")); - } - }); - - grpSentMissing.setVisibility(sent_missing ? View.VISIBLE : View.GONE); - - tvAddressError.setText(address_error == null ? mx_error : address_error); - tvAddressError.setVisibility(address_error == null && mx_error == null ? View.GONE : View.VISIBLE); - - tvRemindDsn.setVisibility(remind_dsn ? View.VISIBLE : View.GONE); - - tvRemindSize.setText(getString(R.string.title_size_reminder, - Helper.humanReadableByteCount(size), - Helper.humanReadableByteCount(max_size))); - tvRemindSize.setVisibility(remind_size ? View.VISIBLE : View.GONE); - - tvRemindPgp.setVisibility(remind_pgp ? View.VISIBLE : View.GONE); - tvRemindSmime.setVisibility(remind_smime ? View.VISIBLE : View.GONE); - - tvRemindTo.setVisibility(remind_to ? View.VISIBLE : View.GONE); - tvRemindExtra.setVisibility(send_reminders && remind_extra ? View.VISIBLE : View.GONE); - tvRemindNoReply.setVisibility(remind_noreply ? View.VISIBLE : View.GONE); - tvRemindExternal.setVisibility(remind_external ? View.VISIBLE : View.GONE); - tvRemindSubject.setVisibility(send_reminders && remind_subject ? View.VISIBLE : View.GONE); - tvRemindText.setVisibility(send_reminders && remind_text ? View.VISIBLE : View.GONE); - tvRemindAttachment.setVisibility(send_reminders && remind_attachment ? View.VISIBLE : View.GONE); - - tvRemindExtension.setText(getString(R.string.title_attachment_warning, remind_extension)); - tvRemindExtension.setVisibility(send_reminders && remind_extension != null ? View.VISIBLE : View.GONE); - - tvRemindInternet.setVisibility(send_reminders && remind_internet ? View.VISIBLE : View.GONE); - - tvTo.setText(null); - tvVia.setText(null); - tvPlainHint.setVisibility(View.GONE); - tvReceiptHint.setVisibility(View.GONE); - spEncrypt.setTag(0); - spEncrypt.setSelection(0); - spPriority.setTag(1); - spPriority.setSelection(1); - spSensitivity.setTag(0); - spSensitivity.setSelection(0); - tvSendAt.setText(null); - cbArchive.setEnabled(false); - cbNotAgain.setChecked(!send_dialog); - cbNotAgain.setVisibility(send_dialog ? View.VISIBLE : View.GONE); - tvNotAgain.setVisibility(cbNotAgain.isChecked() ? View.VISIBLE : View.GONE); - - Helper.setViewsEnabled(dview, false); - - boolean reminder = (remind_extra || remind_subject || remind_text || - remind_attachment || remind_extension != null || remind_internet); - swSendReminders.setChecked(send_reminders); - swSendReminders.setVisibility(send_reminders && reminder ? View.VISIBLE : View.GONE); - tvSendRemindersHint.setVisibility(View.GONE); - swSendReminders.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { - prefs.edit().putBoolean("send_reminders", checked).apply(); - tvRemindExtra.setVisibility(checked && remind_extra ? View.VISIBLE : View.GONE); - tvRemindSubject.setVisibility(checked && remind_subject ? View.VISIBLE : View.GONE); - tvRemindText.setVisibility(checked && remind_text ? View.VISIBLE : View.GONE); - tvRemindAttachment.setVisibility(checked && remind_attachment ? View.VISIBLE : View.GONE); - tvRemindExtension.setVisibility(checked && remind_extension != null ? View.VISIBLE : View.GONE); - tvRemindInternet.setVisibility(checked && remind_internet ? View.VISIBLE : View.GONE); - tvSendRemindersHint.setVisibility(checked ? View.GONE : View.VISIBLE); - } - }); - - cbNotAgain.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - prefs.edit().putBoolean("send_dialog", !isChecked).apply(); - tvNotAgain.setVisibility(isChecked ? View.VISIBLE : View.GONE); - } - }); - - cbPlainOnly.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { - tvPlainHint.setVisibility(checked && styled ? View.VISIBLE : View.GONE); - - Bundle args = new Bundle(); - args.putLong("id", id); - args.putBoolean("plain_only", checked); - - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) { - long id = args.getLong("id"); - boolean plain_only = args.getBoolean("plain_only"); - - DB db = DB.getInstance(context); - db.message().setMessagePlainOnly(id, plain_only ? 1 : 0); - - return null; - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(getParentFragmentManager(), ex); - } - }.serial().execute(FragmentDialogSend.this, args, "compose:plain_only"); - } - }); - - cbReceipt.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { - tvReceiptHint.setVisibility(checked ? View.VISIBLE : View.GONE); - - Bundle args = new Bundle(); - args.putLong("id", id); - args.putBoolean("receipt", checked); - - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) { - long id = args.getLong("id"); - boolean receipt = args.getBoolean("receipt"); - - DB db = DB.getInstance(context); - db.message().setMessageReceiptRequest(id, receipt); - - return null; - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(getParentFragmentManager(), ex); - } - }.serial().execute(FragmentDialogSend.this, args, "compose:receipt"); - } - }); - - spEncrypt.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - int last = (int) spEncrypt.getTag(); - if (last != position) { - spEncrypt.setTag(position); - setEncrypt(encryptValues[position]); - - if ((encryptValues[position] == EntityMessage.PGP_SIGNONLY || - encryptValues[position] == EntityMessage.PGP_ENCRYPTONLY || - encryptValues[position] == EntityMessage.PGP_SIGNENCRYPT) && - Helper.isOpenKeychainInstalled(context)) { - tvEncrypt.setPaintFlags(tvEncrypt.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); - tvEncrypt.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - String pkg = Helper.getOpenKeychainPackage(v.getContext()); - PackageManager pm = v.getContext().getPackageManager(); - v.getContext().startActivity(pm.getLaunchIntentForPackage(pkg)); - } - }); - } else { - tvEncrypt.setPaintFlags(tvEncrypt.getPaintFlags() & ~Paint.UNDERLINE_TEXT_FLAG); - tvEncrypt.setOnClickListener(null); - } - } - } - - @Override - public void onNothingSelected(AdapterView parent) { - spEncrypt.setTag(0); - setEncrypt(encryptValues[0]); - } - - private void setEncrypt(int encrypt) { - Bundle args = new Bundle(); - args.putLong("id", id); - args.putInt("encrypt", encrypt); - - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) { - long id = args.getLong("id"); - int encrypt = args.getInt("encrypt"); - - DB db = DB.getInstance(context); - try { - db.beginTransaction(); - - EntityMessage message = db.message().getMessage(id); - if (message == null) - return null; - - db.message().setMessageUiEncrypt(message.id, encrypt); - - List attachments = db.attachment().getAttachments(message.id); - if (attachments == null) - return null; - for (EntityAttachment attachment : attachments) - if (attachment.isEncryption()) - db.attachment().deleteAttachment(attachment.id); - - if (encrypt != EntityMessage.ENCRYPT_NONE && - message.identity != null) { - int iencrypt = - (encrypt == EntityMessage.SMIME_SIGNONLY || - encrypt == EntityMessage.SMIME_SIGNENCRYPT - ? 1 : 0); - db.identity().setIdentityEncrypt(message.identity, iencrypt); - } - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - - return null; - } - - @Override - protected void onExecuted(Bundle args, Void data) { - int encrypt = args.getInt("encrypt"); - - boolean none = EntityMessage.ENCRYPT_NONE.equals(encrypt); - tvRemindPgp.setVisibility(remind_pgp && none ? View.VISIBLE : View.GONE); - tvRemindSmime.setVisibility(remind_smime && none ? View.VISIBLE : View.GONE); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(getParentFragmentManager(), ex); - } - }.serial().execute(FragmentDialogSend.this, args, "compose:encrypt"); - } - }); - - ibEncryption.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Helper.viewFAQ(v.getContext(), 12); - } - }); - - spPriority.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - int last = (int) spPriority.getTag(); - if (last != position) { - spPriority.setTag(position); - setPriority(position); - } - } - - @Override - public void onNothingSelected(AdapterView parent) { - spPriority.setTag(1); - setPriority(1); - } - - private void setPriority(int priority) { - Bundle args = new Bundle(); - args.putLong("id", id); - args.putInt("priority", priority); - - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) { - long id = args.getLong("id"); - int priority = args.getInt("priority"); - - DB db = DB.getInstance(context); - db.message().setMessagePriority(id, priority); - - return null; - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(getParentFragmentManager(), ex); - } - }.serial().execute(FragmentDialogSend.this, args, "compose:priority"); - } - }); - - spSensitivity.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - int last = (int) spSensitivity.getTag(); - if (last != position) { - spSensitivity.setTag(position); - setSensitivity(position); - } - } - - @Override - public void onNothingSelected(AdapterView parent) { - spSensitivity.setTag(0); - setSensitivity(0); - } - - private void setSensitivity(int sensitivity) { - Bundle args = new Bundle(); - args.putLong("id", id); - args.putInt("sensitivity", sensitivity); - - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) { - long id = args.getLong("id"); - int sensitivity = args.getInt("sensitivity"); - - DB db = DB.getInstance(context); - db.message().setMessageSensitivity(id, sensitivity < 1 ? null : sensitivity); - - return null; - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(getParentFragmentManager(), ex); - } - }.serial().execute(FragmentDialogSend.this, args, "compose:sensitivity"); - } - }); - - ibSensitivity.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Helper.viewFAQ(v.getContext(), 177); - } - }); - - View.OnClickListener sendAt = new View.OnClickListener() { - @Override - public void onClick(View view) { - Bundle args = new Bundle(); - args.putString("title", getString(R.string.title_send_at)); - args.putLong("id", id); - - FragmentDialogDuration fragment = new FragmentDialogDuration(); - fragment.setArguments(args); - fragment.setTargetFragment(FragmentDialogSend.this, 1); - fragment.show(getParentFragmentManager(), "send:snooze"); - } - }; - - tvSendAt.setOnClickListener(sendAt); - ibSendAt.setOnClickListener(sendAt); - - cbArchive.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - prefs.edit().putBoolean("send_archive", isChecked).apply(); - } - }); - - DB db = DB.getInstance(context); - db.message().liveMessage(id).observe(getViewLifecycleOwner(), new Observer() { - @Override - public void onChanged(TupleMessageEx draft) { - if (draft == null) { - dismiss(); - return; - } - - boolean dsn = (draft.dsn != null && !EntityMessage.DSN_NONE.equals(draft.dsn)); - int to = (draft.to == null ? 0 : draft.to.length); - int extra = (draft.cc == null ? 0 : draft.cc.length) + (draft.bcc == null ? 0 : draft.bcc.length); - - List
t = new ArrayList<>(); - if (draft.to != null) - if (to <= MAX_SHOW_RECIPIENTS) - t.addAll(Arrays.asList(draft.to)); - else { - t.addAll((Arrays.asList(Arrays.copyOf(draft.to, MAX_SHOW_RECIPIENTS)))); - extra += draft.to.length - MAX_SHOW_RECIPIENTS; - } - Address[] tos = t.toArray(new Address[0]); - - if (extra == 0) - tvTo.setText(MessageHelper.formatAddresses(tos, email_format, false)); - else - tvTo.setText(getString(R.string.title_name_plus, - MessageHelper.formatAddresses(tos, email_format, false), extra)); - tvTo.setTextColor(Helper.resolveColor(context, - to + extra > RECIPIENTS_WARNING ? R.attr.colorWarning : android.R.attr.textColorPrimary)); - if (draft.identityColor != null && draft.identityColor != Color.TRANSPARENT) - tvViaTitle.setTextColor(draft.identityColor); - tvVia.setText(draft.identityEmail); - - cbPlainOnly.setChecked(draft.isPlainOnly() && !dsn); - cbReceipt.setChecked(draft.receipt_request != null && draft.receipt_request && !dsn); - - int encrypt = (draft.ui_encrypt == null || dsn ? EntityMessage.ENCRYPT_NONE : draft.ui_encrypt); - for (int i = 0; i < encryptValues.length; i++) - if (encryptValues[i] == encrypt) { - spEncrypt.setTag(i); - spEncrypt.setSelection(i); - break; - } - - int priority = (draft.priority == null ? 1 : draft.priority); - spPriority.setTag(priority); - spPriority.setSelection(priority); - - int sensitivity = (draft.sensitivity == null ? 0 : draft.sensitivity); - spSensitivity.setTag(sensitivity); - spSensitivity.setSelection(sensitivity); - - if (draft.ui_snoozed == null) { - if (send_delayed == 0) - tvSendAt.setText(getString(R.string.title_now)); - else - for (int pos = 0; pos < sendDelayedValues.length; pos++) - if (sendDelayedValues[pos] == send_delayed) { - tvSendAt.setText(getString(R.string.title_after, sendDelayedNames[pos])); - break; - } - } else { - DateFormat DTF = Helper.getDateTimeInstance(context, SimpleDateFormat.MEDIUM, SimpleDateFormat.SHORT); - DateFormat D = new SimpleDateFormat("E"); - tvSendAt.setText(D.format(draft.ui_snoozed) + " " + DTF.format(draft.ui_snoozed)); - } - - grpDsn.setVisibility(dsn ? View.GONE : View.VISIBLE); - - Helper.setViewsEnabled(dview, true); - } - }); - - Bundle aargs = new Bundle(); - aargs.putLong("id", id); - - new SimpleTask() { - @Override - protected @NonNull - Boolean onExecute(Context context, Bundle args) { - long id = args.getLong("id"); - - DB db = DB.getInstance(context); - EntityMessage draft = db.message().getMessage(id); - if (draft == null) { - args.putString("reason", "Draft gone"); - return false; - } - - if (TextUtils.isEmpty(draft.inreplyto)) { - args.putString("reason", "No in-reply-to"); - return false; - } - - EntityFolder archive = db.folder().getFolderByType(draft.account, EntityFolder.ARCHIVE); - if (archive == null) { - args.putString("reason", "No archive"); - return false; - } - - List messages = db.message().getMessagesByMsgId(draft.account, draft.inreplyto); - if (messages == null || messages.size() == 0) { - args.putString("reason", "In-reply-to gone"); - return false; - } - - for (EntityMessage message : messages) { - EntityFolder folder = db.folder().getFolder(message.folder); - if (folder == null) - continue; - if (EntityFolder.INBOX.equals(folder.type) || EntityFolder.USER.equals(folder.type)) - return true; - } - - args.putString("reason", "Not in inbox or unread"); - return false; - } - - @Override - protected void onExecuted(Bundle args, Boolean data) { - if (!data) { - String reason = args.getString("reason"); - if (BuildConfig.DEBUG) - cbArchive.setText(reason); - else - Log.i("Auto archive reason=" + reason); - } - if (send_archive && data) - cbArchive.setChecked(true); - cbArchive.setEnabled(data); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - // Ignored - } - }.serial().execute(FragmentDialogSend.this, aargs, "send:archive"); - - AlertDialog.Builder builder = new AlertDialog.Builder(context) - .setView(dview) - .setNegativeButton(android.R.string.cancel, null); - - if (address_error == null && !remind_to && !remind_size) { - if (send_delayed != 0) - builder.setNeutralButton(R.string.title_send_now, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - getArguments().putBoolean("archive", cbArchive.isChecked()); - sendResult(Activity.RESULT_FIRST_USER); - } - }); - builder.setPositiveButton(R.string.title_send, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - getArguments().putBoolean("archive", cbArchive.isChecked()); - sendResult(Activity.RESULT_OK); - } - }); - } - - return builder.create(); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, @Nullable Intent intent) { - super.onActivityResult(requestCode, resultCode, intent); - - if (resultCode == RESULT_OK && intent != null) { - Bundle data = intent.getBundleExtra("args"); - long id = data.getLong("id"); - long duration = data.getLong("duration"); - long time = data.getLong("time"); - - Bundle args = new Bundle(); - args.putLong("id", id); - args.putLong("wakeup", duration == 0 ? -1 : time); - - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) { - long id = args.getLong("id"); - long wakeup = args.getLong("wakeup"); - - DB db = DB.getInstance(context); - db.message().setMessageSnoozed(id, wakeup < 0 ? null : wakeup); - - return null; - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(getParentFragmentManager(), ex); - } - }.serial().execute(this, args, "compose:snooze"); - } - } - } - @NonNull private static UriInfo getInfo(Uri uri, Context context) { UriInfo result = new UriInfo(); diff --git a/app/src/main/java/eu/faircode/email/FragmentContacts.java b/app/src/main/java/eu/faircode/email/FragmentContacts.java index 9f4776f771..da632d681f 100644 --- a/app/src/main/java/eu/faircode/email/FragmentContacts.java +++ b/app/src/main/java/eu/faircode/email/FragmentContacts.java @@ -21,10 +21,8 @@ package eu.faircode.email; import static android.app.Activity.RESULT_OK; -import android.app.Dialog; import android.content.ContentResolver; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; @@ -36,15 +34,10 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.SearchView; import androidx.constraintlayout.widget.Group; import androidx.lifecycle.Lifecycle; @@ -322,7 +315,7 @@ public class FragmentContacts extends FragmentBase { args.putLong("account", account == null ? -1L : account); args.putBoolean("junk", junk); - FragmentDelete fragment = new FragmentDelete(); + FragmentDialogContactDelete fragment = new FragmentDialogContactDelete(); fragment.setArguments(args); fragment.show(getParentFragmentManager(), "contacts:delete"); } @@ -332,7 +325,7 @@ public class FragmentContacts extends FragmentBase { args.putInt("type", junk ? EntityContact.TYPE_JUNK : EntityContact.TYPE_TO); args.putLong("account", account); - FragmentDialogEditContact fragment = new FragmentDialogEditContact(); + FragmentDialogContactEdit fragment = new FragmentDialogContactEdit(); fragment.setArguments(args); fragment.setTargetFragment(this, REQUEST_EDIT_CONTACT); fragment.show(getParentFragmentManager(), "contacts:add"); @@ -686,102 +679,4 @@ public class FragmentContacts extends FragmentBase { } }.execute(this, args, "contacts:name"); } - - public static class FragmentDelete extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - return new AlertDialog.Builder(getContext()) - .setIcon(R.drawable.twotone_warning_24) - .setTitle(getString(R.string.title_delete_contacts)) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) { - Long account = args.getLong("account"); - boolean junk = args.getBoolean("junk"); - - if (account < 0) - account = null; - int[] types = (junk - ? new int[]{EntityContact.TYPE_JUNK, EntityContact.TYPE_NO_JUNK} - : new int[]{EntityContact.TYPE_FROM, EntityContact.TYPE_TO}); - - DB db = DB.getInstance(context); - int count = db.contact().clearContacts(account, types); - Log.i("Cleared contacts=" + count); - return null; - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(getParentFragmentManager(), ex); - } - }.execute(getContext(), getActivity(), getArguments(), "contacts:delete"); - } - }) - .setNegativeButton(android.R.string.cancel, null) - .create(); - } - } - - public static class FragmentDialogEditContact extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - final Context context = getContext(); - View view = LayoutInflater.from(context).inflate(R.layout.dialog_edit_contact, null); - final Spinner spType = view.findViewById(R.id.spType); - final EditText etEmail = view.findViewById(R.id.etEmail); - final EditText etName = view.findViewById(R.id.etName); - final EditText etGroup = view.findViewById(R.id.etGroup); - - final Bundle args = getArguments(); - int type = args.getInt("type"); - - boolean junk = (type == EntityContact.TYPE_JUNK || type == EntityContact.TYPE_NO_JUNK); - String[] values = getResources().getStringArray(R.array.contactTypes); - ArrayAdapter adapter = new ArrayAdapter(context, R.layout.spinner_item1, android.R.id.text1, values) { - @Override - public boolean isEnabled(int position) { - if (junk) - return (position == EntityContact.TYPE_JUNK || position == EntityContact.TYPE_NO_JUNK); - else - return (position == EntityContact.TYPE_TO || position == EntityContact.TYPE_FROM); - } - - @Override - public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { - View view = super.getDropDownView(position, convertView, parent); - TextView tv = view.findViewById(android.R.id.text1); - tv.setEnabled(isEnabled(position)); - return view; - } - }; - adapter.setDropDownViewResource(R.layout.spinner_item1_dropdown); - spType.setAdapter(adapter); - - spType.setSelection(args.getInt("type")); - etEmail.setText(args.getString("email")); - etName.setText(args.getString("name")); - etGroup.setText(args.getString("group")); - - return new AlertDialog.Builder(context) - .setView(view) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - args.putInt("type", spType.getSelectedItemPosition()); - args.putString("email", etEmail.getText().toString().trim()); - args.putString("name", etName.getText().toString()); - args.putString("group", etGroup.getText().toString().trim()); - sendResult(RESULT_OK); - } - }) - .setNegativeButton(android.R.string.cancel, null) - .create(); - } - } } diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogAddImage.java b/app/src/main/java/eu/faircode/email/FragmentDialogAddImage.java new file mode 100644 index 0000000000..8ffebf3c6d --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogAddImage.java @@ -0,0 +1,164 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import static android.app.Activity.RESULT_OK; + +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.provider.MediaStore; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.RadioGroup; +import android.widget.Spinner; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.PreferenceManager; + +public class FragmentDialogAddImage extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + int title = getArguments().getInt("title"); + + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + boolean add_inline = prefs.getBoolean("add_inline", true); + boolean resize_images = prefs.getBoolean("resize_images", true); + int resize = prefs.getInt("resize", FragmentCompose.REDUCED_IMAGE_SIZE); + boolean privacy_images = prefs.getBoolean("privacy_images", false); + boolean image_dialog = prefs.getBoolean("image_dialog", true); + + final ViewGroup dview = (ViewGroup) LayoutInflater.from(getContext()).inflate(R.layout.dialog_add_image, null); + final ImageView ivType = dview.findViewById(R.id.ivType); + final RadioGroup rgAction = dview.findViewById(R.id.rgAction); + final ImageButton ibSettings = dview.findViewById(R.id.ibSettings); + final CheckBox cbResize = dview.findViewById(R.id.cbResize); + final ImageButton ibResize = dview.findViewById(R.id.ibResize); + final Spinner spResize = dview.findViewById(R.id.spResize); + final TextView tvResize = dview.findViewById(R.id.tvResize); + final CheckBox cbPrivacy = dview.findViewById(R.id.cbPrivacy); + final CheckBox cbNotAgain = dview.findViewById(R.id.cbNotAgain); + final TextView tvNotAgain = dview.findViewById(R.id.tvNotAgain); + + ivType.setImageResource(title == R.string.title_attachment_photo + ? R.drawable.twotone_photo_camera_24 : R.drawable.twotone_image_24); + rgAction.check(add_inline ? R.id.rbInline : R.id.rbAttach); + cbResize.setChecked(resize_images); + spResize.setEnabled(resize_images); + cbPrivacy.setChecked(privacy_images); + + final int[] resizeValues = getResources().getIntArray(R.array.resizeValues); + for (int pos = 0; pos < resizeValues.length; pos++) + if (resizeValues[pos] == resize) { + spResize.setSelection(pos); + tvResize.setText(getString(R.string.title_add_resize_pixels, resizeValues[pos])); + break; + } + + rgAction.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + prefs.edit().putBoolean("add_inline", checkedId == R.id.rbInline).apply(); + } + }); + + // https://developer.android.com/reference/android/provider/MediaStore#ACTION_PICK_IMAGES_SETTINGS + PackageManager pm = getContext().getPackageManager(); + Intent settings = new Intent(MediaStore.ACTION_PICK_IMAGES_SETTINGS); + ibSettings.setVisibility(settings.resolveActivity(pm) == null ? View.GONE : View.VISIBLE); + ibSettings.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + v.getContext().startActivity(settings); + } + }); + + cbResize.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + prefs.edit().putBoolean("resize_images", isChecked).apply(); + spResize.setEnabled(isChecked); + } + }); + + ibResize.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Helper.viewFAQ(v.getContext(), 63); + } + }); + + spResize.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View view, int position, long id) { + prefs.edit().putInt("resize", resizeValues[position]).apply(); + tvResize.setText(getString(R.string.title_add_resize_pixels, resizeValues[position])); + } + + @Override + public void onNothingSelected(AdapterView parent) { + prefs.edit().remove("resize").apply(); + } + }); + + cbPrivacy.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + prefs.edit().putBoolean("privacy_images", isChecked).apply(); + } + }); + + cbNotAgain.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + prefs.edit().putBoolean("image_dialog", !isChecked).apply(); + tvNotAgain.setVisibility(isChecked ? View.VISIBLE : View.GONE); + } + }); + + cbNotAgain.setChecked(!image_dialog); + tvNotAgain.setVisibility(cbNotAgain.isChecked() ? View.VISIBLE : View.GONE); + + return new AlertDialog.Builder(getContext()) + .setView(dview) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(title, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + sendResult(RESULT_OK); + } + }) + .create(); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogAskSpam.java b/app/src/main/java/eu/faircode/email/FragmentDialogAskSpam.java new file mode 100644 index 0000000000..3e4106ac63 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogAskSpam.java @@ -0,0 +1,72 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CheckBox; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.PreferenceManager; + +public class FragmentDialogAskSpam extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + Bundle args = getArguments(); + int count = args.getInt("count"); + + final Context context = getContext(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + + boolean block_sender = prefs.getBoolean("block_sender", true); + String text = getResources().getQuantityString(R.plurals.title_ask_spam, count, count); + + View dview = LayoutInflater.from(context).inflate(R.layout.dialog_ask_spam, null); + TextView tvMessage = dview.findViewById(R.id.tvMessage); + CheckBox cbBlockSender = dview.findViewById(R.id.cbBlockSender); + + tvMessage.setText(text); + cbBlockSender.setChecked(block_sender); + + return new AlertDialog.Builder(context) + .setView(dview) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + boolean block = cbBlockSender.isChecked(); + prefs.edit().putBoolean("block_sender", block).apply(); + getArguments().putBoolean("block", block); + sendResult(Activity.RESULT_OK); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogBoundaryError.java b/app/src/main/java/eu/faircode/email/FragmentDialogBoundaryError.java new file mode 100644 index 0000000000..e52c1d3fd5 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogBoundaryError.java @@ -0,0 +1,63 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; + +public class FragmentDialogBoundaryError extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + String error = getArguments().getString("error"); + + final Context context = getContext(); + View dview = LayoutInflater.from(context).inflate(R.layout.dialog_boundary_error, null); + TextView tvError = dview.findViewById(R.id.tvError); + + tvError.setText(error); + + return new AlertDialog.Builder(context) + .setView(dview) + .setPositiveButton(R.string.title_boundary_retry, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + sendResult(Activity.RESULT_OK); + } + }) + .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + sendResult(Activity.RESULT_CANCELED); + } + }) + .create(); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogContactDelete.java b/app/src/main/java/eu/faircode/email/FragmentDialogContactDelete.java new file mode 100644 index 0000000000..dc844f420b --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogContactDelete.java @@ -0,0 +1,69 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; + +public class FragmentDialogContactDelete extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + return new AlertDialog.Builder(getContext()) + .setIcon(R.drawable.twotone_warning_24) + .setTitle(getString(R.string.title_delete_contacts)) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) { + Long account = args.getLong("account"); + boolean junk = args.getBoolean("junk"); + + if (account < 0) + account = null; + int[] types = (junk + ? new int[]{EntityContact.TYPE_JUNK, EntityContact.TYPE_NO_JUNK} + : new int[]{EntityContact.TYPE_FROM, EntityContact.TYPE_TO}); + + DB db = DB.getInstance(context); + int count = db.contact().clearContacts(account, types); + Log.i("Cleared contacts=" + count); + return null; + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + }.execute(getContext(), getActivity(), getArguments(), "contacts:delete"); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogContactEdit.java b/app/src/main/java/eu/faircode/email/FragmentDialogContactEdit.java new file mode 100644 index 0000000000..0f7fb99f0c --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogContactEdit.java @@ -0,0 +1,96 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import static android.app.Activity.RESULT_OK; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; + +public class FragmentDialogContactEdit extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + final Context context = getContext(); + View view = LayoutInflater.from(context).inflate(R.layout.dialog_edit_contact, null); + final Spinner spType = view.findViewById(R.id.spType); + final EditText etEmail = view.findViewById(R.id.etEmail); + final EditText etName = view.findViewById(R.id.etName); + final EditText etGroup = view.findViewById(R.id.etGroup); + + final Bundle args = getArguments(); + int type = args.getInt("type"); + + boolean junk = (type == EntityContact.TYPE_JUNK || type == EntityContact.TYPE_NO_JUNK); + String[] values = getResources().getStringArray(R.array.contactTypes); + ArrayAdapter adapter = new ArrayAdapter(context, R.layout.spinner_item1, android.R.id.text1, values) { + @Override + public boolean isEnabled(int position) { + if (junk) + return (position == EntityContact.TYPE_JUNK || position == EntityContact.TYPE_NO_JUNK); + else + return (position == EntityContact.TYPE_TO || position == EntityContact.TYPE_FROM); + } + + @Override + public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { + View view = super.getDropDownView(position, convertView, parent); + TextView tv = view.findViewById(android.R.id.text1); + tv.setEnabled(isEnabled(position)); + return view; + } + }; + adapter.setDropDownViewResource(R.layout.spinner_item1_dropdown); + spType.setAdapter(adapter); + + spType.setSelection(args.getInt("type")); + etEmail.setText(args.getString("email")); + etName.setText(args.getString("name")); + etGroup.setText(args.getString("group")); + + return new AlertDialog.Builder(context) + .setView(view) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + args.putInt("type", spType.getSelectedItemPosition()); + args.putString("email", etEmail.getText().toString().trim()); + args.putString("name", etName.getText().toString()); + args.putString("group", etGroup.getText().toString().trim()); + sendResult(RESULT_OK); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogContactGroup.java b/app/src/main/java/eu/faircode/email/FragmentDialogContactGroup.java new file mode 100644 index 0000000000..0c21e256c9 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogContactGroup.java @@ -0,0 +1,184 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import static android.app.Activity.RESULT_CANCELED; +import static android.app.Activity.RESULT_OK; +import static android.widget.AdapterView.INVALID_POSITION; + +import android.Manifest; +import android.app.Dialog; +import android.content.ContentResolver; +import android.content.Context; +import android.content.DialogInterface; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.database.MergeCursor; +import android.net.Uri; +import android.os.Bundle; +import android.provider.ContactsContract; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageButton; +import android.widget.Spinner; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.cursoradapter.widget.SimpleCursorAdapter; + +import java.text.NumberFormat; + +public class FragmentDialogContactGroup extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + final Bundle args = getArguments(); + final long working = args.getLong("working"); + int focussed = args.getInt("focussed"); + + final Context context = getContext(); + View dview = LayoutInflater.from(context).inflate(R.layout.dialog_contact_group, null); + final ImageButton ibInfo = dview.findViewById(R.id.ibInfo); + final Spinner spGroup = dview.findViewById(R.id.spGroup); + final Spinner spTarget = dview.findViewById(R.id.spTarget); + final Spinner spType = dview.findViewById(R.id.spType); + + ibInfo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Helper.view(v.getContext(), Uri.parse(Helper.URI_SUPPORT_CONTACT_GROUP), true); + } + }); + + new SimpleTask() { + @Override + protected Cursor onExecute(Context context, Bundle args) { + final String[] projection = new String[]{ + ContactsContract.Groups._ID, + ContactsContract.Groups.TITLE, + ContactsContract.Groups.SUMMARY_COUNT, + ContactsContract.Groups.ACCOUNT_NAME, + ContactsContract.Groups.ACCOUNT_TYPE, + }; + + Cursor contacts = new MatrixCursor(projection); + if (Helper.hasPermission(context, Manifest.permission.READ_CONTACTS)) + try { + ContentResolver resolver = context.getContentResolver(); + contacts = resolver.query( + ContactsContract.Groups.CONTENT_SUMMARY_URI, + projection, + // ContactsContract.Groups.GROUP_VISIBLE + " = 1" + " AND " + + ContactsContract.Groups.DELETED + " = 0" + + " AND " + ContactsContract.Groups.SUMMARY_COUNT + " > 0", + null, + ContactsContract.Groups.TITLE + ); + } catch (SecurityException ex) { + Log.w(ex); + } + + DB db = DB.getInstance(context); + Cursor local = db.contact().getGroups( + null, + context.getString(R.string.app_name), + BuildConfig.APPLICATION_ID); + + return new MergeCursor(new Cursor[]{contacts, local}); + } + + @Override + protected void onExecuted(Bundle args, Cursor cursor) { + SimpleCursorAdapter adapter = new SimpleCursorAdapter( + context, + R.layout.spinner_contact_group, + cursor, + new String[]{ContactsContract.Groups.TITLE, ContactsContract.Groups.ACCOUNT_NAME}, + new int[]{R.id.tvGroup, R.id.tvAccount}, + 0); + + final NumberFormat NF = NumberFormat.getInstance(); + + adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() { + @Override + public boolean setViewValue(View view, Cursor cursor, int columnIndex) { + if (view.getId() == R.id.tvGroup) { + String title = cursor.getString(1); + if (TextUtils.isEmpty(title)) + title = "-"; + int count = cursor.getInt(2); + ((TextView) view).setText(context.getString(R.string.title_name_count, title, NF.format(count))); + return true; + } else if (view.getId() == R.id.tvAccount) { + String account = cursor.getString(3); + String type = cursor.getString(4); + ((TextView) view).setText(account + (BuildConfig.DEBUG ? "/" + type : "")); + return true; + } else + return false; + } + }); + + spGroup.setAdapter(adapter); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + }.execute(this, new Bundle(), "compose:groups"); + + spTarget.setSelection(focussed); + + return new AlertDialog.Builder(context) + .setView(dview) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + try { + int target = spTarget.getSelectedItemPosition(); + Cursor cursor = (Cursor) spGroup.getSelectedItem(); + if (target != INVALID_POSITION && + cursor != null && cursor.getCount() > 0) { + long group = cursor.getLong(0); + String name = cursor.getString(1); + + Bundle args = getArguments(); + args.putLong("id", working); + args.putInt("target", target); + args.putLong("group", group); + args.putString("name", name); + args.putInt("type", spType.getSelectedItemPosition()); + + sendResult(RESULT_OK); + } else + sendResult(RESULT_CANCELED); + } catch (Throwable ex) { + Log.e(ex); + } + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogDoze.java b/app/src/main/java/eu/faircode/email/FragmentDialogDoze.java new file mode 100644 index 0000000000..fa1ae5d71d --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogDoze.java @@ -0,0 +1,53 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.provider.Settings; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; + +public class FragmentDialogDoze extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + return new AlertDialog.Builder(getContext()) + .setIcon(R.drawable.twotone_info_24) + .setTitle(R.string.title_setup_doze) + .setMessage(R.string.title_setup_doze_instructions) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + try { + startActivity(new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)); + } catch (Throwable ex) { + Log.e(ex); + } + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogExport.java b/app/src/main/java/eu/faircode/email/FragmentDialogExport.java new file mode 100644 index 0000000000..870086d02e --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogExport.java @@ -0,0 +1,136 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import static android.app.Activity.RESULT_OK; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; +import android.widget.Button; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.lifecycle.ViewModelProvider; +import androidx.preference.PreferenceManager; + +import com.google.android.material.textfield.TextInputLayout; + +public class FragmentDialogExport extends FragmentDialogBase { + private TextInputLayout tilPassword1; + private TextInputLayout tilPassword2; + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + outState.putString("fair:password1", tilPassword1 == null ? null : tilPassword1.getEditText().getText().toString()); + outState.putString("fair:password2", tilPassword2 == null ? null : tilPassword2.getEditText().getText().toString()); + super.onSaveInstanceState(outState); + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + Context context = getContext(); + View dview = LayoutInflater.from(context).inflate(R.layout.dialog_export, null); + tilPassword1 = dview.findViewById(R.id.tilPassword1); + tilPassword2 = dview.findViewById(R.id.tilPassword2); + + if (savedInstanceState != null) { + tilPassword1.getEditText().setText(savedInstanceState.getString("fair:password1")); + tilPassword2.getEditText().setText(savedInstanceState.getString("fair:password2")); + } + + Dialog dialog = new AlertDialog.Builder(context) + .setView(dview) + .setPositiveButton(R.string.title_save_file, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ViewModelExport vme = new ViewModelProvider(getActivity()).get(ViewModelExport.class); + vme.setPassword(tilPassword1.getEditText().getText().toString()); + sendResult(RESULT_OK); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + + dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + + return dialog; + } + + @Override + public void onStart() { + super.onStart(); + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + boolean debug = (BuildConfig.DEBUG || prefs.getBoolean("debug", false)); + + Button btnOk = ((AlertDialog) getDialog()).getButton(AlertDialog.BUTTON_POSITIVE); + + TextWatcher w = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // Do nothing + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + // Do nothing + } + + @Override + public void afterTextChanged(Editable s) { + String p1 = tilPassword1.getEditText().getText().toString(); + String p2 = tilPassword2.getEditText().getText().toString(); + btnOk.setEnabled((debug || !TextUtils.isEmpty(p1)) && p1.equals(p2)); + tilPassword2.setHint(!TextUtils.isEmpty(p2) && !p2.equals(p1) + ? R.string.title_setup_password_different + : R.string.title_setup_password_repeat); + } + }; + + tilPassword1.getEditText().addTextChangedListener(w); + tilPassword2.getEditText().addTextChangedListener(w); + w.afterTextChanged(null); + + tilPassword2.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_DONE) { + btnOk.performClick(); + return true; + } else + return false; + } + }); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogFirst.java b/app/src/main/java/eu/faircode/email/FragmentDialogFirst.java new file mode 100644 index 0000000000..0d4863eb36 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogFirst.java @@ -0,0 +1,71 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageButton; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.PreferenceManager; + +public class FragmentDialogFirst extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + LayoutInflater inflater = LayoutInflater.from(getContext()); + View dview = inflater.inflate(R.layout.dialog_first, null); + ImageButton ibBatteryInfo = dview.findViewById(R.id.ibBatteryInfo); + ImageButton ibReformatInfo = dview.findViewById(R.id.ibReformatInfo); + + ibBatteryInfo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Helper.viewFAQ(v.getContext(), 39); + } + }); + + ibReformatInfo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Helper.viewFAQ(v.getContext(), 35); + } + }); + + return new AlertDialog.Builder(getContext()) + .setView(dview) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + prefs.edit().putBoolean("first", false).apply(); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } +} + diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogFoldersApply.java b/app/src/main/java/eu/faircode/email/FragmentDialogFoldersApply.java new file mode 100644 index 0000000000..4c82873eed --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogFoldersApply.java @@ -0,0 +1,149 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.RadioGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; + +import java.util.List; + +public class FragmentDialogFoldersApply extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_folder_all, null); + final RadioGroup rgSynchronize = view.findViewById(R.id.rgSynchronize); + final EditText etSyncDays = view.findViewById(R.id.etSyncDays); + final EditText etKeepDays = view.findViewById(R.id.etKeepDays); + final CheckBox cbKeepAll = view.findViewById(R.id.cbKeepAll); + final CheckBox cbPollSystem = view.findViewById(R.id.cbPollSystem); + final CheckBox cbPollUser = view.findViewById(R.id.cbPollUser); + + cbKeepAll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + etKeepDays.setEnabled(!isChecked); + } + }); + + return new AlertDialog.Builder(getContext()) + .setView(view) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Bundle args = getArguments(); + int optionId = rgSynchronize.getCheckedRadioButtonId(); + if (optionId == R.id.rbEnable) + args.putBoolean("enable", true); + else if (optionId == R.id.rbDisable) + args.putBoolean("enable", false); + args.putString("sync", etSyncDays.getText().toString()); + args.putString("keep", cbKeepAll.isChecked() + ? Integer.toString(Integer.MAX_VALUE) + : etKeepDays.getText().toString()); + args.putBoolean("system", cbPollSystem.isChecked()); + args.putBoolean("user", cbPollUser.isChecked()); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) throws Throwable { + long aid = args.getLong("account"); + Boolean enable = null; + if (args.containsKey("enable")) + enable = args.getBoolean("enable"); + String sync = args.getString("sync"); + String keep = args.getString("keep"); + boolean system = args.getBoolean("system"); + boolean user = args.getBoolean("user"); + + if (TextUtils.isEmpty(sync)) + sync = "7"; + if (TextUtils.isEmpty(keep)) + keep = "30"; + + DB db = DB.getInstance(context); + try { + db.beginTransaction(); + + EntityAccount account = db.account().getAccount(aid); + if (account == null) + return null; + + if (system && account.poll_interval > 15) + db.account().setAccountKeepAliveInterval(account.id, 15); + + List folders = db.folder().getFolders(aid, false, true); + if (folders == null) + return null; + + for (EntityFolder folder : folders) { + if (EntityFolder.USER.equals(folder.type)) { + if (enable != null) { + folder.synchronize = enable; + db.folder().setFolderSynchronize(folder.id, folder.synchronize); + } + + db.folder().setFolderProperties( + folder.id, + Integer.parseInt(sync), + Integer.parseInt(keep)); + } + + if (folder.synchronize && !folder.poll) + if (EntityFolder.USER.equals(folder.type) + ? user + : system && !EntityFolder.INBOX.equals(folder.type)) + db.folder().setFolderPoll(folder.id, true); + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + ServiceSynchronize.reload(context, aid, false, "Apply"); + + return null; + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + }.execute(FragmentDialogFoldersApply.this, args, "folders:all"); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogImport.java b/app/src/main/java/eu/faircode/email/FragmentDialogImport.java new file mode 100644 index 0000000000..57c583e249 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogImport.java @@ -0,0 +1,136 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import static android.app.Activity.RESULT_CANCELED; +import static android.app.Activity.RESULT_OK; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.lifecycle.ViewModelProvider; +import androidx.preference.PreferenceManager; + +import com.google.android.material.textfield.TextInputLayout; + +public class FragmentDialogImport extends FragmentDialogBase { + private TextInputLayout tilPassword1; + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + outState.putString("fair:password1", tilPassword1 == null ? null : tilPassword1.getEditText().getText().toString()); + super.onSaveInstanceState(outState); + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + Context context = getContext(); + View dview = LayoutInflater.from(context).inflate(R.layout.dialog_import, null); + tilPassword1 = dview.findViewById(R.id.tilPassword1); + CheckBox cbAccounts = dview.findViewById(R.id.cbAccounts); + CheckBox cbDelete = dview.findViewById(R.id.cbDelete); + CheckBox cbRules = dview.findViewById(R.id.cbRules); + CheckBox cbContacts = dview.findViewById(R.id.cbContacts); + CheckBox cbAnswers = dview.findViewById(R.id.cbAnswers); + CheckBox cbSearches = dview.findViewById(R.id.cbSearches); + CheckBox cbSettings = dview.findViewById(R.id.cbSettings); + + cbAccounts.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { + cbRules.setEnabled(checked); + cbContacts.setEnabled(checked); + } + }); + + if (savedInstanceState != null) + tilPassword1.getEditText().setText(savedInstanceState.getString("fair:password1")); + + Dialog dialog = new AlertDialog.Builder(context) + .setView(dview) + .setPositiveButton(R.string.title_add_image_select, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String password1 = tilPassword1.getEditText().getText().toString(); + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + boolean debug = prefs.getBoolean("debug", false); + + if (TextUtils.isEmpty(password1) && !(debug || BuildConfig.DEBUG)) { + ToastEx.makeText(context, R.string.title_setup_password_missing, Toast.LENGTH_LONG).show(); + sendResult(RESULT_CANCELED); + } else { + ViewModelExport vme = new ViewModelProvider(getActivity()).get(ViewModelExport.class); + vme.setPassword(password1); + vme.setOptions("accounts", cbAccounts.isChecked()); + vme.setOptions("delete", cbDelete.isChecked()); + vme.setOptions("rules", cbRules.isChecked()); + vme.setOptions("contacts", cbContacts.isChecked()); + vme.setOptions("answers", cbAnswers.isChecked()); + vme.setOptions("searches", cbSearches.isChecked()); + vme.setOptions("settings", cbSettings.isChecked()); + sendResult(RESULT_OK); + } + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + + dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + + return dialog; + } + + @Override + public void onStart() { + super.onStart(); + + Button btnOk = ((AlertDialog) getDialog()).getButton(AlertDialog.BUTTON_POSITIVE); + + tilPassword1.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_DONE) { + btnOk.performClick(); + return true; + } else + return false; + } + }); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogOperationsDelete.java b/app/src/main/java/eu/faircode/email/FragmentDialogOperationsDelete.java new file mode 100644 index 0000000000..2b3dfd71ff --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogOperationsDelete.java @@ -0,0 +1,176 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CheckBox; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; + +import java.util.ArrayList; +import java.util.List; + +public class FragmentDialogOperationsDelete extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + final Context context = getContext(); + final View dview = LayoutInflater.from(context).inflate(R.layout.dialog_delete_operations, null); + final CheckBox cbError = dview.findViewById(R.id.cbError); + final CheckBox cbFetch = dview.findViewById(R.id.cbFetch); + final CheckBox cbMove = dview.findViewById(R.id.cbMove); + final CheckBox cbFlag = dview.findViewById(R.id.cbFlag); + final CheckBox cbDelete = dview.findViewById(R.id.cbDelete); + + return new AlertDialog.Builder(context) + .setView(dview) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Bundle args = new Bundle(); + args.putBoolean("error", cbError.isChecked()); + args.putBoolean("fetch", cbFetch.isChecked()); + args.putBoolean("move", cbMove.isChecked()); + args.putBoolean("flag", cbFlag.isChecked()); + args.putBoolean("delete", cbDelete.isChecked()); + + new SimpleTask() { + private Toast toast = null; + + @Override + protected void onPostExecute(Bundle args) { + toast = ToastEx.makeText(context, R.string.title_executing, Toast.LENGTH_LONG); + toast.show(); + } + + @Override + protected void onPreExecute(Bundle args) { + if (toast != null) + toast.cancel(); + } + + @Override + protected Integer onExecute(Context context, Bundle args) { + boolean error = args.getBoolean("error"); + boolean fetch = args.getBoolean("fetch"); + boolean move = args.getBoolean("move"); + boolean flag = args.getBoolean("flag"); + boolean delete = args.getBoolean("delete"); + + int deleted = 0; + DB db = DB.getInstance(context); + try { + db.beginTransaction(); + + List ops = new ArrayList<>(); + + // ADD, SEND, EXISTS, SUBSCRIBE + + if (error) + addAll(ops, db.operation().getOperationsError()); + + if (fetch) { + addAll(ops, db.operation().getOperations(EntityOperation.FETCH)); + addAll(ops, db.operation().getOperations(EntityOperation.DOWNLOAD)); + addAll(ops, db.operation().getOperations(EntityOperation.RAW)); + addAll(ops, db.operation().getOperations(EntityOperation.BODY)); + addAll(ops, db.operation().getOperations(EntityOperation.ATTACHMENT)); + addAll(ops, db.operation().getOperations(EntityOperation.HEADERS)); + addAll(ops, db.operation().getOperations(EntityOperation.RULE)); + addAll(ops, db.operation().getOperations(EntityOperation.SYNC)); + } + + if (move) { + addAll(ops, db.operation().getOperations(EntityOperation.MOVE)); + addAll(ops, db.operation().getOperations(EntityOperation.COPY)); + } + + if (flag) { + addAll(ops, db.operation().getOperations(EntityOperation.SEEN)); + addAll(ops, db.operation().getOperations(EntityOperation.ANSWERED)); + addAll(ops, db.operation().getOperations(EntityOperation.FLAG)); + addAll(ops, db.operation().getOperations(EntityOperation.KEYWORD)); + addAll(ops, db.operation().getOperations(EntityOperation.LABEL)); + addAll(ops, db.operation().getOperations(EntityOperation.REPORT)); + } + + if (delete) { + addAll(ops, db.operation().getOperations(EntityOperation.DELETE)); + addAll(ops, db.operation().getOperations(EntityOperation.PURGE)); + addAll(ops, db.operation().getOperations(EntityOperation.EXPUNGE)); + } + + for (EntityOperation op : ops) { + EntityLog.log(context, "Deleting operation=" + op.id + ":" + op.name + " error=" + op.error); + + if (db.operation().deleteOperation(op.id) > 0) { + op.cleanup(context, false); + deleted++; + } + + if (EntityOperation.SYNC.equals(op.name)) + db.folder().setFolderSyncState(op.folder, null); + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + return deleted; + } + + @Override + protected void onExecuted(Bundle args, Integer deleted) { + if (deleted == null) + deleted = -1; + Context context = getContext(); + if (context == null) + return; + ToastEx.makeText( + context, + getString(R.string.title_delete_operation_deleted, deleted), + Toast.LENGTH_LONG).show(); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + + private void addAll(List list, List sublist) { + if (sublist != null) + list.addAll(sublist); + } + }.execute(context, getActivity(), args, "operations:delete"); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogPin.java b/app/src/main/java/eu/faircode/email/FragmentDialogPin.java new file mode 100644 index 0000000000..d4685ec838 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogPin.java @@ -0,0 +1,115 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.lifecycle.Lifecycle; +import androidx.preference.PreferenceManager; + +public class FragmentDialogPin extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + final View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_pin_set, null); + final EditText etPin = dview.findViewById(R.id.etPin); + + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()) + .setView(dview) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String pin = etPin.getText().toString(); + if (TextUtils.isEmpty(pin)) + prefs.edit().remove("pin").apply(); + else { + boolean pro = ActivityBilling.isPro(getContext()); + if (pro) { + Helper.setAuthenticated(getContext()); + prefs.edit() + .remove("biometrics") + .putString("pin", pin) + .apply(); + } else + startActivity(new Intent(getContext(), ActivityBilling.class)); + } + } + }) + .setNegativeButton(android.R.string.cancel, null); + + String pin = prefs.getString("pin", null); + if (!TextUtils.isEmpty(pin)) + builder.setNeutralButton(R.string.title_reset, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + prefs.edit().remove("pin").apply(); + } + }); + + final Dialog dialog = builder.create(); + + etPin.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_DONE) { + ((AlertDialog) getDialog()).getButton(DialogInterface.BUTTON_POSITIVE).performClick(); + return true; + } else + return false; + } + }); + + etPin.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (hasFocus) + dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + } + }); + + ApplicationEx.getMainHandler().post(new Runnable() { + @Override + public void run() { + if (!getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) + return; + etPin.requestFocus(); + } + }); + + return dialog; + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogQuickActions.java b/app/src/main/java/eu/faircode/email/FragmentDialogQuickActions.java new file mode 100644 index 0000000000..99738d95e5 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogQuickActions.java @@ -0,0 +1,118 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CheckBox; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.PreferenceManager; + +public class FragmentDialogQuickActions extends FragmentDialogBase { + static final int MAX_QUICK_ACTIONS = 5; + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + final Context context = getContext(); + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + + final View dview = LayoutInflater.from(context).inflate(R.layout.dialog_quick_actions, null); + final TextView tvHint = dview.findViewById(R.id.tvHint); + final CheckBox cbSeen = dview.findViewById(R.id.cbSeen); + final CheckBox cbUnseen = dview.findViewById(R.id.cbUnseen); + final CheckBox cbSnooze = dview.findViewById(R.id.cbSnooze); + final CheckBox cbHide = dview.findViewById(R.id.cbHide); + final CheckBox cbFlag = dview.findViewById(R.id.cbFlag); + final CheckBox cbFlagColor = dview.findViewById(R.id.cbFlagColor); + final CheckBox cbImportanceLow = dview.findViewById(R.id.cbImportanceLow); + final CheckBox cbImportanceNormal = dview.findViewById(R.id.cbImportanceNormal); + final CheckBox cbImportanceHigh = dview.findViewById(R.id.cbImportanceHigh); + final CheckBox cbInbox = dview.findViewById(R.id.cbInbox); + final CheckBox cbArchive = dview.findViewById(R.id.cbArchive); + final CheckBox cbJunk = dview.findViewById(R.id.cbJunk); + final CheckBox cbTrash = dview.findViewById(R.id.cbTrash); + final CheckBox cbDelete = dview.findViewById(R.id.cbDelete); + final CheckBox cbMove = dview.findViewById(R.id.cbMove); + final CheckBox cbClear = dview.findViewById(R.id.cbClear); + + tvHint.setText(getString(R.string.title_quick_actions_hint, MAX_QUICK_ACTIONS)); + cbSeen.setChecked(prefs.getBoolean("more_seen", true)); + cbUnseen.setChecked(prefs.getBoolean("more_unseen", false)); + cbSnooze.setChecked(prefs.getBoolean("more_snooze", false)); + cbHide.setChecked(prefs.getBoolean("more_hide", false)); + cbFlag.setChecked(prefs.getBoolean("more_flag", false)); + cbFlagColor.setChecked(prefs.getBoolean("more_flag_color", false)); + cbImportanceLow.setChecked(prefs.getBoolean("more_importance_low", false)); + cbImportanceNormal.setChecked(prefs.getBoolean("more_importance_normal", false)); + cbImportanceHigh.setChecked(prefs.getBoolean("more_importance_high", false)); + cbInbox.setChecked(prefs.getBoolean("more_inbox", true)); + cbArchive.setChecked(prefs.getBoolean("more_archive", true)); + cbJunk.setChecked(prefs.getBoolean("more_junk", true)); + cbTrash.setChecked(prefs.getBoolean("more_trash", true)); + cbDelete.setChecked(prefs.getBoolean("more_delete", false)); + cbMove.setChecked(prefs.getBoolean("more_move", true)); + cbClear.setChecked(prefs.getBoolean("more_clear", true)); + + return new AlertDialog.Builder(getContext()) + .setView(dview) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean("more_seen", cbSeen.isChecked()); + editor.putBoolean("more_unseen", cbUnseen.isChecked()); + editor.putBoolean("more_snooze", cbSnooze.isChecked()); + editor.putBoolean("more_hide", cbHide.isChecked()); + editor.putBoolean("more_flag", cbFlag.isChecked()); + editor.putBoolean("more_flag_color", cbFlagColor.isChecked()); + editor.putBoolean("more_importance_low", cbImportanceLow.isChecked()); + editor.putBoolean("more_importance_normal", cbImportanceNormal.isChecked()); + editor.putBoolean("more_importance_high", cbImportanceHigh.isChecked()); + editor.putBoolean("more_inbox", cbInbox.isChecked()); + editor.putBoolean("more_archive", cbArchive.isChecked()); + editor.putBoolean("more_junk", cbJunk.isChecked()); + editor.putBoolean("more_trash", cbTrash.isChecked()); + editor.putBoolean("more_delete", cbDelete.isChecked()); + editor.putBoolean("more_move", cbMove.isChecked()); + editor.putBoolean("more_clear", cbClear.isChecked()); + editor.apply(); + sendResult(Activity.RESULT_OK); + } + }) + .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + sendResult(Activity.RESULT_CANCELED); + } + }) + .create(); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogRate.java b/app/src/main/java/eu/faircode/email/FragmentDialogRate.java new file mode 100644 index 0000000000..09d681b1cc --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogRate.java @@ -0,0 +1,50 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; + +public class FragmentDialogRate extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + return new AlertDialog.Builder(getContext()) + .setMessage(R.string.title_issue) + .setPositiveButton(R.string.title_yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Helper.viewFAQ(getContext(), 0); + } + }) + .setNegativeButton(R.string.title_no, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Helper.view(getContext(), Helper.getIntentRate(getContext())); + } + }) + .create(); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogReporting.java b/app/src/main/java/eu/faircode/email/FragmentDialogReporting.java new file mode 100644 index 0000000000..dd4c2f3656 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogReporting.java @@ -0,0 +1,68 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.Button; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.PreferenceManager; + +public class FragmentDialogReporting extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_error_reporting, null); + Button btnInfo = dview.findViewById(R.id.btnInfo); + + btnInfo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Helper.viewFAQ(v.getContext(), 104); + } + }); + + return new AlertDialog.Builder(getContext()) + .setView(dview) + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + prefs.edit().putBoolean("crash_reports", true).apply(); + Log.setCrashReporting(true); + } + }) + .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + prefs.edit().putBoolean("crash_reports_asked", true).apply(); + } + }) + .create(); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogReview.java b/app/src/main/java/eu/faircode/email/FragmentDialogReview.java new file mode 100644 index 0000000000..22d16f9235 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogReview.java @@ -0,0 +1,93 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.graphics.Paint; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.PreferenceManager; + +import java.util.Date; + +public class FragmentDialogReview extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_review, null); + TextView tvHelp = dview.findViewById(R.id.tvHelp); + + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + + Dialog dialog = new AlertDialog.Builder(getContext()) + .setView(dview) + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + prefs.edit().putBoolean("review_asked", true).apply(); + startActivity(Helper.getIntentRate(getContext())); + } + }) + .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + prefs.edit().putBoolean("review_asked", true).apply(); + } + }) + .setNeutralButton(R.string.title_later, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + prefs.edit().putLong("review_later", new Date().getTime()).apply(); + } + }) + .create(); + + tvHelp.setPaintFlags(tvHelp.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); + tvHelp.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismiss(); + prefs.edit().putLong("review_later", new Date().getTime()).apply(); + startActivity(Helper.getIntentIssue(v.getContext(), "Review:issue")); + } + }); + + return dialog; + } + + @Override + public void onCancel(@NonNull DialogInterface dialog) { + super.onCancel(dialog); + try { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + prefs.edit().putBoolean("review_asked", true).apply(); + } catch (Throwable ex) { + Log.e(ex); + } + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogRuleCheck.java b/app/src/main/java/eu/faircode/email/FragmentDialogRuleCheck.java new file mode 100644 index 0000000000..da0c488788 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogRuleCheck.java @@ -0,0 +1,213 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.List; + +public class FragmentDialogRuleCheck extends FragmentDialogBase { + private final static int MAX_CHECK = 10; + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + long folder = getArguments().getLong("folder"); + boolean daily = getArguments().getBoolean("daily"); + String condition = getArguments().getString("condition"); + String action = getArguments().getString("action"); + + final View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_rule_match, null); + final TextView tvNoMessages = dview.findViewById(R.id.tvNoMessages); + final RecyclerView rvMessage = dview.findViewById(R.id.rvMessage); + final Button btnExecute = dview.findViewById(R.id.btnExecute); + final ContentLoadingProgressBar pbWait = dview.findViewById(R.id.pbWait); + + rvMessage.setHasFixedSize(false); + LinearLayoutManager llm = new LinearLayoutManager(getContext()); + rvMessage.setLayoutManager(llm); + + final AdapterRuleMatch adapter = new AdapterRuleMatch(getContext(), getViewLifecycleOwner()); + rvMessage.setAdapter(adapter); + + tvNoMessages.setVisibility(View.GONE); + rvMessage.setVisibility(View.GONE); + btnExecute.setVisibility(View.GONE); + + final Bundle args = new Bundle(); + args.putLong("folder", folder); + args.putBoolean("daily", daily); + args.putString("condition", condition); + args.putString("action", action); + + btnExecute.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + new SimpleTask() { + private Toast toast = null; + + @Override + protected void onPreExecute(Bundle args) { + toast = ToastEx.makeText(getContext(), R.string.title_executing, Toast.LENGTH_LONG); + toast.show(); + } + + @Override + protected void onPostExecute(Bundle args) { + if (toast != null) + toast.cancel(); + } + + @Override + protected Integer onExecute(Context context, Bundle args) throws Throwable { + EntityRule rule = new EntityRule(); + rule.folder = args.getLong("folder"); + rule.daily = args.getBoolean("daily"); + rule.condition = args.getString("condition"); + rule.action = args.getString("action"); + + int applied = 0; + + DB db = DB.getInstance(context); + List ids = + db.message().getMessageIdsByFolder(rule.folder); + for (long mid : ids) + try { + db.beginTransaction(); + + EntityMessage message = db.message().getMessage(mid); + if (message == null || message.ui_hide) + continue; + + if (rule.matches(context, message, null, null)) + if (rule.execute(context, message)) + applied++; + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + if (applied > 0) + ServiceSynchronize.eval(context, "rules/manual"); + + return applied; + } + + @Override + protected void onExecuted(Bundle args, Integer applied) { + dismiss(); + ToastEx.makeText(getContext(), getString(R.string.title_rule_applied, applied), Toast.LENGTH_LONG).show(); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + if (ex instanceof IllegalArgumentException) + ToastEx.makeText(getContext(), ex.getMessage(), Toast.LENGTH_LONG).show(); + else + Log.unexpectedError(getParentFragmentManager(), ex); + } + }.execute(FragmentDialogRuleCheck.this, args, "rule:execute"); + } + }); + + new SimpleTask>() { + @Override + protected void onPreExecute(Bundle args) { + pbWait.setVisibility(View.VISIBLE); + } + + @Override + protected void onPostExecute(Bundle args) { + pbWait.setVisibility(View.GONE); + } + + @Override + protected List onExecute(Context context, Bundle args) throws Throwable { + EntityRule rule = new EntityRule(); + rule.folder = args.getLong("folder"); + rule.daily = args.getBoolean("daily"); + rule.condition = args.getString("condition"); + rule.action = args.getString("action"); + rule.validate(context); + + List matching = new ArrayList<>(); + + DB db = DB.getInstance(context); + List ids = + db.message().getMessageIdsByFolder(rule.folder); + for (long id : ids) { + EntityMessage message = db.message().getMessage(id); + if (message == null) + continue; + + if (rule.matches(context, message, null, null)) + matching.add(message); + + if (matching.size() >= MAX_CHECK) + break; + } + + return matching; + } + + @Override + protected void onExecuted(Bundle args, List messages) { + adapter.set(messages); + + if (messages.size() > 0) { + rvMessage.setVisibility(View.VISIBLE); + btnExecute.setVisibility(View.VISIBLE); + } else + tvNoMessages.setVisibility(View.VISIBLE); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + if (ex instanceof IllegalArgumentException) { + tvNoMessages.setText(ex.getMessage()); + tvNoMessages.setVisibility(View.VISIBLE); + } else + Log.unexpectedError(getParentFragmentManager(), ex); + } + }.execute(this, args, "rule:check"); + + return new AlertDialog.Builder(getContext()) + .setIcon(R.drawable.baseline_mail_outline_24) + .setTitle(R.string.title_rule_matched) + .setView(dview) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogSaveSearch.java b/app/src/main/java/eu/faircode/email/FragmentDialogSaveSearch.java new file mode 100644 index 0000000000..f8f34fd68e --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogSaveSearch.java @@ -0,0 +1,125 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import static android.app.Activity.RESULT_OK; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; + +public class FragmentDialogSaveSearch extends FragmentDialogBase { + private ViewButtonColor btnColor; + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + final Bundle args = getArguments(); + + BoundaryCallbackMessages.SearchCriteria criteria = + (BoundaryCallbackMessages.SearchCriteria) args.getSerializable("criteria"); + if (criteria == null) + criteria = new BoundaryCallbackMessages.SearchCriteria(); + + final Context context = getContext(); + View dview = LayoutInflater.from(context).inflate(R.layout.dialog_save_search, null); + EditText etName = dview.findViewById(R.id.etName); + EditText etOrder = dview.findViewById(R.id.etOrder); + btnColor = dview.findViewById(R.id.btnColor); + + btnColor.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Helper.hideKeyboard(etName); + + Bundle args = new Bundle(); + args.putInt("color", btnColor.getColor()); + args.putString("title", getString(R.string.title_color)); + args.putBoolean("reset", true); + + FragmentDialogColor fragment = new FragmentDialogColor(); + fragment.setArguments(args); + fragment.setTargetFragment(FragmentDialogSaveSearch.this, 1234); + fragment.show(getParentFragmentManager(), "search:color"); + } + }); + + etName.setText(criteria.name == null ? criteria.getTitle(context) : criteria.name); + etOrder.setText(criteria.order == null ? null : Integer.toString(criteria.order)); + btnColor.setColor(criteria.color); + + AlertDialog.Builder dialog = new AlertDialog.Builder(context) + .setView(dview) + .setPositiveButton(R.string.title_save, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String order = etOrder.getText().toString(); + args.putString("name", etName.getText().toString()); + args.putInt("order", + !TextUtils.isEmpty(order) && TextUtils.isDigitsOnly(order) + ? Integer.parseInt(order) : -1); + args.putInt("color", btnColor.getColor()); + sendResult(Activity.RESULT_OK); + } + }) + .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + sendResult(Activity.RESULT_CANCELED); + } + }); + + if (criteria.id != null) + dialog.setNeutralButton(R.string.title_delete, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + sendResult(Activity.RESULT_FIRST_USER); + } + }); + + return dialog.create(); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + try { + if (resultCode == RESULT_OK && data != null) { + Bundle args = data.getBundleExtra("args"); + int color = args.getInt("color"); + btnColor.setColor(color); + } + } catch (Throwable ex) { + Log.e(ex); + } + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogSend.java b/app/src/main/java/eu/faircode/email/FragmentDialogSend.java new file mode 100644 index 0000000000..4bb7d03acc --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogSend.java @@ -0,0 +1,696 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import static android.app.Activity.RESULT_OK; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.graphics.Paint; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.ImageButton; +import android.widget.Spinner; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.widget.SwitchCompat; +import androidx.constraintlayout.widget.Group; +import androidx.lifecycle.Observer; +import androidx.preference.PreferenceManager; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.mail.Address; + +public class FragmentDialogSend extends FragmentDialogBase { + static final int MAX_SHOW_RECIPIENTS = 5; + static final int RECIPIENTS_WARNING = 10; + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + Bundle args = getArguments(); + long id = args.getLong("id"); + final boolean sent_missing = args.getBoolean("sent_missing", false); + final String address_error = args.getString("address_error"); + final String mx_error = args.getString("mx_error"); + final boolean remind_dsn = args.getBoolean("remind_dsn", false); + final boolean remind_size = args.getBoolean("remind_size", false); + final boolean remind_pgp = args.getBoolean("remind_pgp", false); + final boolean remind_smime = args.getBoolean("remind_smime", false); + final boolean remind_to = args.getBoolean("remind_to", false); + final boolean remind_extra = args.getBoolean("remind_extra", false); + final boolean remind_noreply = args.getBoolean("remind_noreply", false); + final boolean remind_external = args.getBoolean("remind_external", false); + final boolean remind_subject = args.getBoolean("remind_subject", false); + final boolean remind_text = args.getBoolean("remind_text", false); + final boolean remind_attachment = args.getBoolean("remind_attachment", false); + final String remind_extension = args.getString("remind_extension"); + final boolean remind_internet = args.getBoolean("remind_internet", false); + final boolean styled = args.getBoolean("styled", false); + final long size = args.getLong("size", -1); + final long max_size = args.getLong("max_size", -1); + + final Context context = getContext(); + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + final boolean send_reminders = prefs.getBoolean("send_reminders", true); + final int send_delayed = prefs.getInt("send_delayed", 0); + final boolean send_dialog = prefs.getBoolean("send_dialog", true); + final boolean send_archive = prefs.getBoolean("send_archive", false); + final MessageHelper.AddressFormat email_format = MessageHelper.getAddressFormat(getContext()); + + final int[] encryptValues = getResources().getIntArray(R.array.encryptValues); + final int[] sendDelayedValues = getResources().getIntArray(R.array.sendDelayedValues); + final String[] sendDelayedNames = getResources().getStringArray(R.array.sendDelayedNames); + + final ViewGroup dview = (ViewGroup) LayoutInflater.from(context).inflate(R.layout.dialog_send, null); + final Button btnFixSent = dview.findViewById(R.id.btnFixSent); + final TextView tvAddressError = dview.findViewById(R.id.tvAddressError); + final TextView tvRemindDsn = dview.findViewById(R.id.tvRemindDsn); + final TextView tvRemindSize = dview.findViewById(R.id.tvRemindSize); + final TextView tvRemindPgp = dview.findViewById(R.id.tvRemindPgp); + final TextView tvRemindSmime = dview.findViewById(R.id.tvRemindSmime); + final TextView tvRemindTo = dview.findViewById(R.id.tvRemindTo); + final TextView tvRemindExtra = dview.findViewById(R.id.tvRemindExtra); + final TextView tvRemindNoReply = dview.findViewById(R.id.tvRemindNoReply); + final TextView tvRemindExternal = dview.findViewById(R.id.tvRemindExternal); + final TextView tvRemindSubject = dview.findViewById(R.id.tvRemindSubject); + final TextView tvRemindText = dview.findViewById(R.id.tvRemindText); + final TextView tvRemindAttachment = dview.findViewById(R.id.tvRemindAttachment); + final TextView tvRemindExtension = dview.findViewById(R.id.tvRemindExtension); + final TextView tvRemindInternet = dview.findViewById(R.id.tvRemindInternet); + final SwitchCompat swSendReminders = dview.findViewById(R.id.swSendReminders); + final TextView tvSendRemindersHint = dview.findViewById(R.id.tvSendRemindersHint); + final TextView tvTo = dview.findViewById(R.id.tvTo); + final TextView tvViaTitle = dview.findViewById(R.id.tvViaTitle); + final TextView tvVia = dview.findViewById(R.id.tvVia); + final CheckBox cbPlainOnly = dview.findViewById(R.id.cbPlainOnly); + final TextView tvPlainHint = dview.findViewById(R.id.tvPlainHint); + final CheckBox cbReceipt = dview.findViewById(R.id.cbReceipt); + final TextView tvReceiptHint = dview.findViewById(R.id.tvReceiptHint); + final TextView tvEncrypt = dview.findViewById(R.id.tvEncrypt); + final Spinner spEncrypt = dview.findViewById(R.id.spEncrypt); + final ImageButton ibEncryption = dview.findViewById(R.id.ibEncryption); + final Spinner spPriority = dview.findViewById(R.id.spPriority); + final Spinner spSensitivity = dview.findViewById(R.id.spSensitivity); + final ImageButton ibSensitivity = dview.findViewById(R.id.ibSensitivity); + final TextView tvSendAt = dview.findViewById(R.id.tvSendAt); + final ImageButton ibSendAt = dview.findViewById(R.id.ibSendAt); + final CheckBox cbArchive = dview.findViewById(R.id.cbArchive); + final CheckBox cbNotAgain = dview.findViewById(R.id.cbNotAgain); + final TextView tvNotAgain = dview.findViewById(R.id.tvNotAgain); + final Group grpSentMissing = dview.findViewById(R.id.grpSentMissing); + final Group grpDsn = dview.findViewById(R.id.grpDsn); + + btnFixSent.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + v.getContext().startActivity(new Intent(v.getContext(), ActivitySetup.class) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) + .putExtra("target", "accounts")); + } + }); + + grpSentMissing.setVisibility(sent_missing ? View.VISIBLE : View.GONE); + + tvAddressError.setText(address_error == null ? mx_error : address_error); + tvAddressError.setVisibility(address_error == null && mx_error == null ? View.GONE : View.VISIBLE); + + tvRemindDsn.setVisibility(remind_dsn ? View.VISIBLE : View.GONE); + + tvRemindSize.setText(getString(R.string.title_size_reminder, + Helper.humanReadableByteCount(size), + Helper.humanReadableByteCount(max_size))); + tvRemindSize.setVisibility(remind_size ? View.VISIBLE : View.GONE); + + tvRemindPgp.setVisibility(remind_pgp ? View.VISIBLE : View.GONE); + tvRemindSmime.setVisibility(remind_smime ? View.VISIBLE : View.GONE); + + tvRemindTo.setVisibility(remind_to ? View.VISIBLE : View.GONE); + tvRemindExtra.setVisibility(send_reminders && remind_extra ? View.VISIBLE : View.GONE); + tvRemindNoReply.setVisibility(remind_noreply ? View.VISIBLE : View.GONE); + tvRemindExternal.setVisibility(remind_external ? View.VISIBLE : View.GONE); + tvRemindSubject.setVisibility(send_reminders && remind_subject ? View.VISIBLE : View.GONE); + tvRemindText.setVisibility(send_reminders && remind_text ? View.VISIBLE : View.GONE); + tvRemindAttachment.setVisibility(send_reminders && remind_attachment ? View.VISIBLE : View.GONE); + + tvRemindExtension.setText(getString(R.string.title_attachment_warning, remind_extension)); + tvRemindExtension.setVisibility(send_reminders && remind_extension != null ? View.VISIBLE : View.GONE); + + tvRemindInternet.setVisibility(send_reminders && remind_internet ? View.VISIBLE : View.GONE); + + tvTo.setText(null); + tvVia.setText(null); + tvPlainHint.setVisibility(View.GONE); + tvReceiptHint.setVisibility(View.GONE); + spEncrypt.setTag(0); + spEncrypt.setSelection(0); + spPriority.setTag(1); + spPriority.setSelection(1); + spSensitivity.setTag(0); + spSensitivity.setSelection(0); + tvSendAt.setText(null); + cbArchive.setEnabled(false); + cbNotAgain.setChecked(!send_dialog); + cbNotAgain.setVisibility(send_dialog ? View.VISIBLE : View.GONE); + tvNotAgain.setVisibility(cbNotAgain.isChecked() ? View.VISIBLE : View.GONE); + + Helper.setViewsEnabled(dview, false); + + boolean reminder = (remind_extra || remind_subject || remind_text || + remind_attachment || remind_extension != null || remind_internet); + swSendReminders.setChecked(send_reminders); + swSendReminders.setVisibility(send_reminders && reminder ? View.VISIBLE : View.GONE); + tvSendRemindersHint.setVisibility(View.GONE); + swSendReminders.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { + prefs.edit().putBoolean("send_reminders", checked).apply(); + tvRemindExtra.setVisibility(checked && remind_extra ? View.VISIBLE : View.GONE); + tvRemindSubject.setVisibility(checked && remind_subject ? View.VISIBLE : View.GONE); + tvRemindText.setVisibility(checked && remind_text ? View.VISIBLE : View.GONE); + tvRemindAttachment.setVisibility(checked && remind_attachment ? View.VISIBLE : View.GONE); + tvRemindExtension.setVisibility(checked && remind_extension != null ? View.VISIBLE : View.GONE); + tvRemindInternet.setVisibility(checked && remind_internet ? View.VISIBLE : View.GONE); + tvSendRemindersHint.setVisibility(checked ? View.GONE : View.VISIBLE); + } + }); + + cbNotAgain.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + prefs.edit().putBoolean("send_dialog", !isChecked).apply(); + tvNotAgain.setVisibility(isChecked ? View.VISIBLE : View.GONE); + } + }); + + cbPlainOnly.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { + tvPlainHint.setVisibility(checked && styled ? View.VISIBLE : View.GONE); + + Bundle args = new Bundle(); + args.putLong("id", id); + args.putBoolean("plain_only", checked); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) { + long id = args.getLong("id"); + boolean plain_only = args.getBoolean("plain_only"); + + DB db = DB.getInstance(context); + db.message().setMessagePlainOnly(id, plain_only ? 1 : 0); + + return null; + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + }.serial().execute(FragmentDialogSend.this, args, "compose:plain_only"); + } + }); + + cbReceipt.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { + tvReceiptHint.setVisibility(checked ? View.VISIBLE : View.GONE); + + Bundle args = new Bundle(); + args.putLong("id", id); + args.putBoolean("receipt", checked); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) { + long id = args.getLong("id"); + boolean receipt = args.getBoolean("receipt"); + + DB db = DB.getInstance(context); + db.message().setMessageReceiptRequest(id, receipt); + + return null; + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + }.serial().execute(FragmentDialogSend.this, args, "compose:receipt"); + } + }); + + spEncrypt.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + int last = (int) spEncrypt.getTag(); + if (last != position) { + spEncrypt.setTag(position); + setEncrypt(encryptValues[position]); + + if ((encryptValues[position] == EntityMessage.PGP_SIGNONLY || + encryptValues[position] == EntityMessage.PGP_ENCRYPTONLY || + encryptValues[position] == EntityMessage.PGP_SIGNENCRYPT) && + Helper.isOpenKeychainInstalled(context)) { + tvEncrypt.setPaintFlags(tvEncrypt.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); + tvEncrypt.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String pkg = Helper.getOpenKeychainPackage(v.getContext()); + PackageManager pm = v.getContext().getPackageManager(); + v.getContext().startActivity(pm.getLaunchIntentForPackage(pkg)); + } + }); + } else { + tvEncrypt.setPaintFlags(tvEncrypt.getPaintFlags() & ~Paint.UNDERLINE_TEXT_FLAG); + tvEncrypt.setOnClickListener(null); + } + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + spEncrypt.setTag(0); + setEncrypt(encryptValues[0]); + } + + private void setEncrypt(int encrypt) { + Bundle args = new Bundle(); + args.putLong("id", id); + args.putInt("encrypt", encrypt); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) { + long id = args.getLong("id"); + int encrypt = args.getInt("encrypt"); + + DB db = DB.getInstance(context); + try { + db.beginTransaction(); + + EntityMessage message = db.message().getMessage(id); + if (message == null) + return null; + + db.message().setMessageUiEncrypt(message.id, encrypt); + + List attachments = db.attachment().getAttachments(message.id); + if (attachments == null) + return null; + for (EntityAttachment attachment : attachments) + if (attachment.isEncryption()) + db.attachment().deleteAttachment(attachment.id); + + if (encrypt != EntityMessage.ENCRYPT_NONE && + message.identity != null) { + int iencrypt = + (encrypt == EntityMessage.SMIME_SIGNONLY || + encrypt == EntityMessage.SMIME_SIGNENCRYPT + ? 1 : 0); + db.identity().setIdentityEncrypt(message.identity, iencrypt); + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + return null; + } + + @Override + protected void onExecuted(Bundle args, Void data) { + int encrypt = args.getInt("encrypt"); + + boolean none = EntityMessage.ENCRYPT_NONE.equals(encrypt); + tvRemindPgp.setVisibility(remind_pgp && none ? View.VISIBLE : View.GONE); + tvRemindSmime.setVisibility(remind_smime && none ? View.VISIBLE : View.GONE); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + }.serial().execute(FragmentDialogSend.this, args, "compose:encrypt"); + } + }); + + ibEncryption.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Helper.viewFAQ(v.getContext(), 12); + } + }); + + spPriority.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + int last = (int) spPriority.getTag(); + if (last != position) { + spPriority.setTag(position); + setPriority(position); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + spPriority.setTag(1); + setPriority(1); + } + + private void setPriority(int priority) { + Bundle args = new Bundle(); + args.putLong("id", id); + args.putInt("priority", priority); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) { + long id = args.getLong("id"); + int priority = args.getInt("priority"); + + DB db = DB.getInstance(context); + db.message().setMessagePriority(id, priority); + + return null; + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + }.serial().execute(FragmentDialogSend.this, args, "compose:priority"); + } + }); + + spSensitivity.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + int last = (int) spSensitivity.getTag(); + if (last != position) { + spSensitivity.setTag(position); + setSensitivity(position); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + spSensitivity.setTag(0); + setSensitivity(0); + } + + private void setSensitivity(int sensitivity) { + Bundle args = new Bundle(); + args.putLong("id", id); + args.putInt("sensitivity", sensitivity); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) { + long id = args.getLong("id"); + int sensitivity = args.getInt("sensitivity"); + + DB db = DB.getInstance(context); + db.message().setMessageSensitivity(id, sensitivity < 1 ? null : sensitivity); + + return null; + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + }.serial().execute(FragmentDialogSend.this, args, "compose:sensitivity"); + } + }); + + ibSensitivity.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Helper.viewFAQ(v.getContext(), 177); + } + }); + + View.OnClickListener sendAt = new View.OnClickListener() { + @Override + public void onClick(View view) { + Bundle args = new Bundle(); + args.putString("title", getString(R.string.title_send_at)); + args.putLong("id", id); + + FragmentDialogDuration fragment = new FragmentDialogDuration(); + fragment.setArguments(args); + fragment.setTargetFragment(FragmentDialogSend.this, 1); + fragment.show(getParentFragmentManager(), "send:snooze"); + } + }; + + tvSendAt.setOnClickListener(sendAt); + ibSendAt.setOnClickListener(sendAt); + + cbArchive.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + prefs.edit().putBoolean("send_archive", isChecked).apply(); + } + }); + + DB db = DB.getInstance(context); + db.message().liveMessage(id).observe(getViewLifecycleOwner(), new Observer() { + @Override + public void onChanged(TupleMessageEx draft) { + if (draft == null) { + dismiss(); + return; + } + + boolean dsn = (draft.dsn != null && !EntityMessage.DSN_NONE.equals(draft.dsn)); + int to = (draft.to == null ? 0 : draft.to.length); + int extra = (draft.cc == null ? 0 : draft.cc.length) + (draft.bcc == null ? 0 : draft.bcc.length); + + List
t = new ArrayList<>(); + if (draft.to != null) + if (to <= MAX_SHOW_RECIPIENTS) + t.addAll(Arrays.asList(draft.to)); + else { + t.addAll((Arrays.asList(Arrays.copyOf(draft.to, MAX_SHOW_RECIPIENTS)))); + extra += draft.to.length - MAX_SHOW_RECIPIENTS; + } + Address[] tos = t.toArray(new Address[0]); + + if (extra == 0) + tvTo.setText(MessageHelper.formatAddresses(tos, email_format, false)); + else + tvTo.setText(getString(R.string.title_name_plus, + MessageHelper.formatAddresses(tos, email_format, false), extra)); + tvTo.setTextColor(Helper.resolveColor(context, + to + extra > RECIPIENTS_WARNING ? R.attr.colorWarning : android.R.attr.textColorPrimary)); + if (draft.identityColor != null && draft.identityColor != Color.TRANSPARENT) + tvViaTitle.setTextColor(draft.identityColor); + tvVia.setText(draft.identityEmail); + + cbPlainOnly.setChecked(draft.isPlainOnly() && !dsn); + cbReceipt.setChecked(draft.receipt_request != null && draft.receipt_request && !dsn); + + int encrypt = (draft.ui_encrypt == null || dsn ? EntityMessage.ENCRYPT_NONE : draft.ui_encrypt); + for (int i = 0; i < encryptValues.length; i++) + if (encryptValues[i] == encrypt) { + spEncrypt.setTag(i); + spEncrypt.setSelection(i); + break; + } + + int priority = (draft.priority == null ? 1 : draft.priority); + spPriority.setTag(priority); + spPriority.setSelection(priority); + + int sensitivity = (draft.sensitivity == null ? 0 : draft.sensitivity); + spSensitivity.setTag(sensitivity); + spSensitivity.setSelection(sensitivity); + + if (draft.ui_snoozed == null) { + if (send_delayed == 0) + tvSendAt.setText(getString(R.string.title_now)); + else + for (int pos = 0; pos < sendDelayedValues.length; pos++) + if (sendDelayedValues[pos] == send_delayed) { + tvSendAt.setText(getString(R.string.title_after, sendDelayedNames[pos])); + break; + } + } else { + DateFormat DTF = Helper.getDateTimeInstance(context, SimpleDateFormat.MEDIUM, SimpleDateFormat.SHORT); + DateFormat D = new SimpleDateFormat("E"); + tvSendAt.setText(D.format(draft.ui_snoozed) + " " + DTF.format(draft.ui_snoozed)); + } + + grpDsn.setVisibility(dsn ? View.GONE : View.VISIBLE); + + Helper.setViewsEnabled(dview, true); + } + }); + + Bundle aargs = new Bundle(); + aargs.putLong("id", id); + + new SimpleTask() { + @Override + protected @NonNull + Boolean onExecute(Context context, Bundle args) { + long id = args.getLong("id"); + + DB db = DB.getInstance(context); + EntityMessage draft = db.message().getMessage(id); + if (draft == null) { + args.putString("reason", "Draft gone"); + return false; + } + + if (TextUtils.isEmpty(draft.inreplyto)) { + args.putString("reason", "No in-reply-to"); + return false; + } + + EntityFolder archive = db.folder().getFolderByType(draft.account, EntityFolder.ARCHIVE); + if (archive == null) { + args.putString("reason", "No archive"); + return false; + } + + List messages = db.message().getMessagesByMsgId(draft.account, draft.inreplyto); + if (messages == null || messages.size() == 0) { + args.putString("reason", "In-reply-to gone"); + return false; + } + + for (EntityMessage message : messages) { + EntityFolder folder = db.folder().getFolder(message.folder); + if (folder == null) + continue; + if (EntityFolder.INBOX.equals(folder.type) || EntityFolder.USER.equals(folder.type)) + return true; + } + + args.putString("reason", "Not in inbox or unread"); + return false; + } + + @Override + protected void onExecuted(Bundle args, Boolean data) { + if (!data) { + String reason = args.getString("reason"); + if (BuildConfig.DEBUG) + cbArchive.setText(reason); + else + Log.i("Auto archive reason=" + reason); + } + if (send_archive && data) + cbArchive.setChecked(true); + cbArchive.setEnabled(data); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + // Ignored + } + }.serial().execute(FragmentDialogSend.this, aargs, "send:archive"); + + AlertDialog.Builder builder = new AlertDialog.Builder(context) + .setView(dview) + .setNegativeButton(android.R.string.cancel, null); + + if (address_error == null && !remind_to && !remind_size) { + if (send_delayed != 0) + builder.setNeutralButton(R.string.title_send_now, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + getArguments().putBoolean("archive", cbArchive.isChecked()); + sendResult(Activity.RESULT_FIRST_USER); + } + }); + builder.setPositiveButton(R.string.title_send, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + getArguments().putBoolean("archive", cbArchive.isChecked()); + sendResult(Activity.RESULT_OK); + } + }); + } + + return builder.create(); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, @Nullable Intent intent) { + super.onActivityResult(requestCode, resultCode, intent); + + if (resultCode == RESULT_OK && intent != null) { + Bundle data = intent.getBundleExtra("args"); + long id = data.getLong("id"); + long duration = data.getLong("duration"); + long time = data.getLong("time"); + + Bundle args = new Bundle(); + args.putLong("id", id); + args.putLong("wakeup", duration == 0 ? -1 : time); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) { + long id = args.getLong("id"); + long wakeup = args.getLong("wakeup"); + + DB db = DB.getInstance(context); + db.message().setMessageSnoozed(id, wakeup < 0 ? null : wakeup); + + return null; + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + }.serial().execute(this, args, "compose:snooze"); + } + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogSwipes.java b/app/src/main/java/eu/faircode/email/FragmentDialogSwipes.java new file mode 100644 index 0000000000..51c5867aaa --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogSwipes.java @@ -0,0 +1,158 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.Spinner; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.PreferenceManager; + +import java.util.ArrayList; +import java.util.List; + +public class FragmentDialogSwipes extends FragmentDialogBase { + private Spinner spLeft; + private Spinner spRight; + private ArrayAdapter adapter; + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_swipes, null); + spLeft = dview.findViewById(R.id.spLeft); + spRight = dview.findViewById(R.id.spRight); + + adapter = new ArrayAdapter<>(getContext(), R.layout.spinner_item1, android.R.id.text1, new ArrayList()); + adapter.setDropDownViewResource(R.layout.spinner_item1_dropdown); + + spLeft.setAdapter(adapter); + spRight.setAdapter(adapter); + + List folders = FragmentAccount.getFolderActions(getContext()); + + EntityFolder trash = new EntityFolder(); + trash.id = 2L; + trash.name = getString(R.string.title_trash); + folders.add(1, trash); + + EntityFolder archive = new EntityFolder(); + archive.id = 1L; + archive.name = getString(R.string.title_archive); + folders.add(1, archive); + + adapter.addAll(folders); + + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + int leftPos = prefs.getInt("swipe_left_default", 2); // Trash + int rightPos = prefs.getInt("swipe_right_default", 1); // Archive + + spLeft.setSelection(leftPos); + spRight.setSelection(rightPos); + + return new AlertDialog.Builder(getContext()) + .setView(dview) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + prefs.edit() + .putInt("swipe_left_default", spLeft.getSelectedItemPosition()) + .putInt("swipe_right_default", spRight.getSelectedItemPosition()) + .apply(); + + EntityFolder left = (EntityFolder) spLeft.getSelectedItem(); + EntityFolder right = (EntityFolder) spRight.getSelectedItem(); + + if ((left != null && EntityMessage.SWIPE_ACTION_HIDE.equals(left.id)) || + (right != null && EntityMessage.SWIPE_ACTION_HIDE.equals(right.id))) + prefs.edit() + .putBoolean("message_tools", true) + .putBoolean("button_hide", true) + .apply(); + + Bundle args = new Bundle(); + args.putLong("left", left == null ? 0 : left.id); + args.putLong("right", right == null ? 0 : right.id); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) { + long left = args.getLong("left"); + long right = args.getLong("right"); + + DB db = DB.getInstance(context); + try { + db.beginTransaction(); + + List accounts = db.account().getAccounts(); + for (EntityAccount account : accounts) + if (account.protocol == EntityAccount.TYPE_IMAP) + db.account().setAccountSwipes( + account.id, + getAction(context, left, account.id), + getAction(context, right, account.id)); + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + return null; + } + + @Override + protected void onExecuted(Bundle args, Void data) { + ToastEx.makeText(getContext(), R.string.title_completed, Toast.LENGTH_LONG).show(); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + + private Long getAction(Context context, long selection, long account) { + if (selection < 0) + return selection; + else if (selection == 0) + return null; + else { + DB db = DB.getInstance(context); + String type = (selection == 2 ? EntityFolder.TRASH : EntityFolder.ARCHIVE); + EntityFolder archive = db.folder().getFolderByType(account, type); + return (archive == null ? null : archive.id); + } + } + }.execute(getContext(), getViewLifecycleOwner(), args, "dialog:swipe"); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogVirusTotal.java b/app/src/main/java/eu/faircode/email/FragmentDialogVirusTotal.java new file mode 100644 index 0000000000..44409a4efe --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogVirusTotal.java @@ -0,0 +1,248 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.app.Dialog; +import android.content.Context; +import android.graphics.Typeface; +import android.net.Uri; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.Button; +import android.widget.ProgressBar; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import java.io.File; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; + +public class FragmentDialogVirusTotal extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + Bundle args = getArguments(); + String apiKey = args.getString("apiKey"); + String name = args.getString("name"); + + final Context context = getContext(); + View view = LayoutInflater.from(context).inflate(R.layout.dialog_virus_total, null); + final TextView tvName = view.findViewById(R.id.tvName); + final TextView tvError = view.findViewById(R.id.tvError); + final TextView tvUnknown = view.findViewById(R.id.tvUnknown); + final TextView tvSummary = view.findViewById(R.id.tvSummary); + final TextView tvLabel = view.findViewById(R.id.tvLabel); + final TextView tvReport = view.findViewById(R.id.tvReport); + final RecyclerView rvScan = view.findViewById(R.id.rvScan); + final Button btnUpload = view.findViewById(R.id.btnUpload); + final ProgressBar pbUpload = view.findViewById(R.id.pbUpload); + final TextView tvAnalyzing = view.findViewById(R.id.tvAnalyzing); + final TextView tvPrivacy = view.findViewById(R.id.tvPrivacy); + final ProgressBar pbWait = view.findViewById(R.id.pbWait); + + tvName.setText(name); + tvName.setVisibility(TextUtils.isEmpty(name) ? View.GONE : View.VISIBLE); + tvError.setVisibility(View.GONE); + tvUnknown.setVisibility(View.GONE); + tvSummary.setVisibility(View.GONE); + tvLabel.setVisibility(View.GONE); + tvReport.setVisibility(View.GONE); + tvReport.getPaint().setUnderlineText(true); + + rvScan.setHasFixedSize(false); + LinearLayoutManager llm = new LinearLayoutManager(getContext()); + rvScan.setLayoutManager(llm); + + final AdapterVirusTotal adapter = new AdapterVirusTotal(getContext(), getViewLifecycleOwner()); + rvScan.setAdapter(adapter); + + rvScan.setVisibility(View.GONE); + + btnUpload.setVisibility(View.GONE); + pbUpload.setVisibility(View.GONE); + tvAnalyzing.setVisibility(View.GONE); + tvPrivacy.setVisibility(View.GONE); + tvPrivacy.getPaint().setUnderlineText(true); + pbWait.setVisibility(View.GONE); + + final SimpleTask taskLookup = new SimpleTask() { + @Override + protected void onPreExecute(Bundle args) { + tvError.setVisibility(View.GONE); + pbWait.setVisibility(View.VISIBLE); + } + + @Override + protected void onPostExecute(Bundle args) { + pbWait.setVisibility(View.GONE); + } + + @Override + protected Bundle onExecute(Context context, Bundle args) throws Throwable { + String apiKey = args.getString("apiKey"); + File file = (File) args.getSerializable("file"); + return VirusTotal.lookup(context, file, apiKey); + } + + @Override + protected void onExecuted(Bundle args, Bundle result) { + List scans = result.getParcelableArrayList("scans"); + String label = result.getString("label"); + String analysis = args.getString("analysis"); + + int malicious = 0; + if (scans != null) + for (VirusTotal.ScanResult scan : scans) + if ("malicious".equals(scan.category)) + malicious++; + + NumberFormat NF = NumberFormat.getNumberInstance(); + + tvUnknown.setVisibility(scans == null ? View.VISIBLE : View.GONE); + tvSummary.setText(getString(R.string.title_vt_summary, NF.format(malicious))); + tvSummary.setTextColor(Helper.resolveColor(context, + malicious == 0 ? android.R.attr.textColorPrimary : R.attr.colorWarning)); + tvSummary.setTypeface(malicious == 0 ? Typeface.DEFAULT : Typeface.DEFAULT_BOLD); + tvSummary.setVisibility(scans == null ? View.GONE : View.VISIBLE); + tvLabel.setText(label); + tvReport.setVisibility(scans == null ? View.GONE : View.VISIBLE); + adapter.set(scans == null ? new ArrayList<>() : scans); + rvScan.setVisibility(scans == null ? View.GONE : View.VISIBLE); + btnUpload.setVisibility(scans == null && !TextUtils.isEmpty(apiKey) ? View.VISIBLE : View.GONE); + tvPrivacy.setVisibility(btnUpload.getVisibility()); + + if (analysis != null && args.getBoolean("init")) { + args.remove("init"); + btnUpload.callOnClick(); + } + } + + @Override + protected void onException(Bundle args, Throwable ex) { + tvError.setText(Log.formatThrowable(ex, false)); + tvError.setVisibility(View.VISIBLE); + } + }; + + final SimpleTask taskUpload = new SimpleTask() { + @Override + protected void onPreExecute(Bundle args) { + btnUpload.setEnabled(false); + pbUpload.setVisibility(View.VISIBLE); + } + + @Override + protected void onPostExecute(Bundle args) { + btnUpload.setEnabled(true); + tvAnalyzing.setVisibility(View.GONE); + pbUpload.setVisibility(View.GONE); + } + + @Override + protected Void onExecute(Context context, Bundle args) throws Throwable { + String apiKey = args.getString("apiKey"); + File file = (File) args.getSerializable("file"); + + String analysis = args.getString("analysis"); + if (analysis == null) { + analysis = VirusTotal.upload(context, file, apiKey); + args.putString("analysis", analysis); + } + postProgress(analysis); + VirusTotal.waitForAnalysis(context, analysis, apiKey); + return null; + } + + @Override + protected void onProgress(CharSequence status, Bundle data) { + tvAnalyzing.setVisibility(View.VISIBLE); + } + + @Override + protected void onExecuted(Bundle args, Void data) { + taskLookup.execute(FragmentDialogVirusTotal.this, args, "attachment:lookup"); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + }; + + final SimpleTask taskUrl = new SimpleTask() { + @Override + protected String onExecute(Context context, Bundle args) throws Throwable { + File file = (File) args.getSerializable("file"); + return VirusTotal.getUrl(file); + } + + @Override + protected void onExecuted(Bundle args, String uri) { + Helper.view(context, Uri.parse(uri), true); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getParentFragmentManager(), ex); + } + }; + + tvReport.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + taskUrl.execute(FragmentDialogVirusTotal.this, args, "attachment:report"); + } + }); + + btnUpload.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + taskUpload.execute(FragmentDialogVirusTotal.this, args, "attachment:upload"); + } + }); + + tvPrivacy.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Helper.view(v.getContext(), Uri.parse(VirusTotal.URI_PRIVACY), true); + } + }); + + if (TextUtils.isEmpty(apiKey)) + pbWait.setVisibility(View.GONE); + else { + args.putBoolean("init", true); + taskLookup.execute(this, args, "attachment:lookup"); + } + + return new AlertDialog.Builder(context) + .setView(view) + .setNegativeButton(R.string.title_setup_done, null) + .create(); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogWeekend.java b/app/src/main/java/eu/faircode/email/FragmentDialogWeekend.java new file mode 100644 index 0000000000..3c2aae0a7a --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentDialogWeekend.java @@ -0,0 +1,70 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.PreferenceManager; + +import java.text.DateFormatSymbols; +import java.util.Arrays; +import java.util.Calendar; + +public class FragmentDialogWeekend extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + boolean[] days = new boolean[7]; + + final Context context = getContext(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + + String[] daynames = Arrays.copyOfRange(new DateFormatSymbols().getWeekdays(), 1, 8); + + String weekend = prefs.getString("weekend", Calendar.SATURDAY + "," + Calendar.SUNDAY); + for (String day : weekend.split(",")) + days[Integer.parseInt(day) - 1] = true; + + return new AlertDialog.Builder(context) + .setTitle(R.string.title_advanced_schedule_weekend) + .setMultiChoiceItems(daynames, days, new DialogInterface.OnMultiChoiceClickListener() { + @Override + public void onClick(DialogInterface dialog, int which, boolean isChecked) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < days.length; i++) + if (days[i]) { + if (sb.length() > 0) + sb.append(","); + sb.append(i + 1); + } + prefs.edit().putString("weekend", sb.toString()).apply(); + } + }) + .setNegativeButton(R.string.title_setup_done, null) + .create(); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentFolders.java b/app/src/main/java/eu/faircode/email/FragmentFolders.java index 9741486656..7ccb8f5b1a 100644 --- a/app/src/main/java/eu/faircode/email/FragmentFolders.java +++ b/app/src/main/java/eu/faircode/email/FragmentFolders.java @@ -22,12 +22,10 @@ package eu.faircode.email; import static android.app.Activity.RESULT_OK; import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; -import android.app.Dialog; import android.app.Notification; import android.app.NotificationManager; import android.content.ContentResolver; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Canvas; @@ -42,17 +40,12 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.EditText; import android.widget.ImageButton; -import android.widget.RadioGroup; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.SearchView; import androidx.constraintlayout.widget.Group; import androidx.core.app.NotificationCompat; @@ -889,7 +882,7 @@ public class FragmentFolders extends FragmentBase { Bundle args = new Bundle(); args.putLong("account", account); - FragmentDialogApply fragment = new FragmentDialogApply(); + FragmentDialogFoldersApply fragment = new FragmentDialogFoldersApply(); fragment.setArguments(args); fragment.show(getParentFragmentManager(), "folders:apply"); } @@ -1505,115 +1498,4 @@ public class FragmentFolders extends FragmentBase { } }.execute(this, args, "edit:color"); } - - public static class FragmentDialogApply extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_folder_all, null); - final RadioGroup rgSynchronize = view.findViewById(R.id.rgSynchronize); - final EditText etSyncDays = view.findViewById(R.id.etSyncDays); - final EditText etKeepDays = view.findViewById(R.id.etKeepDays); - final CheckBox cbKeepAll = view.findViewById(R.id.cbKeepAll); - final CheckBox cbPollSystem = view.findViewById(R.id.cbPollSystem); - final CheckBox cbPollUser = view.findViewById(R.id.cbPollUser); - - cbKeepAll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - etKeepDays.setEnabled(!isChecked); - } - }); - - return new AlertDialog.Builder(getContext()) - .setView(view) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - Bundle args = getArguments(); - int optionId = rgSynchronize.getCheckedRadioButtonId(); - if (optionId == R.id.rbEnable) - args.putBoolean("enable", true); - else if (optionId == R.id.rbDisable) - args.putBoolean("enable", false); - args.putString("sync", etSyncDays.getText().toString()); - args.putString("keep", cbKeepAll.isChecked() - ? Integer.toString(Integer.MAX_VALUE) - : etKeepDays.getText().toString()); - args.putBoolean("system", cbPollSystem.isChecked()); - args.putBoolean("user", cbPollUser.isChecked()); - - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) throws Throwable { - long aid = args.getLong("account"); - Boolean enable = null; - if (args.containsKey("enable")) - enable = args.getBoolean("enable"); - String sync = args.getString("sync"); - String keep = args.getString("keep"); - boolean system = args.getBoolean("system"); - boolean user = args.getBoolean("user"); - - if (TextUtils.isEmpty(sync)) - sync = "7"; - if (TextUtils.isEmpty(keep)) - keep = "30"; - - DB db = DB.getInstance(context); - try { - db.beginTransaction(); - - EntityAccount account = db.account().getAccount(aid); - if (account == null) - return null; - - if (system && account.poll_interval > 15) - db.account().setAccountKeepAliveInterval(account.id, 15); - - List folders = db.folder().getFolders(aid, false, true); - if (folders == null) - return null; - - for (EntityFolder folder : folders) { - if (EntityFolder.USER.equals(folder.type)) { - if (enable != null) { - folder.synchronize = enable; - db.folder().setFolderSynchronize(folder.id, folder.synchronize); - } - - db.folder().setFolderProperties( - folder.id, - Integer.parseInt(sync), - Integer.parseInt(keep)); - } - - if (folder.synchronize && !folder.poll) - if (EntityFolder.USER.equals(folder.type) - ? user - : system && !EntityFolder.INBOX.equals(folder.type)) - db.folder().setFolderPoll(folder.id, true); - } - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - - ServiceSynchronize.reload(context, aid, false, "Apply"); - - return null; - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(getParentFragmentManager(), ex); - } - }.execute(FragmentDialogApply.this, args, "folders:all"); - } - }) - .setNegativeButton(android.R.string.cancel, null) - .create(); - } - } } diff --git a/app/src/main/java/eu/faircode/email/FragmentMessages.java b/app/src/main/java/eu/faircode/email/FragmentMessages.java index 131c48f1b7..fbe03ecb99 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessages.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessages.java @@ -37,8 +37,6 @@ import static me.everything.android.ui.overscroll.OverScrollBounceEffectDecorato import android.Manifest; import android.animation.ObjectAnimator; -import android.app.Activity; -import android.app.Dialog; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ActivityNotFoundException; @@ -55,7 +53,6 @@ import android.content.res.ColorStateList; import android.database.sqlite.SQLiteConstraintException; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Typeface; @@ -117,10 +114,6 @@ import android.view.inputmethod.EditorInfo; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.EditText; import android.widget.ImageButton; import android.widget.SeekBar; import android.widget.TextView; @@ -418,7 +411,6 @@ public class FragmentMessages extends FragmentBase private static final int SWIPE_DISABLE_SELECT_DURATION = 1500; // milliseconds private static final float LUMINANCE_THRESHOLD = 0.7f; private static final int ITEM_CACHE_SIZE = 10; // Default: 2 items - private static final int MAX_QUICK_ACTIONS = 5; private static final int REQUEST_RAW = 1; private static final int REQUEST_OPENPGP = 4; @@ -6371,72 +6363,72 @@ public class FragmentMessages extends FragmentBase int count = 0; - boolean move = (more_move && count < MAX_QUICK_ACTIONS && result.canMove()); + boolean move = (more_move && count < FragmentDialogQuickActions.MAX_QUICK_ACTIONS && result.canMove()); if (move) count++; - boolean delete = (more_delete && count < MAX_QUICK_ACTIONS && result.canDelete()); + boolean delete = (more_delete && count < FragmentDialogQuickActions.MAX_QUICK_ACTIONS && result.canDelete()); if (delete) count++; - boolean trash = (more_trash && count < MAX_QUICK_ACTIONS && result.canTrash()); + boolean trash = (more_trash && count < FragmentDialogQuickActions.MAX_QUICK_ACTIONS && result.canTrash()); if (trash) count++; if (!delete && !trash && (inTrash || inJunk) && - more_trash && count < MAX_QUICK_ACTIONS && result.canDelete()) { + more_trash && count < FragmentDialogQuickActions.MAX_QUICK_ACTIONS && result.canDelete()) { delete = true; count++; } - boolean junk = (more_junk && count < MAX_QUICK_ACTIONS && result.canJunk()); + boolean junk = (more_junk && count < FragmentDialogQuickActions.MAX_QUICK_ACTIONS && result.canJunk()); if (junk) count++; - boolean archive = (more_archive && count < MAX_QUICK_ACTIONS && result.canArchive()); + boolean archive = (more_archive && count < FragmentDialogQuickActions.MAX_QUICK_ACTIONS && result.canArchive()); if (archive) count++; - boolean inbox = ((more_inbox || (more_junk && inJunk)) && count < MAX_QUICK_ACTIONS && result.canInbox()); + boolean inbox = ((more_inbox || (more_junk && inJunk)) && count < FragmentDialogQuickActions.MAX_QUICK_ACTIONS && result.canInbox()); if (inbox) count++; - boolean importance_high = (more_importance_high && count < MAX_QUICK_ACTIONS && + boolean importance_high = (more_importance_high && count < FragmentDialogQuickActions.MAX_QUICK_ACTIONS && !EntityMessage.PRIORITIY_HIGH.equals(result.importance)); if (importance_high) count++; - boolean importance_normal = (more_importance_normal && count < MAX_QUICK_ACTIONS && + boolean importance_normal = (more_importance_normal && count < FragmentDialogQuickActions.MAX_QUICK_ACTIONS && !EntityMessage.PRIORITIY_NORMAL.equals(result.importance)); if (importance_normal) count++; - boolean importance_low = (more_importance_low && count < MAX_QUICK_ACTIONS && + boolean importance_low = (more_importance_low && count < FragmentDialogQuickActions.MAX_QUICK_ACTIONS && !EntityMessage.PRIORITIY_LOW.equals(result.importance)); if (importance_low) count++; - boolean flag = (more_flag && count < MAX_QUICK_ACTIONS && result.unflagged); + boolean flag = (more_flag && count < FragmentDialogQuickActions.MAX_QUICK_ACTIONS && result.unflagged); if (flag) count++; - boolean flag_color = (more_flag_color && count < MAX_QUICK_ACTIONS && (result.unflagged || result.flagged)); + boolean flag_color = (more_flag_color && count < FragmentDialogQuickActions.MAX_QUICK_ACTIONS && (result.unflagged || result.flagged)); if (flag_color) count++; - boolean hide = (more_hide && count < MAX_QUICK_ACTIONS && result.visible); + boolean hide = (more_hide && count < FragmentDialogQuickActions.MAX_QUICK_ACTIONS && result.visible); if (hide) count++; - boolean snooze = (more_snooze && count < MAX_QUICK_ACTIONS); + boolean snooze = (more_snooze && count < FragmentDialogQuickActions.MAX_QUICK_ACTIONS); if (snooze) count++; - boolean unseen = (more_unseen && count < MAX_QUICK_ACTIONS && result.seen); + boolean unseen = (more_unseen && count < FragmentDialogQuickActions.MAX_QUICK_ACTIONS && result.seen); if (unseen) count++; - boolean seen = (more_seen && count < MAX_QUICK_ACTIONS && result.unseen); + boolean seen = (more_seen && count < FragmentDialogQuickActions.MAX_QUICK_ACTIONS && result.unseen); if (seen) count++; @@ -7432,7 +7424,7 @@ public class FragmentMessages extends FragmentBase return; } - String title = getString(R.string.title_move_undo, getNames(result, true), result.size()); + String title = getString(R.string.title_move_undo, FragmentMoveAsk.getNames(result, true), result.size()); ((ActivityView) activity).undo(title, args, taskUndoMove, taskUndoShow); if (viewType == AdapterMessage.ViewType.THREAD) { @@ -7518,45 +7510,6 @@ public class FragmentMessages extends FragmentBase } }; - private static String getNames(ArrayList result, boolean dest) { - boolean across = false; - for (MessageTarget target : result) - if (target.isAcross()) - across = true; - - Map nameCount = new HashMap<>(); - for (MessageTarget target : result) { - String name = ""; - if (across) - name += (dest ? target.targetAccount.name : target.sourceAccount.name) + "/"; - name += (dest ? target.targetFolder.display : target.sourceFolder.display); - if (!nameCount.containsKey(name)) - nameCount.put(name, 0); - nameCount.put(name, nameCount.get(name) + 1); - } - - List keys = new ArrayList(nameCount.keySet()); - - Collator collator = Collator.getInstance(Locale.getDefault()); - collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc - Collections.sort(keys, collator); - - NumberFormat NF = NumberFormat.getNumberInstance(); - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < keys.size(); i++) { - if (i > 0) - sb.append(", "); - sb.append(keys.get(i)); - if (!dest && keys.size() > 1) { - int count = nameCount.get(keys.get(i)); - sb.append('(').append(NF.format(count)).append(')'); - } - } - - return sb.toString(); - } - static String getSort(Context context, AdapterMessage.ViewType viewType, String type) { if (viewType == AdapterMessage.ViewType.UNIFIED) return "sort_unified"; @@ -10723,435 +10676,4 @@ public class FragmentMessages extends FragmentBase } } } - - public static class FragmentDialogAskSpam extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - Bundle args = getArguments(); - int count = args.getInt("count"); - - final Context context = getContext(); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - - boolean block_sender = prefs.getBoolean("block_sender", true); - String text = getResources().getQuantityString(R.plurals.title_ask_spam, count, count); - - View dview = LayoutInflater.from(context).inflate(R.layout.dialog_ask_spam, null); - TextView tvMessage = dview.findViewById(R.id.tvMessage); - CheckBox cbBlockSender = dview.findViewById(R.id.cbBlockSender); - - tvMessage.setText(text); - cbBlockSender.setChecked(block_sender); - - return new AlertDialog.Builder(context) - .setView(dview) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - boolean block = cbBlockSender.isChecked(); - prefs.edit().putBoolean("block_sender", block).apply(); - getArguments().putBoolean("block", block); - sendResult(Activity.RESULT_OK); - } - }) - .setNegativeButton(android.R.string.cancel, null) - .create(); - } - } - - public static class FragmentDialogReporting extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_error_reporting, null); - Button btnInfo = dview.findViewById(R.id.btnInfo); - - btnInfo.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Helper.viewFAQ(v.getContext(), 104); - } - }); - - return new AlertDialog.Builder(getContext()) - .setView(dview) - .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - prefs.edit().putBoolean("crash_reports", true).apply(); - Log.setCrashReporting(true); - } - }) - .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - prefs.edit().putBoolean("crash_reports_asked", true).apply(); - } - }) - .create(); - } - } - - public static class FragmentDialogReview extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_review, null); - TextView tvHelp = dview.findViewById(R.id.tvHelp); - - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - - Dialog dialog = new AlertDialog.Builder(getContext()) - .setView(dview) - .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - prefs.edit().putBoolean("review_asked", true).apply(); - startActivity(Helper.getIntentRate(getContext())); - } - }) - .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - prefs.edit().putBoolean("review_asked", true).apply(); - } - }) - .setNeutralButton(R.string.title_later, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - prefs.edit().putLong("review_later", new Date().getTime()).apply(); - } - }) - .create(); - - tvHelp.setPaintFlags(tvHelp.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); - tvHelp.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - dismiss(); - prefs.edit().putLong("review_later", new Date().getTime()).apply(); - startActivity(Helper.getIntentIssue(v.getContext(), "Review:issue")); - } - }); - - return dialog; - } - - @Override - public void onCancel(@NonNull DialogInterface dialog) { - super.onCancel(dialog); - try { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - prefs.edit().putBoolean("review_asked", true).apply(); - } catch (Throwable ex) { - Log.e(ex); - } - } - } - - public static class FragmentDialogBoundaryError extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - String error = getArguments().getString("error"); - - final Context context = getContext(); - View dview = LayoutInflater.from(context).inflate(R.layout.dialog_boundary_error, null); - TextView tvError = dview.findViewById(R.id.tvError); - - tvError.setText(error); - - return new AlertDialog.Builder(context) - .setView(dview) - .setPositiveButton(R.string.title_boundary_retry, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - sendResult(Activity.RESULT_OK); - } - }) - .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - sendResult(Activity.RESULT_CANCELED); - } - }) - .create(); - } - } - - public static class FragmentMoveAsk extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - String notagain = getArguments().getString("notagain"); - ArrayList result = getArguments().getParcelableArrayList("result"); - - final Context context = getContext(); - View dview = LayoutInflater.from(context).inflate(R.layout.dialog_ask_move, null); - TextView tvMessages = dview.findViewById(R.id.tvMessages); - TextView tvSourceFolders = dview.findViewById(R.id.tvSourceFolders); - TextView tvTargetFolders = dview.findViewById(R.id.tvTargetFolders); - CheckBox cbNotAgain = dview.findViewById(R.id.cbNotAgain); - TextView tvJunkLearn = dview.findViewById(R.id.tvJunkLearn); - - String question = context.getResources() - .getQuantityString(R.plurals.title_moving_messages, - result.size(), result.size()); - - tvMessages.setText(question); - tvSourceFolders.setText(getNames(result, false)); - tvTargetFolders.setText(getNames(result, true)); - - List sources = new ArrayList<>(); - List targets = new ArrayList<>(); - Integer sourceColor = null; - Integer targetColor = null; - boolean junk = false; - for (MessageTarget t : result) { - if (!sources.contains(t.sourceFolder.type)) - sources.add(t.sourceFolder.type); - if (!targets.contains(t.targetFolder.type)) - targets.add(t.targetFolder.type); - if (sourceColor == null) - sourceColor = t.sourceFolder.color; - if (targetColor == null) - targetColor = t.targetFolder.color; - if (!junk && - (EntityFolder.JUNK.equals(t.sourceFolder.type) || - EntityFolder.JUNK.equals(t.targetFolder.type))) - junk = true; - } - - Drawable source = null; - if (sources.size() == 1) { - source = ContextCompat.getDrawable(context, EntityFolder.getIcon(sources.get(0))); - if (source != null) - source.setBounds(0, 0, source.getIntrinsicWidth(), source.getIntrinsicHeight()); - if (sourceColor == null) - sourceColor = EntityFolder.getDefaultColor(sources.get(0), context); - } else { - source = ContextCompat.getDrawable(context, R.drawable.twotone_folders_24); - source.setBounds(0, 0, source.getIntrinsicWidth(), source.getIntrinsicHeight()); - sourceColor = null; - } - - Drawable target = null; - if (targets.size() == 1) { - target = ContextCompat.getDrawable(context, EntityFolder.getIcon(targets.get(0))); - if (target != null) - target.setBounds(0, 0, target.getIntrinsicWidth(), target.getIntrinsicHeight()); - if (targetColor == null) - targetColor = EntityFolder.getDefaultColor(targets.get(0), context); - } else - targetColor = null; - - tvSourceFolders.setCompoundDrawablesRelative(source, null, null, null); - tvTargetFolders.setCompoundDrawablesRelative(target, null, null, null); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (sourceColor != null) - tvSourceFolders.setCompoundDrawableTintList(ColorStateList.valueOf(sourceColor)); - if (targetColor != null) - tvTargetFolders.setCompoundDrawableTintList(ColorStateList.valueOf(targetColor)); - } - - if (notagain != null) - cbNotAgain.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(buttonView.getContext()); - prefs.edit().putBoolean(notagain, isChecked).apply(); - } - }); - - tvJunkLearn.setVisibility(junk ? View.VISIBLE : View.GONE); - - return new AlertDialog.Builder(context) - .setView(dview) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - sendResult(Activity.RESULT_OK); - } - }) - .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - sendResult(Activity.RESULT_CANCELED); - } - }) - .create(); - } - } - - public static class FragmentDialogSaveSearch extends FragmentDialogBase { - private ViewButtonColor btnColor; - - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - final Bundle args = getArguments(); - - BoundaryCallbackMessages.SearchCriteria criteria = - (BoundaryCallbackMessages.SearchCriteria) args.getSerializable("criteria"); - if (criteria == null) - criteria = new BoundaryCallbackMessages.SearchCriteria(); - - final Context context = getContext(); - View dview = LayoutInflater.from(context).inflate(R.layout.dialog_save_search, null); - EditText etName = dview.findViewById(R.id.etName); - EditText etOrder = dview.findViewById(R.id.etOrder); - btnColor = dview.findViewById(R.id.btnColor); - - btnColor.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Helper.hideKeyboard(etName); - - Bundle args = new Bundle(); - args.putInt("color", btnColor.getColor()); - args.putString("title", getString(R.string.title_color)); - args.putBoolean("reset", true); - - FragmentDialogColor fragment = new FragmentDialogColor(); - fragment.setArguments(args); - fragment.setTargetFragment(FragmentDialogSaveSearch.this, 1234); - fragment.show(getParentFragmentManager(), "search:color"); - } - }); - - etName.setText(criteria.name == null ? criteria.getTitle(context) : criteria.name); - etOrder.setText(criteria.order == null ? null : Integer.toString(criteria.order)); - btnColor.setColor(criteria.color); - - AlertDialog.Builder dialog = new AlertDialog.Builder(context) - .setView(dview) - .setPositiveButton(R.string.title_save, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - String order = etOrder.getText().toString(); - args.putString("name", etName.getText().toString()); - args.putInt("order", - !TextUtils.isEmpty(order) && TextUtils.isDigitsOnly(order) - ? Integer.parseInt(order) : -1); - args.putInt("color", btnColor.getColor()); - sendResult(Activity.RESULT_OK); - } - }) - .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - sendResult(Activity.RESULT_CANCELED); - } - }); - - if (criteria.id != null) - dialog.setNeutralButton(R.string.title_delete, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - sendResult(Activity.RESULT_FIRST_USER); - } - }); - - return dialog.create(); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - try { - if (resultCode == RESULT_OK && data != null) { - Bundle args = data.getBundleExtra("args"); - int color = args.getInt("color"); - btnColor.setColor(color); - } - } catch (Throwable ex) { - Log.e(ex); - } - } - } - - public static class FragmentDialogQuickActions extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - final Context context = getContext(); - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - - final View dview = LayoutInflater.from(context).inflate(R.layout.dialog_quick_actions, null); - final TextView tvHint = dview.findViewById(R.id.tvHint); - final CheckBox cbSeen = dview.findViewById(R.id.cbSeen); - final CheckBox cbUnseen = dview.findViewById(R.id.cbUnseen); - final CheckBox cbSnooze = dview.findViewById(R.id.cbSnooze); - final CheckBox cbHide = dview.findViewById(R.id.cbHide); - final CheckBox cbFlag = dview.findViewById(R.id.cbFlag); - final CheckBox cbFlagColor = dview.findViewById(R.id.cbFlagColor); - final CheckBox cbImportanceLow = dview.findViewById(R.id.cbImportanceLow); - final CheckBox cbImportanceNormal = dview.findViewById(R.id.cbImportanceNormal); - final CheckBox cbImportanceHigh = dview.findViewById(R.id.cbImportanceHigh); - final CheckBox cbInbox = dview.findViewById(R.id.cbInbox); - final CheckBox cbArchive = dview.findViewById(R.id.cbArchive); - final CheckBox cbJunk = dview.findViewById(R.id.cbJunk); - final CheckBox cbTrash = dview.findViewById(R.id.cbTrash); - final CheckBox cbDelete = dview.findViewById(R.id.cbDelete); - final CheckBox cbMove = dview.findViewById(R.id.cbMove); - final CheckBox cbClear = dview.findViewById(R.id.cbClear); - - tvHint.setText(getString(R.string.title_quick_actions_hint, MAX_QUICK_ACTIONS)); - cbSeen.setChecked(prefs.getBoolean("more_seen", true)); - cbUnseen.setChecked(prefs.getBoolean("more_unseen", false)); - cbSnooze.setChecked(prefs.getBoolean("more_snooze", false)); - cbHide.setChecked(prefs.getBoolean("more_hide", false)); - cbFlag.setChecked(prefs.getBoolean("more_flag", false)); - cbFlagColor.setChecked(prefs.getBoolean("more_flag_color", false)); - cbImportanceLow.setChecked(prefs.getBoolean("more_importance_low", false)); - cbImportanceNormal.setChecked(prefs.getBoolean("more_importance_normal", false)); - cbImportanceHigh.setChecked(prefs.getBoolean("more_importance_high", false)); - cbInbox.setChecked(prefs.getBoolean("more_inbox", true)); - cbArchive.setChecked(prefs.getBoolean("more_archive", true)); - cbJunk.setChecked(prefs.getBoolean("more_junk", true)); - cbTrash.setChecked(prefs.getBoolean("more_trash", true)); - cbDelete.setChecked(prefs.getBoolean("more_delete", false)); - cbMove.setChecked(prefs.getBoolean("more_move", true)); - cbClear.setChecked(prefs.getBoolean("more_clear", true)); - - return new AlertDialog.Builder(getContext()) - .setView(dview) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean("more_seen", cbSeen.isChecked()); - editor.putBoolean("more_unseen", cbUnseen.isChecked()); - editor.putBoolean("more_snooze", cbSnooze.isChecked()); - editor.putBoolean("more_hide", cbHide.isChecked()); - editor.putBoolean("more_flag", cbFlag.isChecked()); - editor.putBoolean("more_flag_color", cbFlagColor.isChecked()); - editor.putBoolean("more_importance_low", cbImportanceLow.isChecked()); - editor.putBoolean("more_importance_normal", cbImportanceNormal.isChecked()); - editor.putBoolean("more_importance_high", cbImportanceHigh.isChecked()); - editor.putBoolean("more_inbox", cbInbox.isChecked()); - editor.putBoolean("more_archive", cbArchive.isChecked()); - editor.putBoolean("more_junk", cbJunk.isChecked()); - editor.putBoolean("more_trash", cbTrash.isChecked()); - editor.putBoolean("more_delete", cbDelete.isChecked()); - editor.putBoolean("more_move", cbMove.isChecked()); - editor.putBoolean("more_clear", cbClear.isChecked()); - editor.apply(); - sendResult(Activity.RESULT_OK); - } - }) - .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - sendResult(Activity.RESULT_CANCELED); - } - }) - .create(); - } - } } diff --git a/app/src/main/java/eu/faircode/email/FragmentMoveAsk.java b/app/src/main/java/eu/faircode/email/FragmentMoveAsk.java new file mode 100644 index 0000000000..65ac5fc7ea --- /dev/null +++ b/app/src/main/java/eu/faircode/email/FragmentMoveAsk.java @@ -0,0 +1,194 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2023 by Marcel Bokhorst (M66B) +*/ + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.content.res.ColorStateList; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.core.content.ContextCompat; +import androidx.preference.PreferenceManager; + +import java.text.Collator; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public class FragmentMoveAsk extends FragmentDialogBase { + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + String notagain = getArguments().getString("notagain"); + ArrayList result = getArguments().getParcelableArrayList("result"); + + final Context context = getContext(); + View dview = LayoutInflater.from(context).inflate(R.layout.dialog_ask_move, null); + TextView tvMessages = dview.findViewById(R.id.tvMessages); + TextView tvSourceFolders = dview.findViewById(R.id.tvSourceFolders); + TextView tvTargetFolders = dview.findViewById(R.id.tvTargetFolders); + CheckBox cbNotAgain = dview.findViewById(R.id.cbNotAgain); + TextView tvJunkLearn = dview.findViewById(R.id.tvJunkLearn); + + String question = context.getResources() + .getQuantityString(R.plurals.title_moving_messages, + result.size(), result.size()); + + tvMessages.setText(question); + tvSourceFolders.setText(getNames(result, false)); + tvTargetFolders.setText(getNames(result, true)); + + List sources = new ArrayList<>(); + List targets = new ArrayList<>(); + Integer sourceColor = null; + Integer targetColor = null; + boolean junk = false; + for (FragmentMessages.MessageTarget t : result) { + if (!sources.contains(t.sourceFolder.type)) + sources.add(t.sourceFolder.type); + if (!targets.contains(t.targetFolder.type)) + targets.add(t.targetFolder.type); + if (sourceColor == null) + sourceColor = t.sourceFolder.color; + if (targetColor == null) + targetColor = t.targetFolder.color; + if (!junk && + (EntityFolder.JUNK.equals(t.sourceFolder.type) || + EntityFolder.JUNK.equals(t.targetFolder.type))) + junk = true; + } + + Drawable source = null; + if (sources.size() == 1) { + source = ContextCompat.getDrawable(context, EntityFolder.getIcon(sources.get(0))); + if (source != null) + source.setBounds(0, 0, source.getIntrinsicWidth(), source.getIntrinsicHeight()); + if (sourceColor == null) + sourceColor = EntityFolder.getDefaultColor(sources.get(0), context); + } else { + source = ContextCompat.getDrawable(context, R.drawable.twotone_folders_24); + source.setBounds(0, 0, source.getIntrinsicWidth(), source.getIntrinsicHeight()); + sourceColor = null; + } + + Drawable target = null; + if (targets.size() == 1) { + target = ContextCompat.getDrawable(context, EntityFolder.getIcon(targets.get(0))); + if (target != null) + target.setBounds(0, 0, target.getIntrinsicWidth(), target.getIntrinsicHeight()); + if (targetColor == null) + targetColor = EntityFolder.getDefaultColor(targets.get(0), context); + } else + targetColor = null; + + tvSourceFolders.setCompoundDrawablesRelative(source, null, null, null); + tvTargetFolders.setCompoundDrawablesRelative(target, null, null, null); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (sourceColor != null) + tvSourceFolders.setCompoundDrawableTintList(ColorStateList.valueOf(sourceColor)); + if (targetColor != null) + tvTargetFolders.setCompoundDrawableTintList(ColorStateList.valueOf(targetColor)); + } + + if (notagain != null) + cbNotAgain.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(buttonView.getContext()); + prefs.edit().putBoolean(notagain, isChecked).apply(); + } + }); + + tvJunkLearn.setVisibility(junk ? View.VISIBLE : View.GONE); + + return new AlertDialog.Builder(context) + .setView(dview) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + sendResult(Activity.RESULT_OK); + } + }) + .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + sendResult(Activity.RESULT_CANCELED); + } + }) + .create(); + } + + static String getNames(ArrayList result, boolean dest) { + boolean across = false; + for (FragmentMessages.MessageTarget target : result) + if (target.isAcross()) + across = true; + + Map nameCount = new HashMap<>(); + for (FragmentMessages.MessageTarget target : result) { + String name = ""; + if (across) + name += (dest ? target.targetAccount.name : target.sourceAccount.name) + "/"; + name += (dest ? target.targetFolder.display : target.sourceFolder.display); + if (!nameCount.containsKey(name)) + nameCount.put(name, 0); + nameCount.put(name, nameCount.get(name) + 1); + } + + List keys = new ArrayList(nameCount.keySet()); + + Collator collator = Collator.getInstance(Locale.getDefault()); + collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc + Collections.sort(keys, collator); + + NumberFormat NF = NumberFormat.getNumberInstance(); + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < keys.size(); i++) { + if (i > 0) + sb.append(", "); + sb.append(keys.get(i)); + if (!dest && keys.size() > 1) { + int count = nameCount.get(keys.get(i)); + sb.append('(').append(NF.format(count)).append(')'); + } + } + + return sb.toString(); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentOperations.java b/app/src/main/java/eu/faircode/email/FragmentOperations.java index 6bf348f7e8..4a83e0de95 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOperations.java +++ b/app/src/main/java/eu/faircode/email/FragmentOperations.java @@ -19,9 +19,6 @@ package eu.faircode.email; Copyright 2018-2023 by Marcel Bokhorst (M66B) */ -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; import android.os.Bundle; import android.view.LayoutInflater; import android.view.Menu; @@ -29,13 +26,10 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.CheckBox; import android.widget.TextView; -import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; import androidx.constraintlayout.widget.Group; import androidx.lifecycle.Observer; import androidx.recyclerview.widget.LinearLayoutManager; @@ -117,7 +111,7 @@ public class FragmentOperations extends FragmentBase { onMenuHelp(); return true; } else if (itemId == R.id.menu_delete) { - new FragmentDialogDelete().show(getParentFragmentManager(), "operations:delete"); + new FragmentDialogOperationsDelete().show(getParentFragmentManager(), "operations:delete"); return true; } return super.onOptionsItemSelected(item); @@ -126,144 +120,4 @@ public class FragmentOperations extends FragmentBase { private void onMenuHelp() { Helper.viewFAQ(getContext(), 3); } - - public static class FragmentDialogDelete extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - final Context context = getContext(); - final View dview = LayoutInflater.from(context).inflate(R.layout.dialog_delete_operations, null); - final CheckBox cbError = dview.findViewById(R.id.cbError); - final CheckBox cbFetch = dview.findViewById(R.id.cbFetch); - final CheckBox cbMove = dview.findViewById(R.id.cbMove); - final CheckBox cbFlag = dview.findViewById(R.id.cbFlag); - final CheckBox cbDelete = dview.findViewById(R.id.cbDelete); - - return new AlertDialog.Builder(context) - .setView(dview) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - Bundle args = new Bundle(); - args.putBoolean("error", cbError.isChecked()); - args.putBoolean("fetch", cbFetch.isChecked()); - args.putBoolean("move", cbMove.isChecked()); - args.putBoolean("flag", cbFlag.isChecked()); - args.putBoolean("delete", cbDelete.isChecked()); - - new SimpleTask() { - private Toast toast = null; - - @Override - protected void onPostExecute(Bundle args) { - toast = ToastEx.makeText(context, R.string.title_executing, Toast.LENGTH_LONG); - toast.show(); - } - - @Override - protected void onPreExecute(Bundle args) { - if (toast != null) - toast.cancel(); - } - - @Override - protected Integer onExecute(Context context, Bundle args) { - boolean error = args.getBoolean("error"); - boolean fetch = args.getBoolean("fetch"); - boolean move = args.getBoolean("move"); - boolean flag = args.getBoolean("flag"); - boolean delete = args.getBoolean("delete"); - - int deleted = 0; - DB db = DB.getInstance(context); - try { - db.beginTransaction(); - - List ops = new ArrayList<>(); - - // ADD, SEND, EXISTS, SUBSCRIBE - - if (error) - addAll(ops, db.operation().getOperationsError()); - - if (fetch) { - addAll(ops, db.operation().getOperations(EntityOperation.FETCH)); - addAll(ops, db.operation().getOperations(EntityOperation.DOWNLOAD)); - addAll(ops, db.operation().getOperations(EntityOperation.RAW)); - addAll(ops, db.operation().getOperations(EntityOperation.BODY)); - addAll(ops, db.operation().getOperations(EntityOperation.ATTACHMENT)); - addAll(ops, db.operation().getOperations(EntityOperation.HEADERS)); - addAll(ops, db.operation().getOperations(EntityOperation.RULE)); - addAll(ops, db.operation().getOperations(EntityOperation.SYNC)); - } - - if (move) { - addAll(ops, db.operation().getOperations(EntityOperation.MOVE)); - addAll(ops, db.operation().getOperations(EntityOperation.COPY)); - } - - if (flag) { - addAll(ops, db.operation().getOperations(EntityOperation.SEEN)); - addAll(ops, db.operation().getOperations(EntityOperation.ANSWERED)); - addAll(ops, db.operation().getOperations(EntityOperation.FLAG)); - addAll(ops, db.operation().getOperations(EntityOperation.KEYWORD)); - addAll(ops, db.operation().getOperations(EntityOperation.LABEL)); - addAll(ops, db.operation().getOperations(EntityOperation.REPORT)); - } - - if (delete) { - addAll(ops, db.operation().getOperations(EntityOperation.DELETE)); - addAll(ops, db.operation().getOperations(EntityOperation.PURGE)); - addAll(ops, db.operation().getOperations(EntityOperation.EXPUNGE)); - } - - for (EntityOperation op : ops) { - EntityLog.log(context, "Deleting operation=" + op.id + ":" + op.name + " error=" + op.error); - - if (db.operation().deleteOperation(op.id) > 0) { - op.cleanup(context, false); - deleted++; - } - - if (EntityOperation.SYNC.equals(op.name)) - db.folder().setFolderSyncState(op.folder, null); - } - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - - return deleted; - } - - @Override - protected void onExecuted(Bundle args, Integer deleted) { - if (deleted == null) - deleted = -1; - Context context = getContext(); - if (context == null) - return; - ToastEx.makeText( - context, - getString(R.string.title_delete_operation_deleted, deleted), - Toast.LENGTH_LONG).show(); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(getParentFragmentManager(), ex); - } - - private void addAll(List list, List sublist) { - if (sublist != null) - list.addAll(sublist); - } - }.execute(context, getActivity(), args, "operations:delete"); - } - }) - .setNegativeButton(android.R.string.cancel, null) - .create(); - } - } } diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsBackup.java b/app/src/main/java/eu/faircode/email/FragmentOptionsBackup.java index ba31e25e40..f26dd52490 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsBackup.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsBackup.java @@ -19,11 +19,9 @@ package eu.faircode.email; Copyright 2018-2023 by Marcel Bokhorst (M66B) */ -import static android.app.Activity.RESULT_CANCELED; import static android.app.Activity.RESULT_OK; import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_GMAIL; -import android.app.Dialog; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; @@ -38,19 +36,14 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.OperationCanceledException; -import android.text.Editable; import android.text.SpannableStringBuilder; import android.text.TextUtils; -import android.text.TextWatcher; import android.text.style.ForegroundColorSpan; import android.text.style.StyleSpan; import android.util.Pair; -import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.WindowManager; -import android.view.inputmethod.EditorInfo; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; @@ -1642,182 +1635,4 @@ public class FragmentOptionsBackup extends FragmentBase implements SharedPrefere } }.execute(FragmentOptionsBackup.this, args, "cloud"); } - - public static class FragmentDialogExport extends FragmentDialogBase { - private TextInputLayout tilPassword1; - private TextInputLayout tilPassword2; - - @Override - public void onSaveInstanceState(@NonNull Bundle outState) { - outState.putString("fair:password1", tilPassword1 == null ? null : tilPassword1.getEditText().getText().toString()); - outState.putString("fair:password2", tilPassword2 == null ? null : tilPassword2.getEditText().getText().toString()); - super.onSaveInstanceState(outState); - } - - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - Context context = getContext(); - View dview = LayoutInflater.from(context).inflate(R.layout.dialog_export, null); - tilPassword1 = dview.findViewById(R.id.tilPassword1); - tilPassword2 = dview.findViewById(R.id.tilPassword2); - - if (savedInstanceState != null) { - tilPassword1.getEditText().setText(savedInstanceState.getString("fair:password1")); - tilPassword2.getEditText().setText(savedInstanceState.getString("fair:password2")); - } - - Dialog dialog = new AlertDialog.Builder(context) - .setView(dview) - .setPositiveButton(R.string.title_save_file, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - ViewModelExport vme = new ViewModelProvider(getActivity()).get(ViewModelExport.class); - vme.setPassword(tilPassword1.getEditText().getText().toString()); - sendResult(RESULT_OK); - } - }) - .setNegativeButton(android.R.string.cancel, null) - .create(); - - dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); - - return dialog; - } - - @Override - public void onStart() { - super.onStart(); - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - boolean debug = (BuildConfig.DEBUG || prefs.getBoolean("debug", false)); - - Button btnOk = ((AlertDialog) getDialog()).getButton(AlertDialog.BUTTON_POSITIVE); - - TextWatcher w = new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - // Do nothing - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - // Do nothing - } - - @Override - public void afterTextChanged(Editable s) { - String p1 = tilPassword1.getEditText().getText().toString(); - String p2 = tilPassword2.getEditText().getText().toString(); - btnOk.setEnabled((debug || !TextUtils.isEmpty(p1)) && p1.equals(p2)); - tilPassword2.setHint(!TextUtils.isEmpty(p2) && !p2.equals(p1) - ? R.string.title_setup_password_different - : R.string.title_setup_password_repeat); - } - }; - - tilPassword1.getEditText().addTextChangedListener(w); - tilPassword2.getEditText().addTextChangedListener(w); - w.afterTextChanged(null); - - tilPassword2.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() { - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (actionId == EditorInfo.IME_ACTION_DONE) { - btnOk.performClick(); - return true; - } else - return false; - } - }); - } - } - - public static class FragmentDialogImport extends FragmentDialogBase { - private TextInputLayout tilPassword1; - - @Override - public void onSaveInstanceState(@NonNull Bundle outState) { - outState.putString("fair:password1", tilPassword1 == null ? null : tilPassword1.getEditText().getText().toString()); - super.onSaveInstanceState(outState); - } - - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - Context context = getContext(); - View dview = LayoutInflater.from(context).inflate(R.layout.dialog_import, null); - tilPassword1 = dview.findViewById(R.id.tilPassword1); - CheckBox cbAccounts = dview.findViewById(R.id.cbAccounts); - CheckBox cbDelete = dview.findViewById(R.id.cbDelete); - CheckBox cbRules = dview.findViewById(R.id.cbRules); - CheckBox cbContacts = dview.findViewById(R.id.cbContacts); - CheckBox cbAnswers = dview.findViewById(R.id.cbAnswers); - CheckBox cbSearches = dview.findViewById(R.id.cbSearches); - CheckBox cbSettings = dview.findViewById(R.id.cbSettings); - - cbAccounts.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { - cbRules.setEnabled(checked); - cbContacts.setEnabled(checked); - } - }); - - if (savedInstanceState != null) - tilPassword1.getEditText().setText(savedInstanceState.getString("fair:password1")); - - Dialog dialog = new AlertDialog.Builder(context) - .setView(dview) - .setPositiveButton(R.string.title_add_image_select, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - String password1 = tilPassword1.getEditText().getText().toString(); - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - boolean debug = prefs.getBoolean("debug", false); - - if (TextUtils.isEmpty(password1) && !(debug || BuildConfig.DEBUG)) { - ToastEx.makeText(context, R.string.title_setup_password_missing, Toast.LENGTH_LONG).show(); - sendResult(RESULT_CANCELED); - } else { - ViewModelExport vme = new ViewModelProvider(getActivity()).get(ViewModelExport.class); - vme.setPassword(password1); - vme.setOptions("accounts", cbAccounts.isChecked()); - vme.setOptions("delete", cbDelete.isChecked()); - vme.setOptions("rules", cbRules.isChecked()); - vme.setOptions("contacts", cbContacts.isChecked()); - vme.setOptions("answers", cbAnswers.isChecked()); - vme.setOptions("searches", cbSearches.isChecked()); - vme.setOptions("settings", cbSettings.isChecked()); - sendResult(RESULT_OK); - } - } - }) - .setNegativeButton(android.R.string.cancel, null) - .create(); - - dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); - - return dialog; - } - - @Override - public void onStart() { - super.onStart(); - - Button btnOk = ((AlertDialog) getDialog()).getButton(AlertDialog.BUTTON_POSITIVE); - - tilPassword1.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() { - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (actionId == EditorInfo.IME_ACTION_DONE) { - btnOk.performClick(); - return true; - } else - return false; - } - }); - } - } } diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsBehavior.java b/app/src/main/java/eu/faircode/email/FragmentOptionsBehavior.java index 6890c4c0fe..0db3e858f9 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsBehavior.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsBehavior.java @@ -21,9 +21,6 @@ package eu.faircode.email; import static android.app.Activity.RESULT_OK; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; @@ -40,7 +37,6 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CompoundButton; import android.widget.EditText; @@ -48,18 +44,13 @@ import android.widget.ImageButton; import android.widget.SeekBar; import android.widget.Spinner; import android.widget.TextView; -import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.SwitchCompat; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.preference.PreferenceManager; -import java.util.ArrayList; -import java.util.List; - public class FragmentOptionsBehavior extends FragmentBase implements SharedPreferences.OnSharedPreferenceChangeListener { private View view; private ImageButton ibHelp; @@ -730,123 +721,4 @@ public class FragmentOptionsBehavior extends FragmentBase implements SharedPrefe else prefs.edit().putString("default_folder", uri.toString()).apply(); } - - public static class FragmentDialogSwipes extends FragmentDialogBase { - private Spinner spLeft; - private Spinner spRight; - private ArrayAdapter adapter; - - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_swipes, null); - spLeft = dview.findViewById(R.id.spLeft); - spRight = dview.findViewById(R.id.spRight); - - adapter = new ArrayAdapter<>(getContext(), R.layout.spinner_item1, android.R.id.text1, new ArrayList()); - adapter.setDropDownViewResource(R.layout.spinner_item1_dropdown); - - spLeft.setAdapter(adapter); - spRight.setAdapter(adapter); - - List folders = FragmentAccount.getFolderActions(getContext()); - - EntityFolder trash = new EntityFolder(); - trash.id = 2L; - trash.name = getString(R.string.title_trash); - folders.add(1, trash); - - EntityFolder archive = new EntityFolder(); - archive.id = 1L; - archive.name = getString(R.string.title_archive); - folders.add(1, archive); - - adapter.addAll(folders); - - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - int leftPos = prefs.getInt("swipe_left_default", 2); // Trash - int rightPos = prefs.getInt("swipe_right_default", 1); // Archive - - spLeft.setSelection(leftPos); - spRight.setSelection(rightPos); - - return new AlertDialog.Builder(getContext()) - .setView(dview) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - prefs.edit() - .putInt("swipe_left_default", spLeft.getSelectedItemPosition()) - .putInt("swipe_right_default", spRight.getSelectedItemPosition()) - .apply(); - - EntityFolder left = (EntityFolder) spLeft.getSelectedItem(); - EntityFolder right = (EntityFolder) spRight.getSelectedItem(); - - if ((left != null && EntityMessage.SWIPE_ACTION_HIDE.equals(left.id)) || - (right != null && EntityMessage.SWIPE_ACTION_HIDE.equals(right.id))) - prefs.edit() - .putBoolean("message_tools", true) - .putBoolean("button_hide", true) - .apply(); - - Bundle args = new Bundle(); - args.putLong("left", left == null ? 0 : left.id); - args.putLong("right", right == null ? 0 : right.id); - - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) { - long left = args.getLong("left"); - long right = args.getLong("right"); - - DB db = DB.getInstance(context); - try { - db.beginTransaction(); - - List accounts = db.account().getAccounts(); - for (EntityAccount account : accounts) - if (account.protocol == EntityAccount.TYPE_IMAP) - db.account().setAccountSwipes( - account.id, - getAction(context, left, account.id), - getAction(context, right, account.id)); - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - - return null; - } - - @Override - protected void onExecuted(Bundle args, Void data) { - ToastEx.makeText(getContext(), R.string.title_completed, Toast.LENGTH_LONG).show(); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(getParentFragmentManager(), ex); - } - - private Long getAction(Context context, long selection, long account) { - if (selection < 0) - return selection; - else if (selection == 0) - return null; - else { - DB db = DB.getInstance(context); - String type = (selection == 2 ? EntityFolder.TRASH : EntityFolder.ARCHIVE); - EntityFolder archive = db.folder().getFolderByType(account, type); - return (archive == null ? null : archive.id); - } - } - }.execute(getContext(), getViewLifecycleOwner(), args, "dialog:swipe"); - } - }) - .setNegativeButton(android.R.string.cancel, null) - .create(); - } - } } diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsPrivacy.java b/app/src/main/java/eu/faircode/email/FragmentOptionsPrivacy.java index 7b317c3bff..ff7f2c6a1d 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsPrivacy.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsPrivacy.java @@ -19,30 +19,24 @@ package eu.faircode.email; Copyright 2018-2023 by Marcel Bokhorst (M66B) */ -import android.app.Dialog; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.text.TextUtils; -import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.view.WindowManager; -import android.view.inputmethod.EditorInfo; import android.widget.AdapterView; import android.widget.Button; import android.widget.CompoundButton; -import android.widget.EditText; import android.widget.ImageButton; import android.widget.Spinner; import android.widget.TextView; @@ -50,10 +44,8 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.SwitchCompat; import androidx.constraintlayout.widget.Group; -import androidx.lifecycle.Lifecycle; import androidx.preference.PreferenceManager; import androidx.webkit.WebViewFeature; @@ -622,79 +614,4 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer swMnemonic.setChecked(mnemonic != null); tvMnemonic.setText(mnemonic); } - - public static class FragmentDialogPin extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - final View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_pin_set, null); - final EditText etPin = dview.findViewById(R.id.etPin); - - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()) - .setView(dview) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - String pin = etPin.getText().toString(); - if (TextUtils.isEmpty(pin)) - prefs.edit().remove("pin").apply(); - else { - boolean pro = ActivityBilling.isPro(getContext()); - if (pro) { - Helper.setAuthenticated(getContext()); - prefs.edit() - .remove("biometrics") - .putString("pin", pin) - .apply(); - } else - startActivity(new Intent(getContext(), ActivityBilling.class)); - } - } - }) - .setNegativeButton(android.R.string.cancel, null); - - String pin = prefs.getString("pin", null); - if (!TextUtils.isEmpty(pin)) - builder.setNeutralButton(R.string.title_reset, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - prefs.edit().remove("pin").apply(); - } - }); - - final Dialog dialog = builder.create(); - - etPin.setOnEditorActionListener(new TextView.OnEditorActionListener() { - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (actionId == EditorInfo.IME_ACTION_DONE) { - ((AlertDialog) getDialog()).getButton(DialogInterface.BUTTON_POSITIVE).performClick(); - return true; - } else - return false; - } - }); - - etPin.setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (hasFocus) - dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); - } - }); - - ApplicationEx.getMainHandler().post(new Runnable() { - @Override - public void run() { - if (!getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) - return; - etPin.requestFocus(); - } - }); - - return dialog; - } - } } diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsSynchronize.java b/app/src/main/java/eu/faircode/email/FragmentOptionsSynchronize.java index 4a76e02088..f9020f8bc7 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsSynchronize.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsSynchronize.java @@ -22,7 +22,6 @@ package eu.faircode.email; import android.app.Dialog; import android.app.TimePickerDialog; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Typeface; @@ -46,7 +45,6 @@ import android.widget.TimePicker; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.SwitchCompat; import androidx.constraintlayout.widget.Group; import androidx.fragment.app.DialogFragment; @@ -60,7 +58,6 @@ import androidx.recyclerview.widget.RecyclerView; import java.text.DateFormatSymbols; import java.util.ArrayList; -import java.util.Arrays; import java.util.Calendar; import java.util.List; import java.util.Objects; @@ -704,41 +701,6 @@ public class FragmentOptionsSynchronize extends FragmentBase implements SharedPr } } - public static class FragmentDialogWeekend extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - boolean[] days = new boolean[7]; - - final Context context = getContext(); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - - String[] daynames = Arrays.copyOfRange(new DateFormatSymbols().getWeekdays(), 1, 8); - - String weekend = prefs.getString("weekend", Calendar.SATURDAY + "," + Calendar.SUNDAY); - for (String day : weekend.split(",")) - days[Integer.parseInt(day) - 1] = true; - - return new AlertDialog.Builder(context) - .setTitle(R.string.title_advanced_schedule_weekend) - .setMultiChoiceItems(daynames, days, new DialogInterface.OnMultiChoiceClickListener() { - @Override - public void onClick(DialogInterface dialog, int which, boolean isChecked) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < days.length; i++) - if (days[i]) { - if (sb.length() > 0) - sb.append(","); - sb.append(i + 1); - } - prefs.edit().putString("weekend", sb.toString()).apply(); - } - }) - .setNegativeButton(R.string.title_setup_done, null) - .create(); - } - } - public static class AdapterAccountExempted extends RecyclerView.Adapter { private Context context; private LifecycleOwner owner; diff --git a/app/src/main/java/eu/faircode/email/FragmentRule.java b/app/src/main/java/eu/faircode/email/FragmentRule.java index 4c99de39b9..c58cd7c37f 100644 --- a/app/src/main/java/eu/faircode/email/FragmentRule.java +++ b/app/src/main/java/eu/faircode/email/FragmentRule.java @@ -58,14 +58,11 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.PopupMenu; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.Group; import androidx.fragment.app.DialogFragment; import androidx.lifecycle.Lifecycle; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.snackbar.Snackbar; @@ -201,8 +198,6 @@ public class FragmentRule extends FragmentBase { private DateFormat DF; - private final static int MAX_CHECK = 10; - private static final int REQUEST_SENDER = 1; private static final int REQUEST_RECIPIENT = 2; private static final int REQUEST_COLOR = 3; @@ -1380,7 +1375,7 @@ public class FragmentRule extends FragmentBase { args.putString("condition", jcondition.toString()); args.putString("action", jaction.toString()); - FragmentDialogCheck fragment = new FragmentDialogCheck(); + FragmentDialogRuleCheck fragment = new FragmentDialogRuleCheck(); fragment.setArguments(args); fragment.show(getParentFragmentManager(), "rule:check"); @@ -1731,177 +1726,4 @@ public class FragmentRule extends FragmentBase { sendResult(RESULT_OK); } } - - public static class FragmentDialogCheck extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - long folder = getArguments().getLong("folder"); - boolean daily = getArguments().getBoolean("daily"); - String condition = getArguments().getString("condition"); - String action = getArguments().getString("action"); - - final View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_rule_match, null); - final TextView tvNoMessages = dview.findViewById(R.id.tvNoMessages); - final RecyclerView rvMessage = dview.findViewById(R.id.rvMessage); - final Button btnExecute = dview.findViewById(R.id.btnExecute); - final ContentLoadingProgressBar pbWait = dview.findViewById(R.id.pbWait); - - rvMessage.setHasFixedSize(false); - LinearLayoutManager llm = new LinearLayoutManager(getContext()); - rvMessage.setLayoutManager(llm); - - final AdapterRuleMatch adapter = new AdapterRuleMatch(getContext(), getViewLifecycleOwner()); - rvMessage.setAdapter(adapter); - - tvNoMessages.setVisibility(View.GONE); - rvMessage.setVisibility(View.GONE); - btnExecute.setVisibility(View.GONE); - - final Bundle args = new Bundle(); - args.putLong("folder", folder); - args.putBoolean("daily", daily); - args.putString("condition", condition); - args.putString("action", action); - - btnExecute.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - new SimpleTask() { - private Toast toast = null; - - @Override - protected void onPreExecute(Bundle args) { - toast = ToastEx.makeText(getContext(), R.string.title_executing, Toast.LENGTH_LONG); - toast.show(); - } - - @Override - protected void onPostExecute(Bundle args) { - if (toast != null) - toast.cancel(); - } - - @Override - protected Integer onExecute(Context context, Bundle args) throws Throwable { - EntityRule rule = new EntityRule(); - rule.folder = args.getLong("folder"); - rule.daily = args.getBoolean("daily"); - rule.condition = args.getString("condition"); - rule.action = args.getString("action"); - - int applied = 0; - - DB db = DB.getInstance(context); - List ids = - db.message().getMessageIdsByFolder(rule.folder); - for (long mid : ids) - try { - db.beginTransaction(); - - EntityMessage message = db.message().getMessage(mid); - if (message == null || message.ui_hide) - continue; - - if (rule.matches(context, message, null, null)) - if (rule.execute(context, message)) - applied++; - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - - if (applied > 0) - ServiceSynchronize.eval(context, "rules/manual"); - - return applied; - } - - @Override - protected void onExecuted(Bundle args, Integer applied) { - dismiss(); - ToastEx.makeText(getContext(), getString(R.string.title_rule_applied, applied), Toast.LENGTH_LONG).show(); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - if (ex instanceof IllegalArgumentException) - ToastEx.makeText(getContext(), ex.getMessage(), Toast.LENGTH_LONG).show(); - else - Log.unexpectedError(getParentFragmentManager(), ex); - } - }.execute(FragmentDialogCheck.this, args, "rule:execute"); - } - }); - - new SimpleTask>() { - @Override - protected void onPreExecute(Bundle args) { - pbWait.setVisibility(View.VISIBLE); - } - - @Override - protected void onPostExecute(Bundle args) { - pbWait.setVisibility(View.GONE); - } - - @Override - protected List onExecute(Context context, Bundle args) throws Throwable { - EntityRule rule = new EntityRule(); - rule.folder = args.getLong("folder"); - rule.daily = args.getBoolean("daily"); - rule.condition = args.getString("condition"); - rule.action = args.getString("action"); - rule.validate(context); - - List matching = new ArrayList<>(); - - DB db = DB.getInstance(context); - List ids = - db.message().getMessageIdsByFolder(rule.folder); - for (long id : ids) { - EntityMessage message = db.message().getMessage(id); - if (message == null) - continue; - - if (rule.matches(context, message, null, null)) - matching.add(message); - - if (matching.size() >= MAX_CHECK) - break; - } - - return matching; - } - - @Override - protected void onExecuted(Bundle args, List messages) { - adapter.set(messages); - - if (messages.size() > 0) { - rvMessage.setVisibility(View.VISIBLE); - btnExecute.setVisibility(View.VISIBLE); - } else - tvNoMessages.setVisibility(View.VISIBLE); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - if (ex instanceof IllegalArgumentException) { - tvNoMessages.setText(ex.getMessage()); - tvNoMessages.setVisibility(View.VISIBLE); - } else - Log.unexpectedError(getParentFragmentManager(), ex); - } - }.execute(this, args, "rule:check"); - - return new AlertDialog.Builder(getContext()) - .setIcon(R.drawable.baseline_mail_outline_24) - .setTitle(R.string.title_rule_matched) - .setView(dview) - .setNegativeButton(android.R.string.cancel, null) - .create(); - } - } } diff --git a/app/src/main/java/eu/faircode/email/FragmentSetup.java b/app/src/main/java/eu/faircode/email/FragmentSetup.java index d3194b802b..18dc8696f7 100644 --- a/app/src/main/java/eu/faircode/email/FragmentSetup.java +++ b/app/src/main/java/eu/faircode/email/FragmentSetup.java @@ -24,7 +24,6 @@ import static android.app.Activity.RESULT_OK; import android.Manifest; import android.annotation.SuppressLint; import android.app.ActivityManager; -import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -1236,27 +1235,4 @@ public class FragmentSetup extends FragmentBase implements SharedPreferences.OnS tvNoInternet.setVisibility(available ? View.GONE : View.VISIBLE); } }; - - public static class FragmentDialogDoze extends FragmentDialogBase { - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - return new AlertDialog.Builder(getContext()) - .setIcon(R.drawable.twotone_info_24) - .setTitle(R.string.title_setup_doze) - .setMessage(R.string.title_setup_doze_instructions) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - try { - startActivity(new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)); - } catch (Throwable ex) { - Log.e(ex); - } - } - }) - .setNegativeButton(android.R.string.cancel, null) - .create(); - } - } }