diff --git a/app/src/main/java/eu/faircode/email/ActivitySetup.java b/app/src/main/java/eu/faircode/email/ActivitySetup.java index ef6423ea20..c1c841261f 100644 --- a/app/src/main/java/eu/faircode/email/ActivitySetup.java +++ b/app/src/main/java/eu/faircode/email/ActivitySetup.java @@ -74,12 +74,16 @@ import com.microsoft.identity.client.IPublicClientApplication; import com.microsoft.identity.client.PublicClientApplication; import com.microsoft.identity.client.exception.MsalException; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemReader; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedInputStream; import java.io.BufferedReader; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -88,9 +92,13 @@ import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; import java.security.spec.KeySpec; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -106,6 +114,7 @@ import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; +import javax.security.auth.x500.X500Principal; public class ActivitySetup extends ActivityBase implements FragmentManager.OnBackStackChangedListener { private View view; @@ -127,6 +136,7 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac static final int REQUEST_IMPORT_OAUTH = 5; static final int REQUEST_CHOOSE_ACCOUNT = 6; static final int REQUEST_DONE = 7; + static final int REQUEST_IMPORT_CERTIFICATE = 7; static final String ACTION_QUICK_GMAIL = BuildConfig.APPLICATION_ID + ".ACTION_QUICK_GMAIL"; static final String ACTION_QUICK_OUTLOOK = BuildConfig.APPLICATION_ID + ".ACTION_QUICK_OUTLOOK"; @@ -135,7 +145,9 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac static final String ACTION_VIEW_IDENTITIES = BuildConfig.APPLICATION_ID + ".ACTION_VIEW_IDENTITIES"; static final String ACTION_EDIT_ACCOUNT = BuildConfig.APPLICATION_ID + ".EDIT_ACCOUNT"; static final String ACTION_EDIT_IDENTITY = BuildConfig.APPLICATION_ID + ".EDIT_IDENTITY"; - static final String ACTION_MANAGE_LOCAL_CONTACTS = BuildConfig.APPLICATION_ID + ".LOCAL_CONTACTS"; + static final String ACTION_MANAGE_LOCAL_CONTACTS = BuildConfig.APPLICATION_ID + ".MANAGE_LOCAL_CONTACTS"; + static final String ACTION_MANAGE_CERTIFICATES = BuildConfig.APPLICATION_ID + ".MANAGE_CERTIFICATES"; + static final String ACTION_IMPORT_CERTIFICATE = BuildConfig.APPLICATION_ID + ".IMPORT_CERTIFICATE"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -303,6 +315,8 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac iff.addAction(ACTION_EDIT_ACCOUNT); iff.addAction(ACTION_EDIT_IDENTITY); iff.addAction(ACTION_MANAGE_LOCAL_CONTACTS); + iff.addAction(ACTION_MANAGE_CERTIFICATES); + iff.addAction(ACTION_IMPORT_CERTIFICATE); lbm.registerReceiver(receiver, iff); } @@ -366,6 +380,10 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac case REQUEST_IMPORT_OAUTH: ServiceSynchronize.reload(this, "oauth"); break; + case REQUEST_IMPORT_CERTIFICATE: + if (resultCode == RESULT_OK && data != null) + handleImportCertificate(data); + break; } } catch (Throwable ex) { Log.e(ex); @@ -989,6 +1007,63 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac }.execute(this, args, "setup:import"); } + private void handleImportCertificate(Intent data) { + Uri uri = data.getData(); + if (uri != null) { + Bundle args = new Bundle(); + args.putParcelable("uri", uri); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) throws Throwable { + Uri uri = args.getParcelable("uri"); + + PemObject pem; + try (InputStream is = context.getContentResolver().openInputStream(uri)) { + pem = new PemReader(new InputStreamReader(is)).readPemObject(); + } + + ByteArrayInputStream bis = new ByteArrayInputStream(pem.getContent()); + CertificateFactory fact = CertificateFactory.getInstance("X.509"); + X509Certificate cert = (X509Certificate) fact.generateCertificate(bis); + + String email = "?"; + try { + Collection> altNames = cert.getSubjectAlternativeNames(); + if (altNames != null) + for (List altName : altNames) + if (altName.get(0).equals(GeneralName.rfc822Name)) + email = (String) altName.get(1); + else + Log.i("Alt type=" + altName.get(0) + " data=" + altName.get(1)); + } catch (CertificateParsingException ex) { + Log.w(ex); + } + + String fingerprint = Helper.sha256(cert.getEncoded()); + + DB db = DB.getInstance(context); + EntityCertificate record = db.certificate().getCertificate(fingerprint, email); + if (record == null) { + record = new EntityCertificate(); + record.fingerprint = Helper.sha256(cert.getEncoded()); + record.email = email; + record.subject = cert.getSubjectX500Principal().getName(X500Principal.RFC2253); + record.setEncoded(cert.getEncoded()); + record.id = db.certificate().insertCertificate(record); + } + + return null; + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(getSupportFragmentManager(), ex); + } + }.execute(this, args, "setup:cert"); + } + } + @RequiresApi(api = Build.VERSION_CODES.O) private JSONObject channelToJSON(NotificationChannel channel) throws JSONException { JSONObject jchannel = new JSONObject(); @@ -1302,6 +1377,23 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac fragmentTransaction.commit(); } + private void onManageCertificates(Intent intent) { + FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); + fragmentTransaction.replace(R.id.content_frame, new FragmentCertificates()).addToBackStack("certificates"); + fragmentTransaction.commit(); + } + + private void onImportCertificate(Intent intent) { + Intent open = new Intent(Intent.ACTION_OPEN_DOCUMENT); + open.addCategory(Intent.CATEGORY_OPENABLE); + open.setType("*/*"); + Helper.openAdvanced(open); + if (open.resolveActivity(getPackageManager()) == null) + ToastEx.makeText(this, R.string.title_no_saf, Toast.LENGTH_LONG).show(); + else + startActivityForResult(Helper.getChooser(this, open), REQUEST_IMPORT_CERTIFICATE); + } + private static Intent getIntentExport() { Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); @@ -1397,6 +1489,10 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac onEditIdentity(intent); else if (ACTION_MANAGE_LOCAL_CONTACTS.equals(action)) onManageLocalContacts(intent); + else if (ACTION_MANAGE_CERTIFICATES.equals(action)) + onManageCertificates(intent); + else if (ACTION_IMPORT_CERTIFICATE.equals(action)) + onImportCertificate(intent); } } }; diff --git a/app/src/main/java/eu/faircode/email/AdapterCertificate.java b/app/src/main/java/eu/faircode/email/AdapterCertificate.java index 8304d41efb..03365b5320 100644 --- a/app/src/main/java/eu/faircode/email/AdapterCertificate.java +++ b/app/src/main/java/eu/faircode/email/AdapterCertificate.java @@ -23,11 +23,14 @@ import android.content.Context; import android.graphics.Typeface; import android.os.Bundle; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.appcompat.widget.PopupMenu; import androidx.fragment.app.Fragment; import androidx.lifecycle.LifecycleOwner; import androidx.recyclerview.widget.DiffUtil; @@ -52,6 +55,8 @@ public class AdapterCertificate extends RecyclerView.Adapter() { + popupMenu.getMenu().add(Menu.NONE, 0, 0, certificate.email).setEnabled(false); + + popupMenu.getMenu().add(Menu.NONE, R.string.title_delete, 1, R.string.title_delete); + + popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override - protected Void onExecute(Context context, Bundle args) throws Throwable { - long id = args.getLong("id"); + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.string.title_delete: + onActionDelete(); + return true; - DB db = DB.getInstance(context); - db.certificate().deleteCertificate(id); - - return null; + default: + return false; + } } - @Override - protected void onException(Bundle args, Throwable ex) { - // TODO: report error + private void onActionDelete() { + Bundle args = new Bundle(); + args.putLong("id", certificate.id); + + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) throws Throwable { + long id = args.getLong("id"); + + DB db = DB.getInstance(context); + db.certificate().deleteCertificate(id); + + return null; + } + + @Override + protected void onException(Bundle args, Throwable ex) { + // TODO: report error + } + }.execute(context, owner, args, "certificate:delete"); } - }.execute(context, owner, args, "certificate:delete"); + }); + + popupMenu.show(); return true; } private void wire() { - view.setOnClickListener(this); + if (intf != null) + view.setOnClickListener(this); view.setOnLongClickListener(this); } private void unwire() { - view.setOnClickListener(null); + if (intf != null) + view.setOnClickListener(null); + view.setOnLongClickListener(null); } private void bindTo(EntityCertificate certificate) { @@ -221,6 +253,11 @@ public class AdapterCertificate extends RecyclerView.Adapter. + + Copyright 2018-2019 by Marcel Bokhorst (M66B) +*/ + +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.constraintlayout.widget.Group; +import androidx.lifecycle.Observer; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; + +import java.util.ArrayList; +import java.util.List; + +public class FragmentCertificates extends FragmentBase { + private RecyclerView rvCertificate; + private ContentLoadingProgressBar pbWait; + private Group grpReady; + private FloatingActionButton fab; + + private AdapterCertificate adapter; + + @Override + @Nullable + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + setSubtitle(R.string.title_advanced_manage_certificates); + setHasOptionsMenu(true); + + View view = inflater.inflate(R.layout.fragment_certificates, container, false); + + // Get controls + rvCertificate = view.findViewById(R.id.rvCertificate); + pbWait = view.findViewById(R.id.pbWait); + grpReady = view.findViewById(R.id.grpReady); + fab = view.findViewById(R.id.fab); + + // Wire controls + + rvCertificate.setHasFixedSize(false); + LinearLayoutManager llm = new LinearLayoutManager(getContext()); + rvCertificate.setLayoutManager(llm); + + DividerItemDecoration itemDecorator = new DividerItemDecoration(getContext(), llm.getOrientation()); + itemDecorator.setDrawable(getContext().getDrawable(R.drawable.divider)); + rvCertificate.addItemDecoration(itemDecorator); + + adapter = new AdapterCertificate(this, null); + rvCertificate.setAdapter(adapter); + + fab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext()); + lbm.sendBroadcast(new Intent(ActivitySetup.ACTION_IMPORT_CERTIFICATE)); + } + }); + + // Initialize + grpReady.setVisibility(View.GONE); + pbWait.setVisibility(View.VISIBLE); + + return view; + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + DB db = DB.getInstance(getContext()); + db.certificate().liveCertificates(null).observe(getViewLifecycleOwner(), new Observer>() { + @Override + public void onChanged(List certificates) { + if (certificates == null) + certificates = new ArrayList<>(); + + adapter.set(null, certificates); + + pbWait.setVisibility(View.GONE); + grpReady.setVisibility(View.VISIBLE); + } + }); + } +} diff --git a/app/src/main/java/eu/faircode/email/FragmentCompose.java b/app/src/main/java/eu/faircode/email/FragmentCompose.java index 430244ebb0..6a91de7b4b 100644 --- a/app/src/main/java/eu/faircode/email/FragmentCompose.java +++ b/app/src/main/java/eu/faircode/email/FragmentCompose.java @@ -71,7 +71,6 @@ import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.ArrayAdapter; -import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.EditText; @@ -94,6 +93,7 @@ import androidx.documentfile.provider.DocumentFile; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Observer; import androidx.preference.PreferenceManager; +import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -120,8 +120,6 @@ import org.bouncycastle.operator.OutputEncryptor; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.bouncycastle.util.Store; -import org.bouncycastle.util.io.pem.PemObject; -import org.bouncycastle.util.io.pem.PemReader; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.nodes.TextNode; @@ -138,7 +136,6 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.OutputStream; import java.net.UnknownHostException; import java.security.PrivateKey; @@ -173,7 +170,6 @@ import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import javax.mail.internet.ParseException; import javax.mail.util.ByteArrayDataSource; -import javax.security.auth.x500.X500Principal; import static android.app.Activity.RESULT_CANCELED; import static android.app.Activity.RESULT_OK; @@ -966,10 +962,10 @@ public class FragmentCompose extends FragmentBase { int colorEncrypt = Helper.resolveColor(getContext(), R.attr.colorEncrypt); ImageButton ib = (ImageButton) menu.findItem(R.id.menu_encrypt).getActionView(); ib.setEnabled(!busy); - if (EntityMessage.PGP_SIGNONLY.equals(encrypt)) { + if (EntityMessage.PGP_SIGNONLY.equals(encrypt) || EntityMessage.SMIME_SIGNONLY.equals(encrypt)) { ib.setImageResource(R.drawable.baseline_gesture_24); ib.setImageTintList(null); - } else if (EntityMessage.PGP_SIGNENCRYPT.equals(encrypt)) { + } else if (EntityMessage.PGP_SIGNENCRYPT.equals(encrypt) || EntityMessage.SMIME_SIGNENCRYPT.equals(encrypt)) { ib.setImageResource(R.drawable.baseline_lock_24); ib.setImageTintList(ColorStateList.valueOf(colorEncrypt)); } else { @@ -1037,12 +1033,25 @@ public class FragmentCompose extends FragmentBase { } private void onMenuEncrypt() { - if (EntityMessage.PGP_SIGNENCRYPT.equals(encrypt)) - encrypt = EntityMessage.PGP_SIGNONLY; - else if (EntityMessage.PGP_SIGNONLY.equals(encrypt)) - encrypt = EntityMessage.ENCRYPT_NONE; - else - encrypt = EntityMessage.PGP_SIGNENCRYPT; + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + String encrypt_method = prefs.getString("default_encrypt_method", "pgp"); + + if ("pgp".equals(encrypt_method)) { + if (EntityMessage.ENCRYPT_NONE.equals(encrypt) || encrypt == null) + encrypt = EntityMessage.PGP_SIGNENCRYPT; + else if (EntityMessage.PGP_SIGNENCRYPT.equals(encrypt)) + encrypt = EntityMessage.PGP_SIGNONLY; + else + encrypt = EntityMessage.ENCRYPT_NONE; + } else { + if (EntityMessage.ENCRYPT_NONE.equals(encrypt) || encrypt == null) + encrypt = EntityMessage.SMIME_SIGNENCRYPT; + else if (EntityMessage.SMIME_SIGNENCRYPT.equals(encrypt)) + encrypt = EntityMessage.SMIME_SIGNONLY; + else + encrypt = EntityMessage.ENCRYPT_NONE; + } + getActivity().invalidateOptionsMenu(); Bundle args = new Bundle(); @@ -2481,6 +2490,7 @@ public class FragmentCompose extends FragmentBase { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); boolean plain_only = prefs.getBoolean("plain_only", false); + String encrypt_method = prefs.getString("default_encrypt_method", "pgp"); boolean sign_default = prefs.getBoolean("sign_default", false); boolean encrypt_default = prefs.getBoolean("encrypt_default", false); boolean receipt_default = prefs.getBoolean("receipt_default", false); @@ -2517,9 +2527,15 @@ public class FragmentCompose extends FragmentBase { if (plain_only) data.draft.plain_only = true; if (encrypt_default) - data.draft.encrypt = EntityMessage.PGP_SIGNENCRYPT; + if ("s/mime".equals(encrypt_method)) + data.draft.encrypt = EntityMessage.SMIME_SIGNENCRYPT; + else + data.draft.encrypt = EntityMessage.PGP_SIGNENCRYPT; else if (sign_default) - data.draft.encrypt = EntityMessage.PGP_SIGNONLY; + if ("s/mime".equals(encrypt_method)) + data.draft.encrypt = EntityMessage.SMIME_SIGNONLY; + else + data.draft.encrypt = EntityMessage.PGP_SIGNONLY; if (receipt_default) data.draft.receipt_request = true; @@ -3957,7 +3973,6 @@ public class FragmentCompose extends FragmentBase { View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_certificate, null); final RecyclerView rvCertificate = dview.findViewById(R.id.rvCertificate); - final Button btnImport = dview.findViewById(R.id.btnImport); final ProgressBar pbWait = dview.findViewById(R.id.pbWait); final Dialog dialog = new AlertDialog.Builder(getContext()) @@ -3969,6 +3984,12 @@ public class FragmentCompose extends FragmentBase { LinearLayoutManager llm = new LinearLayoutManager(getContext()); rvCertificate.setLayoutManager(llm); + DividerItemDecoration itemDecorator = new DividerItemDecoration(getContext(), llm.getOrientation()); + Drawable divider = getContext().getDrawable(R.drawable.divider); + divider.mutate().setTint(getContext().getResources().getColor(R.color.lightColorSeparator)); + itemDecorator.setDrawable(divider); + rvCertificate.addItemDecoration(itemDecorator); + final AdapterCertificate adapter = new AdapterCertificate(this, new AdapterCertificate.ICertificate() { @Override public void onSelected(EntityCertificate certificate) { @@ -3979,22 +4000,6 @@ public class FragmentCompose extends FragmentBase { }); rvCertificate.setAdapter(adapter); - btnImport.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("*/*"); - Helper.openAdvanced(intent); - PackageManager pm = getContext().getPackageManager(); - if (intent.resolveActivity(pm) == null) - ToastEx.makeText(getContext(), R.string.title_no_saf, Toast.LENGTH_LONG).show(); - else - startActivityForResult(Helper.getChooser(getContext(), intent), 1); - } - }); - btnImport.setEnabled(email != null); - rvCertificate.setVisibility(View.GONE); pbWait.setVisibility(View.VISIBLE); @@ -4010,54 +4015,6 @@ public class FragmentCompose extends FragmentBase { return dialog; } - - @Override - public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { - if (resultCode == RESULT_OK && data != null) { - Uri uri = data.getData(); - if (uri != null) { - Bundle args = new Bundle(); - args.putParcelable("uri", uri); - - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) throws Throwable { - Uri uri = args.getParcelable("uri"); - - PemObject pem; - try (InputStream is = context.getContentResolver().openInputStream(uri)) { - pem = new PemReader(new InputStreamReader(is)).readPemObject(); - } - - ByteArrayInputStream bis = new ByteArrayInputStream(pem.getContent()); - CertificateFactory fact = CertificateFactory.getInstance("X.509"); - X509Certificate cert = (X509Certificate) fact.generateCertificate(bis); - - String fingerprint = Helper.sha256(cert.getEncoded()); - - DB db = DB.getInstance(context); - EntityCertificate record = db.certificate().getCertificate(fingerprint, email); - if (record == null) { - record = new EntityCertificate(); - record.fingerprint = Helper.sha256(cert.getEncoded()); - record.email = email; - record.subject = cert.getSubjectX500Principal().getName(X500Principal.RFC2253); - record.setEncoded(cert.getEncoded()); - record.id = db.certificate().insertCertificate(record); - } - // TODO: report exists - - return null; - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(getParentFragmentManager(), ex); - } - }.execute(this, args, "compose:cert"); - } - } - } } public static class FragmentDialogSend extends FragmentDialogBase { diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsPrivacy.java b/app/src/main/java/eu/faircode/email/FragmentOptionsPrivacy.java index ea7604fd55..861b618246 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsPrivacy.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsPrivacy.java @@ -51,6 +51,7 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.SwitchCompat; import androidx.lifecycle.Lifecycle; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.preference.PreferenceManager; import org.openintents.openpgp.util.OpenPgpApi; @@ -62,6 +63,7 @@ import java.util.List; public class FragmentOptionsPrivacy extends FragmentBase implements SharedPreferences.OnSharedPreferenceChangeListener { private SwitchCompat swDisableTracking; private SwitchCompat swDisplayHidden; + private Spinner spEncryptMethod; private Spinner spOpenPgp; private SwitchCompat swSign; private SwitchCompat swEncrypt; @@ -70,6 +72,7 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer private Button btnBiometrics; private Button btnPin; private Spinner spBiometricsTimeout; + private Button btnManageCertificates; private Button btnImportKey; private TextView tvKeySize; @@ -77,7 +80,7 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer private final static String[] RESET_OPTIONS = new String[]{ "disable_tracking", "display_hidden", - "openpgp_provider", "sign_default", "encrypt_default", "auto_decrypt", + "default_encrypt_method", "openpgp_provider", "sign_default", "encrypt_default", "auto_decrypt", "secure", "biometrics", "pin", "biometrics_timeout" }; @@ -94,6 +97,7 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer swDisableTracking = view.findViewById(R.id.swDisableTracking); swDisplayHidden = view.findViewById(R.id.swDisplayHidden); + spEncryptMethod = view.findViewById(R.id.spEncryptMethod); spOpenPgp = view.findViewById(R.id.spOpenPgp); swSign = view.findViewById(R.id.swSign); swEncrypt = view.findViewById(R.id.swEncrypt); @@ -102,6 +106,7 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer btnBiometrics = view.findViewById(R.id.btnBiometrics); btnPin = view.findViewById(R.id.btnPin); spBiometricsTimeout = view.findViewById(R.id.spBiometricsTimeout); + btnManageCertificates = view.findViewById(R.id.btnManageCertificates); btnImportKey = view.findViewById(R.id.btnImportKey); tvKeySize = view.findViewById(R.id.tvKeySize); @@ -136,6 +141,21 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer } }); + spEncryptMethod.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (position == 1) + prefs.edit().putString("default_encrypt_method", "s/mime").apply(); + else + onNothingSelected(parent); + } + + @Override + public void onNothingSelected(AdapterView parent) { + prefs.edit().remove("default_encrypt_method").apply(); + } + }); + spOpenPgp.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView adapterView, View view, int position, long id) { @@ -225,6 +245,14 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer } }); + btnManageCertificates.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext()); + lbm.sendBroadcast(new Intent(ActivitySetup.ACTION_MANAGE_CERTIFICATES)); + } + }); + final Intent importKey = KeyChain.createInstallIntent(); btnImportKey.setEnabled(importKey.resolveActivity(getContext().getPackageManager()) != null); btnImportKey.setOnClickListener(new View.OnClickListener() { @@ -236,7 +264,7 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer try { int maxKeySize = javax.crypto.Cipher.getMaxAllowedKeyLength("AES"); - tvKeySize.setText(getString(R.string.title_aes_key_size, maxKeySize)); + tvKeySize.setText(getString(R.string.title_advanced_aes_key_size, maxKeySize)); } catch (NoSuchAlgorithmException ex) { tvKeySize.setText(Helper.formatThrowable(ex)); } @@ -290,6 +318,10 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer swDisableTracking.setChecked(prefs.getBoolean("disable_tracking", true)); swDisplayHidden.setChecked(prefs.getBoolean("display_hidden", false)); + String encrypt_method = prefs.getString("default_encrypt_method", "pgp"); + if ("s/mime".equals(encrypt_method)) + spEncryptMethod.setSelection(1); + String provider = prefs.getString("openpgp_provider", "org.sufficientlysecure.keychain"); for (int pos = 0; pos < openPgpProvider.size(); pos++) if (provider.equals(openPgpProvider.get(pos))) { diff --git a/app/src/main/res/layout/dialog_certificate.xml b/app/src/main/res/layout/dialog_certificate.xml index 6deab15eb5..ff77d6a8e4 100644 --- a/app/src/main/res/layout/dialog_certificate.xml +++ b/app/src/main/res/layout/dialog_certificate.xml @@ -2,31 +2,19 @@ + android:layout_height="wrap_content"> -