Feature to change key configuration in create key

This commit is contained in:
Dominik Schürmann 2014-09-21 21:50:56 +02:00
parent d4387c0179
commit bf0104af2e
9 changed files with 208 additions and 53 deletions

View File

@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;

View File

@ -217,7 +217,7 @@ public class SaveKeyringParcel implements Parcelable {
out += "mChangeSubKeys: " + mChangeSubKeys + "\n"; out += "mChangeSubKeys: " + mChangeSubKeys + "\n";
out += "mChangePrimaryUserId: " + mChangePrimaryUserId + "\n"; out += "mChangePrimaryUserId: " + mChangePrimaryUserId + "\n";
out += "mRevokeUserIds: " + mRevokeUserIds + "\n"; out += "mRevokeUserIds: " + mRevokeUserIds + "\n";
out += "mRevokeSubKeys: " + mRevokeSubKeys; out += "mRevokeSubKeys: " + mRevokeSubKeys + "\n";
out += "mStripSubKeys: " + mStripSubKeys; out += "mStripSubKeys: " + mStripSubKeys;
return out; return out;

View File

@ -47,6 +47,8 @@ import org.sufficientlysecure.keychain.util.Log;
public class CreateKeyFinalFragment extends Fragment { public class CreateKeyFinalFragment extends Fragment {
public static final int REQUEST_EDIT_KEY = 0x00008007;
CreateKeyActivity mCreateKeyActivity; CreateKeyActivity mCreateKeyActivity;
TextView mNameEdit; TextView mNameEdit;
@ -54,6 +56,8 @@ public class CreateKeyFinalFragment extends Fragment {
CheckBox mUploadCheckbox; CheckBox mUploadCheckbox;
View mBackButton; View mBackButton;
View mCreateButton; View mCreateButton;
TextView mEditText;
View mEditButton;
public static final String ARG_NAME = "name"; public static final String ARG_NAME = "name";
public static final String ARG_EMAIL = "email"; public static final String ARG_EMAIL = "email";
@ -63,6 +67,8 @@ public class CreateKeyFinalFragment extends Fragment {
String mEmail; String mEmail;
String mPassphrase; String mPassphrase;
SaveKeyringParcel mSaveKeyringParcel;
/** /**
* Creates new instance of this fragment * Creates new instance of this fragment
*/ */
@ -88,6 +94,8 @@ public class CreateKeyFinalFragment extends Fragment {
mUploadCheckbox = (CheckBox) view.findViewById(R.id.create_key_upload); mUploadCheckbox = (CheckBox) view.findViewById(R.id.create_key_upload);
mBackButton = view.findViewById(R.id.create_key_back_button); mBackButton = view.findViewById(R.id.create_key_back_button);
mCreateButton = view.findViewById(R.id.create_key_create_button); mCreateButton = view.findViewById(R.id.create_key_create_button);
mEditText = (TextView) view.findViewById(R.id.create_key_edit_text);
mEditButton = view.findViewById(R.id.create_key_edit_button);
// get args // get args
mName = getArguments().getString(ARG_NAME); mName = getArguments().getString(ARG_NAME);
@ -112,14 +120,53 @@ public class CreateKeyFinalFragment extends Fragment {
} }
}); });
mEditButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent edit = new Intent(getActivity(), EditKeyActivity.class);
edit.putExtra(EditKeyActivity.EXTRA_SAVE_KEYRING_PARCEL, mSaveKeyringParcel);
startActivityForResult(edit, REQUEST_EDIT_KEY);
}
});
return view; return view;
} }
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_EDIT_KEY: {
if (resultCode == Activity.RESULT_OK) {
mSaveKeyringParcel = data.getParcelableExtra(EditKeyActivity.EXTRA_SAVE_KEYRING_PARCEL);
mEditText.setText(R.string.create_key_custom);
}
break;
}
default:
super.onActivityResult(requestCode, resultCode, data);
}
}
@Override @Override
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
mCreateKeyActivity = (CreateKeyActivity) getActivity(); mCreateKeyActivity = (CreateKeyActivity) getActivity();
if (mSaveKeyringParcel == null) {
mSaveKeyringParcel = new SaveKeyringParcel();
mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 4096, null, KeyFlags.CERTIFY_OTHER, 0L));
mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 4096, null, KeyFlags.SIGN_DATA, 0L));
mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 4096, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L));
String userId = KeyRing.createUserId(mName, mEmail, null);
mSaveKeyringParcel.mAddUserIds.add(userId);
mSaveKeyringParcel.mChangePrimaryUserId = userId;
mSaveKeyringParcel.mNewPassphrase = mPassphrase;
}
} }
private void createKey() { private void createKey() {
@ -163,20 +210,8 @@ public class CreateKeyFinalFragment extends Fragment {
// fill values for this action // fill values for this action
Bundle data = new Bundle(); Bundle data = new Bundle();
SaveKeyringParcel parcel = new SaveKeyringParcel();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 4096, null, KeyFlags.CERTIFY_OTHER, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 4096, null, KeyFlags.SIGN_DATA, 0L));
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 4096, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L));
String userId = KeyRing.createUserId(mName, mEmail, null);
parcel.mAddUserIds.add(userId);
parcel.mChangePrimaryUserId = userId;
parcel.mNewPassphrase = mPassphrase;
// get selected key entries // get selected key entries
data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, parcel); data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, mSaveKeyringParcel);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data); intent.putExtra(KeychainIntentService.EXTRA_DATA, data);

View File

@ -23,10 +23,13 @@ import android.support.v7.app.ActionBarActivity;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
public class EditKeyActivity extends ActionBarActivity { public class EditKeyActivity extends ActionBarActivity {
public static final String EXTRA_SAVE_KEYRING_PARCEL = "save_keyring_parcel";
private EditKeyFragment mEditKeyFragment; private EditKeyFragment mEditKeyFragment;
@Override @Override
@ -36,16 +39,17 @@ public class EditKeyActivity extends ActionBarActivity {
setContentView(R.layout.edit_key_activity); setContentView(R.layout.edit_key_activity);
Uri dataUri = getIntent().getData(); Uri dataUri = getIntent().getData();
if (dataUri == null) { SaveKeyringParcel saveKeyringParcel = getIntent().getParcelableExtra(EXTRA_SAVE_KEYRING_PARCEL);
Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); if (dataUri == null && saveKeyringParcel == null) {
Log.e(Constants.TAG, "Either a key Uri or EXTRA_SAVE_KEYRING_PARCEL is required!");
finish(); finish();
return; return;
} }
loadFragment(savedInstanceState, dataUri); loadFragment(savedInstanceState, dataUri, saveKeyringParcel);
} }
private void loadFragment(Bundle savedInstanceState, Uri dataUri) { private void loadFragment(Bundle savedInstanceState, Uri dataUri, SaveKeyringParcel saveKeyringParcel) {
// However, if we're being restored from a previous state, // However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else // then we don't need to do anything and should return or else
// we could end up with overlapping fragments. // we could end up with overlapping fragments.
@ -54,7 +58,11 @@ public class EditKeyActivity extends ActionBarActivity {
} }
// Create an instance of the fragment // Create an instance of the fragment
mEditKeyFragment = EditKeyFragment.newInstance(dataUri); if (dataUri != null) {
mEditKeyFragment = EditKeyFragment.newInstance(dataUri);
} else {
mEditKeyFragment = EditKeyFragment.newInstance(saveKeyringParcel);
}
// Add the fragment to the 'fragment_container' FrameLayout // Add the fragment to the 'fragment_container' FrameLayout
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes! // NOTE: We use commitAllowingStateLoss() to prevent weird crashes!

View File

@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain.ui; package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
@ -71,6 +72,7 @@ public class EditKeyFragment extends LoaderFragment implements
LoaderManager.LoaderCallbacks<Cursor> { LoaderManager.LoaderCallbacks<Cursor> {
public static final String ARG_DATA_URI = "uri"; public static final String ARG_DATA_URI = "uri";
public static final String ARG_SAVE_KEYRING_PARCEL = "save_keyring_parcel";
private ListView mUserIdsList; private ListView mUserIdsList;
private ListView mSubkeysList; private ListView mSubkeysList;
@ -112,6 +114,17 @@ public class EditKeyFragment extends LoaderFragment implements
return frag; return frag;
} }
public static EditKeyFragment newInstance(SaveKeyringParcel saveKeyringParcel) {
EditKeyFragment frag = new EditKeyFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_SAVE_KEYRING_PARCEL, saveKeyringParcel);
frag.setArguments(args);
return frag;
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
View root = super.onCreateView(inflater, superContainer, savedInstanceState); View root = super.onCreateView(inflater, superContainer, savedInstanceState);
@ -138,29 +151,54 @@ public class EditKeyFragment extends LoaderFragment implements
new OnClickListener() { new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
// Save // if we are working on an Uri, save directly
save(mCurrentPassphrase); if (mDataUri == null) {
returnKeyringParcel();
} else {
saveInDatabase(mCurrentPassphrase);
}
} }
}, R.string.menu_key_edit_cancel, R.drawable.ic_action_cancel, }, R.string.menu_key_edit_cancel, R.drawable.ic_action_cancel,
new OnClickListener() { new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
// Cancel // cancel
getActivity().setResult(Activity.RESULT_CANCELED);
getActivity().finish(); getActivity().finish();
} }
} }
); );
Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); Uri dataUri = getArguments().getParcelable(ARG_DATA_URI);
if (dataUri == null) { SaveKeyringParcel saveKeyringParcel = getArguments().getParcelable(ARG_SAVE_KEYRING_PARCEL);
Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); if (dataUri == null && saveKeyringParcel == null) {
Log.e(Constants.TAG, "Either a key Uri or ARG_SAVE_KEYRING_PARCEL is required!");
getActivity().finish(); getActivity().finish();
return; return;
} }
loadData(dataUri); initView();
if (dataUri != null) {
loadData(dataUri);
} else {
loadSaveKeyringParcel(saveKeyringParcel);
}
} }
private void loadSaveKeyringParcel(SaveKeyringParcel saveKeyringParcel) {
mSaveKeyringParcel = saveKeyringParcel;
mPrimaryUserId = saveKeyringParcel.mChangePrimaryUserId;
mCurrentPassphrase = saveKeyringParcel.mNewPassphrase;
mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mSaveKeyringParcel.mAddUserIds);
mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter);
mSubkeysAddedAdapter = new SubkeysAddedAdapter(getActivity(), mSaveKeyringParcel.mAddSubKeys);
mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter);
// show directly
setContentShown(true);
}
private void loadData(Uri dataUri) { private void loadData(Uri dataUri) {
mDataUri = dataUri; mDataUri = dataUri;
@ -228,6 +266,21 @@ public class EditKeyFragment extends LoaderFragment implements
getLoaderManager().initLoader(LOADER_ID_SUBKEYS, null, EditKeyFragment.this); getLoaderManager().initLoader(LOADER_ID_SUBKEYS, null, EditKeyFragment.this);
} }
mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0, mSaveKeyringParcel);
mUserIdsList.setAdapter(mUserIdsAdapter);
// TODO: SaveParcel from savedInstance?!
mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mSaveKeyringParcel.mAddUserIds);
mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter);
mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0, mSaveKeyringParcel);
mSubkeysList.setAdapter(mSubkeysAdapter);
mSubkeysAddedAdapter = new SubkeysAddedAdapter(getActivity(), mSaveKeyringParcel.mAddSubKeys);
mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter);
}
private void initView() {
mChangePassphrase.setOnClickListener(new View.OnClickListener() { mChangePassphrase.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -249,23 +302,6 @@ public class EditKeyFragment extends LoaderFragment implements
} }
}); });
mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0, mSaveKeyringParcel);
mUserIdsList.setAdapter(mUserIdsAdapter);
mUserIdsList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
editUserId(position);
}
});
// TODO: SaveParcel from savedInstance?!
mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mSaveKeyringParcel.mAddUserIds);
mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter);
mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0, mSaveKeyringParcel);
mSubkeysList.setAdapter(mSubkeysAdapter);
mSubkeysList.setOnItemClickListener(new AdapterView.OnItemClickListener() { mSubkeysList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@ -273,9 +309,12 @@ public class EditKeyFragment extends LoaderFragment implements
} }
}); });
mSubkeysAddedAdapter = new SubkeysAddedAdapter(getActivity(), mSaveKeyringParcel.mAddSubKeys); mUserIdsList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter); @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
editUserId(position);
}
});
} }
public Loader<Cursor> onCreateLoader(int id, Bundle args) { public Loader<Cursor> onCreateLoader(int id, Bundle args) {
@ -505,8 +544,12 @@ public class EditKeyFragment extends LoaderFragment implements
} }
private void addSubkey() { private void addSubkey() {
boolean willBeMasterKey = mSubkeysAdapter.getCount() == 0 boolean willBeMasterKey;
&& mSubkeysAddedAdapter.getCount() == 0; if (mSubkeysAdapter != null) {
willBeMasterKey = mSubkeysAdapter.getCount() == 0 && mSubkeysAddedAdapter.getCount() == 0;
} else {
willBeMasterKey = mSubkeysAddedAdapter.getCount() == 0;
}
AddSubkeyDialogFragment addSubkeyDialogFragment = AddSubkeyDialogFragment addSubkeyDialogFragment =
AddSubkeyDialogFragment.newInstance(willBeMasterKey); AddSubkeyDialogFragment.newInstance(willBeMasterKey);
@ -522,7 +565,14 @@ public class EditKeyFragment extends LoaderFragment implements
addSubkeyDialogFragment.show(getActivity().getSupportFragmentManager(), "addSubkeyDialog"); addSubkeyDialogFragment.show(getActivity().getSupportFragmentManager(), "addSubkeyDialog");
} }
private void save(String passphrase) { private void returnKeyringParcel() {
Intent returnIntent = new Intent();
returnIntent.putExtra(EditKeyActivity.EXTRA_SAVE_KEYRING_PARCEL, mSaveKeyringParcel);
getActivity().setResult(Activity.RESULT_OK, returnIntent);
getActivity().finish();
}
private void saveInDatabase(String passphrase) {
Log.d(Constants.TAG, "mSaveKeyringParcel:\n" + mSaveKeyringParcel.toString()); Log.d(Constants.TAG, "mSaveKeyringParcel:\n" + mSaveKeyringParcel.toString());
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
@ -582,10 +632,11 @@ public class EditKeyFragment extends LoaderFragment implements
// start service with intent // start service with intent
getActivity().startService(intent); getActivity().startService(intent);
} }
/** Closes this activity, returning a result parcel with a single error log entry. */ /**
* Closes this activity, returning a result parcel with a single error log entry.
*/
void finishWithError(LogType reason) { void finishWithError(LogType reason) {
// Prepare an intent with an EXTRA_RESULT // Prepare an intent with an EXTRA_RESULT

View File

@ -89,6 +89,8 @@ public class LogDisplayFragment extends ListFragment implements OnTouchListener
} }
}); });
getListView().setFastScrollEnabled(true);
} }
public void decreaseLogLevel() { public void decreaseLogLevel() {

View File

@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.graphics.Typeface;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -105,6 +106,14 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd
holder.mModel.mKeySize, holder.mModel.mKeySize,
holder.mModel.mCurve holder.mModel.mCurve
); );
boolean isMasterKey = position == 0;
if (isMasterKey) {
holder.vKeyId.setTypeface(null, Typeface.BOLD);
} else {
holder.vKeyId.setTypeface(null, Typeface.NORMAL);
}
holder.vKeyId.setText(R.string.edit_key_new_subkey); holder.vKeyId.setText(R.string.edit_key_new_subkey);
holder.vKeyDetails.setText(algorithmStr); holder.vKeyDetails.setText(algorithmStr);

View File

@ -69,6 +69,54 @@
android:text="@string/create_key_upload" android:text="@string/create_key_upload"
android:id="@+id/create_key_upload" /> android:id="@+id/create_key_upload" />
<View
android:layout_width="match_parent"
android:layout_height="1dip"
android:background="?android:attr/listDivider" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/create_key_edit_text"
android:padding="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/create_key_rsa"
android:textColor="@color/android_green_dark"
android:textAppearance="?android:attr/textAppearanceMedium"
android:minHeight="?android:attr/listPreferredItemHeight"
android:clickable="true"
android:gravity="center_vertical"
android:layout_gravity="center_vertical" />
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="?android:attr/listDivider" />
<TextView
android:id="@+id/create_key_edit_button"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/create_key_edit"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:clickable="true"
style="@style/SelectableItem"
android:layout_gravity="center_vertical" />
</LinearLayout>
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dip" android:layout_height="1dip"

View File

@ -495,7 +495,7 @@
<item>"Revoke Subkey"</item> <item>"Revoke Subkey"</item>
<item>"Strip Subkey"</item> <item>"Strip Subkey"</item>
</string-array> </string-array>
<string name="edit_key_new_subkey">"new"</string> <string name="edit_key_new_subkey">"new subkey"</string>
<string name="edit_key_select_flag">"Please select at least one flag!"</string> <string name="edit_key_select_flag">"Please select at least one flag!"</string>
<!-- Create key --> <!-- Create key -->
@ -503,9 +503,12 @@
<string name="create_key_empty">"This field is required"</string> <string name="create_key_empty">"This field is required"</string>
<string name="create_key_passphrases_not_equal">"Passphrases do not match"</string> <string name="create_key_passphrases_not_equal">"Passphrases do not match"</string>
<string name="create_key_final_text">"You entered the following identity:"</string> <string name="create_key_final_text">"You entered the following identity:"</string>
<string name="create_key_final_robot_text">"Creating a key may take a while, have a cup of coffee in the meantime…\n(3 subkeys, RSA, 4096 bit)"</string> <string name="create_key_final_robot_text">"Creating a key may take a while, have a cup of coffee in the meantime…"</string>
<string name="create_key_rsa">"(3 subkeys, RSA, 4096 bit)"</string>
<string name="create_key_custom">"(custom key configuration)"</string>
<string name="create_key_text">"Enter your full name, email address, and choose a passhrase."</string> <string name="create_key_text">"Enter your full name, email address, and choose a passhrase."</string>
<string name="create_key_hint_full_name">"Full Name, e.g. Max Mustermann"</string> <string name="create_key_hint_full_name">"Full Name, e.g. Max Mustermann"</string>
<string name="create_key_edit">"Change key configuration"</string>
<!-- View key --> <!-- View key -->
<string name="view_key_revoked">"This key has been revoked!"</string> <string name="view_key_revoked">"This key has been revoked!"</string>