inline subkey editing (wip commit)
This commit is contained in:
committed by
Dominik Schürmann
parent
fd119bda00
commit
00e97586b0
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user