mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-15 21:35:05 -05:00
Work on multiselect in key list
This commit is contained in:
parent
28d9a2f26b
commit
708baaa68c
@ -194,6 +194,7 @@
|
|||||||
<string name="specify_file_to_export_to">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string>
|
<string name="specify_file_to_export_to">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string>
|
||||||
<string name="specify_file_to_export_secret_keys_to">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string>
|
<string name="specify_file_to_export_secret_keys_to">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string>
|
||||||
<string name="key_deletion_confirmation">Do you really want to delete the key \'%s\'?\nYou can\'t undo this!</string>
|
<string name="key_deletion_confirmation">Do you really want to delete the key \'%s\'?\nYou can\'t undo this!</string>
|
||||||
|
<string name="key_deletion_confirmation_multi">Do you really want to delete all selected keys?\nYou can\'t undo this!</string>
|
||||||
<string name="secret_key_deletion_confirmation">Do you really want to delete the SECRET key \'%s\'?\nYou can\'t undo this!</string>
|
<string name="secret_key_deletion_confirmation">Do you really want to delete the SECRET key \'%s\'?\nYou can\'t undo this!</string>
|
||||||
<string name="keys_added_and_updated">Successfully added %1$s key(s) and updated %2$s key(s).</string>
|
<string name="keys_added_and_updated">Successfully added %1$s key(s) and updated %2$s key(s).</string>
|
||||||
<string name="keys_added">Successfully added %s key(s).</string>
|
<string name="keys_added">Successfully added %s key(s).</string>
|
||||||
@ -348,4 +349,9 @@
|
|||||||
<string name="share_qr_code_dialog_progress">QR Code %1$d of %2$d</string>
|
<string name="share_qr_code_dialog_progress">QR Code %1$d of %2$d</string>
|
||||||
<string name="share_nfc_dialog">Share with NFC</string>
|
<string name="share_nfc_dialog">Share with NFC</string>
|
||||||
|
|
||||||
|
<plurals name="key_list_selected_keys">
|
||||||
|
<item quantity="one">1 key selected.</item>
|
||||||
|
<item quantity="other">%d keys selected.</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -225,9 +225,9 @@ public class KeyListActivity extends SherlockFragmentActivity {
|
|||||||
/**
|
/**
|
||||||
* Show dialog to delete key
|
* Show dialog to delete key
|
||||||
*
|
*
|
||||||
* @param keyRingId
|
* @param keyRingIds
|
||||||
*/
|
*/
|
||||||
public void showDeleteKeyDialog(long keyRingId) {
|
public void showDeleteKeyDialog(long[] keyRingIds) {
|
||||||
// Message is received after key is deleted
|
// Message is received after key is deleted
|
||||||
Handler returnHandler = new Handler() {
|
Handler returnHandler = new Handler() {
|
||||||
@Override
|
@Override
|
||||||
@ -242,7 +242,7 @@ public class KeyListActivity extends SherlockFragmentActivity {
|
|||||||
Messenger messenger = new Messenger(returnHandler);
|
Messenger messenger = new Messenger(returnHandler);
|
||||||
|
|
||||||
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
|
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
|
||||||
keyRingId, mKeyType);
|
keyRingIds, mKeyType);
|
||||||
|
|
||||||
deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog");
|
deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog");
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,16 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui;
|
package org.sufficientlysecure.keychain.ui;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Id;
|
import org.sufficientlysecure.keychain.Id;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
|
||||||
import org.sufficientlysecure.keychain.ui.adapter.KeyListPublicAdapter;
|
import org.sufficientlysecure.keychain.ui.adapter.KeyListPublicAdapter;
|
||||||
|
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
||||||
|
|
||||||
import se.emilsjolander.stickylistheaders.ApiLevelTooLowException;
|
import se.emilsjolander.stickylistheaders.ApiLevelTooLowException;
|
||||||
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
|
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
|
||||||
@ -30,23 +34,32 @@ import android.annotation.SuppressLint;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.Messenger;
|
||||||
|
import android.provider.BaseColumns;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.LoaderManager;
|
import android.support.v4.app.LoaderManager;
|
||||||
import android.support.v4.content.CursorLoader;
|
import android.support.v4.content.CursorLoader;
|
||||||
import android.support.v4.content.Loader;
|
import android.support.v4.content.Loader;
|
||||||
|
import android.view.ActionMode;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AbsListView.MultiChoiceModeListener;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ListView;
|
||||||
import com.actionbarsherlock.app.SherlockFragment;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
|
* Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
|
||||||
* StickyListHeaders library which does not extend upon ListView.
|
* StickyListHeaders library which does not extend upon ListView.
|
||||||
*/
|
*/
|
||||||
public class KeyListPublicFragment extends SherlockFragment implements
|
public class KeyListPublicFragment extends Fragment implements AdapterView.OnItemClickListener,
|
||||||
AdapterView.OnItemClickListener, LoaderManager.LoaderCallbacks<Cursor> {
|
LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
|
|
||||||
private KeyListPublicActivity mKeyListPublicActivity;
|
private KeyListPublicActivity mKeyListPublicActivity;
|
||||||
private KeyListPublicAdapter mAdapter;
|
private KeyListPublicAdapter mAdapter;
|
||||||
@ -84,6 +97,73 @@ public class KeyListPublicFragment extends SherlockFragment implements
|
|||||||
// this view is made visible if no data is available
|
// this view is made visible if no data is available
|
||||||
mStickyList.setEmptyView(getActivity().findViewById(R.id.empty));
|
mStickyList.setEmptyView(getActivity().findViewById(R.id.empty));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ActionBarSherlock does not support MultiChoiceModeListener. Thus multi-selection is only
|
||||||
|
* available for Android >= 3.0
|
||||||
|
*/
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||||
|
mStickyList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
||||||
|
mStickyList.getWrappedList().setMultiChoiceModeListener(new MultiChoiceModeListener() {
|
||||||
|
|
||||||
|
private int count = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||||
|
android.view.MenuInflater inflater = getActivity().getMenuInflater();
|
||||||
|
inflater.inflate(R.menu.key_list_multi_selection, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||||
|
// StringBuilder sb = new StringBuilder();
|
||||||
|
Set<Integer> positions = mAdapter.getCurrentCheckedPosition();
|
||||||
|
// for (Integer pos : positions) {
|
||||||
|
// sb.append(" " + pos + ",");
|
||||||
|
// }
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.delete_entry:
|
||||||
|
long[] ids = new long[positions.size()];
|
||||||
|
for (int i=0; i < positions.size(); i++) {
|
||||||
|
ids[i] = mAdapter.getItemId(positions.);
|
||||||
|
}
|
||||||
|
showDeleteKeyDialog(ids.to);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyActionMode(ActionMode mode) {
|
||||||
|
count = 0;
|
||||||
|
mAdapter.clearSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
|
||||||
|
boolean checked) {
|
||||||
|
if (checked) {
|
||||||
|
count++;
|
||||||
|
mAdapter.setNewSelection(position, checked);
|
||||||
|
} else {
|
||||||
|
count--;
|
||||||
|
mAdapter.removeSelection(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
String keysSelected = getResources().getQuantityString(
|
||||||
|
R.plurals.key_list_selected_keys, count, count);
|
||||||
|
mode.setTitle(keysSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: Not supported by StickyListHeader, thus no indicator is shown while loading
|
// NOTE: Not supported by StickyListHeader, thus no indicator is shown while loading
|
||||||
// Start out with a progress indicator.
|
// Start out with a progress indicator.
|
||||||
// setListShown(false);
|
// setListShown(false);
|
||||||
@ -152,4 +232,31 @@ public class KeyListPublicFragment extends SherlockFragment implements
|
|||||||
startActivity(detailsIntent);
|
startActivity(detailsIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* Show dialog to delete key
|
||||||
|
*
|
||||||
|
* TODO: no messenger needed etc!
|
||||||
|
*
|
||||||
|
* @param keyRingRowIds
|
||||||
|
*/
|
||||||
|
public void showDeleteKeyDialog(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) {
|
||||||
|
// no further actions needed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,273 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui.adapter;
|
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
|
||||||
import org.sufficientlysecure.keychain.Id;
|
|
||||||
import org.sufficientlysecure.keychain.helper.OtherHelper;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
|
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.database.DatabaseUtils;
|
|
||||||
import android.database.MergeCursor;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.provider.BaseColumns;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.CursorTreeAdapter;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
public class KeyListAdapter extends CursorTreeAdapter {
|
|
||||||
private Context mContext;
|
|
||||||
private LayoutInflater mInflater;
|
|
||||||
|
|
||||||
protected int mKeyType;
|
|
||||||
|
|
||||||
private static final int CHILD_KEY = 0;
|
|
||||||
private static final int CHILD_USER_ID = 1;
|
|
||||||
private static final int CHILD_FINGERPRINT = 2;
|
|
||||||
|
|
||||||
public KeyListAdapter(Context context, Cursor groupCursor, int keyType) {
|
|
||||||
super(groupCursor, context);
|
|
||||||
mContext = context;
|
|
||||||
mInflater = LayoutInflater.from(context);
|
|
||||||
mKeyType = keyType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inflate new view for group items
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public View newGroupView(Context context, Cursor cursor, boolean isExpanded, ViewGroup parent) {
|
|
||||||
return mInflater.inflate(R.layout.key_list_item, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds TextViews from group view to results from database group cursor.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded) {
|
|
||||||
int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID);
|
|
||||||
|
|
||||||
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
|
|
||||||
mainUserId.setText(R.string.unknown_user_id);
|
|
||||||
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
|
|
||||||
mainUserIdRest.setText("");
|
|
||||||
|
|
||||||
String userId = cursor.getString(userIdIndex);
|
|
||||||
if (userId != null) {
|
|
||||||
String[] userIdSplit = OtherHelper.splitUserId(userId);
|
|
||||||
|
|
||||||
if (userIdSplit[1] != null) {
|
|
||||||
mainUserIdRest.setText(userIdSplit[1]);
|
|
||||||
}
|
|
||||||
mainUserId.setText(userIdSplit[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mainUserId.getText().length() == 0) {
|
|
||||||
mainUserId.setText(R.string.unknown_user_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mainUserIdRest.getText().length() == 0) {
|
|
||||||
mainUserIdRest.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
mainUserIdRest.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inflate new view for child items
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public View newChildView(Context context, Cursor cursor, boolean isLastChild, ViewGroup parent) {
|
|
||||||
return mInflater.inflate(R.layout.key_list_child_item, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind TextViews from view of childs based on query results
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) {
|
|
||||||
LinearLayout keyLayout = (LinearLayout) view.findViewById(R.id.keyLayout);
|
|
||||||
LinearLayout userIdLayout = (LinearLayout) view.findViewById(R.id.userIdLayout);
|
|
||||||
|
|
||||||
// first entry is fingerprint
|
|
||||||
if (cursor.getPosition() == 0) {
|
|
||||||
// show only userId layout
|
|
||||||
keyLayout.setVisibility(View.GONE);
|
|
||||||
userIdLayout.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
String fingerprint = PgpKeyHelper.getFingerPrint(context,
|
|
||||||
cursor.getLong(cursor.getColumnIndex(Keys.KEY_ID)));
|
|
||||||
fingerprint = fingerprint.replace(" ", "\n");
|
|
||||||
|
|
||||||
TextView userId = (TextView) view.findViewById(R.id.userId);
|
|
||||||
if (userId == null) {
|
|
||||||
Log.d(Constants.TAG, "userId is null!");
|
|
||||||
}
|
|
||||||
userId.setText(context.getString(R.string.fingerprint) + "\n" + fingerprint);
|
|
||||||
} else {
|
|
||||||
// differentiate between keys and userIds in MergeCursor
|
|
||||||
if (cursor.getColumnIndex(Keys.KEY_ID) != -1) {
|
|
||||||
keyLayout.setVisibility(View.VISIBLE);
|
|
||||||
userIdLayout.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(cursor
|
|
||||||
.getColumnIndex(Keys.KEY_ID)));
|
|
||||||
String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
|
|
||||||
cursor.getInt(cursor.getColumnIndex(Keys.ALGORITHM)),
|
|
||||||
cursor.getInt(cursor.getColumnIndex(Keys.KEY_SIZE)));
|
|
||||||
|
|
||||||
TextView keyId = (TextView) view.findViewById(R.id.keyId);
|
|
||||||
keyId.setText(keyIdStr);
|
|
||||||
|
|
||||||
TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
|
|
||||||
keyDetails.setText("(" + algorithmStr + ")");
|
|
||||||
|
|
||||||
ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey);
|
|
||||||
if (cursor.getInt(cursor.getColumnIndex(Keys.IS_MASTER_KEY)) != 1) {
|
|
||||||
masterKeyIcon.setVisibility(View.INVISIBLE);
|
|
||||||
} else {
|
|
||||||
masterKeyIcon.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
|
|
||||||
if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_CERTIFY)) != 1) {
|
|
||||||
certifyIcon.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
certifyIcon.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
|
|
||||||
if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_ENCRYPT)) != 1) {
|
|
||||||
encryptIcon.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
encryptIcon.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
|
|
||||||
if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_SIGN)) != 1) {
|
|
||||||
signIcon.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
signIcon.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
keyLayout.setVisibility(View.GONE);
|
|
||||||
userIdLayout.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
String userIdStr = cursor.getString(cursor.getColumnIndex(UserIds.USER_ID));
|
|
||||||
|
|
||||||
TextView userId = (TextView) view.findViewById(R.id.userId);
|
|
||||||
userId.setText(userIdStr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given the group cursor, we start cursors for a fingerprint, keys, and userIds, which are
|
|
||||||
* merged together and build the child cursor
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected Cursor getChildrenCursor(Cursor groupCursor) {
|
|
||||||
final long keyRingRowId = groupCursor.getLong(groupCursor.getColumnIndex(BaseColumns._ID));
|
|
||||||
|
|
||||||
Cursor fingerprintCursor = getChildCursor(keyRingRowId, CHILD_FINGERPRINT);
|
|
||||||
Cursor keyCursor = getChildCursor(keyRingRowId, CHILD_KEY);
|
|
||||||
Cursor userIdCursor = getChildCursor(keyRingRowId, CHILD_USER_ID);
|
|
||||||
|
|
||||||
MergeCursor mergeCursor = new MergeCursor(new Cursor[] { fingerprintCursor, keyCursor,
|
|
||||||
userIdCursor });
|
|
||||||
Log.d(Constants.TAG, "mergeCursor:" + DatabaseUtils.dumpCursorToString(mergeCursor));
|
|
||||||
|
|
||||||
return mergeCursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This builds a cursor for a specific type of children
|
|
||||||
*
|
|
||||||
* @param keyRingRowId
|
|
||||||
* foreign row id of the keyRing
|
|
||||||
* @param type
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private Cursor getChildCursor(long keyRingRowId, int type) {
|
|
||||||
Uri uri = null;
|
|
||||||
String[] projection = null;
|
|
||||||
String sortOrder = null;
|
|
||||||
String selection = null;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case CHILD_FINGERPRINT:
|
|
||||||
projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM,
|
|
||||||
Keys.KEY_SIZE, Keys.CAN_CERTIFY, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, };
|
|
||||||
sortOrder = Keys.RANK + " ASC";
|
|
||||||
|
|
||||||
// use only master key for fingerprint
|
|
||||||
selection = Keys.IS_MASTER_KEY + " = 1 ";
|
|
||||||
|
|
||||||
if (mKeyType == Id.type.public_key) {
|
|
||||||
uri = Keys.buildPublicKeysUri(String.valueOf(keyRingRowId));
|
|
||||||
} else {
|
|
||||||
uri = Keys.buildSecretKeysUri(String.valueOf(keyRingRowId));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CHILD_KEY:
|
|
||||||
projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM,
|
|
||||||
Keys.KEY_SIZE, Keys.CAN_CERTIFY, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, };
|
|
||||||
sortOrder = Keys.RANK + " ASC";
|
|
||||||
|
|
||||||
if (mKeyType == Id.type.public_key) {
|
|
||||||
uri = Keys.buildPublicKeysUri(String.valueOf(keyRingRowId));
|
|
||||||
} else {
|
|
||||||
uri = Keys.buildSecretKeysUri(String.valueOf(keyRingRowId));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CHILD_USER_ID:
|
|
||||||
projection = new String[] { UserIds._ID, UserIds.USER_ID, UserIds.RANK, };
|
|
||||||
sortOrder = UserIds.RANK + " ASC";
|
|
||||||
|
|
||||||
// not the main user id
|
|
||||||
selection = UserIds.RANK + " > 0 ";
|
|
||||||
|
|
||||||
if (mKeyType == Id.type.public_key) {
|
|
||||||
uri = UserIds.buildPublicUserIdsUri(String.valueOf(keyRingRowId));
|
|
||||||
} else {
|
|
||||||
uri = UserIds.buildSecretUserIdsUri(String.valueOf(keyRingRowId));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return mContext.getContentResolver().query(uri, projection, selection, null, sortOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -17,15 +17,18 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.ui.adapter;
|
package org.sufficientlysecure.keychain.ui.adapter;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Set;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.helper.OtherHelper;
|
import org.sufficientlysecure.keychain.helper.OtherHelper;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
|
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.support.v4.widget.CursorAdapter;
|
import android.support.v4.widget.CursorAdapter;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -39,6 +42,9 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
|
|||||||
private LayoutInflater mInflater;
|
private LayoutInflater mInflater;
|
||||||
private int mSectionColumnIndex;
|
private int mSectionColumnIndex;
|
||||||
|
|
||||||
|
@SuppressLint("UseSparseArrays")
|
||||||
|
private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>();
|
||||||
|
|
||||||
public KeyListPublicAdapter(Context context, Cursor c, int flags, int sectionColumnIndex) {
|
public KeyListPublicAdapter(Context context, Cursor c, int flags, int sectionColumnIndex) {
|
||||||
super(context, c, flags);
|
super(context, c, flags);
|
||||||
|
|
||||||
@ -138,7 +144,8 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
|
|||||||
}
|
}
|
||||||
|
|
||||||
// return the first character of the name as ID because this is what
|
// return the first character of the name as ID because this is what
|
||||||
// headers are based upon
|
// headers private HashMap<Integer, Boolean> mSelection = new HashMap<Integer,
|
||||||
|
// Boolean>();are based upon
|
||||||
return mCursor.getString(mSectionColumnIndex).subSequence(0, 1).charAt(0);
|
return mCursor.getString(mSectionColumnIndex).subSequence(0, 1).charAt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,4 +153,46 @@ public class KeyListPublicAdapter extends CursorAdapter implements StickyListHea
|
|||||||
TextView text;
|
TextView text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** -------------------------- MULTI-SELECTION METHODS -------------- */
|
||||||
|
public void setNewSelection(int position, boolean value) {
|
||||||
|
mSelection.put(position, value);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPositionChecked(int position) {
|
||||||
|
Boolean result = mSelection.get(position);
|
||||||
|
return result == null ? false : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Integer> getCurrentCheckedPosition() {
|
||||||
|
return mSelection.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeSelection(int position) {
|
||||||
|
mSelection.remove(position);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearSelection() {
|
||||||
|
mSelection.clear();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
// let the adapter handle setting up the row views
|
||||||
|
View v = super.getView(position, convertView, parent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change color for multi-selection
|
||||||
|
*/
|
||||||
|
// default color
|
||||||
|
v.setBackgroundColor(Color.TRANSPARENT);
|
||||||
|
if (mSelection.get(position) != null) {
|
||||||
|
// this is a selected position, change color!
|
||||||
|
v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis));
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,19 +19,14 @@ package org.sufficientlysecure.keychain.ui.adapter;
|
|||||||
|
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.helper.OtherHelper;
|
import org.sufficientlysecure.keychain.helper.OtherHelper;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
|
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.support.v4.widget.CursorAdapter;
|
import android.support.v4.widget.CursorAdapter;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
public class KeyListSecretAdapter extends CursorAdapter {
|
public class KeyListSecretAdapter extends CursorAdapter {
|
||||||
|
@ -38,7 +38,7 @@ import android.support.v4.app.FragmentActivity;
|
|||||||
|
|
||||||
public class DeleteKeyDialogFragment extends DialogFragment {
|
public class DeleteKeyDialogFragment extends DialogFragment {
|
||||||
private static final String ARG_MESSENGER = "messenger";
|
private static final String ARG_MESSENGER = "messenger";
|
||||||
private static final String ARG_DELETE_KEY_RING_ROW_ID = "delete_file";
|
private static final String ARG_DELETE_KEY_RING_ROW_IDS = "delete_file";
|
||||||
private static final String ARG_KEY_TYPE = "key_type";
|
private static final String ARG_KEY_TYPE = "key_type";
|
||||||
|
|
||||||
public static final int MESSAGE_OKAY = 1;
|
public static final int MESSAGE_OKAY = 1;
|
||||||
@ -48,13 +48,13 @@ public class DeleteKeyDialogFragment extends DialogFragment {
|
|||||||
/**
|
/**
|
||||||
* Creates new instance of this delete file dialog fragment
|
* Creates new instance of this delete file dialog fragment
|
||||||
*/
|
*/
|
||||||
public static DeleteKeyDialogFragment newInstance(Messenger messenger, long deleteKeyRingRowId,
|
public static DeleteKeyDialogFragment newInstance(Messenger messenger, long[] keyRingRowIds,
|
||||||
int keyType) {
|
int keyType) {
|
||||||
DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment();
|
DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment();
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
|
|
||||||
args.putParcelable(ARG_MESSENGER, messenger);
|
args.putParcelable(ARG_MESSENGER, messenger);
|
||||||
args.putLong(ARG_DELETE_KEY_RING_ROW_ID, deleteKeyRingRowId);
|
args.putLongArray(ARG_DELETE_KEY_RING_ROW_IDS, keyRingRowIds);
|
||||||
args.putInt(ARG_KEY_TYPE, keyType);
|
args.putInt(ARG_KEY_TYPE, keyType);
|
||||||
|
|
||||||
frag.setArguments(args);
|
frag.setArguments(args);
|
||||||
@ -70,36 +70,48 @@ public class DeleteKeyDialogFragment extends DialogFragment {
|
|||||||
final FragmentActivity activity = getActivity();
|
final FragmentActivity activity = getActivity();
|
||||||
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
|
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
|
||||||
|
|
||||||
final long deleteKeyRingRowId = getArguments().getLong(ARG_DELETE_KEY_RING_ROW_ID);
|
final long[] keyRingRowIds = getArguments().getLongArray(ARG_DELETE_KEY_RING_ROW_IDS);
|
||||||
final int keyType = getArguments().getInt(ARG_KEY_TYPE);
|
final int keyType = getArguments().getInt(ARG_KEY_TYPE);
|
||||||
|
|
||||||
// TODO: better way to do this?
|
|
||||||
String userId = activity.getString(R.string.unknown_user_id);
|
|
||||||
|
|
||||||
if (keyType == Id.type.public_key) {
|
|
||||||
PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByRowId(activity,
|
|
||||||
deleteKeyRingRowId);
|
|
||||||
userId = PgpKeyHelper.getMainUserIdSafe(activity, PgpKeyHelper.getMasterKey(keyRing));
|
|
||||||
} else {
|
|
||||||
PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByRowId(activity,
|
|
||||||
deleteKeyRingRowId);
|
|
||||||
userId = PgpKeyHelper.getMainUserIdSafe(activity, PgpKeyHelper.getMasterKey(keyRing));
|
|
||||||
}
|
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||||
builder.setTitle(R.string.warning);
|
builder.setTitle(R.string.warning);
|
||||||
builder.setMessage(getString(
|
|
||||||
keyType == Id.type.public_key ? R.string.key_deletion_confirmation
|
if (keyRingRowIds.length == 1) {
|
||||||
: R.string.secret_key_deletion_confirmation, userId));
|
// TODO: better way to do this?
|
||||||
|
String userId = activity.getString(R.string.unknown_user_id);
|
||||||
|
|
||||||
|
if (keyType == Id.type.public_key) {
|
||||||
|
PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByRowId(activity,
|
||||||
|
keyRingRowIds[0]);
|
||||||
|
userId = PgpKeyHelper.getMainUserIdSafe(activity,
|
||||||
|
PgpKeyHelper.getMasterKey(keyRing));
|
||||||
|
} else {
|
||||||
|
PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByRowId(activity,
|
||||||
|
keyRingRowIds[0]);
|
||||||
|
userId = PgpKeyHelper.getMainUserIdSafe(activity,
|
||||||
|
PgpKeyHelper.getMasterKey(keyRing));
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.setMessage(getString(
|
||||||
|
keyType == Id.type.public_key ? R.string.key_deletion_confirmation
|
||||||
|
: R.string.secret_key_deletion_confirmation, userId));
|
||||||
|
} else {
|
||||||
|
builder.setMessage(R.string.key_deletion_confirmation_multi);
|
||||||
|
}
|
||||||
|
|
||||||
builder.setIcon(android.R.drawable.ic_dialog_alert);
|
builder.setIcon(android.R.drawable.ic_dialog_alert);
|
||||||
builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {
|
builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
if (keyType == Id.type.public_key) {
|
if (keyType == Id.type.public_key) {
|
||||||
ProviderHelper.deletePublicKeyRing(activity, deleteKeyRingRowId);
|
for (long keyRowId : keyRingRowIds) {
|
||||||
|
ProviderHelper.deletePublicKeyRing(activity, keyRowId);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ProviderHelper.deleteSecretKeyRing(activity, deleteKeyRingRowId);
|
for (long keyRowId : keyRingRowIds) {
|
||||||
|
ProviderHelper.deleteSecretKeyRing(activity, keyRowId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dismiss();
|
dismiss();
|
||||||
|
@ -17,8 +17,8 @@ I am happy about every code contribution and appreciate your effort to help us d
|
|||||||
## Build with Gradle
|
## Build with Gradle
|
||||||
|
|
||||||
1. Have Android SDK "tools", "platform-tools", and "build-tools" directories in your PATH (http://developer.android.com/sdk/index.html)
|
1. Have Android SDK "tools", "platform-tools", and "build-tools" directories in your PATH (http://developer.android.com/sdk/index.html)
|
||||||
2. Export ANDROID_HOME pointing to your Android SDK
|
2. Open the Android SDK Manager (shell command: ``android``). Expand the Extras directory and install "Android Support Repository"
|
||||||
3. Download Android Support Repository, and Google Repository using Android SDK Manager
|
3. Export ANDROID_HOME pointing to your Android SDK
|
||||||
4. Execute ``./gradlew build``
|
4. Execute ``./gradlew build``
|
||||||
|
|
||||||
## Development with Eclipse
|
## Development with Eclipse
|
||||||
|
Loading…
Reference in New Issue
Block a user