aboutsummaryrefslogtreecommitdiffstats
path: root/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui
diff options
context:
space:
mode:
authorDominik Schürmann <dominik@dominikschuermann.de>2014-03-30 18:04:43 +0200
committerDominik Schürmann <dominik@dominikschuermann.de>2014-03-30 18:04:43 +0200
commit6b5d60e1f4889fabf8277c0720a60e3a24ec8caf (patch)
treeed65b0f6204ceaed0d121b6d0238ad5afb8d7004 /OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui
parentb2342744b5f8e5bce62db18f93468fc0781a2c05 (diff)
parentbe007ecc56809cc0b6d489ab09be656e2ced9c32 (diff)
downloadopen-keychain-6b5d60e1f4889fabf8277c0720a60e3a24ec8caf.tar.gz
open-keychain-6b5d60e1f4889fabf8277c0720a60e3a24ec8caf.tar.bz2
open-keychain-6b5d60e1f4889fabf8277c0720a60e3a24ec8caf.zip
Merge pull request #491 from openpgp-keychain/edit-key
Edit key changes
Diffstat (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui')
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java333
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java7
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java192
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java7
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java161
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java134
7 files changed, 599 insertions, 239 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 6eb5b9d2d..b7fffc7ff 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
@@ -17,6 +17,12 @@
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 android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
@@ -41,7 +47,6 @@ 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;
@@ -51,19 +56,27 @@ 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 java.util.ArrayList;
-import java.util.GregorianCalendar;
-import java.util.Vector;
-public class EditKeyActivity extends ActionBarActivity {
+import android.app.AlertDialog;
+import android.support.v4.app.ActivityCompat;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class EditKeyActivity extends ActionBarActivity implements EditorListener {
// Actions for internal use only:
public static final String ACTION_CREATE_KEY = Constants.INTENT_PREFIX + "CREATE_KEY";
@@ -90,6 +103,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;
@@ -102,12 +118,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>();
@@ -128,24 +174,10 @@ public class EditKeyActivity extends ActionBarActivity {
* @param intent
*/
private void handleActionCreateKey(Intent intent) {
- // Inflate a "Save"/"Cancel" custom action bar
- ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_save, R.drawable.ic_action_save,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- saveClicked();
- }
- }, R.string.btn_do_not_save, R.drawable.ic_action_cancel, 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
@@ -203,22 +235,24 @@ 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);
}
}
};
@@ -234,7 +268,7 @@ public class EditKeyActivity extends ActionBarActivity {
}
}
} else {
- buildLayout();
+ buildLayout(false);
}
}
@@ -244,40 +278,29 @@ public class EditKeyActivity extends ActionBarActivity {
* @param intent
*/
private void handleActionEditKey(Intent intent) {
- // Inflate a "Save"/"Cancel" custom action bar
- ActionBarHelper.setOneButtonView(getSupportActionBar(), R.string.btn_save, R.drawable.ic_action_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);
// get master key id using row id
long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri);
- mMasterCanSign = ProviderHelper.getMasterKeyCanSign(this, mDataUri);
- finallyEdit(masterKeyId, mMasterCanSign);
+ mMasterCanSign = ProviderHelper.getMasterKeyCanCertify(this, mDataUri);
+ 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();
}
}
@@ -299,59 +322,65 @@ 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 R.id.menu_key_edit_cancel:
- cancelClicked();
- return true;
- case R.id.menu_key_edit_export_file:
+ 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:
+ if (needsSaving()) {
+ Toast.makeText(this, R.string.error_save_first, Toast.LENGTH_LONG).show();
+ } else {
long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri);
long[] ids = new long[]{masterKeyId};
mExportHelper.showExportKeysDialog(ids, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC,
null);
return true;
- case R.id.menu_key_edit_delete: {
- //Convert the uri to one based on rowId
- long rowId= ProviderHelper.getRowId(this,mDataUri);
- Uri convertUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(Long.toString(rowId));
-
- // Message is received after key is deleted
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
- setResult(RESULT_CANCELED);
- finish();
- }
+ }
+ return true;
+ case R.id.menu_key_edit_delete:
+ long rowId= ProviderHelper.getRowId(this,mDataUri);
+ Uri convertUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(Long.toString(rowId));
+ // Message is received after key is deleted
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
+ setResult(RESULT_CANCELED);
+ finish();
}
- };
+ }};
+ mExportHelper.deleteKey(convertUri, returnHandler);
+ return true;
- mExportHelper.deleteKey(convertUri, 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,16 +395,24 @@ 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);
}
}
}
mCurrentPassphrase = "";
+ buildLayout(false);
+
mIsPassPhraseSet = PassphraseCacheService.hasPassphrase(this, masterKeyId);
- buildLayout();
if (!mIsPassPhraseSet) {
// check "no passphrase" checkbox and remove button
mNoPassphrase.setChecked(true);
@@ -399,6 +436,7 @@ public class EditKeyActivity extends ActionBarActivity {
.getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE);
updatePassPhraseButtonText();
+ somethingChanged();
}
}
};
@@ -407,7 +445,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 {
@@ -423,8 +461,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
@@ -442,11 +481,13 @@ public class EditKeyActivity extends ActionBarActivity {
mUserIdsView.setType(Id.type.user_id);
mUserIdsView.setCanEdit(mMasterCanSign);
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(mMasterCanSign);
- mKeysView.setKeys(mKeys, mKeysUsages);
+ mKeysView.setKeys(mKeys, mKeysUsages, newKeys);
+ mKeysView.setEditorListener(this);
container.addView(mKeysView);
updatePassPhraseButtonText();
@@ -457,7 +498,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
@@ -471,6 +512,7 @@ public class EditKeyActivity extends ActionBarActivity {
mNewPassPhrase = mSavedNewPassPhrase;
mChangePassphrase.setVisibility(View.VISIBLE);
}
+ somethingChanged();
}
});
}
@@ -493,27 +535,54 @@ public class EditKeyActivity extends ActionBarActivity {
}
}
- private void saveClicked() {
- long masterKeyId = getMasterKeyId();
- if (!isPassphraseSet()) {
- Log.e(Constants.TAG, "No passphrase has been set");
- Toast.makeText(this, R.string.set_a_passphrase, Toast.LENGTH_LONG).show();
- } else {
- String passphrase = null;
- if (mIsPassPhraseSet) {
- passphrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId);
+ public boolean hasPassphraseChanged()
+ {
+ if (mNoPassphrase != null) {
+ if (mNoPassphrase.isChecked()) {
+ return mIsPassPhraseSet;
} else {
- passphrase = "";
+ return (mNewPassPhrase != null && !mNewPassPhrase.equals(""));
}
- if (passphrase == null) {
- showPassphraseDialog(masterKeyId, mMasterCanSign);
- } else {
- mCurrentPassphrase = passphrase;
- finallySaveClicked();
+ }else {
+ return false;
+ }
+ }
+
+ private void saveClicked() {
+ long masterKeyId = getMasterKeyId();
+ 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;
+ 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();
}
}
}
+ 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
@@ -521,22 +590,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);
+ saveParams.originalPrimaryID = mUserIdsView.getOriginalPrimaryID();
+
+
// 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, mMasterCanSign);
+ data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, saveParams);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
@@ -579,8 +652,35 @@ public class EditKeyActivity extends ActionBarActivity {
}
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,19 +697,8 @@ 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;
- 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));
- }
-
- if (userId.equals("")) {
- continue;
- }
+ String userId;
+ userId = editor.getValue();
if (editor.isMainUserId()) {
userIds.add(0, userId);
@@ -698,4 +787,4 @@ public class EditKeyActivity extends ActionBarActivity {
: getString(R.string.btn_set_passphrase));
}
-} \ No newline at end of file
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java
index d890f35cb..719378274 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java
@@ -91,10 +91,15 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O
}
}
- public void onDeleted(Editor editor) {
+ public void onDeleted(Editor editor, boolean wasNewItem) {
// nothing to do
}
+ @Override
+ public void onEdited() {
+
+ }
+
public void onClick(View v) {
KeyServerEditor view = (KeyServerEditor) mInflater.inflate(R.layout.key_server_editor,
mEditors, false);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java
index 1cf510d3a..7b21c189d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java
@@ -18,8 +18,10 @@ package org.sufficientlysecure.keychain.ui.widget;
public interface Editor {
public interface EditorListener {
- public void onDeleted(Editor editor);
+ public void onDeleted(Editor editor, boolean wasNewItem);
+ public void onEdited();
}
public void setEditorListener(EditorListener listener);
+ public boolean needsSaving();
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
index 7e0acfa28..0dfc6dc5e 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
@@ -16,6 +16,20 @@
package org.sufficientlysecure.keychain.ui.widget;
+import java.text.DateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+import java.util.Vector;
+
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.spongycastle.openpgp.PGPKeyFlags;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.util.Choice;
import android.annotation.TargetApi;
import android.app.DatePickerDialog;
import android.app.Dialog;
@@ -26,7 +40,13 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.*;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.DatePicker;
+import android.widget.LinearLayout;
+import android.widget.TableLayout;
+import android.widget.TableRow;
+import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKey;
@@ -47,11 +67,30 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
BootstrapButton mDeleteButton;
TextView mAlgorithm;
TextView mKeyId;
- Spinner mUsage;
TextView mCreationDate;
BootstrapButton mExpiryDateButton;
GregorianCalendar mCreatedDate;
GregorianCalendar mExpiryDate;
+ GregorianCalendar mOriginalExpiryDate = null;
+ CheckBox mChkCertify;
+ CheckBox mChkSign;
+ CheckBox mChkEncrypt;
+ CheckBox mChkAuthenticate;
+ int mUsage;
+ int mOriginalUsage;
+ boolean mIsNewKey;
+
+ private CheckBox.OnCheckedChangeListener mCheckChanged = new CheckBox.OnCheckedChangeListener()
+ {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
+ {
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
+ }
+ };
+
private int mDatePickerResultCount = 0;
private DatePickerDialog.OnDateSetListener mExpiryDateSetListener =
@@ -61,7 +100,18 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
if (mDatePickerResultCount++ == 0) {
GregorianCalendar date = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
date.set(year, monthOfYear, dayOfMonth);
- setExpiryDate(date);
+ if (mOriginalExpiryDate != null) {
+ long numDays = (date.getTimeInMillis() / 86400000) - (mOriginalExpiryDate.getTimeInMillis() / 86400000);
+ if (numDays == 0)
+ setExpiryDate(mOriginalExpiryDate);
+ else
+ setExpiryDate(date);
+ } else {
+ setExpiryDate(date);
+ }
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
}
};
@@ -83,21 +133,17 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
mKeyId = (TextView) findViewById(R.id.keyId);
mCreationDate = (TextView) findViewById(R.id.creation);
mExpiryDateButton = (BootstrapButton) findViewById(R.id.expiry);
- mUsage = (Spinner) findViewById(R.id.usage);
- Choice choices[] = {
- new Choice(Id.choice.usage.sign_only, getResources().getString(
- R.string.choice_sign_only)),
- new Choice(Id.choice.usage.encrypt_only, getResources().getString(
- R.string.choice_encrypt_only)),
- new Choice(Id.choice.usage.sign_and_encrypt, getResources().getString(
- R.string.choice_sign_and_encrypt)), };
- ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(getContext(),
- android.R.layout.simple_spinner_item, choices);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mUsage.setAdapter(adapter);
mDeleteButton = (BootstrapButton) findViewById(R.id.delete);
mDeleteButton.setOnClickListener(this);
+ mChkCertify = (CheckBox) findViewById(R.id.chkCertify);
+ mChkCertify.setOnCheckedChangeListener(mCheckChanged);
+ mChkSign = (CheckBox) findViewById(R.id.chkSign);
+ mChkSign.setOnCheckedChangeListener(mCheckChanged);
+ mChkEncrypt = (CheckBox) findViewById(R.id.chkEncrypt);
+ mChkEncrypt.setOnCheckedChangeListener(mCheckChanged);
+ mChkAuthenticate = (CheckBox) findViewById(R.id.chkAuthenticate);
+ mChkAuthenticate.setOnCheckedChangeListener(mCheckChanged);
setExpiryDate(null);
@@ -125,6 +171,9 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
// Note: Ignore results after the first one - android sends multiples.
if (mDatePickerResultCount++ == 0) {
setExpiryDate(null);
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
}
});
@@ -155,12 +204,14 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
public void setCanEdit(boolean bCanEdit) {
if (!bCanEdit) {
mDeleteButton.setVisibility(View.INVISIBLE);
- mUsage.setEnabled(false);
mExpiryDateButton.setEnabled(false);
+ mChkSign.setEnabled(false); //certify is always disabled
+ mChkEncrypt.setEnabled(false);
+ mChkAuthenticate.setEnabled(false);
}
}
- public void setValue(PGPSecretKey key, boolean isMasterKey, int usage) {
+ public void setValue(PGPSecretKey key, boolean isMasterKey, int usage, boolean isNewKey) {
mKey = key;
mIsMasterKey = isMasterKey;
@@ -175,47 +226,45 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
Vector<Choice> choices = new Vector<Choice>();
boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT);
boolean isDSAKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.DSA);
- if (!isElGamalKey) {
- choices.add(new Choice(Id.choice.usage.sign_only, getResources().getString(
- R.string.choice_sign_only)));
+ if (isElGamalKey) {
+ mChkSign.setVisibility(View.INVISIBLE);
+ TableLayout table = (TableLayout)findViewById(R.id.table_keylayout);
+ TableRow row = (TableRow)findViewById(R.id.row_sign);
+ table.removeView(row);
}
- if (!mIsMasterKey && !isDSAKey) {
- choices.add(new Choice(Id.choice.usage.encrypt_only, getResources().getString(
- R.string.choice_encrypt_only)));
+ if (isDSAKey) {
+ mChkEncrypt.setVisibility(View.INVISIBLE);
+ TableLayout table = (TableLayout)findViewById(R.id.table_keylayout);
+ TableRow row = (TableRow)findViewById(R.id.row_encrypt);
+ table.removeView(row);
}
- if (!isElGamalKey && !isDSAKey) {
- choices.add(new Choice(Id.choice.usage.sign_and_encrypt, getResources().getString(
- R.string.choice_sign_and_encrypt)));
+ if (!mIsMasterKey) {
+ mChkCertify.setVisibility(View.INVISIBLE);
+ TableLayout table = (TableLayout)findViewById(R.id.table_keylayout);
+ TableRow row = (TableRow)findViewById(R.id.row_certify);
+ table.removeView(row);
+ } else {
+ TextView mLabelUsage2= (TextView) findViewById(R.id.label_usage2);
+ mLabelUsage2.setVisibility(View.INVISIBLE);
}
- ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(getContext(),
- android.R.layout.simple_spinner_item, choices);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mUsage.setAdapter(adapter);
-
- // Set value in choice dropdown to key
int selectId = 0;
- if (PgpKeyHelper.isEncryptionKey(key)) {
- if (PgpKeyHelper.isSigningKey(key)) {
- selectId = Id.choice.usage.sign_and_encrypt;
- } else {
- selectId = Id.choice.usage.encrypt_only;
- }
+ mIsNewKey = isNewKey;
+ if (isNewKey) {
+ mUsage = usage;
+ mChkCertify.setChecked((usage &= KeyFlags.CERTIFY_OTHER) == KeyFlags.CERTIFY_OTHER);
+ mChkSign.setChecked((usage &= KeyFlags.SIGN_DATA) == KeyFlags.SIGN_DATA);
+ mChkEncrypt.setChecked(((usage &= KeyFlags.ENCRYPT_COMMS) == KeyFlags.ENCRYPT_COMMS) ||
+ ((usage &= KeyFlags.ENCRYPT_STORAGE) == KeyFlags.ENCRYPT_STORAGE));
+ mChkAuthenticate.setChecked((usage &= KeyFlags.AUTHENTICATION) == KeyFlags.AUTHENTICATION);
} else {
- // set usage if it is predefined
- if (usage != -1) {
- selectId = usage;
- } else {
- selectId = Id.choice.usage.sign_only;
- }
-
- }
-
- for (int i = 0; i < choices.size(); ++i) {
- if (choices.get(i).getId() == selectId) {
- mUsage.setSelection(i);
- break;
- }
+ mUsage = PgpKeyHelper.getKeyUsage(key);
+ mOriginalUsage = mUsage;
+ if (key.isMasterKey())
+ mChkCertify.setChecked(PgpKeyHelper.isCertificationKey(key));
+ mChkSign.setChecked(PgpKeyHelper.isSigningKey(key));
+ mChkEncrypt.setChecked(PgpKeyHelper.isEncryptionKey(key));
+ mChkAuthenticate.setChecked(PgpKeyHelper.isAuthenticationKey(key));
}
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
@@ -228,6 +277,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
} else {
cal.setTime(PgpKeyHelper.getExpiryDate(key));
setExpiryDate(cal);
+ mOriginalExpiryDate = cal;
}
}
@@ -241,7 +291,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
if (v == mDeleteButton) {
parent.removeView(this);
if (mEditorListener != null) {
- mEditorListener.onDeleted(this);
+ mEditorListener.onDeleted(this, mIsNewKey);
}
}
}
@@ -273,7 +323,41 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
}
public int getUsage() {
- return ((Choice) mUsage.getSelectedItem()).getId();
+ mUsage = (mUsage & ~KeyFlags.CERTIFY_OTHER) | (mChkCertify.isChecked() ? KeyFlags.CERTIFY_OTHER : 0);
+ mUsage = (mUsage & ~KeyFlags.SIGN_DATA) | (mChkSign.isChecked() ? KeyFlags.SIGN_DATA : 0);
+ mUsage = (mUsage & ~KeyFlags.ENCRYPT_COMMS) | (mChkEncrypt.isChecked() ? KeyFlags.ENCRYPT_COMMS : 0);
+ mUsage = (mUsage & ~KeyFlags.ENCRYPT_STORAGE) | (mChkEncrypt.isChecked() ? KeyFlags.ENCRYPT_STORAGE : 0);
+ mUsage = (mUsage & ~KeyFlags.AUTHENTICATION) | (mChkAuthenticate.isChecked() ? KeyFlags.AUTHENTICATION : 0);
+
+ return mUsage;
+ }
+
+ public boolean needsSaving()
+ {
+ if (mIsNewKey)
+ return true;
+
+ boolean retval = (getUsage() != mOriginalUsage);
+
+ boolean dateChanged;
+ boolean mOEDNull = (mOriginalExpiryDate == null);
+ boolean mEDNull = (mExpiryDate == null);
+ if (mOEDNull != mEDNull) {
+ dateChanged = true;
+ } else {
+ if(mOEDNull) //both null, no change
+ dateChanged = false;
+ else
+ dateChanged = ((mExpiryDate.compareTo(mOriginalExpiryDate)) != 0);
+ }
+ retval |= dateChanged;
+
+ return retval;
+ }
+
+ public boolean getIsNewKey()
+ {
+ return mIsNewKey;
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
index f92c7532a..171763672 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
@@ -66,11 +66,16 @@ public class KeyServerEditor extends LinearLayout implements Editor, OnClickList
if (v == mDeleteButton) {
parent.removeView(this);
if (mEditorListener != null) {
- mEditorListener.onDeleted(this);
+ mEditorListener.onDeleted(this, false);
}
}
}
+ @Override
+ public boolean needsSaving() {
+ return false;
+ }
+
public void setEditorListener(EditorListener listener) {
mEditorListener = listener;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
index 1ef178f15..6c7737e6d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
@@ -16,6 +16,23 @@
package org.sufficientlysecure.keychain.ui.widget;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+import org.spongycastle.openpgp.PGPKeyFlags;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
+import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;
+import org.sufficientlysecure.keychain.util.Choice;
+
+import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
@@ -44,23 +61,31 @@ import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;
import org.sufficientlysecure.keychain.util.Choice;
-import java.util.Vector;
-public class SectionView extends LinearLayout implements OnClickListener, EditorListener {
+
+public class SectionView extends LinearLayout implements OnClickListener, EditorListener, Editor {
private LayoutInflater mInflater;
private BootstrapButton mPlusButton;
private ViewGroup mEditors;
private TextView mTitle;
private int mType = 0;
+ private EditorListener mEditorListener = null;
private Choice mNewKeyAlgorithmChoice;
private int mNewKeySize;
+ private boolean oldItemDeleted = false;
+ private ArrayList<String> mDeletedIDs = new ArrayList<String>();
+ private ArrayList<PGPSecretKey> mDeletedKeys = new ArrayList<PGPSecretKey>();
private boolean mCanEdit = true;
private ActionBarActivity mActivity;
private ProgressDialogFragment mGeneratingDialog;
+ public void setEditorListener(EditorListener listener) {
+ mEditorListener = listener;
+ }
+
public SectionView(Context context) {
super(context);
mActivity = (ActionBarActivity) context;
@@ -124,8 +149,26 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
/**
* {@inheritDoc}
*/
- public void onDeleted(Editor editor) {
+ public void onDeleted(Editor editor, boolean wasNewItem) {
+ oldItemDeleted |= !wasNewItem;
+ if (oldItemDeleted) {
+ if (mType == Id.type.user_id)
+ mDeletedIDs.add(((UserIdEditor)editor).getOriginalID());
+ else if (mType == Id.type.key)
+ mDeletedKeys.add(((KeyEditor)editor).getValue());
+
+ }
this.updateEditorsVisible();
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
+ }
+
+ @Override
+ public void onEdited() {
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
protected void updateEditorsVisible() {
@@ -133,6 +176,94 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
mEditors.setVisibility(hasChildren ? View.VISIBLE : View.GONE);
}
+ public boolean needsSaving()
+ {
+ //check each view for needs saving, take account of deleted items
+ boolean ret = oldItemDeleted;
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ Editor editor = (Editor) mEditors.getChildAt(i);
+ ret |= editor.needsSaving();
+ if (mType == Id.type.user_id)
+ ret |= ((UserIdEditor)editor).primarySwapped();
+ }
+ return ret;
+ }
+
+ public boolean primaryChanged()
+ {
+ boolean ret = false;
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ Editor editor = (Editor) mEditors.getChildAt(i);
+ if (mType == Id.type.user_id)
+ ret |= ((UserIdEditor)editor).primarySwapped();
+ }
+ return ret;
+ }
+
+ public String getOriginalPrimaryID()
+ { //NB: this will have to change when we change how Primary IDs are chosen, and so we need to be
+ // careful about where Master key capabilities are stored... multiple primaries and
+ // revoked ones make this harder than the simple case we are continuing to assume here
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ Editor editor = (Editor) mEditors.getChildAt(i);
+ if (mType == Id.type.user_id) {
+ if(((UserIdEditor)editor).getIsOriginallyMainUserID()) {
+ return ((UserIdEditor)editor).getOriginalID();
+ }
+ }
+ }
+ return null;
+ }
+
+ public ArrayList<String> getOriginalIDs()
+ {
+ ArrayList<String> orig = new ArrayList<String>();
+ if (mType == Id.type.user_id) {
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ UserIdEditor editor = (UserIdEditor) mEditors.getChildAt(i);
+ if (editor.isMainUserId())
+ orig.add(0, editor.getOriginalID());
+ else
+ orig.add(editor.getOriginalID());
+ }
+ return orig;
+ } else {
+ return null;
+ }
+ }
+
+ public ArrayList<String> getDeletedIDs()
+ {
+ return mDeletedIDs;
+ }
+
+ public ArrayList<PGPSecretKey> getDeletedKeys()
+ {
+ return mDeletedKeys;
+ }
+
+ public List<Boolean> getNeedsSavingArray()
+ {
+ ArrayList<Boolean> mList = new ArrayList<Boolean>();
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ Editor editor = (Editor) mEditors.getChildAt(i);
+ mList.add(editor.needsSaving());
+ }
+ return mList;
+ }
+
+ public List<Boolean> getNewKeysArray()
+ {
+ ArrayList<Boolean> mList = new ArrayList<Boolean>();
+ if (mType == Id.type.key) {
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ KeyEditor editor = (KeyEditor) mEditors.getChildAt(i);
+ mList.add(editor.getIsNewKey());
+ }
+ }
+ return mList;
+ }
+
/**
* {@inheritDoc}
*/
@@ -143,10 +274,11 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
UserIdEditor view = (UserIdEditor) mInflater.inflate(
R.layout.edit_key_user_id_item, mEditors, false);
view.setEditorListener(this);
- if (mEditors.getChildCount() == 0) {
- view.setIsMainUserId(true);
- }
+ view.setValue("", mEditors.getChildCount() == 0, true);
mEditors.addView(view);
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
break;
}
@@ -185,10 +317,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
UserIdEditor view = (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item,
mEditors, false);
view.setEditorListener(this);
- view.setValue(userId);
- if (mEditors.getChildCount() == 0) {
- view.setIsMainUserId(true);
- }
+ view.setValue(userId, mEditors.getChildCount() == 0, false);
view.setCanEdit(mCanEdit);
mEditors.addView(view);
}
@@ -196,7 +325,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
this.updateEditorsVisible();
}
- public void setKeys(Vector<PGPSecretKey> list, Vector<Integer> usages) {
+ public void setKeys(Vector<PGPSecretKey> list, Vector<Integer> usages, boolean newKeys) {
if (mType != Id.type.key) {
return;
}
@@ -209,7 +338,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
false);
view.setEditorListener(this);
boolean isMasterKey = (mEditors.getChildCount() == 0);
- view.setValue(list.get(i), isMasterKey, usages.get(i));
+ view.setValue(list.get(i), isMasterKey, usages.get(i), newKeys);
view.setCanEdit(mCanEdit);
mEditors.addView(view);
}
@@ -289,8 +418,14 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item,
mEditors, false);
view.setEditorListener(SectionView.this);
- view.setValue(newKey, newKey.isMasterKey(), -1);
+ int usage = 0;
+ if (mEditors.getChildCount() == 0)
+ usage = PGPKeyFlags.CAN_CERTIFY;
+ view.setValue(newKey, newKey.isMasterKey(), usage, true);
mEditors.addView(view);
SectionView.this.updateEditorsVisible();
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
index ed81b162e..d4b15613a 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
@@ -16,6 +16,11 @@
package org.sufficientlysecure.keychain.ui.widget;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
@@ -26,28 +31,23 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.*;
import com.beardedhen.androidbootstrap.BootstrapButton;
-import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ContactHelper;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
public class UserIdEditor extends LinearLayout implements Editor, OnClickListener {
private EditorListener mEditorListener = null;
private BootstrapButton mDeleteButton;
private RadioButton mIsMainUserId;
+ private String mOriginalID;
private EditText mName;
+ private String mOriginalName;
private AutoCompleteTextView mEmail;
+ private String mOriginalEmail;
private EditText mComment;
-
- public static class NoNameException extends Exception {
- static final long serialVersionUID = 0xf812773343L;
-
- public NoNameException(String message) {
- super(message);
- }
- }
+ private String mOriginalComment;
+ private boolean mOriginallyMainUserID;
+ private boolean mIsNewId;
public void setCanEdit(boolean bCanEdit) {
if (!bCanEdit) {
@@ -59,14 +59,6 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
}
}
- public static class NoEmailException extends Exception {
- static final long serialVersionUID = 0xf812773344L;
-
- public NoEmailException(String message) {
- super(message);
- }
- }
-
public static class InvalidEmailException extends Exception {
static final long serialVersionUID = 0xf812773345L;
@@ -83,6 +75,24 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
super(context, attrs);
}
+ TextWatcher mTextWatcher = new TextWatcher() {
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s)
+ {
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
+ }
+ };
+
@Override
protected void onFinishInflate() {
setDrawingCacheEnabled(true);
@@ -94,8 +104,10 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
mIsMainUserId.setOnClickListener(this);
mName = (EditText) findViewById(R.id.name);
+ mName.addTextChangedListener(mTextWatcher);
mEmail = (AutoCompleteTextView) findViewById(R.id.email);
mComment = (EditText) findViewById(R.id.comment);
+ mComment.addTextChangedListener(mTextWatcher);
mEmail.setThreshold(1); // Start working from first character
@@ -127,36 +139,45 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
// remove drawable if email is empty
mEmail.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
});
super.onFinishInflate();
}
- public void setValue(String userId) {
+ public void setValue(String userId, boolean isMainID, boolean isNewId) {
+
mName.setText("");
+ mOriginalName = "";
mComment.setText("");
+ mOriginalComment = "";
mEmail.setText("");
-
- Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$");
- Matcher matcher = withComment.matcher(userId);
- if (matcher.matches()) {
- mName.setText(matcher.group(1));
- mComment.setText(matcher.group(2));
- mEmail.setText(matcher.group(3));
- return;
+ mOriginalEmail = "";
+ mIsNewId = isNewId;
+ mOriginalID = userId;
+
+ String[] result = PgpKeyHelper.splitUserId(userId);
+ if (result[0] != null) {
+ mName.setText(result[0]);
+ mOriginalName = result[0];
}
-
- Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$");
- matcher = withoutComment.matcher(userId);
- if (matcher.matches()) {
- mName.setText(matcher.group(1));
- mEmail.setText(matcher.group(2));
- return;
+ if (result[1] != null) {
+ mEmail.setText(result[1]);
+ mOriginalEmail = result[1];
+ }
+ if (result[2] != null) {
+ mComment.setText(result[2]);
+ mOriginalComment = result[2];
}
+
+ mOriginallyMainUserID = isMainID;
+ setIsMainUserId(isMainID);
}
- public String getValue() throws NoNameException, NoEmailException {
+ public String getValue() {
String name = ("" + mName.getText()).trim();
String email = ("" + mEmail.getText()).trim();
String comment = ("" + mComment.getText()).trim();
@@ -173,16 +194,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
// ok, empty one...
return userId;
}
-
- // otherwise make sure that name and email exist
- if (name.equals("")) {
- throw new NoNameException("need a name");
- }
-
- if (email.equals("")) {
- throw new NoEmailException("need an email");
- }
-
+ //TODO: check gpg accepts an entirely empty ID packet. specs say this is allowed
return userId;
}
@@ -192,7 +204,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
boolean wasMainUserId = mIsMainUserId.isChecked();
parent.removeView(this);
if (mEditorListener != null) {
- mEditorListener.onDeleted(this);
+ mEditorListener.onDeleted(this, mIsNewId);
}
if (wasMainUserId && parent.getChildCount() > 0) {
UserIdEditor editor = (UserIdEditor) parent.getChildAt(0);
@@ -207,6 +219,9 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
editor.setIsMainUserId(false);
}
}
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
}
@@ -221,4 +236,29 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
public void setEditorListener(EditorListener listener) {
mEditorListener = listener;
}
+
+ @Override
+ public boolean needsSaving() {
+ boolean retval = false; //(mOriginallyMainUserID != isMainUserId());
+ retval |= !(mOriginalName.equals( ("" + mName.getText()).trim() ) );
+ retval |= !(mOriginalEmail.equals( ("" + mEmail.getText()).trim() ) );
+ retval |= !(mOriginalComment.equals( ("" + mComment.getText()).trim() ) );
+ retval |= mIsNewId;
+ return retval;
+ }
+
+ public boolean getIsOriginallyMainUserID()
+ {
+ return mOriginallyMainUserID;
+ }
+
+ public boolean primarySwapped()
+ {
+ return (mOriginallyMainUserID != isMainUserId());
+ }
+
+ public String getOriginalID()
+ {
+ return mOriginalID;
+ }
}