From 4851f7f8fc5ca82d60a55e95730bf6c11675979c Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 5 Mar 2014 15:54:27 +0100 Subject: copy public keylist as stub for unified keylist --- .../keychain/ui/KeyListActivity.java | 71 +++++ .../keychain/ui/KeyListFragment.java | 338 +++++++++++++++++++++ .../keychain/ui/adapter/KeyListAdapter.java | 232 ++++++++++++++ 3 files changed, 641 insertions(+) create mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java create mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java create mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui') diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java new file mode 100644 index 000000000..dd7aa9a26 --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2012-2014 Dominik Schürmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.ui; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ExportHelper; + +import android.content.Intent; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; + +public class KeyListActivity extends DrawerActivity { + + ExportHelper mExportHelper; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mExportHelper = new ExportHelper(this); + + setContentView(R.layout.key_list_public_activity); + + // now setup navigation drawer in DrawerActivity... + setupDrawerNavigation(savedInstanceState); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.key_list_public, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_key_list_public_import: + Intent intentImport = new Intent(this, ImportKeysActivity.class); + startActivityForResult(intentImport, 0); + + return true; + case R.id.menu_key_list_public_export: + mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.path.APP_DIR + + "/pubexport.asc"); + + return true; + default: + return super.onOptionsItemSelected(item); + } + } + +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java new file mode 100644 index 000000000..6f94ffd1a --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2013 Dominik Schürmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.ui; + +import java.util.ArrayList; +import java.util.Set; + +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.adapter.KeyListAdapter; +import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; + +import se.emilsjolander.stickylistheaders.ApiLevelTooLowException; +import se.emilsjolander.stickylistheaders.StickyListHeadersListView; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.content.Intent; +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; +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.View.OnClickListener; +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; + +/** + * Public key list with sticky list headers. It does _not_ extend ListFragment because it uses + * StickyListHeaders library which does not extend upon ListView. + */ +public class KeyListFragment extends Fragment implements AdapterView.OnItemClickListener, + LoaderManager.LoaderCallbacks { + + private KeyListAdapter mAdapter; + private StickyListHeadersListView mStickyList; + + // empty list layout + private BootstrapButton mButtonEmptyCreate; + private BootstrapButton mButtonEmptyImport; + + /** + * Load custom layout with StickyListView from library + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.key_list_fragment, container, false); + + mButtonEmptyCreate = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_create); + mButtonEmptyCreate.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + Intent intent = new Intent(getActivity(), EditKeyActivity.class); + intent.setAction(EditKeyActivity.ACTION_CREATE_KEY); + intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true); + intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, ""); // show user id view + startActivityForResult(intent, 0); + } + }); + + mButtonEmptyImport = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_import); + mButtonEmptyImport.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + Intent intent = new Intent(getActivity(), ImportKeysActivity.class); + intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE); + startActivityForResult(intent, 0); + } + }); + + return view; + } + + /** + * Define Adapter and Loader on create of Activity + */ + @SuppressLint("NewApi") + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list); + + mStickyList.setOnItemClickListener(this); + mStickyList.setAreHeadersSticky(true); + mStickyList.setDrawingListUnderStickyHeader(false); + mStickyList.setFastScrollEnabled(true); + try { + mStickyList.setFastScrollAlwaysVisible(true); + } catch (ApiLevelTooLowException e) { + } + + // this view is made visible if no data is available + 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, menu); + return true; + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return false; + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + Set positions = mAdapter.getCurrentCheckedPosition(); + + // get IDs for checked positions as long array + long[] ids = new long[positions.size()]; + int i = 0; + for (int pos : positions) { + ids[i] = mAdapter.getItemId(pos); + i++; + } + + switch (item.getItemId()) { + case R.id.menu_key_list_multi_encrypt: { + encrypt(mode, ids); + break; + } + case R.id.menu_key_list_multi_delete: { + showDeleteKeyDialog(mode, ids); + break; + } + } + return true; + } + + @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 + // Start out with a progress indicator. + // setListShown(false); + + // Create an empty adapter we will use to display the loaded data. + mAdapter = new KeyListAdapter(getActivity(), null, Id.type.public_key, USER_ID_INDEX); + mStickyList.setAdapter(mAdapter); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } + + // These are the rows that we will retrieve. + static final String[] PROJECTION = new String[]{ + KeychainContract.KeyRings._ID, + KeychainContract.KeyRings.MASTER_KEY_ID, + KeychainContract.UserIds.USER_ID, + KeychainContract.Keys.IS_REVOKED + }; + + static final int USER_ID_INDEX = 2; + + static final String SORT_ORDER = UserIds.USER_ID + " ASC"; + + @Override + public Loader onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + Uri baseUri = KeyRings.buildPublicKeyRingsUri(); + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); + + mStickyList.setAdapter(mAdapter); + + // NOTE: Not supported by StickyListHeader, thus no indicator is shown while loading + // The list should now be shown. + // if (isResumed()) { + // setListShown(true); + // } else { + // setListShownNoAnimation(true); + // } + } + + @Override + public void onLoaderReset(Loader loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.swapCursor(null); + } + + /** + * On click on item, start key view activity + */ + @Override + public void onItemClick(AdapterView adapterView, View view, int position, long id) { + Intent viewIntent = null; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + viewIntent = new Intent(getActivity(), ViewKeyActivity.class); + } else { + viewIntent = new Intent(getActivity(), ViewKeyActivityJB.class); + } + viewIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(id))); + startActivity(viewIntent); + } + + @TargetApi(11) + 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++) { + keyRingIds[i] = ProviderHelper.getPublicMasterKeyId(getActivity(), keyRingRowIds[i]); + } + + Intent intent = new Intent(getActivity(), EncryptActivity.class); + intent.setAction(EncryptActivity.ACTION_ENCRYPT); + intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, keyRingIds); + // used instead of startActivity set actionbar based on callingPackage + startActivityForResult(intent, 0); + + mode.finish(); + } + + /** + * Show dialog to delete key + * + * @param keyRingRowIds + */ + @TargetApi(11) + 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) + + getResources().getQuantityString(R.plurals.error_can_not_delete_info, notDeleted.size()), + 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/adapter/KeyListAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java new file mode 100644 index 000000000..2e0d1496b --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2013 Dominik Schürmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.ui.adapter; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.database.Cursor; +import android.graphics.Color; +import android.support.v4.widget.CursorAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.util.Log; + +import java.util.HashMap; +import java.util.Set; + +import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; + +/** + * Implements StickyListHeadersAdapter from library + */ +public class KeyListAdapter extends CursorAdapter implements StickyListHeadersAdapter { + private LayoutInflater mInflater; + private int mSectionColumnIndex; + private int mIndexUserId; + private int mIndexIsRevoked; + + @SuppressLint("UseSparseArrays") + private HashMap mSelection = new HashMap(); + + public KeyListAdapter(Context context, Cursor c, int flags, int sectionColumnIndex) { + super(context, c, flags); + + mInflater = LayoutInflater.from(context); + mSectionColumnIndex = sectionColumnIndex; + initIndex(c); + } + + @Override + public Cursor swapCursor(Cursor newCursor) { + initIndex(newCursor); + + return super.swapCursor(newCursor); + } + + /** + * Get column indexes for performance reasons just once in constructor and swapCursor. For a + * performance comparison see http://stackoverflow.com/a/17999582 + * + * @param cursor + */ + private void initIndex(Cursor cursor) { + if (cursor != null) { + mIndexUserId = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.USER_ID); + mIndexIsRevoked = cursor.getColumnIndexOrThrow(KeychainContract.Keys.IS_REVOKED); + } + } + + /** + * Bind cursor data to the item list view + *

+ * NOTE: CursorAdapter already implements the ViewHolder pattern in its getView() method. Thus + * no ViewHolder is required here. + */ + @Override + public void bindView(View view, Context context, Cursor cursor) { + TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); + TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); + TextView revoked = (TextView) view.findViewById(R.id.revoked); + + String userId = cursor.getString(mIndexUserId); + String[] userIdSplit = PgpKeyHelper.splitUserId(userId); + if (userIdSplit[0] != null) { + mainUserId.setText(userIdSplit[0]); + } else { + mainUserId.setText(R.string.user_id_no_name); + } + if (userIdSplit[1] != null) { + mainUserIdRest.setText(userIdSplit[1]); + mainUserIdRest.setVisibility(View.VISIBLE); + } else { + mainUserIdRest.setVisibility(View.GONE); + } + + boolean isRevoked = cursor.getInt(mIndexIsRevoked) > 0; + if (isRevoked) { + revoked.setVisibility(View.VISIBLE); + } else { + revoked.setVisibility(View.GONE); + } + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return mInflater.inflate(R.layout.key_list_item, null); + } + + /** + * Creates a new header view and binds the section headers to it. It uses the ViewHolder + * pattern. Most functionality is similar to getView() from Android's CursorAdapter. + *

+ * NOTE: The variables mDataValid and mCursor are available due to the super class + * CursorAdapter. + */ + @Override + public View getHeaderView(int position, View convertView, ViewGroup parent) { + HeaderViewHolder holder; + if (convertView == null) { + holder = new HeaderViewHolder(); + convertView = mInflater.inflate(R.layout.key_list_header, parent, false); + holder.text = (TextView) convertView.findViewById(R.id.stickylist_header_text); + convertView.setTag(holder); + } else { + holder = (HeaderViewHolder) convertView.getTag(); + } + + if (!mDataValid) { + // no data available at this point + Log.d(Constants.TAG, "getHeaderView: No data available at this point!"); + return convertView; + } + + if (!mCursor.moveToPosition(position)) { + throw new IllegalStateException("couldn't move cursor to position " + position); + } + + // set header text as first char in user id + String userId = mCursor.getString(mSectionColumnIndex); + String headerText = convertView.getResources().getString(R.string.user_id_no_name); + if (userId != null && userId.length() > 0) { + headerText = "" + mCursor.getString(mSectionColumnIndex).subSequence(0, 1).charAt(0); + } + holder.text.setText(headerText); + return convertView; + } + + /** + * Header IDs should be static, position=1 should always return the same Id that is. + */ + @Override + public long getHeaderId(int position) { + if (!mDataValid) { + // no data available at this point + Log.d(Constants.TAG, "getHeaderView: No data available at this point!"); + return -1; + } + + if (!mCursor.moveToPosition(position)) { + throw new IllegalStateException("couldn't move cursor to position " + position); + } + + // return the first character of the name as ID because this is what + // headers are based upon + String userId = mCursor.getString(mSectionColumnIndex); + if (userId != null && userId.length() > 0) { + return userId.subSequence(0, 1).charAt(0); + } else { + return Long.MAX_VALUE; + } + } + + class HeaderViewHolder { + 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 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; + } + +} -- cgit v1.2.3 From 8c6cb8b0ab9e89f9d575af829e0dd1dd9e82e401 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 8 Mar 2014 11:19:34 +0100 Subject: working unified list (no actions yet) --- .../keychain/ui/DrawerActivity.java | 2 +- .../keychain/ui/KeyListActivity.java | 8 +- .../keychain/ui/KeyListFragment.java | 275 ++++++++++++++++++++- .../keychain/ui/adapter/KeyListAdapter.java | 232 ----------------- 4 files changed, 273 insertions(+), 244 deletions(-) delete mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui') diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java index 08ca262c3..d7644ce4d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java @@ -50,7 +50,7 @@ public class DrawerActivity extends ActionBarActivity { private CharSequence mDrawerTitle; private CharSequence mTitle; - private static Class[] mItemsClass = new Class[] { KeyListPublicActivity.class, + private static Class[] mItemsClass = new Class[] { KeyListActivity.class, EncryptActivity.class, DecryptActivity.class, ImportKeysActivity.class, KeyListSecretActivity.class, RegisteredAppsListActivity.class }; private Class mSelectedItem; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java index dd7aa9a26..0bbe2edb1 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java @@ -37,7 +37,7 @@ public class KeyListActivity extends DrawerActivity { mExportHelper = new ExportHelper(this); - setContentView(R.layout.key_list_public_activity); + setContentView(R.layout.key_list_activity); // now setup navigation drawer in DrawerActivity... setupDrawerNavigation(savedInstanceState); @@ -46,19 +46,19 @@ public class KeyListActivity extends DrawerActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); - getMenuInflater().inflate(R.menu.key_list_public, menu); + getMenuInflater().inflate(R.menu.key_list, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case R.id.menu_key_list_public_import: + case R.id.menu_key_list_import: Intent intentImport = new Intent(this, ImportKeysActivity.class); startActivityForResult(intentImport, 0); return true; - case R.id.menu_key_list_public_export: + case R.id.menu_key_list_export: mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.path.APP_DIR + "/pubexport.asc"); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 6f94ffd1a..a638796cd 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -17,25 +17,32 @@ package org.sufficientlysecure.keychain.ui; +import java.util.HashMap; import java.util.ArrayList; import java.util.Set; +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.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.ui.adapter.KeyListAdapter; import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; +import org.sufficientlysecure.keychain.util.Log; import se.emilsjolander.stickylistheaders.ApiLevelTooLowException; import se.emilsjolander.stickylistheaders.StickyListHeadersListView; +import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; import android.annotation.SuppressLint; import android.annotation.TargetApi; +import android.content.Context; import android.content.Intent; import android.database.Cursor; +import android.graphics.Color; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -46,6 +53,7 @@ import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; +import android.support.v4.widget.CursorAdapter; import android.view.ActionMode; import android.view.LayoutInflater; import android.view.Menu; @@ -55,7 +63,9 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AbsListView.MultiChoiceModeListener; import android.widget.AdapterView; +import android.widget.Button; import android.widget.ListView; +import android.widget.TextView; import android.widget.Toast; import com.beardedhen.androidbootstrap.BootstrapButton; @@ -207,7 +217,7 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick // setListShown(false); // Create an empty adapter we will use to display the loaded data. - mAdapter = new KeyListAdapter(getActivity(), null, Id.type.public_key, USER_ID_INDEX); + mAdapter = new KeyListAdapter(getActivity(), null, Id.type.public_key); mStickyList.setAdapter(mAdapter); // Prepare the loader. Either re-connect with an existing one, @@ -218,20 +228,21 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick // These are the rows that we will retrieve. static final String[] PROJECTION = new String[]{ KeychainContract.KeyRings._ID, + KeychainContract.KeyRings.TYPE, KeychainContract.KeyRings.MASTER_KEY_ID, KeychainContract.UserIds.USER_ID, KeychainContract.Keys.IS_REVOKED }; - static final int USER_ID_INDEX = 2; - + static final int INDEX_TYPE = 1; + static final int INDEX_UID = 3; static final String SORT_ORDER = UserIds.USER_ID + " ASC"; @Override public Loader onCreateLoader(int id, Bundle args) { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. - Uri baseUri = KeyRings.buildPublicKeyRingsUri(); + Uri baseUri = KeyRings.buildUnifiedKeyRingsUri(); // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. @@ -274,10 +285,14 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick } else { viewIntent = new Intent(getActivity(), ViewKeyActivityJB.class); } - viewIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(id))); + if(mAdapter.getKeyType(position) == KeyTypes.SECRET) { + viewIntent.setData(KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(mAdapter.getMasterKeyId(position)))); + } else { + viewIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(mAdapter.getMasterKeyId(position)))); + } startActivity(viewIntent); } - + @TargetApi(11) public void encrypt(ActionMode mode, long[] keyRingRowIds) { // get master key ids from row ids @@ -335,4 +350,250 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog"); } + /** + * Implements StickyListHeadersAdapter from library + */ + private class KeyListAdapter extends CursorAdapter implements StickyListHeadersAdapter { + private LayoutInflater mInflater; + private int mIndexUserId; + private int mIndexIsRevoked; + private int mMasterKeyId; + + @SuppressLint("UseSparseArrays") + private HashMap mSelection = new HashMap(); + + public KeyListAdapter(Context context, Cursor c, int flags) { + super(context, c, flags); + + mInflater = LayoutInflater.from(context); + initIndex(c); + } + + @Override + public Cursor swapCursor(Cursor newCursor) { + initIndex(newCursor); + + return super.swapCursor(newCursor); + } + + /** + * Get column indexes for performance reasons just once in constructor and swapCursor. For a + * performance comparison see http://stackoverflow.com/a/17999582 + * + * @param cursor + */ + private void initIndex(Cursor cursor) { + if (cursor != null) { + mIndexUserId = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.USER_ID); + mIndexIsRevoked = cursor.getColumnIndexOrThrow(KeychainContract.Keys.IS_REVOKED); + mMasterKeyId = cursor.getColumnIndexOrThrow(KeychainContract.KeyRings.MASTER_KEY_ID); + } + } + + /** + * Bind cursor data to the item list view + *

+ * NOTE: CursorAdapter already implements the ViewHolder pattern in its getView() method. Thus + * no ViewHolder is required here. + */ + @Override + public void bindView(View view, Context context, Cursor cursor) { + + { // set name and stuff, common to both key types + TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); + TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); + + String userId = cursor.getString(mIndexUserId); + String[] userIdSplit = PgpKeyHelper.splitUserId(userId); + if (userIdSplit[0] != null) { + mainUserId.setText(userIdSplit[0]); + } else { + mainUserId.setText(R.string.user_id_no_name); + } + if (userIdSplit[1] != null) { + mainUserIdRest.setText(userIdSplit[1]); + mainUserIdRest.setVisibility(View.VISIBLE); + } else { + mainUserIdRest.setVisibility(View.GONE); + } + } + + { // set edit button and revoked info, specific by key type + Button button = (Button) view.findViewById(R.id.edit); + TextView revoked = (TextView) view.findViewById(R.id.revoked); + + if(cursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) { + // this is a secret key - show the edit button + revoked.setVisibility(View.GONE); + button.setVisibility(View.VISIBLE); + + final long id = cursor.getLong(mMasterKeyId); + button.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + Log.d(Constants.TAG, "swag"); + Intent editIntent = new Intent(getActivity(), EditKeyActivity.class); + // editIntent.setData(KeychainContract.KeyRings.buildSecretKeyRingsUri(Long.toString(1))); + editIntent.setData(KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(id))); + editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY); + startActivityForResult(editIntent, 0); + } + }); + } else { + // this is a public key - hide the edit button, show if it's revoked + button.setVisibility(View.GONE); + + boolean isRevoked = cursor.getInt(mIndexIsRevoked) > 0; + revoked.setVisibility(isRevoked ? View.VISIBLE : View.GONE); + } + } + + } + + public long getMasterKeyId(int id) { + + if (!mCursor.moveToPosition(id)) { + throw new IllegalStateException("couldn't move cursor to position " + id); + } + + return mCursor.getLong(mMasterKeyId); + + } + + public int getKeyType(int position) { + + if (!mCursor.moveToPosition(position)) { + throw new IllegalStateException("couldn't move cursor to position " + position); + } + + return mCursor.getInt(KeyListFragment.INDEX_TYPE); + + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return mInflater.inflate(R.layout.key_list_item, parent, false); + } + + /** + * Creates a new header view and binds the section headers to it. It uses the ViewHolder + * pattern. Most functionality is similar to getView() from Android's CursorAdapter. + *

+ * NOTE: The variables mDataValid and mCursor are available due to the super class + * CursorAdapter. + */ + @Override + public View getHeaderView(int position, View convertView, ViewGroup parent) { + HeaderViewHolder holder; + if (convertView == null) { + holder = new HeaderViewHolder(); + convertView = mInflater.inflate(R.layout.key_list_header, parent, false); + holder.text = (TextView) convertView.findViewById(R.id.stickylist_header_text); + convertView.setTag(holder); + } else { + holder = (HeaderViewHolder) convertView.getTag(); + } + + if (!mDataValid) { + // no data available at this point + Log.d(Constants.TAG, "getHeaderView: No data available at this point!"); + return convertView; + } + + if (!mCursor.moveToPosition(position)) { + throw new IllegalStateException("couldn't move cursor to position " + position); + } + + if(mCursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) { + holder.text.setText("My Keys"); + return convertView; + } + + // set header text as first char in user id + String userId = mCursor.getString(KeyListFragment.INDEX_UID); + String headerText = convertView.getResources().getString(R.string.user_id_no_name); + if (userId != null && userId.length() > 0) { + headerText = "" + mCursor.getString(KeyListFragment.INDEX_UID).subSequence(0, 1).charAt(0); + } + holder.text.setText(headerText); + return convertView; + } + + /** + * Header IDs should be static, position=1 should always return the same Id that is. + */ + @Override + public long getHeaderId(int position) { + if (!mDataValid) { + // no data available at this point + Log.d(Constants.TAG, "getHeaderView: No data available at this point!"); + return -1; + } + + if (!mCursor.moveToPosition(position)) { + throw new IllegalStateException("couldn't move cursor to position " + position); + } + + // early breakout: all secret keys are assigned id 0 + if(mCursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) + return 1L; + + // otherwise, return the first character of the name as ID + String userId = mCursor.getString(KeyListFragment.INDEX_UID); + if (userId != null && userId.length() > 0) { + return userId.charAt(0); + } else { + return Long.MAX_VALUE; + } + } + + class HeaderViewHolder { + 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 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; + } + + } + } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java deleted file mode 100644 index 2e0d1496b..000000000 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2013 Dominik Schürmann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui.adapter; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.database.Cursor; -import android.graphics.Color; -import android.support.v4.widget.CursorAdapter; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.util.Log; - -import java.util.HashMap; -import java.util.Set; - -import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; - -/** - * Implements StickyListHeadersAdapter from library - */ -public class KeyListAdapter extends CursorAdapter implements StickyListHeadersAdapter { - private LayoutInflater mInflater; - private int mSectionColumnIndex; - private int mIndexUserId; - private int mIndexIsRevoked; - - @SuppressLint("UseSparseArrays") - private HashMap mSelection = new HashMap(); - - public KeyListAdapter(Context context, Cursor c, int flags, int sectionColumnIndex) { - super(context, c, flags); - - mInflater = LayoutInflater.from(context); - mSectionColumnIndex = sectionColumnIndex; - initIndex(c); - } - - @Override - public Cursor swapCursor(Cursor newCursor) { - initIndex(newCursor); - - return super.swapCursor(newCursor); - } - - /** - * Get column indexes for performance reasons just once in constructor and swapCursor. For a - * performance comparison see http://stackoverflow.com/a/17999582 - * - * @param cursor - */ - private void initIndex(Cursor cursor) { - if (cursor != null) { - mIndexUserId = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.USER_ID); - mIndexIsRevoked = cursor.getColumnIndexOrThrow(KeychainContract.Keys.IS_REVOKED); - } - } - - /** - * Bind cursor data to the item list view - *

- * NOTE: CursorAdapter already implements the ViewHolder pattern in its getView() method. Thus - * no ViewHolder is required here. - */ - @Override - public void bindView(View view, Context context, Cursor cursor) { - TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); - TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); - TextView revoked = (TextView) view.findViewById(R.id.revoked); - - String userId = cursor.getString(mIndexUserId); - String[] userIdSplit = PgpKeyHelper.splitUserId(userId); - if (userIdSplit[0] != null) { - mainUserId.setText(userIdSplit[0]); - } else { - mainUserId.setText(R.string.user_id_no_name); - } - if (userIdSplit[1] != null) { - mainUserIdRest.setText(userIdSplit[1]); - mainUserIdRest.setVisibility(View.VISIBLE); - } else { - mainUserIdRest.setVisibility(View.GONE); - } - - boolean isRevoked = cursor.getInt(mIndexIsRevoked) > 0; - if (isRevoked) { - revoked.setVisibility(View.VISIBLE); - } else { - revoked.setVisibility(View.GONE); - } - } - - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(R.layout.key_list_item, null); - } - - /** - * Creates a new header view and binds the section headers to it. It uses the ViewHolder - * pattern. Most functionality is similar to getView() from Android's CursorAdapter. - *

- * NOTE: The variables mDataValid and mCursor are available due to the super class - * CursorAdapter. - */ - @Override - public View getHeaderView(int position, View convertView, ViewGroup parent) { - HeaderViewHolder holder; - if (convertView == null) { - holder = new HeaderViewHolder(); - convertView = mInflater.inflate(R.layout.key_list_header, parent, false); - holder.text = (TextView) convertView.findViewById(R.id.stickylist_header_text); - convertView.setTag(holder); - } else { - holder = (HeaderViewHolder) convertView.getTag(); - } - - if (!mDataValid) { - // no data available at this point - Log.d(Constants.TAG, "getHeaderView: No data available at this point!"); - return convertView; - } - - if (!mCursor.moveToPosition(position)) { - throw new IllegalStateException("couldn't move cursor to position " + position); - } - - // set header text as first char in user id - String userId = mCursor.getString(mSectionColumnIndex); - String headerText = convertView.getResources().getString(R.string.user_id_no_name); - if (userId != null && userId.length() > 0) { - headerText = "" + mCursor.getString(mSectionColumnIndex).subSequence(0, 1).charAt(0); - } - holder.text.setText(headerText); - return convertView; - } - - /** - * Header IDs should be static, position=1 should always return the same Id that is. - */ - @Override - public long getHeaderId(int position) { - if (!mDataValid) { - // no data available at this point - Log.d(Constants.TAG, "getHeaderView: No data available at this point!"); - return -1; - } - - if (!mCursor.moveToPosition(position)) { - throw new IllegalStateException("couldn't move cursor to position " + position); - } - - // return the first character of the name as ID because this is what - // headers are based upon - String userId = mCursor.getString(mSectionColumnIndex); - if (userId != null && userId.length() > 0) { - return userId.subSequence(0, 1).charAt(0); - } else { - return Long.MAX_VALUE; - } - } - - class HeaderViewHolder { - 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 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; - } - -} -- cgit v1.2.3 From 191c5b8130d1a6253ed715e35046db4678a16a8c Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 8 Mar 2014 11:58:26 +0100 Subject: Make EditKeyActivity's uri handling (more) sane This activity didn't actually use the uri for anything, but just chopped off the last path segment and interpreted it as row id. What the hell. --- .../java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui') diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 0bed6f264..e2822c898 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -265,12 +265,10 @@ public class EditKeyActivity extends ActionBarActivity { } else { Log.d(Constants.TAG, "uri: " + mDataUri); - long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment()); - // get master key id using row id - long masterKeyId = ProviderHelper.getSecretMasterKeyId(this, keyRingRowId); + long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri); - masterCanSign = ProviderHelper.getSecretMasterKeyCanSign(this, keyRingRowId); + masterCanSign = ProviderHelper.getMasterKeyCanSign(this, mDataUri); finallyEdit(masterKeyId, masterCanSign); } } -- cgit v1.2.3 From aa8a8f069fb978563bf8d06994cfa7ef1fe316bd Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 8 Mar 2014 12:00:24 +0100 Subject: Normalize public key uri in KeyListFragment Find the explicit row id of the public key id given in the uri in KeyListFragment and work with that. This way, passing in uris by master key id, mail address, or any other criteria, works. Might be a good idea to add an actual check if the row id is non-zero here, but not sure how to do a "bad intent" thing --- .../java/org/sufficientlysecure/keychain/ui/KeyListFragment.java | 6 +----- .../java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java | 9 ++++++++- 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui') diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index a638796cd..dbc24504e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -285,11 +285,7 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick } else { viewIntent = new Intent(getActivity(), ViewKeyActivityJB.class); } - if(mAdapter.getKeyType(position) == KeyTypes.SECRET) { - viewIntent.setData(KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(mAdapter.getMasterKeyId(position)))); - } else { - viewIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(mAdapter.getMasterKeyId(position)))); - } + viewIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(mAdapter.getMasterKeyId(position)))); startActivity(viewIntent); } 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 0a452bc8a..ab46b8efc 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 @@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.helper.ExportHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter; import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; @@ -83,7 +84,13 @@ public class ViewKeyActivity extends ActionBarActivity { selectedTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB); } - mDataUri = getIntent().getData(); + { + // normalize mDataUri to a "by row id" query, to ensure it works with any + // given valid /public/ query + long rowId = ProviderHelper.getRowId(this, getIntent().getData()); + // TODO: handle (rowId == 0) with something else than a crash + mDataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)) ; + } Bundle mainBundle = new Bundle(); mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, mDataUri); -- cgit v1.2.3 From 7ad8977cf3f4117782d6568322ec76c5cd8e2475 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 8 Mar 2014 12:28:20 +0100 Subject: move some placeholders into resources --- .../main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java | 3 +-- .../main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui') diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index dbc24504e..0099fb80d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -426,7 +426,6 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick final long id = cursor.getLong(mMasterKeyId); button.setOnClickListener(new OnClickListener() { public void onClick(View view) { - Log.d(Constants.TAG, "swag"); Intent editIntent = new Intent(getActivity(), EditKeyActivity.class); // editIntent.setData(KeychainContract.KeyRings.buildSecretKeyRingsUri(Long.toString(1))); editIntent.setData(KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(id))); @@ -500,7 +499,7 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick } if(mCursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) { - holder.text.setText("My Keys"); + holder.text.setText(convertView.getResources().getString(R.string.my_keys)); return convertView; } 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 ab46b8efc..8f177dc6b 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 @@ -114,7 +114,7 @@ public class ViewKeyActivity extends ActionBarActivity { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: - Intent homeIntent = new Intent(this, KeyListPublicActivity.class); + Intent homeIntent = new Intent(this, KeyListActivity.class); homeIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(homeIntent); return true; -- cgit v1.2.3 From 706e212b6b1f5fbd30643d33336f3967d88e8b5b Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 8 Mar 2014 13:17:52 +0100 Subject: add edit button and secret key info to public key view --- .../keychain/ui/KeyListFragment.java | 1 - .../keychain/ui/ViewKeyMainFragment.java | 30 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui') diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 0099fb80d..bdae88e55 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -427,7 +427,6 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick button.setOnClickListener(new OnClickListener() { public void onClick(View view) { Intent editIntent = new Intent(getActivity(), EditKeyActivity.class); - // editIntent.setData(KeychainContract.KeyRings.buildSecretKeyRingsUri(Long.toString(1))); editIntent.setData(KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(id))); editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY); startActivityForResult(editIntent, 0); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index 495764eaf..c7403b0a0 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; import android.database.Cursor; +import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -59,6 +60,8 @@ public class ViewKeyMainFragment extends Fragment implements private TextView mExpiry; private TextView mCreation; private TextView mFingerprint; + private TextView mSecretKey; + private BootstrapButton mActionEdit; private BootstrapButton mActionEncrypt; private ListView mUserIds; @@ -85,8 +88,10 @@ public class ViewKeyMainFragment extends Fragment implements mCreation = (TextView) view.findViewById(R.id.creation); mExpiry = (TextView) view.findViewById(R.id.expiry); mFingerprint = (TextView) view.findViewById(R.id.fingerprint); + mSecretKey = (TextView) view.findViewById(R.id.secret_key); mUserIds = (ListView) view.findViewById(R.id.user_ids); mKeys = (ListView) view.findViewById(R.id.keys); + mActionEdit = (BootstrapButton) view.findViewById(R.id.action_edit); mActionEncrypt = (BootstrapButton) view.findViewById(R.id.action_encrypt); return view; @@ -116,6 +121,31 @@ public class ViewKeyMainFragment extends Fragment implements Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); + { // label whether secret key is available, and edit button if it is + final long masterKeyId = ProviderHelper.getMasterKeyId(getActivity(), mDataUri); + if(ProviderHelper.hasSecretKeyByMasterKeyId(getActivity(), masterKeyId)) { + // set this attribute. this is a LITTLE unclean, but we have the info available + // right here, so why not. + mSecretKey.setTextColor(getResources().getColor(R.color.emphasis)); + mSecretKey.setText(R.string.secret_key_yes); + + // edit button + mActionEdit.setVisibility(View.VISIBLE); + mActionEdit.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + Intent editIntent = new Intent(getActivity(), EditKeyActivity.class); + editIntent.setData(KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId))); + editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY); + startActivityForResult(editIntent, 0); + } + }); + } else { + mSecretKey.setTextColor(Color.BLACK); + mSecretKey.setText(getResources().getString(R.string.secret_key_no)); + mActionEdit.setVisibility(View.GONE); + } + } + mActionEncrypt.setOnClickListener(new View.OnClickListener() { @Override -- cgit v1.2.3 From 16116ba72572fc85fe00ec422248818da516506a Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 8 Mar 2014 13:42:50 +0100 Subject: ditch old key list activities --- .../keychain/ui/DrawerActivity.java | 3 +- .../keychain/ui/KeyListPublicActivity.java | 71 ----- .../keychain/ui/KeyListPublicFragment.java | 338 --------------------- .../keychain/ui/KeyListSecretActivity.java | 94 ------ .../keychain/ui/KeyListSecretFragment.java | 234 -------------- .../keychain/ui/adapter/KeyListPublicAdapter.java | 233 -------------- .../keychain/ui/adapter/KeyListSecretAdapter.java | 138 --------- 7 files changed, 1 insertion(+), 1110 deletions(-) delete mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java delete mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java delete mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java delete mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java delete mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java delete mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui') diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java index d7644ce4d..ebb520197 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java @@ -52,7 +52,7 @@ public class DrawerActivity extends ActionBarActivity { private static Class[] mItemsClass = new Class[] { KeyListActivity.class, EncryptActivity.class, DecryptActivity.class, ImportKeysActivity.class, - KeyListSecretActivity.class, RegisteredAppsListActivity.class }; + RegisteredAppsListActivity.class }; private Class mSelectedItem; private static final int MENU_ID_PREFERENCE = 222; @@ -72,7 +72,6 @@ public class DrawerActivity extends ActionBarActivity { new NavItem("fa-lock", getString(R.string.nav_encrypt)), new NavItem("fa-unlock", getString(R.string.nav_decrypt)), new NavItem("fa-download", getString(R.string.nav_import)), - new NavItem("fa-key", getString(R.string.nav_secret_keys)), new NavItem("fa-android", getString(R.string.nav_apps)) }; mDrawerList.setAdapter(new NavigationDrawerAdapter(this, R.layout.drawer_list_item, diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java deleted file mode 100644 index 4521786f7..000000000 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2012-2014 Dominik Schürmann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.ExportHelper; - -import android.content.Intent; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; - -public class KeyListPublicActivity extends DrawerActivity { - - ExportHelper mExportHelper; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mExportHelper = new ExportHelper(this); - - setContentView(R.layout.key_list_public_activity); - - // now setup navigation drawer in DrawerActivity... - setupDrawerNavigation(savedInstanceState); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - getMenuInflater().inflate(R.menu.key_list_public, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_key_list_public_import: - Intent intentImport = new Intent(this, ImportKeysActivity.class); - startActivityForResult(intentImport, 0); - - return true; - case R.id.menu_key_list_public_export: - mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.path.APP_DIR - + "/pubexport.asc"); - - return true; - default: - return super.onOptionsItemSelected(item); - } - } - -} 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 deleted file mode 100644 index 0afa556cb..000000000 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright (C) 2013 Dominik Schürmann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui; - -import java.util.ArrayList; -import java.util.Set; - -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.ui.adapter.KeyListPublicAdapter; -import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; - -import se.emilsjolander.stickylistheaders.ApiLevelTooLowException; -import se.emilsjolander.stickylistheaders.StickyListHeadersListView; - -import android.annotation.SuppressLint; -import android.annotation.TargetApi; -import android.content.Intent; -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; -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.View.OnClickListener; -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; - -/** - * Public key list with sticky list headers. It does _not_ extend ListFragment because it uses - * StickyListHeaders library which does not extend upon ListView. - */ -public class KeyListPublicFragment extends Fragment implements AdapterView.OnItemClickListener, - LoaderManager.LoaderCallbacks { - - private KeyListPublicAdapter mAdapter; - private StickyListHeadersListView mStickyList; - - // empty list layout - private BootstrapButton mButtonEmptyCreate; - private BootstrapButton mButtonEmptyImport; - - /** - * Load custom layout with StickyListView from library - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.key_list_public_fragment, container, false); - - mButtonEmptyCreate = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_create); - mButtonEmptyCreate.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - Intent intent = new Intent(getActivity(), EditKeyActivity.class); - intent.setAction(EditKeyActivity.ACTION_CREATE_KEY); - intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true); - intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, ""); // show user id view - startActivityForResult(intent, 0); - } - }); - - mButtonEmptyImport = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_import); - mButtonEmptyImport.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - Intent intent = new Intent(getActivity(), ImportKeysActivity.class); - intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE); - startActivityForResult(intent, 0); - } - }); - - return view; - } - - /** - * Define Adapter and Loader on create of Activity - */ - @SuppressLint("NewApi") - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list); - - mStickyList.setOnItemClickListener(this); - mStickyList.setAreHeadersSticky(true); - mStickyList.setDrawingListUnderStickyHeader(false); - mStickyList.setFastScrollEnabled(true); - try { - mStickyList.setFastScrollAlwaysVisible(true); - } catch (ApiLevelTooLowException e) { - } - - // this view is made visible if no data is available - 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_public_multi, menu); - return true; - } - - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return false; - } - - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - Set positions = mAdapter.getCurrentCheckedPosition(); - - // get IDs for checked positions as long array - long[] ids = new long[positions.size()]; - int i = 0; - for (int pos : positions) { - ids[i] = mAdapter.getItemId(pos); - i++; - } - - switch (item.getItemId()) { - case R.id.menu_key_list_public_multi_encrypt: { - encrypt(mode, ids); - break; - } - case R.id.menu_key_list_public_multi_delete: { - showDeleteKeyDialog(mode, ids); - break; - } - } - return true; - } - - @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 - // Start out with a progress indicator. - // setListShown(false); - - // Create an empty adapter we will use to display the loaded data. - mAdapter = new KeyListPublicAdapter(getActivity(), null, Id.type.public_key, USER_ID_INDEX); - mStickyList.setAdapter(mAdapter); - - // Prepare the loader. Either re-connect with an existing one, - // or start a new one. - getLoaderManager().initLoader(0, null, this); - } - - // These are the rows that we will retrieve. - static final String[] PROJECTION = new String[]{ - KeychainContract.KeyRings._ID, - KeychainContract.KeyRings.MASTER_KEY_ID, - KeychainContract.UserIds.USER_ID, - KeychainContract.Keys.IS_REVOKED - }; - - static final int USER_ID_INDEX = 2; - - static final String SORT_ORDER = UserIds.USER_ID + " ASC"; - - @Override - public Loader onCreateLoader(int id, Bundle args) { - // This is called when a new Loader needs to be created. This - // sample only has one Loader, so we don't care about the ID. - Uri baseUri = KeyRings.buildPublicKeyRingsUri(); - - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER); - } - - @Override - public void onLoadFinished(Loader loader, Cursor data) { - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - mAdapter.swapCursor(data); - - mStickyList.setAdapter(mAdapter); - - // NOTE: Not supported by StickyListHeader, thus no indicator is shown while loading - // The list should now be shown. - // if (isResumed()) { - // setListShown(true); - // } else { - // setListShownNoAnimation(true); - // } - } - - @Override - public void onLoaderReset(Loader loader) { - // This is called when the last Cursor provided to onLoadFinished() - // above is about to be closed. We need to make sure we are no - // longer using it. - mAdapter.swapCursor(null); - } - - /** - * On click on item, start key view activity - */ - @Override - public void onItemClick(AdapterView adapterView, View view, int position, long id) { - Intent viewIntent = null; - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { - viewIntent = new Intent(getActivity(), ViewKeyActivity.class); - } else { - viewIntent = new Intent(getActivity(), ViewKeyActivityJB.class); - } - viewIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(id))); - startActivity(viewIntent); - } - - @TargetApi(11) - 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++) { - keyRingIds[i] = ProviderHelper.getPublicMasterKeyId(getActivity(), keyRingRowIds[i]); - } - - Intent intent = new Intent(getActivity(), EncryptActivity.class); - intent.setAction(EncryptActivity.ACTION_ENCRYPT); - intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, keyRingIds); - // used instead of startActivity set actionbar based on callingPackage - startActivityForResult(intent, 0); - - mode.finish(); - } - - /** - * Show dialog to delete key - * - * @param keyRingRowIds - */ - @TargetApi(11) - 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) - + getResources().getQuantityString(R.plurals.error_can_not_delete_info, notDeleted.size()), - 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/KeyListSecretActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java deleted file mode 100644 index e58ebe819..000000000 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2013 Dominik Schürmann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.ExportHelper; - -import android.content.Intent; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; - -public class KeyListSecretActivity extends DrawerActivity { - - ExportHelper mExportHelper; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mExportHelper = new ExportHelper(this); - - setContentView(R.layout.key_list_secret_activity); - - // now setup navigation drawer in DrawerActivity... - setupDrawerNavigation(savedInstanceState); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - getMenuInflater().inflate(R.menu.key_list_secret, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_key_list_secret_create: - createKey(); - - return true; - case R.id.menu_key_list_secret_create_expert: - createKeyExpert(); - - return true; - case R.id.menu_key_list_secret_export: - mExportHelper.showExportKeysDialog(null, Id.type.secret_key, Constants.path.APP_DIR - + "/secexport.asc"); - - return true; - case R.id.menu_key_list_secret_import: - Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class); - intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE); - startActivityForResult(intentImportFromFile, 0); - - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - private void createKey() { - Intent intent = new Intent(this, EditKeyActivity.class); - intent.setAction(EditKeyActivity.ACTION_CREATE_KEY); - intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true); - intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, ""); // show user id view - startActivityForResult(intent, 0); - } - - private void createKeyExpert() { - Intent intent = new Intent(this, EditKeyActivity.class); - intent.setAction(EditKeyActivity.ACTION_CREATE_KEY); - startActivityForResult(intent, 0); - } - -} 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 deleted file mode 100644 index 7bb77b60f..000000000 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2013 Dominik Schürmann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui; - -import java.util.ArrayList; -import java.util.Set; - -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; -import org.sufficientlysecure.keychain.ui.adapter.KeyListSecretAdapter; -import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; - -import android.annotation.SuppressLint; -import android.annotation.TargetApi; -import android.content.Intent; -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; -import android.support.v4.content.Loader; -import android.view.ActionMode; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -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 { - - private KeyListSecretActivity mKeyListSecretActivity; - private KeyListSecretAdapter mAdapter; - - /** - * Define Adapter and Loader on create of Activity - */ - @SuppressLint("NewApi") - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mKeyListSecretActivity = (KeyListSecretActivity) getActivity(); - - getListView().setOnItemClickListener(this); - - // Give some text to display if there is no data. In a real - // application this would come from a resource. - setEmptyText(getString(R.string.list_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) { - getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); - getListView().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_secret_multi, menu); - return true; - } - - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return false; - } - - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - Set positions = mAdapter.getCurrentCheckedPosition(); - - // get IDs for checked positions as long array - long[] ids = new long[positions.size()]; - int i = 0; - for (int pos : positions) { - ids[i] = mAdapter.getItemId(pos); - i++; - } - - switch (item.getItemId()) { - case R.id.menu_key_list_public_multi_delete: { - showDeleteKeyDialog(mode, ids); - break; - } - } - return true; - } - - @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); - } - - }); - } - - // We have a menu item to show in action bar. - setHasOptionsMenu(true); - - // Start out with a progress indicator. - setListShown(false); - - // Create an empty adapter we will use to display the loaded data. - mAdapter = new KeyListSecretAdapter(mKeyListSecretActivity, null, 0); - setListAdapter(mAdapter); - - // Prepare the loader. Either re-connect with an existing one, - // or start a new one. - getLoaderManager().initLoader(0, null, this); - } - - // 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 SORT_ORDER = UserIds.USER_ID + " COLLATE LOCALIZED ASC"; - - public Loader onCreateLoader(int id, Bundle args) { - // This is called when a new Loader needs to be created. This - // sample only has one Loader, so we don't care about the ID. - // First, pick the base URI to use depending on whether we are - // currently filtering. - Uri baseUri = KeyRings.buildSecretKeyRingsUri(); - - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER); - } - - public void onLoadFinished(Loader loader, Cursor data) { - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - mAdapter.swapCursor(data); - - // The list should now be shown. - if (isResumed()) { - setListShown(true); - } else { - setListShownNoAnimation(true); - } - } - - public void onLoaderReset(Loader loader) { - // This is called when the last Cursor provided to onLoadFinished() - // above is about to be closed. We need to make sure we are no - // longer using it. - mAdapter.swapCursor(null); - } - - /** - * On click on item, start key view activity - */ - @Override - public void onItemClick(AdapterView adapterView, View view, int position, long id) { - Intent editIntent = new Intent(mKeyListSecretActivity, EditKeyActivity.class); - editIntent.setData(KeychainContract.KeyRings.buildSecretKeyRingsUri(Long.toString(id))); - editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY); - startActivityForResult(editIntent, 0); - } - - /** - * Show dialog to delete key - * - * @param keyRingRowIds - */ - @TargetApi(11) - 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/adapter/KeyListPublicAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java deleted file mode 100644 index 257136cbd..000000000 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (C) 2013 Dominik Schürmann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui.adapter; - -import java.util.HashMap; -import java.util.Set; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.util.Log; - -import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.database.Cursor; -import android.graphics.Color; -import android.support.v4.widget.CursorAdapter; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.RelativeLayout; -import android.widget.TextView; - -/** - * Implements StickyListHeadersAdapter from library - */ -public class KeyListPublicAdapter extends CursorAdapter implements StickyListHeadersAdapter { - private LayoutInflater mInflater; - private int mSectionColumnIndex; - private int mIndexUserId; - private int mIndexIsRevoked; - - @SuppressLint("UseSparseArrays") - private HashMap mSelection = new HashMap(); - - public KeyListPublicAdapter(Context context, Cursor c, int flags, int sectionColumnIndex) { - super(context, c, flags); - - mInflater = LayoutInflater.from(context); - mSectionColumnIndex = sectionColumnIndex; - initIndex(c); - } - - @Override - public Cursor swapCursor(Cursor newCursor) { - initIndex(newCursor); - - return super.swapCursor(newCursor); - } - - /** - * Get column indexes for performance reasons just once in constructor and swapCursor. For a - * performance comparison see http://stackoverflow.com/a/17999582 - * - * @param cursor - */ - private void initIndex(Cursor cursor) { - if (cursor != null) { - mIndexUserId = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.USER_ID); - mIndexIsRevoked = cursor.getColumnIndexOrThrow(KeychainContract.Keys.IS_REVOKED); - } - } - - /** - * Bind cursor data to the item list view - *

- * NOTE: CursorAdapter already implements the ViewHolder pattern in its getView() method. Thus - * no ViewHolder is required here. - */ - @Override - public void bindView(View view, Context context, Cursor cursor) { - TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); - TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); - TextView revoked = (TextView) view.findViewById(R.id.revoked); - - String userId = cursor.getString(mIndexUserId); - String[] userIdSplit = PgpKeyHelper.splitUserId(userId); - if (userIdSplit[0] != null) { - mainUserId.setText(userIdSplit[0]); - } else { - mainUserId.setText(R.string.user_id_no_name); - } - if (userIdSplit[1] != null) { - mainUserIdRest.setText(userIdSplit[1]); - mainUserIdRest.setVisibility(View.VISIBLE); - } else { - mainUserIdRest.setVisibility(View.GONE); - } - - boolean isRevoked = cursor.getInt(mIndexIsRevoked) > 0; - if (isRevoked) { - revoked.setVisibility(View.VISIBLE); - } else { - revoked.setVisibility(View.GONE); - } - } - - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(R.layout.key_list_public_item, null); - } - - /** - * Creates a new header view and binds the section headers to it. It uses the ViewHolder - * pattern. Most functionality is similar to getView() from Android's CursorAdapter. - *

- * NOTE: The variables mDataValid and mCursor are available due to the super class - * CursorAdapter. - */ - @Override - public View getHeaderView(int position, View convertView, ViewGroup parent) { - HeaderViewHolder holder; - if (convertView == null) { - holder = new HeaderViewHolder(); - convertView = mInflater.inflate(R.layout.key_list_public_header, parent, false); - holder.text = (TextView) convertView.findViewById(R.id.stickylist_header_text); - convertView.setTag(holder); - } else { - holder = (HeaderViewHolder) convertView.getTag(); - } - - if (!mDataValid) { - // no data available at this point - Log.d(Constants.TAG, "getHeaderView: No data available at this point!"); - return convertView; - } - - if (!mCursor.moveToPosition(position)) { - throw new IllegalStateException("couldn't move cursor to position " + position); - } - - // set header text as first char in user id - String userId = mCursor.getString(mSectionColumnIndex); - String headerText = convertView.getResources().getString(R.string.user_id_no_name); - if (userId != null && userId.length() > 0) { - headerText = "" + mCursor.getString(mSectionColumnIndex).subSequence(0, 1).charAt(0); - } - holder.text.setText(headerText); - return convertView; - } - - /** - * Header IDs should be static, position=1 should always return the same Id that is. - */ - @Override - public long getHeaderId(int position) { - if (!mDataValid) { - // no data available at this point - Log.d(Constants.TAG, "getHeaderView: No data available at this point!"); - return -1; - } - - if (!mCursor.moveToPosition(position)) { - throw new IllegalStateException("couldn't move cursor to position " + position); - } - - // return the first character of the name as ID because this is what - // headers are based upon - String userId = mCursor.getString(mSectionColumnIndex); - if (userId != null && userId.length() > 0) { - return userId.subSequence(0, 1).charAt(0); - } else { - return Long.MAX_VALUE; - } - } - - class HeaderViewHolder { - 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 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; - } - -} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java deleted file mode 100644 index 11d1e8c17..000000000 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2013 Dominik Schürmann - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui.adapter; - -import java.util.HashMap; -import java.util.Set; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.database.Cursor; -import android.graphics.Color; -import android.support.v4.widget.CursorAdapter; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -public class KeyListSecretAdapter extends CursorAdapter { - private LayoutInflater mInflater; - - private int mIndexUserId; - - @SuppressLint("UseSparseArrays") - private HashMap mSelection = new HashMap(); - - public KeyListSecretAdapter(Context context, Cursor c, int flags) { - super(context, c, flags); - - mInflater = LayoutInflater.from(context); - initIndex(c); - } - - @Override - public Cursor swapCursor(Cursor newCursor) { - initIndex(newCursor); - - return super.swapCursor(newCursor); - } - - /** - * Get column indexes for performance reasons just once in constructor and swapCursor. For a - * performance comparison see http://stackoverflow.com/a/17999582 - * - * @param cursor - */ - private void initIndex(Cursor cursor) { - if (cursor != null) { - mIndexUserId = cursor.getColumnIndexOrThrow(UserIds.USER_ID); - } - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); - TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); - - String userId = cursor.getString(mIndexUserId); - String[] userIdSplit = PgpKeyHelper.splitUserId(userId); - - if (userIdSplit[0] != null) { - mainUserId.setText(userIdSplit[0]); - } else { - mainUserId.setText(R.string.user_id_no_name); - } - if (userIdSplit[1] != null) { - mainUserIdRest.setText(userIdSplit[1]); - } else { - mainUserIdRest.setText(""); - } - } - - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(R.layout.key_list_secret_item, null); - } - - /** -------------------------- 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 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; - } - -} -- cgit v1.2.3 From de6daf5c30c66e1152f06560e0030fb514d5f68f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 8 Mar 2014 14:08:44 +0100 Subject: better handling of multi select in ListKeysFragment --- .../keychain/ui/KeyListFragment.java | 39 ++++++++++++---------- 1 file changed, 22 insertions(+), 17 deletions(-) (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui') diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index bdae88e55..941abdd6d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -164,22 +164,18 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - Set positions = mAdapter.getCurrentCheckedPosition(); // get IDs for checked positions as long array - long[] ids = new long[positions.size()]; - int i = 0; - for (int pos : positions) { - ids[i] = mAdapter.getItemId(pos); - i++; - } + long[] ids; switch (item.getItemId()) { case R.id.menu_key_list_multi_encrypt: { + ids = mAdapter.getCurrentSelectedMasterKeyIds(); encrypt(mode, ids); break; } case R.id.menu_key_list_multi_delete: { + ids = mAdapter.getCurrentSelectedItemIds(); showDeleteKeyDialog(mode, ids); break; } @@ -290,16 +286,10 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick } @TargetApi(11) - 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++) { - keyRingIds[i] = ProviderHelper.getPublicMasterKeyId(getActivity(), keyRingRowIds[i]); - } - + protected void encrypt(ActionMode mode, long[] keyRingMasterKeyIds) { Intent intent = new Intent(getActivity(), EncryptActivity.class); intent.setAction(EncryptActivity.ACTION_ENCRYPT); - intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, keyRingIds); + intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, keyRingMasterKeyIds); // used instead of startActivity set actionbar based on callingPackage startActivityForResult(intent, 0); @@ -312,6 +302,7 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick * @param keyRingRowIds */ @TargetApi(11) + // TODO: this method needs an overhaul to handle both public and secret keys gracefully! public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) { // Message is received after key is deleted Handler returnHandler = new Handler() { @@ -557,8 +548,22 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick return result == null ? false : result; } - public Set getCurrentCheckedPosition() { - return mSelection.keySet(); + public long[] getCurrentSelectedItemIds() { + long[] ids = new long[mSelection.size()]; + int i = 0; + // get master key ids + for (int pos : mSelection.keySet()) + ids[i++] = mAdapter.getItemId(pos); + return ids; + } + + public long[] getCurrentSelectedMasterKeyIds() { + long[] ids = new long[mSelection.size()]; + int i = 0; + // get master key ids + for (int pos : mSelection.keySet()) + ids[i++] = mAdapter.getMasterKeyId(pos); + return ids; } public void removeSelection(int position) { -- cgit v1.2.3 From be558944b52ac038c40bdf84a3d52c9ae2e7a218 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 8 Mar 2014 14:41:25 +0100 Subject: add contact count to My Keys header --- .../org/sufficientlysecure/keychain/ui/KeyListFragment.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui') diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 941abdd6d..96b75cf11 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -19,7 +19,6 @@ package org.sufficientlysecure.keychain.ui; import java.util.HashMap; import java.util.ArrayList; -import java.util.Set; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; @@ -473,6 +472,7 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick holder = new HeaderViewHolder(); convertView = mInflater.inflate(R.layout.key_list_header, parent, false); holder.text = (TextView) convertView.findViewById(R.id.stickylist_header_text); + holder.count = (TextView) convertView.findViewById(R.id.contacts_num); convertView.setTag(holder); } else { holder = (HeaderViewHolder) convertView.getTag(); @@ -489,6 +489,13 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick } if(mCursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) { + { // set contact count + int num = mCursor.getCount(); + String contactsTotal = getResources().getQuantityString(R.plurals.n_contacts, num, num); + holder.count.setText(contactsTotal); + holder.count.setVisibility(View.VISIBLE); + } + holder.text.setText(convertView.getResources().getString(R.string.my_keys)); return convertView; } @@ -500,6 +507,7 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick headerText = "" + mCursor.getString(KeyListFragment.INDEX_UID).subSequence(0, 1).charAt(0); } holder.text.setText(headerText); + holder.count.setVisibility(View.GONE); return convertView; } @@ -533,6 +541,7 @@ public class KeyListFragment extends Fragment implements AdapterView.OnItemClick class HeaderViewHolder { TextView text; + TextView count; } /** -- cgit v1.2.3 From 0b0809ec17203ea14068b064360e45e435796829 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 13 Mar 2014 00:16:11 +0100 Subject: keychainprovider cleanup, kindasorta removed code redundancy, and the query is in the switch thing now, I didn't put everything into its own separate sources (yet?). --- .../java/org/sufficientlysecure/keychain/ui/KeyListFragment.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui') diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 5b57132d4..72af24717 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -31,6 +31,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; import org.sufficientlysecure.keychain.util.Log; @@ -255,7 +256,11 @@ public class KeyListFragment extends Fragment implements SearchView.OnQueryTextL static final int INDEX_TYPE = 1; static final int INDEX_UID = 3; - static final String SORT_ORDER = UserIds.USER_ID + " ASC"; + static final String SORT_ORDER = + // show secret before public key + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings.TYPE + " DESC, " + // sort by user id otherwise + + UserIds.USER_ID + " ASC"; @Override public Loader onCreateLoader(int id, Bundle args) { -- cgit v1.2.3 From 6af65a2a9b5f90b90bf8752820f40f89241d8014 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 13 Mar 2014 13:06:23 +0100 Subject: merge fixes as per comments in merge https://github.com/openpgp-keychain/openpgp-keychain/pull/403/files#r10540753 --- .../keychain/ui/KeyListFragment.java | 49 +++++++++------------- 1 file changed, 19 insertions(+), 30 deletions(-) (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui') diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 72af24717..ea5c60c53 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -33,6 +33,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.adapter.HighlightQueryCursorAdapter; import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; import org.sufficientlysecure.keychain.util.Log; @@ -203,6 +204,20 @@ public class KeyListFragment extends Fragment implements SearchView.OnQueryTextL showDeleteKeyDialog(mode, ids); break; } + case R.id.menu_key_list_multi_export: { + // todo: public/secret needs to be handled differently here + ids = mStickyList.getWrappedList().getCheckedItemIds(); + ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity()); + mExportHelper.showExportKeysDialog(ids, Id.type.public_key, Constants.path.APP_DIR_FILE_PUB); + break; + } + case R.id.menu_key_list_multi_select_all: { + // select all + for (int i = 0; i < mStickyList.getCount(); i++) { + mStickyList.setItemChecked(i, true); + } + break; + } } return true; } @@ -455,7 +470,7 @@ public class KeyListFragment extends Fragment implements SearchView.OnQueryTextL /** * Implements StickyListHeadersAdapter from library */ - private class KeyListAdapter extends CursorAdapter implements StickyListHeadersAdapter { + private class KeyListAdapter extends HighlightQueryCursorAdapter implements StickyListHeadersAdapter { private LayoutInflater mInflater; private int mIndexUserId; private int mIndexIsRevoked; @@ -698,42 +713,16 @@ public class KeyListFragment extends Fragment implements SearchView.OnQueryTextL * 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)); + } else { + v.setBackgroundColor(Color.TRANSPARENT); } - return v; - } - // search highlight methods - - public void setSearchQuery(String searchQuery) { - mCurQuery = searchQuery; - } - - public String getSearchQuery() { - return mCurQuery; + return v; } - protected Spannable highlightSearchQuery(String text) { - Spannable highlight = Spannable.Factory.getInstance().newSpannable(text); - - if (mCurQuery != null) { - Pattern pattern = Pattern.compile("(?i)" + mCurQuery); - Matcher matcher = pattern.matcher(text); - if (matcher.find()) { - highlight.setSpan( - new ForegroundColorSpan(mContext.getResources().getColor(R.color.emphasis)), - matcher.start(), - matcher.end(), - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - return highlight; - } else { - return highlight; - } - } } } -- cgit v1.2.3