inline subkey editing (wip commit)

This commit is contained in:
Vincent Breitmoser
2015-11-14 03:24:07 +01:00
committed by Dominik Schürmann
parent fd119bda00
commit 00e97586b0
26 changed files with 410 additions and 61 deletions

View File

@@ -231,7 +231,8 @@ public class EditKeyFragment extends QueueingCryptoOperationFragment<SaveKeyring
mUserIdsAddedAdapter = new UserIdsAddedAdapter(getActivity(), mSaveKeyringParcel.mAddUserIds, false);
mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter);
mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0, mSaveKeyringParcel);
mSubkeysAdapter = new SubkeysAdapter(getActivity(), null, 0);
mSubkeysAdapter.setEditMode(mSaveKeyringParcel);
mSubkeysList.setAdapter(mSubkeysAdapter);
mSubkeysAddedAdapter = new SubkeysAddedAdapter(getActivity(), mSaveKeyringParcel.mAddSubKeys, false);

View File

@@ -20,30 +20,59 @@ package org.sufficientlysecure.keychain.ui;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.view.ActionMode;
import android.view.ActionMode.Callback;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.ViewAnimator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter;
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAddedAdapter;
import org.sufficientlysecure.keychain.ui.dialog.AddSubkeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyExpiryDialogFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
public static final String ARG_DATA_URI = "data_uri";
public static final int LOADER_ID_SUBKEYS = 0;
private ListView mSubkeysList;
private ListView mSubkeysAddedList;
private View mSubkeysAddedLayout;
private ViewAnimator mSubkeyAddFabLayout;
private SubkeysAdapter mSubkeysAdapter;
private SubkeysAddedAdapter mSubkeysAddedAdapter;
private Uri mDataUriSubkeys;
private boolean mHasSecret;
private SaveKeyringParcel mEditModeSaveKeyringParcel;
/**
* Creates new instance of this fragment
*/
@@ -64,6 +93,36 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
mSubkeysList = (ListView) view.findViewById(R.id.keys);
mSubkeysList = (ListView) view.findViewById(R.id.view_key_user_ids);
mSubkeysAddedList = (ListView) view.findViewById(R.id.view_key_user_ids_added);
mSubkeysAddedLayout = view.findViewById(R.id.view_key_user_ids_add_layout);
mSubkeysList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
editSubkey(position);
}
});
View footer = new View(getActivity());
int spacing = (int) android.util.TypedValue.applyDimension(
android.util.TypedValue.COMPLEX_UNIT_DIP, 72, getResources().getDisplayMetrics()
);
android.widget.AbsListView.LayoutParams params = new android.widget.AbsListView.LayoutParams(
android.widget.AbsListView.LayoutParams.MATCH_PARENT,
spacing
);
footer.setLayoutParams(params);
mSubkeysAddedList.addFooterView(footer, null, false);
mSubkeyAddFabLayout = (ViewAnimator) view.findViewById(R.id.view_key_subkey_fab_layout);
view.findViewById(R.id.view_key_subkey_fab).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
addSubkey();
}
});
return root;
}
@@ -90,7 +149,7 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
// Prepare the loaders. Either re-connect with an existing ones,
// or start new ones.
getLoaderManager().initLoader(0, null, this);
getLoaderManager().initLoader(LOADER_ID_SUBKEYS, null, this);
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
@@ -122,4 +181,215 @@ public class ViewKeyAdvSubkeysFragment extends LoaderFragment implements
mSubkeysAdapter.swapCursor(null);
}
private void enterEditMode() {
FragmentActivity activity = getActivity();
activity.startActionMode(new Callback() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mEditModeSaveKeyringParcel = new SaveKeyringParcel(0L, new byte[0]);
mSubkeysAddedAdapter =
new SubkeysAddedAdapter(getActivity(), mEditModeSaveKeyringParcel.mAddSubKeys, false);
mSubkeysAddedList.setAdapter(mSubkeysAddedAdapter);
mSubkeysAddedLayout.setVisibility(View.VISIBLE);
mSubkeyAddFabLayout.setDisplayedChild(1);
mSubkeysAdapter.setEditMode(mEditModeSaveKeyringParcel);
getLoaderManager().restartLoader(LOADER_ID_SUBKEYS, null, ViewKeyAdvSubkeysFragment.this);
mode.setTitle(R.string.title_edit_subkeys);
mode.getMenuInflater().inflate(R.menu.action_edit_uids, menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
mode.finish();
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
mEditModeSaveKeyringParcel = null;
mSubkeysAdapter.setEditMode(null);
mSubkeysAddedLayout.setVisibility(View.GONE);
mSubkeyAddFabLayout.setDisplayedChild(0);
getLoaderManager().restartLoader(0, null, ViewKeyAdvSubkeysFragment.this);
}
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_edit_subkeys:
enterEditMode();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void addSubkey() {
boolean willBeMasterKey;
if (mSubkeysAdapter != null) {
willBeMasterKey = mSubkeysAdapter.getCount() == 0 && mSubkeysAddedAdapter.getCount() == 0;
} else {
willBeMasterKey = mSubkeysAddedAdapter.getCount() == 0;
}
AddSubkeyDialogFragment addSubkeyDialogFragment =
AddSubkeyDialogFragment.newInstance(willBeMasterKey);
addSubkeyDialogFragment
.setOnAlgorithmSelectedListener(
new AddSubkeyDialogFragment.OnAlgorithmSelectedListener() {
@Override
public void onAlgorithmSelected(SaveKeyringParcel.SubkeyAdd newSubkey) {
mSubkeysAddedAdapter.add(newSubkey);
}
}
);
addSubkeyDialogFragment.show(getActivity().getSupportFragmentManager(), "addSubkeyDialog");
}
private void editSubkey(final int position) {
final long keyId = mSubkeysAdapter.getKeyId(position);
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case EditSubkeyDialogFragment.MESSAGE_CHANGE_EXPIRY:
editSubkeyExpiry(position);
break;
case EditSubkeyDialogFragment.MESSAGE_REVOKE:
// toggle
if (mEditModeSaveKeyringParcel.mRevokeSubKeys.contains(keyId)) {
mEditModeSaveKeyringParcel.mRevokeSubKeys.remove(keyId);
} else {
mEditModeSaveKeyringParcel.mRevokeSubKeys.add(keyId);
}
break;
case EditSubkeyDialogFragment.MESSAGE_STRIP: {
SecretKeyType secretKeyType = mSubkeysAdapter.getSecretKeyType(position);
if (secretKeyType == SecretKeyType.GNU_DUMMY) {
// Key is already stripped; this is a no-op.
break;
}
SubkeyChange change = mEditModeSaveKeyringParcel.getSubkeyChange(keyId);
if (change == null) {
mEditModeSaveKeyringParcel.mChangeSubKeys.add(new SubkeyChange(keyId, true, false));
break;
}
// toggle
change.mDummyStrip = !change.mDummyStrip;
if (change.mDummyStrip && change.mMoveKeyToCard) {
// User had chosen to divert key, but now wants to strip it instead.
change.mMoveKeyToCard = false;
}
break;
}
case EditSubkeyDialogFragment.MESSAGE_MOVE_KEY_TO_CARD: {
// TODO: enable later when Admin PIN handling is resolved
Notify.create(getActivity(),
"This feature will be available in an upcoming OpenKeychain version.",
Notify.Style.WARN).show();
break;
// Activity activity = EditKeyFragment.this.getActivity();
// SecretKeyType secretKeyType = mSubkeysAdapter.getSecretKeyType(position);
// if (secretKeyType == SecretKeyType.DIVERT_TO_CARD ||
// secretKeyType == SecretKeyType.GNU_DUMMY) {
// Notify.create(activity, R.string.edit_key_error_bad_nfc_stripped, Notify.Style.ERROR)
// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
// break;
// }
// int algorithm = mSubkeysAdapter.getAlgorithm(position);
// // these are the PGP constants for RSA_GENERAL, RSA_ENCRYPT and RSA_SIGN
// if (algorithm != 1 && algorithm != 2 && algorithm != 3) {
// Notify.create(activity, R.string.edit_key_error_bad_nfc_algo, Notify.Style.ERROR)
// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
// break;
// }
// if (mSubkeysAdapter.getKeySize(position) != 2048) {
// Notify.create(activity, R.string.edit_key_error_bad_nfc_size, Notify.Style.ERROR)
// .show((ViewGroup) activity.findViewById(R.id.import_snackbar));
// break;
// }
//
//
// SubkeyChange change;
// change = mSaveKeyringParcel.getSubkeyChange(keyId);
// if (change == null) {
// mSaveKeyringParcel.mChangeSubKeys.add(
// new SubkeyChange(keyId, false, true)
// );
// break;
// }
// // toggle
// change.mMoveKeyToCard = !change.mMoveKeyToCard;
// if (change.mMoveKeyToCard && change.mDummyStrip) {
// // User had chosen to strip key, but now wants to divert it.
// change.mDummyStrip = false;
// }
// break;
}
}
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
}
};
// Create a new Messenger for the communication back
final Messenger messenger = new Messenger(returnHandler);
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
public void run() {
EditSubkeyDialogFragment dialogFragment =
EditSubkeyDialogFragment.newInstance(messenger);
dialogFragment.show(getActivity().getSupportFragmentManager(), "editSubkeyDialog");
}
});
}
private void editSubkeyExpiry(final int position) {
final long keyId = mSubkeysAdapter.getKeyId(position);
final Long creationDate = mSubkeysAdapter.getCreationDate(position);
final Long expiryDate = mSubkeysAdapter.getExpiryDate(position);
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case EditSubkeyExpiryDialogFragment.MESSAGE_NEW_EXPIRY:
mEditModeSaveKeyringParcel.getOrCreateSubkeyChange(keyId).mExpiry =
(Long) message.getData().getSerializable(
EditSubkeyExpiryDialogFragment.MESSAGE_DATA_EXPIRY);
break;
}
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
}
};
// Create a new Messenger for the communication back
final Messenger messenger = new Messenger(returnHandler);
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
public void run() {
EditSubkeyExpiryDialogFragment dialogFragment =
EditSubkeyExpiryDialogFragment.newInstance(messenger, creationDate, expiryDate);
dialogFragment.show(getActivity().getSupportFragmentManager(), "editSubkeyExpiryDialog");
}
});
}
}

View File

@@ -101,8 +101,8 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements
footer.setLayoutParams(params);
mUserIdsAddedList.addFooterView(footer, null, false);
mUserIdAddFabLayout = (ViewAnimator) view.findViewById(R.id.view_key_user_id_fab_layout);
view.findViewById(R.id.view_key_user_id_fab).setOnClickListener(new View.OnClickListener() {
mUserIdAddFabLayout = (ViewAnimator) view.findViewById(R.id.view_key_subkey_fab_layout);
view.findViewById(R.id.view_key_subkey_fab).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
addUserId();

View File

@@ -22,6 +22,7 @@ import android.content.res.ColorStateList;
import android.database.Cursor;
import android.graphics.PorterDuff;
import android.graphics.Typeface;
import android.support.annotation.Nullable;
import android.support.v4.widget.CursorAdapter;
import android.text.Spannable;
import android.text.SpannableString;
@@ -49,7 +50,7 @@ public class SubkeysAdapter extends CursorAdapter {
private LayoutInflater mInflater;
private SaveKeyringParcel mSaveKeyringParcel;
private boolean hasAnySecret;
private boolean mHasAnySecret;
private ColorStateList mDefaultTextColor;
public static final String[] SUBKEYS_PROJECTION = new String[]{
@@ -85,16 +86,10 @@ public class SubkeysAdapter extends CursorAdapter {
private static final int INDEX_EXPIRY = 13;
private static final int INDEX_FINGERPRINT = 14;
public SubkeysAdapter(Context context, Cursor c, int flags,
SaveKeyringParcel saveKeyringParcel) {
public SubkeysAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
mInflater = LayoutInflater.from(context);
mSaveKeyringParcel = saveKeyringParcel;
}
public SubkeysAdapter(Context context, Cursor c, int flags) {
this(context, c, flags, null);
}
public long getKeyId(int position) {
@@ -133,12 +128,12 @@ public class SubkeysAdapter extends CursorAdapter {
@Override
public Cursor swapCursor(Cursor newCursor) {
hasAnySecret = false;
mHasAnySecret = false;
if (newCursor != null && newCursor.moveToFirst()) {
do {
SecretKeyType hasSecret = SecretKeyType.fromNum(newCursor.getInt(INDEX_HAS_SECRET));
if (hasSecret.isUsable()) {
hasAnySecret = true;
mHasAnySecret = true;
break;
}
} while (newCursor.moveToNext());
@@ -354,4 +349,18 @@ public class SubkeysAdapter extends CursorAdapter {
}
}
/** Set this adapter into edit mode. This mode displays additional info for
* each item from a supplied SaveKeyringParcel reference.
*
* Note that it is up to the caller to reload the underlying cursor after
* updating the SaveKeyringParcel!
*
* @see SaveKeyringParcel
*
* @param saveKeyringParcel The parcel to get info from, or null to leave edit mode.
*/
public void setEditMode(@Nullable SaveKeyringParcel saveKeyringParcel) {
mSaveKeyringParcel = saveKeyringParcel;
}
}