diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index a4992163a..53ec4870c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -564,6 +564,26 @@ public class ProviderHelper { return fingerprint; } + public static String getUserId(Context context, Uri queryUri) { + String[] projection = new String[]{UserIds.USER_ID}; + Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null); + + String userId = null; + try { + if (cursor != null && cursor.moveToFirst()) { + int col = cursor.getColumnIndexOrThrow(UserIds.USER_ID); + + userId = cursor.getString(col); + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + + return userId; + } + public static ArrayList getKeyRingsAsArmoredString(Context context, Uri uri, long[] masterKeyIds) { ArrayList output = new ArrayList(); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java index 98116290d..eb0ae6309 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java @@ -17,6 +17,7 @@ package org.sufficientlysecure.keychain.ui; +import java.util.ArrayList; import java.util.Set; import org.sufficientlysecure.keychain.Id; @@ -37,6 +38,9 @@ import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; @@ -51,6 +55,7 @@ import android.view.ViewGroup; import android.widget.AbsListView.MultiChoiceModeListener; import android.widget.AdapterView; import android.widget.ListView; +import android.widget.Toast; import com.beardedhen.androidbootstrap.BootstrapButton; @@ -160,17 +165,15 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte switch (item.getItemId()) { case R.id.menu_key_list_public_multi_encrypt: { - encrypt(ids); - + encrypt(mode, ids); break; } case R.id.menu_key_list_public_multi_delete: { - showDeleteKeyDialog(ids); - + showDeleteKeyDialog(mode, ids); break; } } - return false; + return true; } @Override @@ -274,7 +277,7 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte startActivity(viewIntent); } - public void encrypt(long[] keyRingRowIds) { + public void encrypt(ActionMode mode, long[] keyRingRowIds) { // get master key ids from row ids long[] keyRingIds = new long[keyRingRowIds.length]; for (int i = 0; i < keyRingRowIds.length; i++) { @@ -286,6 +289,8 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, keyRingIds); // used instead of startActivity set actionbar based on callingPackage startActivityForResult(intent, 0); + + mode.finish(); } /** @@ -293,8 +298,34 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte * * @param keyRingRowIds */ - public void showDeleteKeyDialog(long[] keyRingRowIds) { - DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(null, + public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) { + // Message is received after key is deleted + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) { + Bundle returnData = message.getData(); + if (returnData != null + && returnData.containsKey(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED)) { + ArrayList notDeleted = + returnData.getStringArrayList(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED); + String notDeletedMsg = ""; + for (String userId : notDeleted) { + notDeletedMsg += userId + "\n"; + } + Toast.makeText(getActivity(), getString(R.string.error_can_not_delete_contacts, notDeletedMsg), + Toast.LENGTH_LONG).show(); + + mode.finish(); + } + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, keyRingRowIds, Id.type.public_key); deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog"); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java index f2e6131f6..f9d267f27 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java @@ -17,6 +17,7 @@ package org.sufficientlysecure.keychain.ui; +import java.util.ArrayList; import java.util.Set; import org.sufficientlysecure.keychain.Id; @@ -33,6 +34,9 @@ import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; import android.support.v4.app.ListFragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; @@ -45,6 +49,7 @@ import android.widget.AdapterView; import android.widget.ListView; import android.widget.AbsListView.MultiChoiceModeListener; import android.widget.AdapterView.OnItemClickListener; +import android.widget.Toast; public class KeyListSecretFragment extends ListFragment implements LoaderManager.LoaderCallbacks, OnItemClickListener { @@ -103,13 +108,12 @@ public class KeyListSecretFragment extends ListFragment implements } switch (item.getItemId()) { - case R.id.menu_key_list_public_multi_delete: { - showDeleteKeyDialog(ids); - - break; + case R.id.menu_key_list_public_multi_delete: { + showDeleteKeyDialog(mode, ids); + break; + } } - } - return false; + return true; } @Override @@ -120,7 +124,7 @@ public class KeyListSecretFragment extends ListFragment implements @Override public void onItemCheckedStateChanged(ActionMode mode, int position, long id, - boolean checked) { + boolean checked) { if (checked) { count++; mAdapter.setNewSelection(position, checked); @@ -153,8 +157,8 @@ public class KeyListSecretFragment extends ListFragment implements } // These are the rows that we will retrieve. - static final String[] PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID, - UserIds.USER_ID }; + static final String[] PROJECTION = new String[]{KeyRings._ID, KeyRings.MASTER_KEY_ID, + UserIds.USER_ID}; static final String SORT_ORDER = UserIds.USER_ID + " COLLATE LOCALIZED ASC"; public Loader onCreateLoader(int id, Bundle args) { @@ -202,13 +206,27 @@ public class KeyListSecretFragment extends ListFragment implements /** * Show dialog to delete key - * + * * @param keyRingRowIds */ - public void showDeleteKeyDialog(long[] keyRingRowIds) { - DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(null, + public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) { + // Message is received after key is deleted + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) { + mode.finish(); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, keyRingRowIds, Id.type.secret_key); deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog"); } + } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 4d9d49c07..0a7e06a62 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -161,18 +161,7 @@ public class ViewKeyActivity extends ActionBarActivity implements copyToClipboard(mDataUri); return true; case R.id.menu_key_view_delete: { - // 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(mDataUri, Id.type.public_key, returnHandler); + deleteKey(mDataUri); return true; } } @@ -476,4 +465,27 @@ public class ViewKeyActivity extends ActionBarActivity implements dialog.show(getSupportFragmentManager(), "shareNfcDialog"); } + private void deleteKey(Uri dataUri) { + // Message is received after key is deleted + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) { + Bundle returnData = message.getData(); + if (returnData != null + && returnData.containsKey(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED)) { + // we delete only this key, so MESSAGE_NOT_DELETED will solely contain this key + Toast.makeText(ViewKeyActivity.this, R.string.error_can_not_delete_contact, + Toast.LENGTH_LONG).show(); + } else { + setResult(RESULT_CANCELED); + finish(); + } + } + } + }; + + mExportHelper.deleteKey(dataUri, Id.type.public_key, returnHandler); + } + } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java index 109bfe929..3c44bff81 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java @@ -23,12 +23,16 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.util.Log; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; +import android.database.Cursor; +import android.net.Uri; import android.os.Bundle; import android.os.Message; import android.os.Messenger; @@ -36,6 +40,8 @@ import android.os.RemoteException; import android.support.v4.app.DialogFragment; import android.support.v4.app.FragmentActivity; +import java.util.ArrayList; + public class DeleteKeyDialogFragment extends DialogFragment { private static final String ARG_MESSENGER = "messenger"; private static final String ARG_DELETE_KEY_RING_ROW_IDS = "delete_file"; @@ -43,13 +49,15 @@ public class DeleteKeyDialogFragment extends DialogFragment { public static final int MESSAGE_OKAY = 1; + public static final String MESSAGE_NOT_DELETED = "not_deleted"; + private Messenger mMessenger; /** * Creates new instance of this delete file dialog fragment */ public static DeleteKeyDialogFragment newInstance(Messenger messenger, long[] keyRingRowIds, - int keyType) { + int keyType) { DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment(); Bundle args = new Bundle(); @@ -77,20 +85,13 @@ public class DeleteKeyDialogFragment extends DialogFragment { builder.setTitle(R.string.warning); if (keyRingRowIds.length == 1) { - // TODO: better way to do this? - String userId = activity.getString(R.string.user_id_no_name); - + Uri dataUri; if (keyType == Id.type.public_key) { - PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByRowId(activity, - keyRingRowIds[0]); - userId = PgpKeyHelper.getMainUserIdSafe(activity, - PgpKeyHelper.getMasterKey(keyRing)); + dataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowIds[0])); } else { - PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByRowId(activity, - keyRingRowIds[0]); - userId = PgpKeyHelper.getMainUserIdSafe(activity, - PgpKeyHelper.getMasterKey(keyRing)); + dataUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowIds[0])); } + String userId = ProviderHelper.getUserId(activity, dataUri); builder.setMessage(getString( keyType == Id.type.public_key ? R.string.key_deletion_confirmation @@ -104,9 +105,61 @@ public class DeleteKeyDialogFragment extends DialogFragment { @Override public void onClick(DialogInterface dialog, int id) { + ArrayList notDeleted = new ArrayList(); + if (keyType == Id.type.public_key) { - for (long keyRowId : keyRingRowIds) { - ProviderHelper.deletePublicKeyRing(activity, keyRowId); + Uri queryUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(); + String[] projection = new String[]{ + KeychainContract.KeyRings._ID, // 0 + KeychainContract.KeyRings.MASTER_KEY_ID, // 1 + KeychainContract.UserIds.USER_ID // 2 + }; + + // make selection with all entries where _ID is one of the given row ids + String selection = KeychainDatabase.Tables.KEY_RINGS + "." + + KeychainContract.KeyRings._ID + " IN("; + String selectionIDs = ""; + for (int i = 0; i < keyRingRowIds.length; i++) { + selectionIDs += "'" + String.valueOf(keyRingRowIds[i]) + "'"; + if (i+1 < keyRingRowIds.length) + selectionIDs += ","; + } + selection += selectionIDs + ")"; + + Cursor cursor = activity.getContentResolver().query(queryUri, projection, + selection, null, null); + + long rowId; + long masterKeyId; + String userId; + try { + while (cursor != null && cursor.moveToNext()) { + rowId = cursor.getLong(0); + masterKeyId = cursor.getLong(1); + userId = cursor.getString(2); + + Log.d(Constants.TAG, "rowId: " + rowId + ", masterKeyId: " + masterKeyId + + ", userId: " + userId); + + // check if a corresponding secret key exists... + Cursor secretCursor = activity.getContentResolver().query( + KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(String.valueOf(masterKeyId)), + null, null, null, null + ); + if (secretCursor != null && secretCursor.getCount() > 0) { + notDeleted.add(userId); + } else { + // it is okay to delete this key, no secret key found! + ProviderHelper.deletePublicKeyRing(activity, rowId); + } + if (secretCursor != null) { + secretCursor.close(); + } + } + } finally { + if (cursor != null) { + cursor.close(); + } } } else { for (long keyRowId : keyRingRowIds) { @@ -116,7 +169,13 @@ public class DeleteKeyDialogFragment extends DialogFragment { dismiss(); - sendMessageToHandler(MESSAGE_OKAY); + if (notDeleted.size() > 0) { + Bundle data = new Bundle(); + data.putStringArrayList(MESSAGE_NOT_DELETED, notDeleted); + sendMessageToHandler(MESSAGE_OKAY, data); + } else { + sendMessageToHandler(MESSAGE_OKAY, null); + } } }); builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @@ -131,13 +190,15 @@ public class DeleteKeyDialogFragment extends DialogFragment { /** * Send message back to handler which is initialized in a activity - * - * @param what - * Message integer you want to send + * + * @param what Message integer you want to send */ - private void sendMessageToHandler(Integer what) { + private void sendMessageToHandler(Integer what, Bundle data) { Message msg = Message.obtain(); msg.what = what; + if (data != null) { + msg.setData(data); + } try { mMessenger.send(msg); diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml index ce919c8f0..6fc4678fe 100644 --- a/OpenPGP-Keychain/src/main/res/values/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values/strings.xml @@ -283,6 +283,8 @@ NFC is not available on your device! Nothing to import! expiry date must come after creation date + you can not delete this contact because it is your own. Please delete it from the \'My Keys\' screen! + you can not delete the following contacts because they are your own:\n%sPlease delete them from the \'My Keys\' screen! done.