diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java')
-rw-r--r-- | OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java | 310 |
1 files changed, 273 insertions, 37 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java index ad437f924..69ccab162 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java @@ -18,24 +18,41 @@ package org.sufficientlysecure.keychain.ui; + +import android.content.Intent; 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.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.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.operations.results.EditKeyResult; +import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; +import org.sufficientlysecure.keychain.ui.adapter.UserIdsAddedAdapter; +import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; +import org.sufficientlysecure.keychain.ui.dialog.AddUserIdDialogFragment; +import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment; +import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; import org.sufficientlysecure.keychain.util.Log; @@ -44,33 +61,124 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements public static final String ARG_DATA_URI = "uri"; - private ListView mUserIds; - private static final int LOADER_ID_UNIFIED = 0; private static final int LOADER_ID_USER_IDS = 1; + private ListView mUserIds; + private ListView mUserIdsAddedList; + private View mUserIdsAddedLayout; + private ViewAnimator mUserIdAddFabLayout; + private UserIdsAdapter mUserIdsAdapter; + private UserIdsAddedAdapter mUserIdsAddedAdapter; + + private CryptoOperationHelper<SaveKeyringParcel, EditKeyResult> mEditKeyHelper; private Uri mDataUri; + private long mMasterKeyId; + private byte[] mFingerprint; + private boolean mHasSecret; + private SaveKeyringParcel mEditModeSaveKeyringParcel; + @Override public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { View root = super.onCreateView(inflater, superContainer, savedInstanceState); - View view = inflater.inflate(R.layout.view_key_adv_main_fragment, getContainer()); + View view = inflater.inflate(R.layout.view_key_adv_user_ids_fragment, getContainer()); mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids); + mUserIdsAddedList = (ListView) view.findViewById(R.id.view_key_user_ids_added); + mUserIdsAddedLayout = view.findViewById(R.id.view_key_user_ids_add_layout); mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - showUserIdInfo(position); + showOrEditUserIdInfo(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); + mUserIdsAddedList.addFooterView(footer, null, false); + + 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(); + } + }); + + setHasOptionsMenu(true); + return root; } + private void showOrEditUserIdInfo(final int position) { + if (mEditModeSaveKeyringParcel != null) { + editUserId(position); + } else { + showUserIdInfo(position); + } + } + + private void editUserId(final int position) { + final String userId = mUserIdsAdapter.getUserId(position); + final boolean isRevoked = mUserIdsAdapter.getIsRevoked(position); + final boolean isRevokedPending = mUserIdsAdapter.getIsRevokedPending(position); + + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + switch (message.what) { + case EditUserIdDialogFragment.MESSAGE_CHANGE_PRIMARY_USER_ID: + // toggle + if (mEditModeSaveKeyringParcel.mChangePrimaryUserId != null + && mEditModeSaveKeyringParcel.mChangePrimaryUserId.equals(userId)) { + mEditModeSaveKeyringParcel.mChangePrimaryUserId = null; + } else { + mEditModeSaveKeyringParcel.mChangePrimaryUserId = userId; + } + break; + case EditUserIdDialogFragment.MESSAGE_REVOKE: + // toggle + if (mEditModeSaveKeyringParcel.mRevokeUserIds.contains(userId)) { + mEditModeSaveKeyringParcel.mRevokeUserIds.remove(userId); + } else { + mEditModeSaveKeyringParcel.mRevokeUserIds.add(userId); + // not possible to revoke and change to primary user id + if (mEditModeSaveKeyringParcel.mChangePrimaryUserId != null + && mEditModeSaveKeyringParcel.mChangePrimaryUserId.equals(userId)) { + mEditModeSaveKeyringParcel.mChangePrimaryUserId = null; + } + } + break; + } + getLoaderManager().getLoader(LOADER_ID_USER_IDS).forceLoad(); + } + }; + + // Create a new Messenger for the communication back + final Messenger messenger = new Messenger(returnHandler); + + DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { + public void run() { + EditUserIdDialogFragment dialogFragment = + EditUserIdDialogFragment.newInstance(messenger, isRevoked, isRevokedPending); + dialogFragment.show(getActivity().getSupportFragmentManager(), "editUserIdDialog"); + } + }); + } + private void showUserIdInfo(final int position) { + final boolean isRevoked = mUserIdsAdapter.getIsRevoked(position); final int isVerified = mUserIdsAdapter.getIsVerified(position); @@ -84,6 +192,30 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements }); } + private void addUserId() { + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == SetPassphraseDialogFragment.MESSAGE_OKAY) { + Bundle data = message.getData(); + + // add new user id + mUserIdsAddedAdapter.add(data + .getString(AddUserIdDialogFragment.MESSAGE_DATA_USER_ID)); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + // pre-fill out primary name + AddUserIdDialogFragment addUserIdDialog = + AddUserIdDialogFragment.newInstance(messenger, "", true); + + addUserIdDialog.show(getActivity().getSupportFragmentManager(), "addUserIdDialog"); + } + @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); @@ -98,10 +230,19 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements loadData(dataUri); } + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (mEditKeyHelper != null) { + mEditKeyHelper.handleActivityResult(requestCode, resultCode, data); + } + + super.onActivityResult(requestCode, resultCode, data); + } + private void loadData(Uri dataUri) { mDataUri = dataUri; - Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); + Log.i(Constants.TAG, "mDataUri: " + mDataUri); mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0); mUserIds.setAdapter(mUserIdsAdapter); @@ -112,27 +253,31 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); } - static final String[] UNIFIED_PROJECTION = new String[]{ - KeyRings._ID, KeyRings.MASTER_KEY_ID, - KeyRings.HAS_ANY_SECRET, KeyRings.IS_REVOKED, KeyRings.IS_EXPIRED, KeyRings.HAS_ENCRYPT + // These are the rows that we will retrieve. + static final String[] PROJECTION = new String[]{ + KeychainContract.KeyRings._ID, + KeychainContract.KeyRings.MASTER_KEY_ID, + KeychainContract.KeyRings.HAS_ANY_SECRET, + KeychainContract.KeyRings.FINGERPRINT, }; - static final int INDEX_UNIFIED_MASTER_KEY_ID = 1; - static final int INDEX_UNIFIED_HAS_ANY_SECRET = 2; - static final int INDEX_UNIFIED_IS_REVOKED = 3; - static final int INDEX_UNIFIED_IS_EXPIRED = 4; - static final int INDEX_UNIFIED_HAS_ENCRYPT = 5; - public Loader<Cursor> onCreateLoader(int id, Bundle args) { - setContentShown(false); + static final int INDEX_MASTER_KEY_ID = 1; + static final int INDEX_HAS_ANY_SECRET = 2; + static final int INDEX_FINGERPRINT = 3; + public Loader<Cursor> onCreateLoader(int id, Bundle args) { switch (id) { case LOADER_ID_UNIFIED: { - Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri); - return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null); + Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri); + return new CursorLoader(getActivity(), baseUri, + PROJECTION, null, null, null); } + case LOADER_ID_USER_IDS: { - Uri baseUri = UserPackets.buildUserIdsUri(mDataUri); - return new CursorLoader(getActivity(), baseUri, + setContentShown(false); + + Uri userIdUri = UserPackets.buildUserIdsUri(mDataUri); + return new CursorLoader(getActivity(), userIdUri, UserIdsAdapter.USER_PACKETS_PROJECTION, null, null, null); } @@ -142,31 +287,29 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements } public void onLoadFinished(Loader<Cursor> loader, Cursor data) { - /* TODO better error handling? May cause problems when a key is deleted, - * because the notification triggers faster than the activity closes. - */ - // Avoid NullPointerExceptions... + // Avoid NullPointerExceptions, if we get an empty result set. if (data.getCount() == 0) { return; } - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) + switch (loader.getId()) { case LOADER_ID_UNIFIED: { - if (data.moveToFirst()) { - + data.moveToFirst(); - break; - } + mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID); + mHasSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0; + mFingerprint = data.getBlob(INDEX_FINGERPRINT); + break; } - case LOADER_ID_USER_IDS: { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) mUserIdsAdapter.swapCursor(data); + + setContentShown(true); break; } - } - setContentShown(true); } /** @@ -174,11 +317,104 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements * We need to make sure we are no longer using it. */ public void onLoaderReset(Loader<Cursor> loader) { - switch (loader.getId()) { - case LOADER_ID_USER_IDS: - mUserIdsAdapter.swapCursor(null); - break; + if (loader.getId() != LOADER_ID_USER_IDS) { + return; + } + mUserIdsAdapter.swapCursor(null); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_action_mode_edit: + enterEditMode(); + return true; + default: + return super.onOptionsItemSelected(item); } } + public void enterEditMode() { + FragmentActivity activity = getActivity(); + if (activity == null) { + return; + } + activity.startActionMode(new ActionMode.Callback() { + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + + mEditModeSaveKeyringParcel = new SaveKeyringParcel(mMasterKeyId, mFingerprint); + + mUserIdsAddedAdapter = + new UserIdsAddedAdapter(getActivity(), mEditModeSaveKeyringParcel.mAddUserIds, false); + mUserIdsAddedList.setAdapter(mUserIdsAddedAdapter); + mUserIdsAddedLayout.setVisibility(View.VISIBLE); + mUserIdAddFabLayout.setDisplayedChild(1); + + mUserIdsAdapter.setEditMode(mEditModeSaveKeyringParcel); + getLoaderManager().restartLoader(LOADER_ID_USER_IDS, null, ViewKeyAdvUserIdsFragment.this); + + mode.setTitle(R.string.title_edit_identities); + 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) { + editKey(mode); + return true; + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + mEditModeSaveKeyringParcel = null; + mUserIdsAdapter.setEditMode(null); + mUserIdsAddedLayout.setVisibility(View.GONE); + mUserIdAddFabLayout.setDisplayedChild(0); + getLoaderManager().restartLoader(LOADER_ID_USER_IDS, null, ViewKeyAdvUserIdsFragment.this); + } + }); + } + + private void editKey(final ActionMode mode) { + CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult> editKeyCallback + = new CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult>() { + + @Override + public SaveKeyringParcel createOperationInput() { + return mEditModeSaveKeyringParcel; + } + + @Override + public void onCryptoOperationSuccess(EditKeyResult result) { + mode.finish(); + result.createNotify(getActivity()).show(); + } + + @Override + public void onCryptoOperationCancelled() { + mode.finish(); + } + + @Override + public void onCryptoOperationError(EditKeyResult result) { + mode.finish(); + result.createNotify(getActivity()).show(); + } + + @Override + public boolean onCryptoSetProgress(String msg, int progress, int max) { + return false; + } + }; + mEditKeyHelper = new CryptoOperationHelper<>(1, this, editKeyCallback, R.string.progress_saving); + mEditKeyHelper.cryptoOperation(); + } + } |