diff options
author | Vincent Breitmoser <valodim@mugenguild.com> | 2014-03-11 02:40:41 +0100 |
---|---|---|
committer | Vincent Breitmoser <valodim@mugenguild.com> | 2014-03-11 02:40:41 +0100 |
commit | 74027abdad96069a3b1af9ac2d2711beff07f76f (patch) | |
tree | ddeaf3bf841197b2f3ad583c19b3d813cb01b4f5 /OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui | |
parent | 3b38ffe55eef7a3a9a0cd4367c7f7ee42e4305b8 (diff) | |
download | open-keychain-74027abdad96069a3b1af9ac2d2711beff07f76f.tar.gz open-keychain-74027abdad96069a3b1af9ac2d2711beff07f76f.tar.bz2 open-keychain-74027abdad96069a3b1af9ac2d2711beff07f76f.zip |
experimental cert list, proper
Diffstat (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui')
2 files changed, 225 insertions, 32 deletions
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 8f177dc6b..055203c0f 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 @@ -84,13 +84,11 @@ public class ViewKeyActivity extends ActionBarActivity { selectedTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB); } - { - // 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)) ; - } + // 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); @@ -98,7 +96,7 @@ public class ViewKeyActivity extends ActionBarActivity { ViewKeyMainFragment.class, mainBundle, (selectedTab == 0 ? true : false)); Bundle certBundle = new Bundle(); - certBundle.putParcelable(ViewKeyCertsFragment.ARG_DATA_URI, mDataUri); + certBundle.putLong(ViewKeyCertsFragment.ARG_KEYRING_ROW_ID, rowId); mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.key_view_tab_certs)), ViewKeyCertsFragment.class, certBundle, (selectedTab == 1 ? true : false)); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java index 36d3e6ace..abed097c1 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java @@ -17,26 +17,66 @@ package org.sufficientlysecure.keychain.ui; +import android.annotation.SuppressLint; +import android.content.Context; 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; +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.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; import com.beardedhen.androidbootstrap.BootstrapButton; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.util.Log; +import java.nio.ByteBuffer; +import java.util.HashMap; -public class ViewKeyCertsFragment extends Fragment { +import se.emilsjolander.stickylistheaders.ApiLevelTooLowException; +import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; +import se.emilsjolander.stickylistheaders.StickyListHeadersListView; - public static final String ARG_DATA_URI = "uri"; - private BootstrapButton mActionCertify; +public class ViewKeyCertsFragment extends Fragment + implements LoaderManager.LoaderCallbacks<Cursor> { + + // These are the rows that we will retrieve. + static final String[] PROJECTION = new String[]{ + KeychainContract.Certs._ID, + KeychainContract.Certs.VERIFIED, + KeychainContract.Certs.RANK, + KeychainContract.Certs.KEY_ID_CERTIFIER, + KeychainContract.UserIds.USER_ID, + "signer_uid" + }; + + // sort by our user id, + static final String SORT_ORDER = + KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.USER_ID + " ASC, " + + KeychainDatabase.Tables.CERTS + "." + KeychainContract.Certs.VERIFIED + " DESC, " + + "signer_uid ASC"; + + public static final String ARG_KEYRING_ROW_ID = "row_id"; + + private StickyListHeadersListView mStickyList; + + private CertListAdapter mAdapter; private Uri mDataUri; @@ -44,8 +84,6 @@ public class ViewKeyCertsFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.view_key_certs_fragment, container, false); - mActionCertify = (BootstrapButton) view.findViewById(R.id.action_certify); - return view; } @@ -53,40 +91,197 @@ public class ViewKeyCertsFragment extends Fragment { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); - if (dataUri == null) { + mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list); + + if (!getArguments().containsKey(ARG_KEYRING_ROW_ID)) { Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); getActivity().finish(); return; } - loadData(dataUri); + long rowId = getArguments().getLong(ARG_KEYRING_ROW_ID); + mDataUri = KeychainContract.Certs.buildCertsByKeyRowIdUri(Long.toString(rowId)); + + mStickyList.setAreHeadersSticky(true); + mStickyList.setDrawingListUnderStickyHeader(false); + mStickyList.setFastScrollEnabled(true); + + try { + mStickyList.setFastScrollAlwaysVisible(true); + } catch (ApiLevelTooLowException e) { + } + + // TODO this view is made visible if no data is available + // mStickyList.setEmptyView(getActivity().findViewById(R.id.empty)); + + + // Create an empty adapter we will use to display the loaded data. + mAdapter = new CertListAdapter(getActivity(), null); + mStickyList.setAdapter(mAdapter); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } - private void loadData(Uri dataUri) { - if (dataUri.equals(mDataUri)) { - Log.d(Constants.TAG, "Same URI, no need to load the data again!"); - return; + @Override + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(getActivity(), mDataUri, PROJECTION, null, null, SORT_ORDER); + } + + @Override + public void onLoadFinished(Loader<Cursor> 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); + } + + @Override + public void onLoaderReset(Loader<Cursor> 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); + } + + /** + * Implements StickyListHeadersAdapter from library + */ + private class CertListAdapter extends CursorAdapter implements StickyListHeadersAdapter { + private LayoutInflater mInflater; + private int mIndexUserId, mIndexRank; + private int mIndexSignerKeyId, mIndexSignerUserId; + private int mIndexVerified; + + public CertListAdapter(Context context, Cursor c) { + super(context, c, 0); + + mInflater = LayoutInflater.from(context); + initIndex(c); } - mDataUri = dataUri; + @Override + public Cursor swapCursor(Cursor newCursor) { + initIndex(newCursor); - Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); + return super.swapCursor(newCursor); + } - mActionCertify.setOnClickListener(new View.OnClickListener() { + /** + * 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) { - @Override - public void onClick(View v) { - certifyKey(mDataUri); + mIndexUserId = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.USER_ID); + mIndexRank = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.RANK); + mIndexVerified = cursor.getColumnIndexOrThrow(KeychainContract.Certs.VERIFIED); + mIndexSignerKeyId = cursor.getColumnIndexOrThrow(KeychainContract.Certs.KEY_ID_CERTIFIER); + mIndexSignerUserId = cursor.getColumnIndexOrThrow("signer_uid"); } - }); + } - } + /** + * Bind cursor data to the item list view + * <p/> + * 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 wSignerKeyId = (TextView) view.findViewById(R.id.signerKeyId); + TextView wSignerUserId = (TextView) view.findViewById(R.id.signerUserId); + TextView wSignStatus = (TextView) view.findViewById(R.id.signStatus); + + String signerKeyId = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexSignerKeyId)); + String signerUserId = cursor.getString(mIndexSignerUserId); + String signStatus = cursor.getInt(mIndexVerified) > 0 ? "ok" : "unknown"; + + wSignerUserId.setText(signerUserId); + wSignerKeyId.setText(signerKeyId); + wSignStatus.setText(signStatus); + + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return mInflater.inflate(R.layout.view_key_certs_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. + * <p/> + * 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.view_key_certs_header, parent, false); + holder.text = (TextView) convertView.findViewById(R.id.stickylist_header_text); + holder.count = (TextView) convertView.findViewById(R.id.certs_num); + 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(mIndexUserId); + holder.text.setText(userId); + holder.count.setVisibility(View.GONE); + 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); + } + + // otherwise, return the first character of the name as ID + return mCursor.getInt(mIndexRank); + + // sort by the first four characters (should be enough I guess?) + // return ByteBuffer.wrap(userId.getBytes()).asLongBuffer().get(0); + } + + class HeaderViewHolder { + TextView text; + TextView count; + } - private void certifyKey(Uri dataUri) { - Intent signIntent = new Intent(getActivity(), CertifyKeyActivity.class); - signIntent.setData(dataUri); - startActivity(signIntent); } }
\ No newline at end of file |