From cb9bdb3cf72ec387e6f34d29a31e701f5f5e7d92 Mon Sep 17 00:00:00 2001 From: Alex Fong Date: Mon, 21 Mar 2016 21:37:19 +0800 Subject: [PATCH] Redesigned subkey creation dialog and changed default key type created to RSA, 3072 bit. Added code to prevent removal of master subkey when modifying a new key. --- .../keychain/ui/CreateKeyFinalFragment.java | 6 +- .../keychain/ui/EditKeyFragment.java | 10 +- .../ui/adapter/SubkeysAddedAdapter.java | 48 +- .../ui/dialog/AddSubkeyDialogFragment.java | 479 ++++++------------ .../util/spinner/FocusFirstItemSpinner.java | 77 +++ .../keychain/util/Choice.java | 9 +- .../res/drawable-hdpi/ic_change_grey_24dp.png | Bin 0 -> 923 bytes .../res/drawable-mdpi/ic_change_grey_24dp.png | Bin 0 -> 623 bytes .../drawable-xhdpi/ic_change_grey_24dp.png | Bin 0 -> 1066 bytes .../drawable-xxhdpi/ic_change_grey_24dp.png | Bin 0 -> 1612 bytes .../drawable-xxxhdpi/ic_change_grey_24dp.png | Bin 0 -> 2223 bytes .../src/main/res/layout/add_subkey_dialog.xml | 161 ++---- .../layout/two_line_spinner_dropdown_item.xml | 32 ++ OpenKeychain/src/main/res/values/strings.xml | 29 +- 14 files changed, 364 insertions(+), 487 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/spinner/FocusFirstItemSpinner.java create mode 100644 OpenKeychain/src/main/res/drawable-hdpi/ic_change_grey_24dp.png create mode 100644 OpenKeychain/src/main/res/drawable-mdpi/ic_change_grey_24dp.png create mode 100644 OpenKeychain/src/main/res/drawable-xhdpi/ic_change_grey_24dp.png create mode 100644 OpenKeychain/src/main/res/drawable-xxhdpi/ic_change_grey_24dp.png create mode 100644 OpenKeychain/src/main/res/drawable-xxxhdpi/ic_change_grey_24dp.png create mode 100644 OpenKeychain/src/main/res/layout/two_line_spinner_dropdown_item.xml diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java index b53bfc1d0..e1d20f2e1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -281,11 +281,11 @@ public class CreateKeyFinalFragment extends Fragment { saveKeyringParcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase(), null); } else { saveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA, - 4096, null, KeyFlags.CERTIFY_OTHER, 0L)); + 3072, null, KeyFlags.CERTIFY_OTHER, 0L)); saveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA, - 4096, null, KeyFlags.SIGN_DATA, 0L)); + 3072, null, KeyFlags.SIGN_DATA, 0L)); saveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Algorithm.RSA, - 4096, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L)); + 3072, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L)); saveKeyringParcel.mNewUnlock = createKeyActivity.mPassphrase != null ? new ChangeUnlockParcel(createKeyActivity.mPassphrase, null) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java index 2d94d0d93..1571a7104 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -562,15 +562,9 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment> choices = new ArrayList<>(); - choices.add(new Choice<>(Algorithm.DSA, getResources().getString( - R.string.dsa))); - if (!mWillBeMasterKey) { - choices.add(new Choice<>(Algorithm.ELGAMAL, getResources().getString( - R.string.elgamal))); - } - choices.add(new Choice<>(Algorithm.RSA, getResources().getString( - R.string.rsa))); - choices.add(new Choice<>(Algorithm.ECDSA, getResources().getString( - R.string.ecdsa))); - choices.add(new Choice<>(Algorithm.ECDH, getResources().getString( - R.string.ecdh))); - ArrayAdapter> adapter = new ArrayAdapter<>(context, + ArrayList> choices = new ArrayList<>(); + choices.add(new Choice<>(SupportedKeyType.RSA_2048, getResources().getString( + R.string.rsa_2048), getResources().getString(R.string.rsa_2048_description_html))); + choices.add(new Choice<>(SupportedKeyType.RSA_3072, getResources().getString( + R.string.rsa_3072), getResources().getString(R.string.rsa_3072_description_html))); + choices.add(new Choice<>(SupportedKeyType.RSA_4096, getResources().getString( + R.string.rsa_4096), getResources().getString(R.string.rsa_4096_description_html))); + choices.add(new Choice<>(SupportedKeyType.ECC_P256, getResources().getString( + R.string.ecc_p256), getResources().getString(R.string.ecc_p256_description_html))); + choices.add(new Choice<>(SupportedKeyType.ECC_P521, getResources().getString( + R.string.ecc_p521), getResources().getString(R.string.ecc_p521_description_html))); + TwoLineArrayAdapter adapter = new TwoLineArrayAdapter(context, android.R.layout.simple_spinner_item, choices); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - mAlgorithmSpinner.setAdapter(adapter); - // make RSA the default + mKeyTypeSpinner.setAdapter(adapter); + // make RSA 3072 the default for (int i = 0; i < choices.size(); ++i) { - if (choices.get(i).getId() == Algorithm.RSA) { - mAlgorithmSpinner.setSelection(i); - break; - } - } - } - - // dynamic ArrayAdapter must be created (instead of ArrayAdapter.getFromResource), because it's content may change - ArrayAdapter keySizeAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, - new ArrayList(Arrays.asList(getResources().getStringArray(R.array.rsa_key_size_spinner_values)))); - keySizeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - mKeySizeSpinner.setAdapter(keySizeAdapter); - mKeySizeSpinner.setSelection(1); // Default to 4096 for the key length - - { - ArrayList> choices = new ArrayList<>(); - - choices.add(new Choice<>(Curve.NIST_P256, getResources().getString( - R.string.key_curve_nist_p256))); - choices.add(new Choice<>(Curve.NIST_P384, getResources().getString( - R.string.key_curve_nist_p384))); - choices.add(new Choice<>(Curve.NIST_P521, getResources().getString( - R.string.key_curve_nist_p521))); - - /* @see SaveKeyringParcel - choices.add(new Choice(Curve.BRAINPOOL_P256, getResources().getString( - R.string.key_curve_bp_p256))); - choices.add(new Choice(Curve.BRAINPOOL_P384, getResources().getString( - R.string.key_curve_bp_p384))); - choices.add(new Choice(Curve.BRAINPOOL_P512, getResources().getString( - R.string.key_curve_bp_p512))); - */ - - ArrayAdapter> adapter = new ArrayAdapter<>(context, - android.R.layout.simple_spinner_item, choices); - mCurveSpinner.setAdapter(adapter); - // make NIST P-256 the default - for (int i = 0; i < choices.size(); ++i) { - if (choices.get(i).getId() == Curve.NIST_P256) { - mCurveSpinner.setSelection(i); + if (choices.get(i).getId() == SupportedKeyType.RSA_3072) { + mKeyTypeSpinner.setSelection(i); break; } } @@ -215,45 +177,35 @@ public class AddSubkeyDialogFragment extends DialogFragment { final AlertDialog alertDialog = dialog.show(); - mCustomKeyEditText.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } + mKeyTypeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable s) { - setOkButtonAvailability(alertDialog); - } - }); - - mKeySizeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { - setCustomKeyVisibility(); - setOkButtonAvailability(alertDialog); + // noinspection unchecked + SupportedKeyType keyType = ((Choice) parent.getSelectedItem()).getId(); + + // RadioGroup.getCheckedRadioButtonId() gives the wrong RadioButton checked + // when programmatically unchecking children radio buttons. Clearing all is the only option. + mUsageRadioGroup.clearCheck(); + + if(mWillBeMasterKey) { + mUsageNone.setChecked(true); + } + + if (keyType == SupportedKeyType.ECC_P521 || keyType == SupportedKeyType.ECC_P256) { + mUsageSignAndEncrypt.setEnabled(false); + if (mWillBeMasterKey) { + mUsageEncrypt.setEnabled(false); + } + } else { + // need to enable if previously disabled for ECC masterkey + mUsageEncrypt.setEnabled(true); + mUsageSignAndEncrypt.setEnabled(true); + } } @Override - public void onNothingSelected(AdapterView parent) { - } - }); - - mAlgorithmSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - updateUiForAlgorithm(((Choice) parent.getSelectedItem()).getId()); - - setCustomKeyVisibility(); - setOkButtonAvailability(alertDialog); - } - - @Override - public void onNothingSelected(AdapterView parent) { - } + public void onNothingSelected(AdapterView parent) {} }); return alertDialog; @@ -269,36 +221,74 @@ public class AddSubkeyDialogFragment extends DialogFragment { positiveButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - if (!mFlagCertify.isChecked() && !mFlagSign.isChecked() - && !mFlagEncrypt.isChecked() && !mFlagAuthenticate.isChecked()) { - Toast.makeText(getActivity(), R.string.edit_key_select_flag, Toast.LENGTH_LONG).show(); + if (mUsageRadioGroup.getCheckedRadioButtonId() == -1) { + Toast.makeText(getActivity(), R.string.edit_key_select_usage, Toast.LENGTH_LONG).show(); return; } - Algorithm algorithm = ((Choice) mAlgorithmSpinner.getSelectedItem()).getId(); + // noinspection unchecked + SupportedKeyType keyType = ((Choice) mKeyTypeSpinner.getSelectedItem()).getId(); Curve curve = null; Integer keySize = null; - // For EC keys, add a curve - if (algorithm == Algorithm.ECDH || algorithm == Algorithm.ECDSA) { - curve = ((Choice) mCurveSpinner.getSelectedItem()).getId(); - // Otherwise, get a keysize - } else { - keySize = getProperKeyLength(algorithm, getSelectedKeyLength()); + Algorithm algorithm = null; + + // set keysize & curve, for RSA & ECC respectively + switch (keyType) { + case RSA_2048: { + keySize = 2048; + break; + } + case RSA_3072: { + keySize = 3072; + break; + } + case RSA_4096: { + keySize = 4096; + break; + } + case ECC_P256: { + curve = Curve.NIST_P256; + break; + } + case ECC_P521: { + curve = Curve.NIST_P521; + break; + } } + // set algorithm + switch (keyType) { + case RSA_2048: + case RSA_3072: + case RSA_4096: { + algorithm = Algorithm.RSA; + break; + } + + case ECC_P256: + case ECC_P521: { + if(mUsageEncrypt.isChecked()) { + algorithm = Algorithm.ECDH; + } else { + algorithm = Algorithm.ECDSA; + } + break; + } + } + + // set flags int flags = 0; - if (mFlagCertify.isChecked()) { + if (mWillBeMasterKey) { flags |= KeyFlags.CERTIFY_OTHER; } - if (mFlagSign.isChecked()) { + if (mUsageSign.isChecked()) { flags |= KeyFlags.SIGN_DATA; - } - if (mFlagEncrypt.isChecked()) { + } else if (mUsageEncrypt.isChecked()) { flags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE; + } else if (mUsageSignAndEncrypt.isChecked()) { + flags |= KeyFlags.SIGN_DATA | KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE; } - if (mFlagAuthenticate.isChecked()) { - flags |= KeyFlags.AUTHENTICATION; - } + long expiry; if (mNoExpiryCheckBox.isChecked()) { @@ -332,206 +322,29 @@ public class AddSubkeyDialogFragment extends DialogFragment { } } - private int getSelectedKeyLength() { - final String selectedItemString = (String) mKeySizeSpinner.getSelectedItem(); - final String customLengthString = getResources().getString(R.string.key_size_custom); - final boolean customSelected = customLengthString.equals(selectedItemString); - String keyLengthString = customSelected ? mCustomKeyEditText.getText().toString() : selectedItemString; - int keySize; - try { - keySize = Integer.parseInt(keyLengthString); - } catch (NumberFormatException e) { - keySize = 0; + private class TwoLineArrayAdapter extends ArrayAdapter> { + public TwoLineArrayAdapter(Context context, int resource, List> objects) { + super(context, resource, objects); } - return keySize; - } - /** - *

RSA

- *

for RSA algorithm, key length must be greater than 2048. Possibility to generate keys bigger - * than 8192 bits is currently disabled, because it's almost impossible to generate them on a mobile device (check - * RSA key length plot and - * Cryptographic Key Length Recommendation). Also, key length must be a - * multiplicity of 8.

- *

ElGamal

- *

For ElGamal algorithm, supported key lengths are 2048, 3072, 4096 or 8192 bits.

- *

DSA

- *

For DSA algorithm key length must be between 2048 and 3072. Also, it must me dividable by 64.

- * - * @return correct key length, according to BouncyCastle specification. Returns -1, if key length is - * inappropriate. - */ - private int getProperKeyLength(Algorithm algorithm, int currentKeyLength) { - final int[] elGamalSupportedLengths = {2048, 3072, 4096, 8192}; - int properKeyLength = -1; - switch (algorithm) { - case RSA: { - if (currentKeyLength >= 2048 && currentKeyLength <= 16384) { - properKeyLength = currentKeyLength + ((8 - (currentKeyLength % 8)) % 8); - } - break; + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + // inflate view if not given one + if (convertView == null) { + convertView = getActivity().getLayoutInflater() + .inflate(R.layout.two_line_spinner_dropdown_item, parent, false); } - case ELGAMAL: { - int[] elGammalKeyDiff = new int[elGamalSupportedLengths.length]; - for (int i = 0; i < elGamalSupportedLengths.length; i++) { - elGammalKeyDiff[i] = Math.abs(elGamalSupportedLengths[i] - currentKeyLength); - } - int minimalValue = Integer.MAX_VALUE; - int minimalIndex = -1; - for (int i = 0; i < elGammalKeyDiff.length; i++) { - if (elGammalKeyDiff[i] <= minimalValue) { - minimalValue = elGammalKeyDiff[i]; - minimalIndex = i; - } - } - properKeyLength = elGamalSupportedLengths[minimalIndex]; - break; - } - case DSA: { - // Bouncy Castle supports 4096 maximum - if (currentKeyLength >= 2048 && currentKeyLength <= 4096) { - properKeyLength = currentKeyLength + ((64 - (currentKeyLength % 64)) % 64); - } - break; - } - } - return properKeyLength; - } - private void setOkButtonAvailability(AlertDialog alertDialog) { - Algorithm algorithm = ((Choice) mAlgorithmSpinner.getSelectedItem()).getId(); - boolean enabled = algorithm == Algorithm.ECDSA || algorithm == Algorithm.ECDH - || getProperKeyLength(algorithm, getSelectedKeyLength()) > 0; - alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(enabled); - } + Choice c = this.getItem(position); - private void setCustomKeyVisibility() { - final String selectedItemString = (String) mKeySizeSpinner.getSelectedItem(); - final String customLengthString = getResources().getString(R.string.key_size_custom); - final boolean customSelected = customLengthString.equals(selectedItemString); - final int visibility = customSelected ? View.VISIBLE : View.GONE; + TextView text1 = (TextView) convertView.findViewById(android.R.id.text1); + TextView text2 = (TextView) convertView.findViewById(android.R.id.text2); - mCustomKeyEditText.setVisibility(visibility); - mCustomKeyTextView.setVisibility(visibility); - mCustomKeyInfoTextView.setVisibility(visibility); + text1.setText(c.getName()); + text2.setText(Html.fromHtml(c.getDescription())); - // hide keyboard after setting visibility to gone - if (visibility == View.GONE) { - InputMethodManager imm = (InputMethodManager) - getActivity().getSystemService(FragmentActivity.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(mCustomKeyEditText.getWindowToken(), 0); - } - } - - private void updateUiForAlgorithm(Algorithm algorithm) { - final ArrayAdapter keySizeAdapter = (ArrayAdapter) mKeySizeSpinner.getAdapter(); - keySizeAdapter.clear(); - switch (algorithm) { - case RSA: { - replaceArrayAdapterContent(keySizeAdapter, R.array.rsa_key_size_spinner_values); - mKeySizeSpinner.setSelection(1); - mKeySizeRow.setVisibility(View.VISIBLE); - mCurveRow.setVisibility(View.GONE); - mCustomKeyInfoTextView.setText(getResources().getString(R.string.key_size_custom_info_rsa)); - // allowed flags: - mFlagSign.setEnabled(true); - mFlagEncrypt.setEnabled(true); - mFlagAuthenticate.setEnabled(true); - - if (mWillBeMasterKey) { - mFlagCertify.setEnabled(true); - - mFlagCertify.setChecked(true); - mFlagSign.setChecked(false); - mFlagEncrypt.setChecked(false); - } else { - mFlagCertify.setEnabled(false); - - mFlagCertify.setChecked(false); - mFlagSign.setChecked(true); - mFlagEncrypt.setChecked(true); - } - mFlagAuthenticate.setChecked(false); - break; - } - case ELGAMAL: { - replaceArrayAdapterContent(keySizeAdapter, R.array.elgamal_key_size_spinner_values); - mKeySizeSpinner.setSelection(3); - mKeySizeRow.setVisibility(View.VISIBLE); - mCurveRow.setVisibility(View.GONE); - mCustomKeyInfoTextView.setText(""); // ElGamal does not support custom key length - // allowed flags: - mFlagCertify.setChecked(false); - mFlagCertify.setEnabled(false); - mFlagSign.setChecked(false); - mFlagSign.setEnabled(false); - mFlagEncrypt.setChecked(true); - mFlagEncrypt.setEnabled(true); - mFlagAuthenticate.setChecked(false); - mFlagAuthenticate.setEnabled(false); - break; - } - case DSA: { - replaceArrayAdapterContent(keySizeAdapter, R.array.dsa_key_size_spinner_values); - mKeySizeSpinner.setSelection(2); - mKeySizeRow.setVisibility(View.VISIBLE); - mCurveRow.setVisibility(View.GONE); - mCustomKeyInfoTextView.setText(getResources().getString(R.string.key_size_custom_info_dsa)); - // allowed flags: - mFlagCertify.setChecked(false); - mFlagCertify.setEnabled(false); - mFlagSign.setChecked(true); - mFlagSign.setEnabled(true); - mFlagEncrypt.setChecked(false); - mFlagEncrypt.setEnabled(false); - mFlagAuthenticate.setChecked(false); - mFlagAuthenticate.setEnabled(false); - break; - } - case ECDSA: { - mKeySizeRow.setVisibility(View.GONE); - mCurveRow.setVisibility(View.VISIBLE); - mCustomKeyInfoTextView.setText(""); - // allowed flags: - mFlagCertify.setEnabled(mWillBeMasterKey); - mFlagCertify.setChecked(mWillBeMasterKey); - mFlagSign.setEnabled(true); - mFlagSign.setChecked(!mWillBeMasterKey); - mFlagEncrypt.setEnabled(false); - mFlagEncrypt.setChecked(false); - mFlagAuthenticate.setEnabled(true); - mFlagAuthenticate.setChecked(false); - break; - } - case ECDH: { - mKeySizeRow.setVisibility(View.GONE); - mCurveRow.setVisibility(View.VISIBLE); - mCustomKeyInfoTextView.setText(""); - // allowed flags: - mFlagCertify.setChecked(false); - mFlagCertify.setEnabled(false); - mFlagSign.setChecked(false); - mFlagSign.setEnabled(false); - mFlagEncrypt.setChecked(true); - mFlagEncrypt.setEnabled(true); - mFlagAuthenticate.setChecked(false); - mFlagAuthenticate.setEnabled(false); - break; - } - } - keySizeAdapter.notifyDataSetChanged(); - - } - - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - private void replaceArrayAdapterContent(ArrayAdapter arrayAdapter, int stringArrayResourceId) { - final String[] spinnerValuesStringArray = getResources().getStringArray(stringArrayResourceId); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - arrayAdapter.addAll(spinnerValuesStringArray); - } else { - for (final String value : spinnerValuesStringArray) { - arrayAdapter.add(value); - } + return convertView; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/spinner/FocusFirstItemSpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/spinner/FocusFirstItemSpinner.java new file mode 100644 index 000000000..7919a0918 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/spinner/FocusFirstItemSpinner.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 Alex Fong Jie Wen + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package org.sufficientlysecure.keychain.ui.util.spinner; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.Spinner; + +/** + * Custom spinner which uses a hack to + * always set focus on first item in list + * + */ +public class FocusFirstItemSpinner extends Spinner { + /** + * Spinner is originally designed to set focus on the currently selected item. + * When Spinner is selected to show dropdown, 'performClick()' is called internally. + * 'getSelectedItemPosition()' is then called to obtain the item to focus on. + * We use a toggle to have 'getSelectedItemPosition()' return the 0th index + * for this particular case. + */ + + private boolean mToggleFlag = true; + + public FocusFirstItemSpinner(Context context, AttributeSet attrs, + int defStyle, int mode) { + super(context, attrs, defStyle, mode); + } + + public FocusFirstItemSpinner(Context context, AttributeSet attrs, + int defStyle) { + super(context, attrs, defStyle); + } + + public FocusFirstItemSpinner(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public FocusFirstItemSpinner(Context context, int mode) { + super(context, mode); + } + + public FocusFirstItemSpinner(Context context) { + super(context); + } + + @Override + public int getSelectedItemPosition() { + if (!mToggleFlag) { + return 0; + } + return super.getSelectedItemPosition(); + } + + @Override + public boolean performClick() { + mToggleFlag = false; + boolean result = super.performClick(); + mToggleFlag = true; + return result; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java index 48f10d4b9..5ffce9f24 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java @@ -18,12 +18,15 @@ package org.sufficientlysecure.keychain.util; public class Choice { + private String mName; private E mId; + private String mDescription; - public Choice(E id, String name) { + public Choice(E id, String name, String description) { mId = id; mName = name; + mDescription = description; } public E getId() { @@ -34,6 +37,10 @@ public class Choice { return mName; } + public String getDescription() { + return mDescription; + } + @Override public String toString() { return mName; diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_change_grey_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_change_grey_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..f625ba4252c2818d8ee325280e325ef2bb56994d GIT binary patch literal 923 zcmV;M17!S(P)BE-6--(D_L?0G~dfE zZ|2Q>-t6!+g4g;#YGq~Ri*dzL9RbU5r6vuIMc6Ro6 z&-0RF2O6R>Pk^6+uiNeR_ghsi$wu0bgmm?d=a?~JOHMExeoIQ;G``+X{wifeSO^%kt=!NzVAmfGcz9m>*>--rBs;Z+p~u%mCCuv$;s(34FBwx z?4YOW^?I$JU#(V4qA0py+x9e26p=HQWqlPz(Qa;qT-QCNwf@BS{o8q`Kd96oegHub zTn2stl!z3%?U_JHl5E6r{KKG0hLjNzKYd@w1zKHQeFwNCBF$q00=ShvALatZaeN+7 zN~yWLs7FLsPrMWxhArY(d1 z7LWkv9LM=`&?JLSjjrpyneC`3~?K@Ru=W zS47-S<58jd&O?nxABUqp(BPAYOCucxw56l^3*19TmWtbn`k$|$)+0;jc xfie7ZJ#@kmV8#wKD$H0_tyW(Rkk|UJ^$+|{EuK=6-}wLl002ovPDHLkV1mq&sUiRX literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_change_grey_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_change_grey_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..6cf9d044a267cef9009952e45a5f50887b214e2f GIT binary patch literal 623 zcmV-#0+9WQP)Vsrz-izTus$3P_Z-K0qLf-as@|9d zh~qe_R4M{k11J`Y`N3fD0C);~2F?H%fE%W1?j=d`VdjYH^}5k+x6@J2ahxXL20<`Z z<9S}L*X!NVT898>nx1d9S}!w4q?9^aDwT4Reh>t=g%BH)zUO&;-}montxce37{;B< zaoDzfPAPR|*4VP-x~_5@=j*69)`1XW-7t(7%L0IHKxYDgwSK?9@)v-gKr{^?a=F~K zWz*9%t!S+`rvaX8tv7AkzL*t~^&T}EjXdx%48zx>{@6V*P4m4F;-(Pdx@B2EYPH&{ zD2m38t7Tb{X__QSvODUR%VoW_we@8ZeCQt7wtW?NB!pN2J^|ZO%4b^ZH-$psTesUi z0MJ@{VHh4dwle_$N~u*T<$d7twBmUnM?MvuPG>(!l84o5bw_Kx5Bvg70VjZye>PCc z-8hb;g*%ug$8j`(5MsmkeQ(zHbN_J0RyAk%QgU7Qc!K{~{RVlUbs{>KW!Zai9ACDXVB``KLX5HrHD-QU z2=Swo@-xr#4&|MokP?zoDrPR?N2^6dXN@s0Mp2ZM2G9?}b=}>~W^-?~TAcxKJ2M{z za6f?K0QPS;7c*a~R4OMMjmCOu0KFiVmX@|%Ln$SaBzX|P)6Dz~5#6#S_g@rp@*WIs_Y7LWb0|5YG7+%`k+?)mQWhX{N zPe>^rum%W%;IQL3k2e~PU-Ou;g;-fxX$m1u0$A%X7-LRZ0eGIb<1py6#(fjdUPv z#)V;c-54_mzyJUdov;EBLcGDupAK6(MJkTtH6r@3{r1k>0G{U^1@NTfIG>kozDsWp zO6UndM9%`CG);5WevwMk^t<-U-)sO*001-ZniK#L{oVHPMQ#AJ>#kC%+&3wJYPEXc zU;q5B4M1lXo|_baG3H?aAH{Ke(FUMXcTd?o=8melxj6^GW5$^0`{nfn=&CzM2=RUp z1bb|Z*q#m5YPHish=n+guUP>IAu;F(MY*4ktN{Q(N_iJEUjVSLJI>615z#pSAGogjl~T$K0vHFxPz_DJ zUaz%Ut@A{5Z;zBK04@OdmWVC__#;iz=FH5@Mv^2qwk8(|F?2`f`@SoLc%7L~0T|fW z3{p`Lxm!xl^A0(V6EX86{|AWN=R$EDuW79xHOAaSM6Uu^w_bd-GbI4UH1#B-Ih{zKDCMVc=iplI`@0*u3aX#nFg kUy7uZJJa}7Q|%D_13h<`LyC^TdjJ3c07*qoM6N<$f=YkWGynhq literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_change_grey_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_change_grey_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..672a9c96c929baf2c722836150cde036bb7cf0cc GIT binary patch literal 1612 zcmV-S2DABzP)%5Z>?0Yx1S5F(&jfCGdCs8-+rApoi+I6%OFY7GtmP*5E?gmW1{8NeR^ zMwxk(h|UQi&Xh`}Q@LF3ubT>x$9E0`I0xW3GauG8?YNY(RI`8r_*q>dqW$G^d2cS4 zJLh%dzSkfjgx?q8c4pozgg9#$#+SOTw|m)mAV0BmIxU$w$OqBSlv2I9T<+&;8;S*y z?a@?qdV70sjm2W^MDz@R)c_XGX_Dv^ zGp{sF^Y_RDnhVlgfGQ=W6oo?J2_eLb%)AD(4?1Ur5G!`>+<7JPfM$c_1W={6Y}xW) zEEbah*4@Y#Gh3RbJ?>=Q6x!2#CXGG4W}YuBzHP19UYL>)x*+f3&WQOEfBxT8L+ z1w^{8-wT&_qG_6aM6}&2NC1$@WWFsHi}wTAdxKLprBbOj-vLP}wRAfDoTh1`n4JE* zH6*=;l+Wj{nx^?OfXy@H78gRi?JFR|FcwTqOuRKVHg-Ttd97MQen0{M(=^{DqE}~t zv@(@S%{ltH0@Tyf(+uE{5aOME`}P&RGUFGdOeV93i1O2Y$6~Q{o&#FDc5N&kj~`*? z4+jPYMtm?My>1f#5{blC0H-UDG4o@d18Qw;l|*!prfDDeVZ<*o0U*xIUxjVft&~y+oW|G!G*$6e0RS`q z5VldbV&)SSUoYB;u?1+V;tK${aN)w`u#LJ^Lqo$z#aCMw+7_V7ioa;lqUNxTy4Csf z=f|#pCZezH#MrGt|5p4`sbp({6-8BCTvaZYKeZEM7f@xzUlF!Zw<;EkOQ${_vHLpK z&7;PKu#LJE5j_c@q?CHyZj4<(l@d&FD8@8 z`Ivbgw7gg>KHJ&Z`Tfw)(67~iNhXuB0#a8;M+Yp+I^m`*%d*C*$pvsY4{B^|+{Vn- z4gTE5%$8vo2Moh_pxVSLAbDDo9QW@ups%m5Nh$RWfTgpXrikc}QtEw8(~e0gmEHCY z!0uOVl)?BMBDvZXbcR{bfr?MJL2*9 zGNsf~W^N;*762^(?#5(2ye&#_0taMAa}D!B!T}=i4|PryG#sFY0EGbus1ZP6!2xOj zP?&Ilhyw~64iI5LQGf$P6i`&)L{jN=T85oNU3Jw}S6y}89Qp?cM^6XdIC75w0000< KMNUMnLSTY$WZ|L! literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_change_grey_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_change_grey_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..5be10100198b3765a79c9b440989304b7d076b0d GIT binary patch literal 2223 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 literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml b/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml index 4b5058a81..b232ed423 100644 --- a/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml +++ b/OpenKeychain/src/main/res/layout/add_subkey_dialog.xml @@ -13,147 +13,68 @@ android:paddingRight="24dp" android:stretchColumns="1"> - - - + android:paddingRight="10dp" + android:text="@string/label_key_type" /> - + - - - - - - - - - - - - - - - - - - - - + android:layout_marginTop="8dp" + android:layout_marginBottom="8dp"> + - + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 70db4029b..4ac0037ac 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -13,6 +13,7 @@ "Encrypt" "Decrypt" "Add subkey" + Change master key "Edit Key" "Create a Linked Identity" "Settings" @@ -165,6 +166,7 @@ "Select OpenPGP keyservers" "Key ID" "Key created %s" + "Type" "Creation" "Expiry" "Usage" @@ -281,23 +283,26 @@ "8 hours" "forever" "Select a Key" - "DSA" - "ElGamal" - "RSA" - "ECDH" - "ECDSA" "Open…" + "RSA 2048" + "smaller filesize, considered secure until 2030" + "RSA 3072" + "recommended, considered secure until 2040" + "RSA 4096" + "larger file size, considered secure until 2040+" + "ECC P-256" + "very tiny filesize, considered secure until 2040 <br/> <u>experimental and not supported by all implementations</u>" + "ECC P-521" + "tiny filesize, considered secure until 2040+ <br/> <u>experimental and not supported by all implementations"</u> + "None (subkey binding only)" + "Sign" + "Encrypt" + "Sign & Encrypt" "Error" "Error: %s" "Dark" "Light" - - "Certify" - "Sign" - "Encrypt" - "Authenticate" - "Wrong password." "No compatible file manager installed." @@ -746,7 +751,7 @@ "Move Subkey to Security Token" "new subkey" - "Please select at least one flag!" + "Please select key usage!" "Add at least one identity!" "Add at least one subkey!" "Algorithm not supported by Security Token!"