From 6d1137190529dc7add74926cea52c377883319be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sun, 6 Apr 2014 12:57:42 +0200 Subject: Rename folder structure from OpenPGP Keychain to OpenKeychain --- .../ui/adapter/AsyncTaskResultWrapper.java | 46 ++++ .../ui/adapter/HighlightQueryCursorAdapter.java | 65 +++++ .../keychain/ui/adapter/ImportKeysAdapter.java | 186 ++++++++++++++ .../keychain/ui/adapter/ImportKeysListEntry.java | 274 +++++++++++++++++++++ .../keychain/ui/adapter/ImportKeysListLoader.java | 167 +++++++++++++ .../ui/adapter/ImportKeysListServerLoader.java | 130 ++++++++++ .../ui/adapter/KeyValueSpinnerAdapter.java | 101 ++++++++ .../keychain/ui/adapter/PagerTabStripAdapter.java | 70 ++++++ .../ui/adapter/SelectKeyCursorAdapter.java | 165 +++++++++++++ .../keychain/ui/adapter/TabsAdapter.java | 101 ++++++++ .../keychain/ui/adapter/ViewKeyKeysAdapter.java | 175 +++++++++++++ .../keychain/ui/adapter/ViewKeyUserIdsAdapter.java | 181 ++++++++++++++ 12 files changed, 1661 insertions(+) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/HighlightQueryCursorAdapter.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java new file mode 100644 index 000000000..5f2aec4fe --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 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.adapter; + +/** + * The AsyncTaskResultWrapper is used to wrap a result from a AsyncTask (for example: Loader). + * You can pass the result and an exception in it if an error occurred. + * Concept found at: + * https://stackoverflow.com/questions/19593577/how-to-handle-errors-in-custom-asynctaskloader + * + * @param - Typ of the result which is wrapped + */ +public class AsyncTaskResultWrapper { + + private final T mResult; + private final Exception mError; + + public AsyncTaskResultWrapper(T result, Exception error) { + this.mResult = result; + this.mError = error; + } + + public T getResult() { + return mResult; + } + + public Exception getError() { + return mError; + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/HighlightQueryCursorAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/HighlightQueryCursorAdapter.java new file mode 100644 index 000000000..a3ed08a4c --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/HighlightQueryCursorAdapter.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 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.adapter; + +import android.content.Context; +import android.database.Cursor; +import android.support.v4.widget.CursorAdapter; +import android.text.Spannable; +import android.text.style.ForegroundColorSpan; +import org.sufficientlysecure.keychain.R; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class HighlightQueryCursorAdapter extends CursorAdapter { + + private String mCurQuery; + + public HighlightQueryCursorAdapter(Context context, Cursor c, int flags) { + super(context, c, flags); + mCurQuery = null; + } + + public void setSearchQuery(String searchQuery) { + mCurQuery = searchQuery; + } + + public String getSearchQuery() { + return mCurQuery; + } + + 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; + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java new file mode 100644 index 000000000..f322ea980 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -0,0 +1,186 @@ +/* + * 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.app.Activity; +import android.content.Context; +import android.graphics.Color; +import android.os.Build; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.CheckBox; +import android.widget.LinearLayout; +import android.widget.LinearLayout.LayoutParams; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; + +import java.util.ArrayList; +import java.util.List; + +public class ImportKeysAdapter extends ArrayAdapter { + protected LayoutInflater mInflater; + protected Activity mActivity; + + protected List mData; + + static class ViewHolder { + public TextView mainUserId; + public TextView mainUserIdRest; + public TextView keyId; + public TextView fingerprint; + public TextView algorithm; + public TextView status; + } + + public ImportKeysAdapter(Activity activity) { + super(activity, -1); + mActivity = activity; + mInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + @SuppressLint("NewApi") + public void setData(List data) { + clear(); + if (data != null) { + this.mData = data; + + // add data to extended ArrayAdapter + if (Build.VERSION.SDK_INT >= 11) { + addAll(data); + } else { + for (ImportKeysListEntry entry : data) { + add(entry); + } + } + } + } + + public List getData() { + return mData; + } + + public ArrayList getSelectedData() { + ArrayList selectedData = new ArrayList(); + for (ImportKeysListEntry entry : mData) { + if (entry.isSelected()) { + selectedData.add(entry); + } + } + return selectedData; + } + + @Override + public boolean hasStableIds() { + return true; + } + + public View getView(int position, View convertView, ViewGroup parent) { + ImportKeysListEntry entry = mData.get(position); + ViewHolder holder; + if (convertView == null) { + holder = new ViewHolder(); + convertView = mInflater.inflate(R.layout.import_keys_list_entry, null); + holder.mainUserId = (TextView) convertView.findViewById(R.id.mainUserId); + holder.mainUserIdRest = (TextView) convertView.findViewById(R.id.mainUserIdRest); + holder.keyId = (TextView) convertView.findViewById(R.id.keyId); + holder.fingerprint = (TextView) convertView.findViewById(R.id.fingerprint); + holder.algorithm = (TextView) convertView.findViewById(R.id.algorithm); + holder.status = (TextView) convertView.findViewById(R.id.status); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + // main user id + String userId = entry.userIds.get(0); + String[] userIdSplit = PgpKeyHelper.splitUserId(userId); + + // name + if (userIdSplit[0] != null) { + // show red user id if it is a secret key + if (entry.secretKey) { + userIdSplit[0] = mActivity.getString(R.string.secret_key) + " " + userIdSplit[0]; + holder.mainUserId.setTextColor(Color.RED); + } + holder.mainUserId.setText(userIdSplit[0]); + } else { + holder.mainUserId.setText(R.string.user_id_no_name); + } + + // email + if (userIdSplit[1] != null) { + holder.mainUserIdRest.setText(userIdSplit[1]); + holder.mainUserIdRest.setVisibility(View.VISIBLE); + } else { + holder.mainUserIdRest.setVisibility(View.GONE); + } + + holder.keyId.setText(entry.keyIdHex); + + if (entry.fingerPrintHex != null) { + holder.fingerprint.setText(PgpKeyHelper.colorizeFingerprint(entry.fingerPrintHex)); + holder.fingerprint.setVisibility(View.VISIBLE); + } else { + holder.fingerprint.setVisibility(View.GONE); + } + + holder.algorithm.setText("" + entry.bitStrength + "/" + entry.algorithm); + + if (entry.revoked) { + holder.status.setText(R.string.revoked); + } else { + holder.status.setVisibility(View.GONE); + } + + LinearLayout ll = (LinearLayout) convertView.findViewById(R.id.list); + ll.removeAllViews(); + if (entry.userIds.size() == 1) { + ll.setVisibility(View.GONE); + } else { + boolean first = true; + boolean second = true; + for (String uid : entry.userIds) { + if (first) { + first = false; + continue; + } + if (!second) { + View sep = new View(mActivity); + sep.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 1)); + sep.setBackgroundResource(android.R.drawable.divider_horizontal_dark); + ll.addView(sep); + } + TextView uidView = (TextView) mInflater.inflate( + R.layout.import_keys_list_entry_user_id, null); + uidView.setText(uid); + ll.addView(uidView); + second = false; + } + } + + CheckBox cBox = (CheckBox) convertView.findViewById(R.id.selected); + cBox.setChecked(entry.isSelected()); + + return convertView; + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java new file mode 100644 index 000000000..5631d40ea --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java @@ -0,0 +1,274 @@ +/* + * 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.os.Parcel; +import android.os.Parcelable; +import android.util.SparseArray; + +import org.spongycastle.openpgp.PGPKeyRing; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.util.IterableIterator; +import org.sufficientlysecure.keychain.util.Log; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; + +public class ImportKeysListEntry implements Serializable, Parcelable { + private static final long serialVersionUID = -7797972103284992662L; + + public ArrayList userIds; + public long keyId; + public String keyIdHex; + public boolean revoked; + public Date date; // TODO: not displayed + public String fingerPrintHex; + public int bitStrength; + public String algorithm; + public boolean secretKey; + + private boolean mSelected; + + private byte[] mBytes = new byte[]{}; + + public ImportKeysListEntry(ImportKeysListEntry b) { + this.userIds = b.userIds; + this.keyId = b.keyId; + this.revoked = b.revoked; + this.date = b.date; + this.fingerPrintHex = b.fingerPrintHex; + this.keyIdHex = b.keyIdHex; + this.bitStrength = b.bitStrength; + this.algorithm = b.algorithm; + this.secretKey = b.secretKey; + this.mSelected = b.mSelected; + this.mBytes = b.mBytes; + } + + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStringList(userIds); + dest.writeLong(keyId); + dest.writeByte((byte) (revoked ? 1 : 0)); + dest.writeSerializable(date); + dest.writeString(fingerPrintHex); + dest.writeString(keyIdHex); + dest.writeInt(bitStrength); + dest.writeString(algorithm); + dest.writeByte((byte) (secretKey ? 1 : 0)); + dest.writeByte((byte) (mSelected ? 1 : 0)); + dest.writeInt(mBytes.length); + dest.writeByteArray(mBytes); + } + + public static final Creator CREATOR = new Creator() { + public ImportKeysListEntry createFromParcel(final Parcel source) { + ImportKeysListEntry vr = new ImportKeysListEntry(); + vr.userIds = new ArrayList(); + source.readStringList(vr.userIds); + vr.keyId = source.readLong(); + vr.revoked = source.readByte() == 1; + vr.date = (Date) source.readSerializable(); + vr.fingerPrintHex = source.readString(); + vr.keyIdHex = source.readString(); + vr.bitStrength = source.readInt(); + vr.algorithm = source.readString(); + vr.secretKey = source.readByte() == 1; + vr.mSelected = source.readByte() == 1; + vr.mBytes = new byte[source.readInt()]; + source.readByteArray(vr.mBytes); + + return vr; + } + + public ImportKeysListEntry[] newArray(final int size) { + return new ImportKeysListEntry[size]; + } + }; + + public String getKeyIdHex() { + return keyIdHex; + } + + public byte[] getBytes() { + return mBytes; + } + + public void setBytes(byte[] bytes) { + this.mBytes = bytes; + } + + public boolean isSelected() { + return mSelected; + } + + public void setSelected(boolean selected) { + this.mSelected = selected; + } + + public long getKeyId() { + return keyId; + } + + public void setKeyId(long keyId) { + this.keyId = keyId; + } + + public void setKeyIdHex(String keyIdHex) { + this.keyIdHex = keyIdHex; + } + + public boolean isRevoked() { + return revoked; + } + + public void setRevoked(boolean revoked) { + this.revoked = revoked; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public String getFingerPrintHex() { + return fingerPrintHex; + } + + public void setFingerPrintHex(String fingerPrintHex) { + this.fingerPrintHex = fingerPrintHex; + } + + public int getBitStrength() { + return bitStrength; + } + + public void setBitStrength(int bitStrength) { + this.bitStrength = bitStrength; + } + + public String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public boolean isSecretKey() { + return secretKey; + } + + public void setSecretKey(boolean secretKey) { + this.secretKey = secretKey; + } + + public ArrayList getUserIds() { + return userIds; + } + + public void setUserIds(ArrayList userIds) { + this.userIds = userIds; + } + + /** + * Constructor for later querying from keyserver + */ + public ImportKeysListEntry() { + // keys from keyserver are always public keys + secretKey = false; + // do not select by default + mSelected = false; + userIds = new ArrayList(); + } + + /** + * Constructor based on key object, used for import from NFC, QR Codes, files + */ + @SuppressWarnings("unchecked") + public ImportKeysListEntry(PGPKeyRing pgpKeyRing) { + // save actual key object into entry, used to import it later + try { + this.mBytes = pgpKeyRing.getEncoded(); + } catch (IOException e) { + Log.e(Constants.TAG, "IOException on pgpKeyRing.getEncoded()", e); + } + + // selected is default + this.mSelected = true; + + if (pgpKeyRing instanceof PGPSecretKeyRing) { + secretKey = true; + } else { + secretKey = false; + } + + userIds = new ArrayList(); + for (String userId : new IterableIterator(pgpKeyRing.getPublicKey().getUserIDs())) { + userIds.add(userId); + } + + this.keyId = pgpKeyRing.getPublicKey().getKeyID(); + this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId); + + this.revoked = pgpKeyRing.getPublicKey().isRevoked(); + this.fingerPrintHex = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey() + .getFingerprint()); + this.bitStrength = pgpKeyRing.getPublicKey().getBitStrength(); + final int algorithm = pgpKeyRing.getPublicKey().getAlgorithm(); + this.algorithm = getAlgorithmFromId(algorithm); + } + + /** + * Based on OpenPGP Message Format + */ + private static final SparseArray ALGORITHM_IDS = new SparseArray() {{ + put(-1, "unknown"); // TODO: with resources + put(0, "unencrypted"); + put(PGPPublicKey.RSA_GENERAL, "RSA"); + put(PGPPublicKey.RSA_ENCRYPT, "RSA"); + put(PGPPublicKey.RSA_SIGN, "RSA"); + put(PGPPublicKey.ELGAMAL_ENCRYPT, "ElGamal"); + put(PGPPublicKey.ELGAMAL_GENERAL, "ElGamal"); + put(PGPPublicKey.DSA, "DSA"); + put(PGPPublicKey.EC, "ECC"); + put(PGPPublicKey.ECDSA, "ECC"); + put(PGPPublicKey.ECDH, "ECC"); + }}; + + /** + * Based on OpenPGP Message Format + */ + public static String getAlgorithmFromId(int algorithmId) { + return (ALGORITHM_IDS.get(algorithmId) != null ? + ALGORITHM_IDS.get(algorithmId) : + ALGORITHM_IDS.get(-1)); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java new file mode 100644 index 000000000..c9983213c --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2012-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.content.Context; +import android.support.v4.content.AsyncTaskLoader; +import org.spongycastle.openpgp.PGPKeyRing; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPUtil; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.util.InputData; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.PositionAwareInputStream; + +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.util.ArrayList; + +public class ImportKeysListLoader + extends AsyncTaskLoader>> { + + public static class FileHasNoContent extends Exception { + + } + + public static class NonPgpPart extends Exception { + private int mCount; + + public NonPgpPart(int count) { + this.mCount = count; + } + + public int getCount() { + return mCount; + } + } + + Context mContext; + + InputData mInputData; + + ArrayList mData = new ArrayList(); + AsyncTaskResultWrapper> mEntryListWrapper; + + public ImportKeysListLoader(Context context, InputData inputData) { + super(context); + this.mContext = context; + this.mInputData = inputData; + } + + @Override + public AsyncTaskResultWrapper> loadInBackground() { + + mEntryListWrapper = new AsyncTaskResultWrapper>(mData, null); + + if (mInputData == null) { + Log.e(Constants.TAG, "Input data is null!"); + return mEntryListWrapper; + } + + generateListOfKeyrings(mInputData); + + return mEntryListWrapper; + } + + @Override + protected void onReset() { + super.onReset(); + + // Ensure the loader is stopped + onStopLoading(); + } + + @Override + protected void onStartLoading() { + forceLoad(); + } + + @Override + protected void onStopLoading() { + cancelLoad(); + } + + @Override + public void deliverResult(AsyncTaskResultWrapper> data) { + super.deliverResult(data); + } + + /** + * Reads all PGPKeyRing objects from input + * + * @param inputData + * @return + */ + private void generateListOfKeyrings(InputData inputData) { + + boolean isEmpty = true; + int nonPgpCounter = 0; + + PositionAwareInputStream progressIn = new PositionAwareInputStream( + inputData.getInputStream()); + + // need to have access to the bufferedInput, so we can reuse it for the possible + // PGPObject chunks after the first one, e.g. files with several consecutive ASCII + // armor blocks + BufferedInputStream bufferedInput = new BufferedInputStream(progressIn); + try { + + // read all available blocks... (asc files can contain many blocks with BEGIN END) + while (bufferedInput.available() > 0) { + isEmpty = false; + InputStream in = PGPUtil.getDecoderStream(bufferedInput); + PGPObjectFactory objectFactory = new PGPObjectFactory(in); + + // go through all objects in this block + Object obj; + while ((obj = objectFactory.nextObject()) != null) { + Log.d(Constants.TAG, "Found class: " + obj.getClass()); + + if (obj instanceof PGPKeyRing) { + PGPKeyRing newKeyring = (PGPKeyRing) obj; + addToData(newKeyring); + } else { + Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!"); + nonPgpCounter++; + } + } + } + } catch (Exception e) { + Log.e(Constants.TAG, "Exception on parsing key file!", e); + mEntryListWrapper = new AsyncTaskResultWrapper>(mData, e); + nonPgpCounter = 0; + } + + if (isEmpty) { + Log.e(Constants.TAG, "File has no content!", new FileHasNoContent()); + mEntryListWrapper = new AsyncTaskResultWrapper> + (mData, new FileHasNoContent()); + } + + if (nonPgpCounter > 0) { + mEntryListWrapper = new AsyncTaskResultWrapper> + (mData, new NonPgpPart(nonPgpCounter)); + } + } + + private void addToData(PGPKeyRing keyring) { + ImportKeysListEntry item = new ImportKeysListEntry(keyring); + mData.add(item); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java new file mode 100644 index 000000000..259e14319 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 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.adapter; + +import android.content.Context; +import android.support.v4.content.AsyncTaskLoader; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.util.HkpKeyServer; +import org.sufficientlysecure.keychain.util.KeyServer; +import org.sufficientlysecure.keychain.util.Log; + +import java.util.ArrayList; + +public class ImportKeysListServerLoader + extends AsyncTaskLoader>> { + Context mContext; + + String mServerQuery; + String mKeyServer; + + private ArrayList mEntryList = new ArrayList(); + private AsyncTaskResultWrapper> mEntryListWrapper; + + public ImportKeysListServerLoader(Context context, String serverQuery, String keyServer) { + super(context); + mContext = context; + mServerQuery = serverQuery; + mKeyServer = keyServer; + } + + @Override + public AsyncTaskResultWrapper> loadInBackground() { + + mEntryListWrapper = new AsyncTaskResultWrapper>(mEntryList, null); + + if (mServerQuery == null) { + Log.e(Constants.TAG, "mServerQuery is null!"); + return mEntryListWrapper; + } + + if (mServerQuery.startsWith("0x") && mServerQuery.length() == 42) { + Log.d(Constants.TAG, "This search is based on a unique fingerprint. Enforce a fingerprint check!"); + queryServer(mServerQuery, mKeyServer, true); + } else { + queryServer(mServerQuery, mKeyServer, false); + } + + return mEntryListWrapper; + } + + @Override + protected void onReset() { + super.onReset(); + + // Ensure the loader is stopped + onStopLoading(); + } + + @Override + protected void onStartLoading() { + forceLoad(); + } + + @Override + protected void onStopLoading() { + cancelLoad(); + } + + @Override + public void deliverResult(AsyncTaskResultWrapper> data) { + super.deliverResult(data); + } + + /** + * Query keyserver + */ + private void queryServer(String query, String keyServer, boolean enforceFingerprint) { + HkpKeyServer server = new HkpKeyServer(keyServer); + try { + ArrayList searchResult = server.search(query); + + mEntryList.clear(); + // add result to data + if (enforceFingerprint) { + String fingerprint = query.substring(2); + Log.d(Constants.TAG, "fingerprint: " + fingerprint); + // query must return only one result! + if (searchResult.size() > 0) { + ImportKeysListEntry uniqueEntry = searchResult.get(0); + /* + * set fingerprint explicitly after query + * to enforce a check when the key is imported by KeychainIntentService + */ + uniqueEntry.setFingerPrintHex(fingerprint); + uniqueEntry.setSelected(true); + mEntryList.add(uniqueEntry); + } + } else { + mEntryList.addAll(searchResult); + } + mEntryListWrapper = new AsyncTaskResultWrapper>(mEntryList, null); + } catch (KeyServer.InsufficientQuery e) { + Log.e(Constants.TAG, "InsufficientQuery", e); + mEntryListWrapper = new AsyncTaskResultWrapper>(mEntryList, e); + } catch (KeyServer.QueryException e) { + Log.e(Constants.TAG, "QueryException", e); + mEntryListWrapper = new AsyncTaskResultWrapper>(mEntryList, e); + } catch (KeyServer.TooManyResponses e) { + Log.e(Constants.TAG, "TooManyResponses", e); + mEntryListWrapper = new AsyncTaskResultWrapper>(mEntryList, e); + } + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java new file mode 100644 index 000000000..5b5d316b6 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java @@ -0,0 +1,101 @@ +/* + * 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.content.Context; +import android.widget.ArrayAdapter; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; + +public class KeyValueSpinnerAdapter extends ArrayAdapter { + private final HashMap mData; + private final int[] mKeys; + private final String[] mValues; + + static > SortedSet> entriesSortedByValues( + Map map) { + SortedSet> sortedEntries = new TreeSet>( + new Comparator>() { + @Override + public int compare(Map.Entry e1, Map.Entry e2) { + return e1.getValue().compareTo(e2.getValue()); + } + }); + sortedEntries.addAll(map.entrySet()); + return sortedEntries; + } + + public KeyValueSpinnerAdapter(Context context, HashMap objects) { + // To make the drop down a simple text box + super(context, android.R.layout.simple_spinner_item); + mData = objects; + + // To make the drop down view a radio button list + setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + + SortedSet> sorted = entriesSortedByValues(objects); + + // Assign hash keys with a position so that we can present and retrieve them + int i = 0; + mKeys = new int[mData.size()]; + mValues = new String[mData.size()]; + for (Map.Entry entry : sorted) { + mKeys[i] = entry.getKey(); + mValues[i] = entry.getValue(); + i++; + } + } + + public int getCount() { + return mData.size(); + } + + /** + * Returns the value + */ + @Override + public String getItem(int position) { + // return the value based on the position. This is displayed in the list. + return mValues[position]; + } + + /** + * Returns item key + */ + public long getItemId(int position) { + // Return an id to represent the item. + + return mKeys[position]; + } + + /** + * Find position from key + */ + public int getPosition(long itemId) { + for (int i = 0; i < mKeys.length; i++) { + if ((int) itemId == mKeys[i]) { + return i; + } + } + return -1; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java new file mode 100644 index 000000000..fd864eb09 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 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.adapter; + +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v7.app.ActionBarActivity; + +import java.util.ArrayList; + +public class PagerTabStripAdapter extends FragmentPagerAdapter { + private final Context mContext; + private final ArrayList mTabs = new ArrayList(); + + static final class TabInfo { + public final Class clss; + public final Bundle args; + public final String title; + + TabInfo(Class clss, Bundle args, String title) { + this.clss = clss; + this.args = args; + this.title = title; + } + } + + public PagerTabStripAdapter(ActionBarActivity activity) { + super(activity.getSupportFragmentManager()); + mContext = activity; + } + + public void addTab(Class clss, Bundle args, String title) { + TabInfo info = new TabInfo(clss, args, title); + mTabs.add(info); + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return mTabs.size(); + } + + @Override + public Fragment getItem(int position) { + TabInfo info = mTabs.get(position); + return Fragment.instantiate(mContext, info.clss.getName(), info.args); + } + + @Override + public CharSequence getPageTitle(int position) { + return mTabs.get(position).title; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java new file mode 100644 index 000000000..fbbb9caa4 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java @@ -0,0 +1,165 @@ +/* + * 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.adapter; + +import android.content.Context; +import android.database.Cursor; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.ListView; +import android.widget.TextView; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; + + +public class SelectKeyCursorAdapter extends HighlightQueryCursorAdapter { + + protected int mKeyType; + + private LayoutInflater mInflater; + private ListView mListView; + + private int mIndexUserId; + private int mIndexMasterKeyId; + private int mIndexProjectionValid; + private int mIndexProjectionAvailable; + + public static final String PROJECTION_ROW_AVAILABLE = "available"; + public static final String PROJECTION_ROW_VALID = "valid"; + + public SelectKeyCursorAdapter(Context context, Cursor c, int flags, ListView listView, + int keyType) { + super(context, c, flags); + + mInflater = LayoutInflater.from(context); + mListView = listView; + mKeyType = keyType; + 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); + mIndexMasterKeyId = cursor.getColumnIndexOrThrow(KeyRings.MASTER_KEY_ID); + mIndexProjectionValid = cursor.getColumnIndexOrThrow(PROJECTION_ROW_VALID); + mIndexProjectionAvailable = cursor.getColumnIndexOrThrow(PROJECTION_ROW_AVAILABLE); + } + } + + public String getUserId(int position) { + mCursor.moveToPosition(position); + return mCursor.getString(mIndexUserId); + } + + public long getMasterKeyId(int position) { + mCursor.moveToPosition(position); + return mCursor.getLong(mIndexMasterKeyId); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + boolean valid = cursor.getInt(mIndexProjectionValid) > 0; + + TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); + TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); + TextView keyId = (TextView) view.findViewById(R.id.keyId); + TextView status = (TextView) view.findViewById(R.id.status); + + String userId = cursor.getString(mIndexUserId); + String[] userIdSplit = PgpKeyHelper.splitUserId(userId); + + if (userIdSplit[0] != null) { + mainUserId.setText(highlightSearchQuery(userIdSplit[0])); + } else { + mainUserId.setText(R.string.user_id_no_name); + } + if (userIdSplit[1] != null) { + mainUserIdRest.setText(highlightSearchQuery(userIdSplit[1])); + } else { + mainUserIdRest.setText(""); + } + + // TODO: needed to key id to no? + keyId.setText(R.string.no_key); + long masterKeyId = cursor.getLong(mIndexMasterKeyId); + keyId.setText(PgpKeyHelper.convertKeyIdToHexShort(masterKeyId)); + + // TODO: needed to set unknown_status? + status.setText(R.string.unknown_status); + if (valid) { + if (mKeyType == Id.type.public_key) { + status.setText(R.string.can_encrypt); + } else { + status.setText(R.string.can_sign); + } + } else { + if (cursor.getInt(mIndexProjectionAvailable) > 0) { + // has some CAN_ENCRYPT keys, but col(ROW_VALID) = 0, so must be revoked or + // expired + status.setText(R.string.expired); + } else { + status.setText(R.string.no_key); + } + } + + CheckBox selected = (CheckBox) view.findViewById(R.id.selected); + if (mKeyType == Id.type.public_key) { + selected.setVisibility(View.VISIBLE); + + if (!valid) { + mListView.setItemChecked(cursor.getPosition(), false); + } + + selected.setChecked(mListView.isItemChecked(cursor.getPosition())); + selected.setEnabled(valid); + } else { + selected.setVisibility(View.GONE); + } + + status.setText(status.getText() + " "); + + view.setEnabled(valid); + mainUserId.setEnabled(valid); + mainUserIdRest.setEnabled(valid); + keyId.setEnabled(valid); + status.setEnabled(valid); + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return mInflater.inflate(R.layout.select_key_item, null); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java new file mode 100644 index 000000000..9ddfa90be --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java @@ -0,0 +1,101 @@ +/* + * 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.adapter; + +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentStatePagerAdapter; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.ViewPager; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarActivity; + +import java.util.ArrayList; + +public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar.TabListener, + ViewPager.OnPageChangeListener { + private final Context mContext; + private final ActionBar mActionBar; + private final ViewPager mViewPager; + private final ArrayList mTabs = new ArrayList(); + + static final class TabInfo { + public final Class clss; + public final Bundle args; + + TabInfo(Class clss, Bundle args) { + this.clss = clss; + this.args = args; + } + } + + public TabsAdapter(ActionBarActivity activity, ViewPager pager) { + super(activity.getSupportFragmentManager()); + mContext = activity; + mActionBar = activity.getSupportActionBar(); + mViewPager = pager; + mViewPager.setAdapter(this); + mViewPager.setOnPageChangeListener(this); + } + + public void addTab(ActionBar.Tab tab, Class clss, Bundle args, boolean selected) { + TabInfo info = new TabInfo(clss, args); + tab.setTag(info); + tab.setTabListener(this); + mTabs.add(info); + mActionBar.addTab(tab, selected); + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return mTabs.size(); + } + + @Override + public Fragment getItem(int position) { + TabInfo info = mTabs.get(position); + return Fragment.instantiate(mContext, info.clss.getName(), info.args); + } + + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } + + public void onPageSelected(int position) { + mActionBar.setSelectedNavigationItem(position); + } + + public void onPageScrollStateChanged(int state) { + } + + public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) { + Object tag = tab.getTag(); + for (int i = 0; i < mTabs.size(); i++) { + if (mTabs.get(i) == tag) { + mViewPager.setCurrentItem(i); + } + } + } + + public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) { + } + + public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) { + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java new file mode 100644 index 000000000..64b735bfa --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 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.adapter; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.database.Cursor; +import android.support.v4.widget.CursorAdapter; +import android.text.format.DateFormat; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; + +import java.util.Date; + +public class ViewKeyKeysAdapter extends CursorAdapter { + private LayoutInflater mInflater; + + private int mIndexKeyId; + private int mIndexAlgorithm; + private int mIndexKeySize; + private int mIndexRank; + private int mIndexCanCertify; + private int mIndexCanEncrypt; + private int mIndexCanSign; + private int mIndexRevokedKey; + private int mIndexExpiry; + + private ColorStateList mDefaultTextColor; + + public ViewKeyKeysAdapter(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) { + mIndexKeyId = cursor.getColumnIndexOrThrow(Keys.KEY_ID); + mIndexAlgorithm = cursor.getColumnIndexOrThrow(Keys.ALGORITHM); + mIndexKeySize = cursor.getColumnIndexOrThrow(Keys.KEY_SIZE); + mIndexRank = cursor.getColumnIndexOrThrow(Keys.RANK); + mIndexCanCertify = cursor.getColumnIndexOrThrow(Keys.CAN_CERTIFY); + mIndexCanEncrypt = cursor.getColumnIndexOrThrow(Keys.CAN_ENCRYPT); + mIndexCanSign = cursor.getColumnIndexOrThrow(Keys.CAN_SIGN); + mIndexRevokedKey = cursor.getColumnIndexOrThrow(Keys.IS_REVOKED); + mIndexExpiry = cursor.getColumnIndexOrThrow(Keys.EXPIRY); + } + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + TextView keyId = (TextView) view.findViewById(R.id.keyId); + TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails); + TextView keyExpiry = (TextView) view.findViewById(R.id.keyExpiry); + ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey); + ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey); + ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey); + ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey); + ImageView revokedKeyIcon = (ImageView) view.findViewById(R.id.ic_revokedKey); + + String keyIdStr = PgpKeyHelper.convertKeyIdToHexShort(cursor.getLong(mIndexKeyId)); + String algorithmStr = PgpKeyHelper.getAlgorithmInfo(cursor.getInt(mIndexAlgorithm), + cursor.getInt(mIndexKeySize)); + + keyId.setText(keyIdStr); + keyDetails.setText("(" + algorithmStr + ")"); + + if (cursor.getInt(mIndexRank) == 0) { + masterKeyIcon.setVisibility(View.INVISIBLE); + } else { + masterKeyIcon.setVisibility(View.VISIBLE); + } + + if (cursor.getInt(mIndexCanCertify) != 1) { + certifyIcon.setVisibility(View.GONE); + } else { + certifyIcon.setVisibility(View.VISIBLE); + } + + if (cursor.getInt(mIndexCanEncrypt) != 1) { + encryptIcon.setVisibility(View.GONE); + } else { + encryptIcon.setVisibility(View.VISIBLE); + } + + if (cursor.getInt(mIndexCanSign) != 1) { + signIcon.setVisibility(View.GONE); + } else { + signIcon.setVisibility(View.VISIBLE); + } + + boolean valid = true; + if (cursor.getInt(mIndexRevokedKey) > 0) { + revokedKeyIcon.setVisibility(View.VISIBLE); + + valid = false; + } else { + keyId.setTextColor(mDefaultTextColor); + keyDetails.setTextColor(mDefaultTextColor); + keyExpiry.setTextColor(mDefaultTextColor); + + revokedKeyIcon.setVisibility(View.GONE); + } + + if (!cursor.isNull(mIndexExpiry)) { + Date expiryDate = new Date(cursor.getLong(mIndexExpiry) * 1000); + + valid = valid && expiryDate.after(new Date()); + keyExpiry.setText("(" + + context.getString(R.string.label_expiry) + ": " + + DateFormat.getDateFormat(context).format(expiryDate) + ")"); + + keyExpiry.setVisibility(View.VISIBLE); + } else { + keyExpiry.setVisibility(View.GONE); + } + // if key is expired or revoked, strike through text + if (!valid) { + keyId.setText(OtherHelper.strikeOutText(keyId.getText())); + keyDetails.setText(OtherHelper.strikeOutText(keyDetails.getText())); + keyExpiry.setText(OtherHelper.strikeOutText(keyExpiry.getText())); + } + keyId.setEnabled(valid); + keyDetails.setEnabled(valid); + keyExpiry.setEnabled(valid); + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + View view = mInflater.inflate(R.layout.view_key_keys_item, null); + if (mDefaultTextColor == null) { + TextView keyId = (TextView) view.findViewById(R.id.keyId); + mDefaultTextColor = keyId.getTextColors(); + } + return view; + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java new file mode 100644 index 000000000..09137f745 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 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.adapter; + +import android.content.Context; +import android.database.Cursor; +import android.support.v4.widget.CursorAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.ImageView; +import android.widget.TextView; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; + +import java.util.ArrayList; + +public class ViewKeyUserIdsAdapter extends CursorAdapter implements AdapterView.OnItemClickListener { + private LayoutInflater mInflater; + + private int mIndexUserId, mIndexRank; + private int mVerifiedId, mIsRevoked, mIsPrimary; + + private final ArrayList mCheckStates; + + public static final String[] USER_IDS_PROJECTION = new String[] { + UserIds._ID, UserIds.USER_ID, UserIds.RANK, + UserIds.VERIFIED, UserIds.IS_PRIMARY, UserIds.IS_REVOKED + }; + + public ViewKeyUserIdsAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes) { + super(context, c, flags); + + mInflater = LayoutInflater.from(context); + + mCheckStates = showCheckBoxes ? new ArrayList() : null; + + initIndex(c); + } + public ViewKeyUserIdsAdapter(Context context, Cursor c, int flags) { + this(context, c, flags, false); + } + + @Override + public Cursor swapCursor(Cursor newCursor) { + initIndex(newCursor); + if (mCheckStates != null) { + mCheckStates.clear(); + if (newCursor != null) { + int count = newCursor.getCount(); + mCheckStates.ensureCapacity(count); + // initialize to true (use case knowledge: we usually want to sign all uids) + for(int i = 0; i < count; i++) { + newCursor.moveToPosition(i); + int verified = newCursor.getInt(mVerifiedId); + mCheckStates.add(verified != Certs.VERIFIED_SECRET); + } + } + } + + 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); + mIndexRank = cursor.getColumnIndexOrThrow(UserIds.RANK); + mVerifiedId = cursor.getColumnIndexOrThrow(UserIds.VERIFIED); + mIsRevoked = cursor.getColumnIndexOrThrow(UserIds.IS_REVOKED); + mIsPrimary = cursor.getColumnIndexOrThrow(UserIds.IS_PRIMARY); + } + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + + TextView vRank = (TextView) view.findViewById(R.id.rank); + TextView vUserId = (TextView) view.findViewById(R.id.userId); + TextView vAddress = (TextView) view.findViewById(R.id.address); + ImageView vVerified = (ImageView) view.findViewById(R.id.certified); + + if(cursor.getInt(mIsPrimary) > 0) { + vRank.setText("+"); + } else { + vRank.setText(Integer.toString(cursor.getInt(mIndexRank))); + } + + String[] userId = PgpKeyHelper.splitUserId(cursor.getString(mIndexUserId)); + if (userId[0] != null) { + vUserId.setText(userId[0]); + } else { + vUserId.setText(R.string.user_id_no_name); + } + vAddress.setText(userId[1]); + + if(cursor.getInt(mIsRevoked) > 0) { + vRank.setText(" "); + vVerified.setImageResource(android.R.drawable.presence_away); + } else { + int verified = cursor.getInt(mVerifiedId); + // TODO introduce own resources for this :) + if(verified == Certs.VERIFIED_SECRET) + vVerified.setImageResource(android.R.drawable.presence_online); + else if(verified == Certs.VERIFIED_SELF) + vVerified.setImageResource(android.R.drawable.presence_invisible); + else + vVerified.setImageResource(android.R.drawable.presence_busy); + } + + // don't care further if checkboxes aren't shown + if (mCheckStates == null) { + return; + } + + final CheckBox vCheckBox = (CheckBox) view.findViewById(R.id.checkBox); + final int position = cursor.getPosition(); + vCheckBox.setOnCheckedChangeListener(null); + vCheckBox.setChecked(mCheckStates.get(position)); + vCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean b) { + mCheckStates.set(position, b); + } + }); + vCheckBox.setClickable(false); + + } + + public void onItemClick(AdapterView adapter, View view, int position, long id) { + CheckBox box = ((CheckBox) view.findViewById(R.id.checkBox)); + if(box != null) { + box.toggle(); + } + } + + public ArrayList getSelectedUserIds() { + ArrayList result = new ArrayList(); + for (int i = 0; i < mCheckStates.size(); i++) { + if (mCheckStates.get(i)) { + mCursor.moveToPosition(i); + result.add(mCursor.getString(mIndexUserId)); + } + } + return result; + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + View view = mInflater.inflate(R.layout.view_key_userids_item, null); + // only need to do this once ever, since mShowCheckBoxes is final + view.findViewById(R.id.checkBox).setVisibility(mCheckStates != null ? View.VISIBLE : View.GONE); + return view; + } + +} -- cgit v1.2.3