diff options
Diffstat (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java')
-rw-r--r-- | OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java | 286 |
1 files changed, 185 insertions, 101 deletions
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 647ab2728..716140655 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -19,14 +19,15 @@ package org.sufficientlysecure.keychain.ui; import java.util.ArrayList; import java.util.GregorianCalendar; +import java.util.List; import java.util.Vector; +import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.ActionBarHelper; import org.sufficientlysecure.keychain.helper.ExportHelper; import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; @@ -35,15 +36,19 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; +import org.sufficientlysecure.keychain.ui.widget.Editor; import org.sufficientlysecure.keychain.ui.widget.KeyEditor; import org.sufficientlysecure.keychain.ui.widget.SectionView; import org.sufficientlysecure.keychain.ui.widget.UserIdEditor; +import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; +import android.app.AlertDialog; import android.app.Activity; import android.app.ProgressDialog; import android.content.Context; @@ -55,6 +60,7 @@ import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.support.v7.app.ActionBarActivity; +import android.support.v4.app.ActivityCompat; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -69,7 +75,7 @@ import android.widget.Toast; import com.beardedhen.androidbootstrap.BootstrapButton; -public class EditKeyActivity extends ActionBarActivity { +public class EditKeyActivity extends ActionBarActivity implements EditorListener { // Actions for internal use only: public static final String ACTION_CREATE_KEY = Constants.INTENT_PREFIX + "CREATE_KEY"; @@ -96,6 +102,9 @@ public class EditKeyActivity extends ActionBarActivity { private String mNewPassPhrase = null; private String mSavedNewPassPhrase = null; private boolean mIsPassPhraseSet; + private boolean mNeedsSaving; + private boolean mIsBrandNewKeyring = false; + private MenuItem mSaveButton; private BootstrapButton mChangePassphrase; @@ -108,12 +117,42 @@ public class EditKeyActivity extends ActionBarActivity { ExportHelper mExportHelper; + public boolean needsSaving() + { + mNeedsSaving = (mUserIdsView == null) ? false : mUserIdsView.needsSaving(); + mNeedsSaving |= (mKeysView == null) ? false : mKeysView.needsSaving(); + mNeedsSaving |= hasPassphraseChanged(); + mNeedsSaving |= mIsBrandNewKeyring; + return mNeedsSaving; + } + + + public void somethingChanged() + { + ActivityCompat.invalidateOptionsMenu(this); + //Toast.makeText(this, "Needs saving: " + Boolean.toString(mNeedsSaving) + "(" + Boolean.toString(mUserIdsView.needsSaving()) + ", " + Boolean.toString(mKeysView.needsSaving()) + ")", Toast.LENGTH_LONG).show(); + } + + public void onDeleted(Editor e, boolean wasNewItem) + { + somethingChanged(); + } + + public void onEdited() + { + somethingChanged(); + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mExportHelper = new ExportHelper(this); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setIcon(android.R.color.transparent); + getSupportActionBar().setHomeButtonEnabled(true); + mUserIds = new Vector<String>(); mKeys = new Vector<PGPSecretKey>(); mKeysUsages = new Vector<Integer>(); @@ -134,23 +173,10 @@ public class EditKeyActivity extends ActionBarActivity { * @param intent */ private void handleActionCreateKey(Intent intent) { - // Inflate a "Done"/"Cancel" custom action bar - ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_save, - new View.OnClickListener() { - @Override - public void onClick(View v) { - saveClicked(); - } - }, R.string.btn_do_not_save, new View.OnClickListener() { - @Override - public void onClick(View v) { - cancelClicked(); - } - }); - Bundle extras = intent.getExtras(); mCurrentPassphrase = ""; + mIsBrandNewKeyring = true; if (extras != null) { // if userId is given, prefill the fields @@ -207,24 +233,26 @@ public class EditKeyActivity extends ActionBarActivity { if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { // get new key from data bundle returned from service Bundle data = message.getData(); - PGPSecretKey masterKey = (PGPSecretKey) PgpConversionHelper + PGPSecretKey masterKey = PgpConversionHelper .BytesToPGPSecretKey(data .getByteArray(KeychainIntentService.RESULT_NEW_KEY)); - PGPSecretKey subKey = (PGPSecretKey) PgpConversionHelper + PGPSecretKey subKey = PgpConversionHelper .BytesToPGPSecretKey(data .getByteArray(KeychainIntentService.RESULT_NEW_KEY2)); + //We must set the key flags here as they are not set when we make the + //key pair. Because we are not generating hashed packets there... // add master key mKeys.add(masterKey); - mKeysUsages.add(Id.choice.usage.sign_only); //TODO: get from key flags + mKeysUsages.add(KeyFlags.CERTIFY_OTHER); // add sub key mKeys.add(subKey); - mKeysUsages.add(Id.choice.usage.encrypt_only); //TODO: get from key flags + mKeysUsages.add(KeyFlags.ENCRYPT_COMMS + KeyFlags.ENCRYPT_STORAGE); - buildLayout(); + buildLayout(true); } - }; + } }; // Create a new Messenger for the communication back @@ -238,7 +266,7 @@ public class EditKeyActivity extends ActionBarActivity { } } } else { - buildLayout(); + buildLayout(false); } } @@ -248,20 +276,10 @@ public class EditKeyActivity extends ActionBarActivity { * @param intent */ private void handleActionEditKey(Intent intent) { - // Inflate a "Done"/"Cancel" custom action bar - ActionBarHelper.setDoneView(getSupportActionBar(), R.string.btn_save, - new View.OnClickListener() { - @Override - public void onClick(View v) { - saveClicked(); - } - }); - mDataUri = intent.getData(); if (mDataUri == null) { Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!"); finish(); - return; } else { Log.d(Constants.TAG, "uri: " + mDataUri); @@ -270,20 +288,19 @@ public class EditKeyActivity extends ActionBarActivity { // get master key id using row id long masterKeyId = ProviderHelper.getSecretMasterKeyId(this, keyRingRowId); - masterCanSign = ProviderHelper.getSecretMasterKeyCanSign(this, keyRingRowId); - finallyEdit(masterKeyId, masterCanSign); + masterCanSign = ProviderHelper.getSecretMasterKeyCanCertify(this, keyRingRowId); + finallyEdit(masterKeyId); } } - private void showPassphraseDialog(final long masterKeyId, final boolean masterCanSign) { + private void showPassphraseDialog(final long masterKeyId) { // Message is received after passphrase is cached Handler returnHandler = new Handler() { @Override public void handleMessage(Message message) { if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { - String passphrase = PassphraseCacheService.getCachedPassphrase( + mCurrentPassphrase = PassphraseCacheService.getCachedPassphrase( EditKeyActivity.this, masterKeyId); - mCurrentPassphrase = passphrase; finallySaveClicked(); } } @@ -305,31 +322,37 @@ public class EditKeyActivity extends ActionBarActivity { } @Override - public boolean onPrepareOptionsMenu(Menu menu) { - // show menu only on edit - if (mDataUri != null) { - return super.onPrepareOptionsMenu(menu); - } else { - return false; - } - } - - @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.key_edit, menu); + mSaveButton = menu.findItem(R.id.menu_key_edit_save); + mSaveButton.setEnabled(needsSaving()); + //totally get rid of some actions for new keys + if (mDataUri == null) { + MenuItem mButton = menu.findItem(R.id.menu_key_edit_export_file); + mButton.setVisible(false); + mButton = menu.findItem(R.id.menu_key_edit_delete); + mButton.setVisible(false); + } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { + case android.R.id.home: + cancelClicked(); //TODO: why isn't this triggered on my tablet - one of many ui problems I've had with this device. A code compatibility issue or a Samsung fail? + return true; case R.id.menu_key_edit_cancel: cancelClicked(); return true; case R.id.menu_key_edit_export_file: - mExportHelper.showExportKeysDialog(mDataUri, Id.type.secret_key, Constants.path.APP_DIR - + "/secexport.asc"); + if (needsSaving()) { + Toast.makeText(this, R.string.error_save_first, Toast.LENGTH_LONG).show(); + } else { + mExportHelper.showExportKeysDialog(mDataUri, Id.type.secret_key, Constants.path.APP_DIR + + "/secexport.asc"); + } return true; case R.id.menu_key_edit_delete: { // Message is received after key is deleted @@ -346,12 +369,15 @@ public class EditKeyActivity extends ActionBarActivity { mExportHelper.deleteKey(mDataUri, Id.type.secret_key, returnHandler); return true; } + case R.id.menu_key_edit_save: + saveClicked(); + return true; } return super.onOptionsItemSelected(item); } @SuppressWarnings("unchecked") - private void finallyEdit(final long masterKeyId, final boolean masterCanSign) { + private void finallyEdit(final long masterKeyId) { if (masterKeyId != 0) { PGPSecretKey masterKey = null; mKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, masterKeyId); @@ -366,8 +392,15 @@ public class EditKeyActivity extends ActionBarActivity { Toast.makeText(this, R.string.error_no_secret_key_found, Toast.LENGTH_LONG).show(); } if (masterKey != null) { + boolean isSet = false; for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) { Log.d(Constants.TAG, "Added userId " + userId); + if (!isSet) { + isSet = true; + String[] parts = PgpKeyHelper.splitUserId(userId); + if (parts[0] != null) + setTitle(parts[0]); + } mUserIds.add(userId); } } @@ -375,7 +408,7 @@ public class EditKeyActivity extends ActionBarActivity { mCurrentPassphrase = ""; - buildLayout(); + buildLayout(false); mIsPassPhraseSet = PassphraseCacheService.hasPassphrase(this, masterKeyId); if (!mIsPassPhraseSet) { // check "no passphrase" checkbox and remove button @@ -400,6 +433,7 @@ public class EditKeyActivity extends ActionBarActivity { .getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE); updatePassPhraseButtonText(); + somethingChanged(); } } }; @@ -408,7 +442,7 @@ public class EditKeyActivity extends ActionBarActivity { Messenger messenger = new Messenger(returnHandler); // set title based on isPassphraseSet() - int title = -1; + int title; if (isPassphraseSet()) { title = R.string.title_change_passphrase; } else { @@ -424,8 +458,9 @@ public class EditKeyActivity extends ActionBarActivity { /** * Build layout based on mUserId, mKeys and mKeysUsages Vectors. It creates Views for every user * id and key. + * @param newKeys */ - private void buildLayout() { + private void buildLayout(boolean newKeys) { setContentView(R.layout.edit_key_activity); // find views @@ -440,11 +475,13 @@ public class EditKeyActivity extends ActionBarActivity { mUserIdsView.setType(Id.type.user_id); mUserIdsView.setCanEdit(masterCanSign); mUserIdsView.setUserIds(mUserIds); + mUserIdsView.setEditorListener(this); container.addView(mUserIdsView); mKeysView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); mKeysView.setType(Id.type.key); mKeysView.setCanEdit(masterCanSign); - mKeysView.setKeys(mKeys, mKeysUsages); + mKeysView.setKeys(mKeys, mKeysUsages, newKeys); + mKeysView.setEditorListener(this); container.addView(mKeysView); updatePassPhraseButtonText(); @@ -455,7 +492,7 @@ public class EditKeyActivity extends ActionBarActivity { } }); - // disable passphrase when no passphrase checkobox is checked! + // disable passphrase when no passphrase checkbox is checked! mNoPassphrase.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override @@ -469,6 +506,7 @@ public class EditKeyActivity extends ActionBarActivity { mNewPassPhrase = mSavedNewPassPhrase; mChangePassphrase.setVisibility(View.VISIBLE); } + somethingChanged(); } }); } @@ -491,30 +529,54 @@ public class EditKeyActivity extends ActionBarActivity { } } + public boolean hasPassphraseChanged() + { + if (mNoPassphrase != null) { + if (mNoPassphrase.isChecked()) { + return mIsPassPhraseSet; + } else { + return (mNewPassPhrase != null && !mNewPassPhrase.equals("")); + } + }else { + return false; + } + } + private void saveClicked() { long masterKeyId = getMasterKeyId(); - try { - if (!isPassphraseSet()) { - throw new PgpGeneralException(this.getString(R.string.set_a_passphrase)); - } + if (needsSaving()) { //make sure, as some versions don't support invalidateOptionsMenu + try { + if (!isPassphraseSet()) { + throw new PgpGeneralException(this.getString(R.string.set_a_passphrase)); + } - String passphrase = null; - if (mIsPassPhraseSet) - passphrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId); - else - passphrase = ""; - if (passphrase == null) { - showPassphraseDialog(masterKeyId, masterCanSign); - } else { - mCurrentPassphrase = passphrase; - finallySaveClicked(); + String passphrase; + if (mIsPassPhraseSet) + passphrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId); + else + passphrase = ""; + if (passphrase == null) { + showPassphraseDialog(masterKeyId); + } else { + mCurrentPassphrase = passphrase; + finallySaveClicked(); + } + } catch (PgpGeneralException e) { + Toast.makeText(this, getString(R.string.error_message, e.getMessage()), + Toast.LENGTH_SHORT).show(); } - } catch (PgpGeneralException e) { - //Toast.makeText(this, getString(R.string.error_message, e.getMessage()), - // Toast.LENGTH_SHORT).show(); } } + private boolean[] toPrimitiveArray(final List<Boolean> booleanList) { + final boolean[] primitives = new boolean[booleanList.size()]; + int index = 0; + for (Boolean object : booleanList) { + primitives[index++] = object; + } + return primitives; + } + private void finallySaveClicked() { try { // Send all information needed to service to edit key in other thread @@ -522,22 +584,26 @@ public class EditKeyActivity extends ActionBarActivity { intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING); + SaveKeyringParcel saveParams = new SaveKeyringParcel(); + saveParams.userIDs = getUserIds(mUserIdsView); + saveParams.originalIDs = mUserIdsView.getOriginalIDs(); + saveParams.deletedIDs = mUserIdsView.getDeletedIDs(); + saveParams.primaryIDChanged = mUserIdsView.primaryChanged(); + saveParams.moddedKeys = toPrimitiveArray(mKeysView.getNeedsSavingArray()); + saveParams.deletedKeys = mKeysView.getDeletedKeys(); + saveParams.keysExpiryDates = getKeysExpiryDates(mKeysView); + saveParams.keysUsages = getKeysUsages(mKeysView); + saveParams.newPassPhrase = mNewPassPhrase; + saveParams.oldPassPhrase = mCurrentPassphrase; + saveParams.newKeys = toPrimitiveArray(mKeysView.getNewKeysArray()); + saveParams.keys = getKeys(mKeysView); + + // fill values for this action Bundle data = new Bundle(); - data.putString(KeychainIntentService.SAVE_KEYRING_CURRENT_PASSPHRASE, - mCurrentPassphrase); - data.putString(KeychainIntentService.SAVE_KEYRING_NEW_PASSPHRASE, mNewPassPhrase); - data.putStringArrayList(KeychainIntentService.SAVE_KEYRING_USER_IDS, - getUserIds(mUserIdsView)); - ArrayList<PGPSecretKey> keys = getKeys(mKeysView); - data.putByteArray(KeychainIntentService.SAVE_KEYRING_KEYS, - PgpConversionHelper.PGPSecretKeyArrayListToBytes(keys)); - data.putIntegerArrayList(KeychainIntentService.SAVE_KEYRING_KEYS_USAGES, - getKeysUsages(mKeysView)); - data.putSerializable(KeychainIntentService.SAVE_KEYRING_KEYS_EXPIRY_DATES, - getKeysExpiryDates(mKeysView)); - data.putLong(KeychainIntentService.SAVE_KEYRING_MASTER_KEY_ID, getMasterKeyId()); + data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, masterCanSign); + data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, saveParams); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); @@ -561,7 +627,7 @@ public class EditKeyActivity extends ActionBarActivity { setResult(RESULT_OK, data); finish(); } - }; + } }; // Create a new Messenger for the communication back @@ -573,14 +639,41 @@ public class EditKeyActivity extends ActionBarActivity { // start service with intent startService(intent); } catch (PgpGeneralException e) { - //Toast.makeText(this, getString(R.string.error_message, e.getMessage()), - // Toast.LENGTH_SHORT).show(); + Toast.makeText(this, getString(R.string.error_message, e.getMessage()), + Toast.LENGTH_SHORT).show(); } } private void cancelClicked() { - setResult(RESULT_CANCELED); - finish(); + if (needsSaving()) { //ask if we want to save + AlertDialog.Builder alert = new AlertDialog.Builder( + EditKeyActivity.this); + + alert.setIcon(android.R.drawable.ic_dialog_alert); + alert.setTitle(R.string.warning); + alert.setMessage(EditKeyActivity.this.getString(R.string.ask_save_changed_key)); + + alert.setPositiveButton(EditKeyActivity.this.getString(android.R.string.yes), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + saveClicked(); + } + }); + alert.setNegativeButton(this.getString(android.R.string.no), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + setResult(RESULT_CANCELED); + finish(); + } + }); + alert.setCancelable(false); + alert.create().show(); + } else { + setResult(RESULT_CANCELED); + finish(); + } } /** @@ -597,22 +690,13 @@ public class EditKeyActivity extends ActionBarActivity { boolean gotMainUserId = false; for (int i = 0; i < userIdEditors.getChildCount(); ++i) { UserIdEditor editor = (UserIdEditor) userIdEditors.getChildAt(i); - String userId = null; + String userId; try { userId = editor.getValue(); - } catch (UserIdEditor.NoNameException e) { - throw new PgpGeneralException(this.getString(R.string.error_user_id_needs_a_name)); - } catch (UserIdEditor.NoEmailException e) { - throw new PgpGeneralException( - this.getString(R.string.error_user_id_needs_an_email_address)); } catch (UserIdEditor.InvalidEmailException e) { throw new PgpGeneralException(e.getMessage()); } - if (userId.equals("")) { - continue; - } - if (editor.isMainUserId()) { userIds.add(0, userId); gotMainUserId = true; |