diff options
Diffstat (limited to 'OpenKeychain/src/main')
27 files changed, 375 insertions, 253 deletions
diff --git a/OpenKeychain/src/main/java/android/support/v4/widget/FixedDrawerLayout.java b/OpenKeychain/src/main/java/android/support/v4/widget/FixedDrawerLayout.java index 1151818b6..6924e0b06 100644 --- a/OpenKeychain/src/main/java/android/support/v4/widget/FixedDrawerLayout.java +++ b/OpenKeychain/src/main/java/android/support/v4/widget/FixedDrawerLayout.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.support.v4.widget; import android.content.Context; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyServer.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java index 85ce6bfcc..f14978b39 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyServer.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java @@ -52,7 +52,7 @@ import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class HkpKeyServer extends KeyServer { +public class HkpKeyserver extends Keyserver { private static class HttpError extends Exception { private static final long serialVersionUID = 1718783705229428893L; private int mCode; @@ -148,7 +148,7 @@ public class HkpKeyServer extends KeyServer { * connect using {@link #PORT_DEFAULT}. However, port may be specified after colon * ("<code>hostname:port</code>", eg. "<code>p80.pool.sks-keyservers.net:80</code>"). */ - public HkpKeyServer(String hostAndPort) { + public HkpKeyserver(String hostAndPort) { String host = hostAndPort; short port = PORT_DEFAULT; final int colonPosition = hostAndPort.lastIndexOf(':'); @@ -161,7 +161,7 @@ public class HkpKeyServer extends KeyServer { mPort = port; } - public HkpKeyServer(String host, short port) { + public HkpKeyserver(String host, short port) { mHost = host; mPort = port; } @@ -237,6 +237,7 @@ public class HkpKeyServer extends KeyServer { final Matcher matcher = PUB_KEY_LINE.matcher(data); while (matcher.find()) { final ImportKeysListEntry entry = new ImportKeysListEntry(); + entry.setQuery(query); entry.setBitStrength(Integer.parseInt(matcher.group(3))); @@ -247,7 +248,7 @@ public class HkpKeyServer extends KeyServer { // see http://bit.ly/1d4bxbk and http://bit.ly/1gD1wwr String fingerprintOrKeyId = matcher.group(1); if (fingerprintOrKeyId.length() > 16) { - entry.setFingerPrintHex(fingerprintOrKeyId.toLowerCase(Locale.US)); + entry.setFingerprintHex(fingerprintOrKeyId.toLowerCase(Locale.US)); entry.setKeyIdHex("0x" + fingerprintOrKeyId.substring(fingerprintOrKeyId.length() - 16, fingerprintOrKeyId.length())); } else { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java index 1199290e0..04b86e295 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java @@ -45,11 +45,13 @@ public class ImportKeysListEntry implements Serializable, Parcelable { public String keyIdHex; public boolean revoked; public Date date; // TODO: not displayed - public String fingerPrintHex; + public String fingerprintHex; public int bitStrength; public String algorithm; public boolean secretKey; public String mPrimaryUserId; + private String mExtraData; + private String mQuery; private boolean mSelected; @@ -66,7 +68,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { dest.writeLong(keyId); dest.writeByte((byte) (revoked ? 1 : 0)); dest.writeSerializable(date); - dest.writeString(fingerPrintHex); + dest.writeString(fingerprintHex); dest.writeString(keyIdHex); dest.writeInt(bitStrength); dest.writeString(algorithm); @@ -74,6 +76,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { dest.writeByte((byte) (mSelected ? 1 : 0)); dest.writeInt(mBytes.length); dest.writeByteArray(mBytes); + dest.writeString(mExtraData); } public static final Creator<ImportKeysListEntry> CREATOR = new Creator<ImportKeysListEntry>() { @@ -85,7 +88,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { vr.keyId = source.readLong(); vr.revoked = source.readByte() == 1; vr.date = (Date) source.readSerializable(); - vr.fingerPrintHex = source.readString(); + vr.fingerprintHex = source.readString(); vr.keyIdHex = source.readString(); vr.bitStrength = source.readInt(); vr.algorithm = source.readString(); @@ -93,6 +96,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { vr.mSelected = source.readByte() == 1; vr.mBytes = new byte[source.readInt()]; source.readByteArray(vr.mBytes); + vr.mExtraData = source.readString(); return vr; } @@ -150,12 +154,12 @@ public class ImportKeysListEntry implements Serializable, Parcelable { this.date = date; } - public String getFingerPrintHex() { - return fingerPrintHex; + public String getFingerprintHex() { + return fingerprintHex; } - public void setFingerPrintHex(String fingerPrintHex) { - this.fingerPrintHex = fingerPrintHex; + public void setFingerprintHex(String fingerprintHex) { + this.fingerprintHex = fingerprintHex; } public int getBitStrength() { @@ -198,6 +202,22 @@ public class ImportKeysListEntry implements Serializable, Parcelable { mPrimaryUserId = uid; } + public String getExtraData() { + return mExtraData; + } + + public void setExtraData(String extraData) { + mExtraData = extraData; + } + + public String getQuery() { + return mQuery; + } + + public void setQuery(String query) { + mQuery = query; + } + /** * Constructor for later querying from keyserver */ @@ -260,7 +280,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId); this.revoked = key.isRevoked(); - this.fingerPrintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint()); + this.fingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint()); this.bitStrength = key.getBitStrength(); final int algorithm = key.getAlgorithm(); this.algorithm = PgpKeyHelper.getAlgorithmInfo(context, algorithm); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyServer.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java index 7ffe123c0..5b66b50c5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyServer.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java @@ -21,6 +21,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.util.JWalk; import org.sufficientlysecure.keychain.util.Log; @@ -28,19 +29,20 @@ import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; -import java.util.GregorianCalendar; -import java.util.TimeZone; -import java.util.WeakHashMap; -public class KeybaseKeyServer extends KeyServer { - - private WeakHashMap<String, String> mKeyCache = new WeakHashMap<String, String>(); +public class KeybaseKeyserver extends Keyserver { + private String mQuery; @Override public ArrayList<ImportKeysListEntry> search(String query) throws QueryException, TooManyResponses, InsufficientQuery { ArrayList<ImportKeysListEntry> results = new ArrayList<ImportKeysListEntry>(); + if (query.startsWith("0x")) { + // cut off "0x" if a user is searching for a key id + query = query.substring(2); + } + JSONObject fromQuery = getFromKeybase("_/api/1.0/user/autocomplete.json?q=", query); try { @@ -50,59 +52,76 @@ public class KeybaseKeyServer extends KeyServer { // only list them if they have a key if (JWalk.optObject(match, "components", "key_fingerprint") != null) { - results.add(makeEntry(match)); + String keybaseId = JWalk.getString(match, "components", "username", "val"); + String fingerprint = JWalk.getString(match, "components", "key_fingerprint", "val"); + fingerprint = fingerprint.replace(" ", "").toUpperCase(); + + if (keybaseId.equals(query) || fingerprint.startsWith(query.toUpperCase())) { + results.add(makeEntry(match)); + } else { + results.add(makeEntry(match)); + } } } } catch (Exception e) { + Log.e(Constants.TAG, "keybase result parsing error", e); throw new QueryException("Unexpected structure in keybase search result: " + e.getMessage()); } return results; } - private JSONObject getUser(String keybaseID) throws QueryException { + private JSONObject getUser(String keybaseId) throws QueryException { try { - return getFromKeybase("_/api/1.0/user/lookup.json?username=", keybaseID); + return getFromKeybase("_/api/1.0/user/lookup.json?username=", keybaseId); } catch (Exception e) { String detail = ""; - if (keybaseID != null) { - detail = ". Query was for user '" + keybaseID + "'"; + if (keybaseId != null) { + detail = ". Query was for user '" + keybaseId + "'"; } throw new QueryException(e.getMessage() + detail); } } private ImportKeysListEntry makeEntry(JSONObject match) throws QueryException, JSONException { - - String keybaseID = JWalk.getString(match, "components", "username", "val"); - String key_fingerprint = JWalk.getString(match, "components", "key_fingerprint", "val"); - key_fingerprint = key_fingerprint.replace(" ", "").toUpperCase(); - match = getUser(keybaseID); - final ImportKeysListEntry entry = new ImportKeysListEntry(); + entry.setQuery(mQuery); - // TODO: Fix; have suggested keybase provide this value to avoid search-time crypto calls - entry.setBitStrength(4096); - entry.setAlgorithm("RSA"); - entry.setKeyIdHex("0x" + key_fingerprint); - entry.setRevoked(false); + String keybaseId = JWalk.getString(match, "components", "username", "val"); + String fullName = JWalk.getString(match, "components", "full_name", "val"); + String fingerprint = JWalk.getString(match, "components", "key_fingerprint", "val"); + fingerprint = fingerprint.replace(" ", "").toUpperCase(); // not strictly necessary but doesn't hurt + entry.setFingerprintHex(fingerprint); - // ctime - final long creationDate = JWalk.getLong(match, "them", "public_keys", "primary", "ctime"); - final GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC")); - tmpGreg.setTimeInMillis(creationDate * 1000); - entry.setDate(tmpGreg.getTime()); + entry.setKeyIdHex("0x" + fingerprint.substring(Math.max(0, fingerprint.length() - 16))); + // store extra info, so we can query for the keybase id directly + entry.setExtraData(keybaseId); - // key bits - // we have to fetch the user object to construct the search-result list, so we might as - // well (weakly) remember the key, in case they try to import it - mKeyCache.put(keybaseID, JWalk.getString(match,"them", "public_keys", "primary", "bundle")); + final int algorithmId = JWalk.getInt(match, "components", "key_fingerprint", "algo"); + entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId)); + final int bitStrength = JWalk.getInt(match, "components", "key_fingerprint", "nbits"); + entry.setBitStrength(bitStrength); - // String displayName = JWalk.getString(match, "them", "profile", "full_name"); ArrayList<String> userIds = new ArrayList<String>(); - String name = "keybase.io/" + keybaseID + " <" + keybaseID + "@keybase.io>"; + String name = fullName + " <keybase.io/" + keybaseId + ">"; userIds.add(name); - userIds.add(keybaseID); + try { + userIds.add("github.com/" + JWalk.getString(match, "components", "github", "val")); + } catch (JSONException e) { + // ignore + } + try { + userIds.add("twitter.com/" + JWalk.getString(match, "components", "twitter", "val")); + } catch (JSONException e) { + // ignore + } + try { + JSONArray array = JWalk.getArray(match, "components", "websites"); + JSONObject website = array.getJSONObject(0); + userIds.add(JWalk.getString(website, "val")); + } catch (JSONException e) { + // ignore + } entry.setUserIds(userIds); entry.setPrimaryUserId(name); return entry; @@ -142,20 +161,16 @@ public class KeybaseKeyServer extends KeyServer { @Override public String get(String id) throws QueryException { - String key = mKeyCache.get(id); - if (key == null) { - try { - JSONObject user = getUser(id); - key = JWalk.getString(user, "them", "public_keys", "primary", "bundle"); - } catch (Exception e) { - throw new QueryException(e.getMessage()); - } + try { + JSONObject user = getUser(id); + return JWalk.getString(user, "them", "public_keys", "primary", "bundle"); + } catch (Exception e) { + throw new QueryException(e.getMessage()); } - return key; } @Override public void add(String armoredKey) throws AddKeyException { throw new AddKeyException(); } -}
\ No newline at end of file +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeyServer.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java index d6ebca5a6..19591eda8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeyServer.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java @@ -23,7 +23,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.List; -public abstract class KeyServer { +public abstract class Keyserver { public static class QueryException extends Exception { private static final long serialVersionUID = 2703768928624654512L; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index 5c4604647..a55765542 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -36,9 +36,9 @@ import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; -import org.sufficientlysecure.keychain.keyimport.HkpKeyServer; +import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; import org.sufficientlysecure.keychain.util.IterableIterator; -import org.sufficientlysecure.keychain.keyimport.KeyServer.AddKeyException; +import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException; import org.sufficientlysecure.keychain.util.Log; import java.io.ByteArrayOutputStream; @@ -100,7 +100,7 @@ public class PgpImportExport { } } - public boolean uploadKeyRingToServer(HkpKeyServer server, WrappedPublicKeyRing keyring) { + public boolean uploadKeyRingToServer(HkpKeyserver server, WrappedPublicKeyRing keyring) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ArmoredOutputStream aos = null; try { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index f0d01e2ce..b866bdf7f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -35,6 +35,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.FileHelper; import org.sufficientlysecure.keychain.helper.OtherHelper; import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.WrappedSecretKey; import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing; @@ -54,9 +55,8 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; -import org.sufficientlysecure.keychain.keyimport.HkpKeyServer; import org.sufficientlysecure.keychain.util.InputData; -import org.sufficientlysecure.keychain.keyimport.KeybaseKeyServer; +import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ProgressScaler; @@ -721,7 +721,7 @@ public class KeychainIntentService extends IntentService // and dataUri! /* Operation */ - HkpKeyServer server = new HkpKeyServer(keyServer); + HkpKeyserver server = new HkpKeyserver(keyServer); ProviderHelper providerHelper = new ProviderHelper(this); WrappedPublicKeyRing keyring = providerHelper.getWrappedPublicKeyRing(dataUri); @@ -740,11 +740,11 @@ public class KeychainIntentService extends IntentService ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST); try { - KeybaseKeyServer server = new KeybaseKeyServer(); + KeybaseKeyserver server = new KeybaseKeyserver(); for (ImportKeysListEntry entry : entries) { // the keybase handle is in userId(1) - String keybaseID = entry.getUserIds().get(1); - byte[] downloadedKeyBytes = server.get(keybaseID).getBytes(); + String keybaseId = entry.getExtraData(); + byte[] downloadedKeyBytes = server.get(keybaseId).getBytes(); // create PGPKeyRing object based on downloaded armored key PGPKeyRing downloadedKey = null; @@ -791,13 +791,13 @@ public class KeychainIntentService extends IntentService String keyServer = data.getString(DOWNLOAD_KEY_SERVER); // this downloads the keys and places them into the ImportKeysListEntry entries - HkpKeyServer server = new HkpKeyServer(keyServer); + HkpKeyserver server = new HkpKeyserver(keyServer); for (ImportKeysListEntry entry : entries) { // if available use complete fingerprint for get request byte[] downloadedKeyBytes; - if (entry.getFingerPrintHex() != null) { - downloadedKeyBytes = server.get("0x" + entry.getFingerPrintHex()).getBytes(); + if (entry.getFingerprintHex() != null) { + downloadedKeyBytes = server.get("0x" + entry.getFingerprintHex()).getBytes(); } else { downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes(); } @@ -807,10 +807,10 @@ public class KeychainIntentService extends IntentService UncachedKeyRing.decodePubkeyFromData(downloadedKeyBytes); // verify downloaded key by comparing fingerprints - if (entry.getFingerPrintHex() != null) { + if (entry.getFingerprintHex() != null) { String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex( downloadedKey.getFingerprint()); - if (downloadedKeyFp.equals(entry.getFingerPrintHex())) { + if (downloadedKeyFp.equals(entry.getFingerprintHex())) { Log.d(Constants.TAG, "fingerprint of downloaded key is the same as " + "the requested fingerprint!"); } else { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java index bf6abcd6f..e93d717e1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -38,7 +38,7 @@ import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListKeybaseLoader; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListServerLoader; import org.sufficientlysecure.keychain.util.InputData; -import org.sufficientlysecure.keychain.keyimport.KeyServer; +import org.sufficientlysecure.keychain.keyimport.Keyserver; import org.sufficientlysecure.keychain.util.Log; import java.io.ByteArrayInputStream; @@ -280,13 +280,13 @@ public class ImportKeysListFragment extends ListFragment implements mAdapter.getCount(), mAdapter.getCount()), AppMsg.STYLE_INFO ).show(); - } else if (error instanceof KeyServer.InsufficientQuery) { + } else if (error instanceof Keyserver.InsufficientQuery) { AppMsg.makeText(getActivity(), R.string.error_keyserver_insufficient_query, AppMsg.STYLE_ALERT).show(); - } else if (error instanceof KeyServer.QueryException) { + } else if (error instanceof Keyserver.QueryException) { AppMsg.makeText(getActivity(), R.string.error_keyserver_query, AppMsg.STYLE_ALERT).show(); - } else if (error instanceof KeyServer.TooManyResponses) { + } else if (error instanceof Keyserver.TooManyResponses) { AppMsg.makeText(getActivity(), R.string.error_keyserver_too_many_responses, AppMsg.STYLE_ALERT).show(); } @@ -300,7 +300,7 @@ public class ImportKeysListFragment extends ListFragment implements mAdapter.getCount(), mAdapter.getCount()), AppMsg.STYLE_INFO ).show(); - } else if (error instanceof KeyServer.QueryException) { + } else if (error instanceof Keyserver.QueryException) { AppMsg.makeText(getActivity(), R.string.error_keyserver_query, AppMsg.STYLE_ALERT).show(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index a3f4b0753..9c90b5eb7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -34,6 +34,7 @@ import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v4.view.MenuItemCompat; import android.support.v7.app.ActionBarActivity; +import android.support.v4.widget.CursorAdapter; import android.support.v7.widget.SearchView; import android.text.TextUtils; import android.view.ActionMode; @@ -61,8 +62,8 @@ import org.sufficientlysecure.keychain.helper.ExportHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.ui.adapter.HighlightQueryCursorAdapter; import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; +import org.sufficientlysecure.keychain.util.Highlighter; import org.sufficientlysecure.keychain.util.Log; import java.util.Date; @@ -82,7 +83,7 @@ public class KeyListFragment extends LoaderFragment private KeyListAdapter mAdapter; private StickyListHeadersListView mStickyList; - private String mCurQuery; + private String mQuery; private SearchView mSearchView; // empty list layout private BootstrapButton mButtonEmptyCreate; @@ -130,7 +131,7 @@ public class KeyListFragment extends LoaderFragment /** * Define Adapter and Loader on create of Activity */ - @SuppressLint("NewApi") + @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); @@ -141,8 +142,7 @@ public class KeyListFragment extends LoaderFragment mStickyList.setFastScrollEnabled(true); /* - * ActionBarSherlock does not support MultiChoiceModeListener. Thus multi-selection is only - * available for Android >= 3.0 + * Multi-selection is only available for Android >= 3.0 */ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { mStickyList.setFastScrollAlwaysVisible(true); @@ -263,9 +263,18 @@ public class KeyListFragment extends LoaderFragment Uri baseUri = KeyRings.buildUnifiedKeyRingsUri(); String where = null; String whereArgs[] = null; - if (mCurQuery != null) { - where = KeyRings.USER_ID + " LIKE ?"; - whereArgs = new String[]{"%" + mCurQuery + "%"}; + if (mQuery != null) { + String[] words = mQuery.trim().split("\\s+"); + whereArgs = new String[words.length]; + for (int i = 0; i < words.length; ++i) { + if (where == null) { + where = ""; + } else { + where += " AND "; + } + where += KeyRings.USER_ID + " LIKE ?"; + whereArgs[i] = "%" + words[i] + "%"; + } } // Now create and return a CursorLoader that will take care of @@ -277,7 +286,7 @@ public class KeyListFragment extends LoaderFragment 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.setSearchQuery(mCurQuery); + mAdapter.setSearchQuery(mQuery); mAdapter.swapCursor(data); mStickyList.setAdapter(mAdapter); @@ -312,7 +321,7 @@ public class KeyListFragment extends LoaderFragment startActivity(viewIntent); } - @TargetApi(11) + @TargetApi(Build.VERSION_CODES.HONEYCOMB) protected void encrypt(ActionMode mode, long[] masterKeyIds) { Intent intent = new Intent(getActivity(), EncryptActivity.class); intent.setAction(EncryptActivity.ACTION_ENCRYPT); @@ -329,7 +338,7 @@ public class KeyListFragment extends LoaderFragment * @param masterKeyIds * @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not */ - @TargetApi(11) + @TargetApi(Build.VERSION_CODES.HONEYCOMB) public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds, boolean hasSecret) { // Can only work on singular secret keys if(hasSecret && masterKeyIds.length > 1) { @@ -379,7 +388,7 @@ public class KeyListFragment extends LoaderFragment @Override public boolean onMenuItemActionCollapse(MenuItem item) { - mCurQuery = null; + mQuery = null; mSearchView.setQuery("", true); getLoaderManager().restartLoader(0, null, KeyListFragment.this); return true; @@ -399,7 +408,7 @@ public class KeyListFragment extends LoaderFragment // Called when the action bar search text has changed. Update // the search filter, and restart the loader to do a new query // with this filter. - mCurQuery = !TextUtils.isEmpty(s) ? s : null; + mQuery = !TextUtils.isEmpty(s) ? s : null; getLoaderManager().restartLoader(0, null, this); return true; } @@ -407,7 +416,8 @@ public class KeyListFragment extends LoaderFragment /** * Implements StickyListHeadersAdapter from library */ - private class KeyListAdapter extends HighlightQueryCursorAdapter implements StickyListHeadersAdapter { + private class KeyListAdapter extends CursorAdapter implements StickyListHeadersAdapter { + private String mQuery; private LayoutInflater mInflater; private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>(); @@ -418,6 +428,10 @@ public class KeyListFragment extends LoaderFragment mInflater = LayoutInflater.from(context); } + public void setSearchQuery(String query) { + mQuery = query; + } + @Override public Cursor swapCursor(Cursor newCursor) { return super.swapCursor(newCursor); @@ -456,18 +470,19 @@ public class KeyListFragment extends LoaderFragment */ @Override public void bindView(View view, Context context, Cursor cursor) { + Highlighter highlighter = new Highlighter(context, mQuery); ItemViewHolder h = (ItemViewHolder) view.getTag(); { // set name and stuff, common to both key types String userId = cursor.getString(INDEX_USER_ID); String[] userIdSplit = PgpKeyHelper.splitUserId(userId); if (userIdSplit[0] != null) { - h.mMainUserId.setText(highlightSearchQuery(userIdSplit[0])); + h.mMainUserId.setText(highlighter.highlight(userIdSplit[0])); } else { h.mMainUserId.setText(R.string.user_id_no_name); } if (userIdSplit[1] != null) { - h.mMainUserIdRest.setText(highlightSearchQuery(userIdSplit[1])); + h.mMainUserIdRest.setText(highlighter.highlight(userIdSplit[1])); h.mMainUserIdRest.setVisibility(View.VISIBLE); } else { h.mMainUserIdRest.setVisibility(View.GONE); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java index 2ad769b00..9343b166a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java @@ -55,7 +55,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T private SelectKeyCursorAdapter mAdapter; private EditText mSearchView; private long mSelectedMasterKeyIds[]; - private String mCurQuery; + private String mQuery; // copied from ListFragment static final int INTERNAL_EMPTY_ID = 0x00ff0001; @@ -281,9 +281,18 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T } String where = null; String whereArgs[] = null; - if (mCurQuery != null) { - where = KeyRings.USER_ID + " LIKE ?"; - whereArgs = new String[]{"%" + mCurQuery + "%"}; + if (mQuery != null) { + String[] words = mQuery.trim().split("\\s+"); + whereArgs = new String[words.length]; + for (int i = 0; i < words.length; ++i) { + if (where == null) { + where = ""; + } else { + where += " AND "; + } + where += KeyRings.USER_ID + " LIKE ?"; + whereArgs[i] = "%" + words[i] + "%"; + } } // Now create and return a CursorLoader that will take care of @@ -295,7 +304,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T 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.setSearchQuery(mCurQuery); + mAdapter.setSearchQuery(mQuery); mAdapter.swapCursor(data); // The list should now be shown. @@ -329,7 +338,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T @Override public void afterTextChanged(Editable editable) { - mCurQuery = !TextUtils.isEmpty(editable.toString()) ? editable.toString() : null; + mQuery = !TextUtils.isEmpty(editable.toString()) ? editable.toString() : null; getLoaderManager().restartLoader(0, null, this); } 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 deleted file mode 100644 index fd7a2dc30..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/HighlightQueryCursorAdapter.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -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 index 9d323c822..9573efdfe 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -33,6 +33,7 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.util.Highlighter; import java.util.ArrayList; import java.util.Iterator; @@ -99,6 +100,7 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> { public View getView(int position, View convertView, ViewGroup parent) { ImportKeysListEntry entry = mData.get(position); + Highlighter highlighter = new Highlighter(mActivity, entry.getQuery()); ViewHolder holder; if (convertView == null) { holder = new ViewHolder(); @@ -128,7 +130,7 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> { + " " + userIdSplit[0]); holder.mainUserId.setTextColor(Color.RED); } else { - holder.mainUserId.setText(userIdSplit[0]); + holder.mainUserId.setText(highlighter.highlight(userIdSplit[0])); holder.mainUserId.setTextColor(Color.BLACK); } } else { @@ -139,21 +141,26 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> { // email if (userIdSplit[1] != null) { holder.mainUserIdRest.setVisibility(View.VISIBLE); - holder.mainUserIdRest.setText(userIdSplit[1]); + holder.mainUserIdRest.setText(highlighter.highlight(userIdSplit[1])); } else { holder.mainUserIdRest.setVisibility(View.GONE); } holder.keyId.setText(entry.keyIdHex); - if (entry.fingerPrintHex != null) { + if (entry.fingerprintHex != null) { holder.fingerprint.setVisibility(View.VISIBLE); - holder.fingerprint.setText(PgpKeyHelper.colorizeFingerprint(entry.fingerPrintHex)); + holder.fingerprint.setText(PgpKeyHelper.colorizeFingerprint(entry.fingerprintHex)); } else { holder.fingerprint.setVisibility(View.GONE); } - holder.algorithm.setText("" + entry.bitStrength + "/" + entry.algorithm); + if (entry.bitStrength != 0 && entry.algorithm != null) { + holder.algorithm.setText("" + entry.bitStrength + "/" + entry.algorithm); + holder.algorithm.setVisibility(View.VISIBLE); + } else { + holder.algorithm.setVisibility(View.INVISIBLE); + } if (entry.revoked) { holder.status.setVisibility(View.VISIBLE); @@ -177,7 +184,7 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> { String uid = it.next(); TextView uidView = (TextView) mInflater.inflate( R.layout.import_keys_list_entry_user_id, null); - uidView.setText(uid); + uidView.setText(highlighter.highlight(uid)); holder.userIdsList.addView(uidView); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListKeybaseLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListKeybaseLoader.java index 420880522..0fdc019d0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListKeybaseLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListKeybaseLoader.java @@ -22,8 +22,8 @@ import android.support.v4.content.AsyncTaskLoader; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; -import org.sufficientlysecure.keychain.keyimport.KeyServer; -import org.sufficientlysecure.keychain.keyimport.KeybaseKeyServer; +import org.sufficientlysecure.keychain.keyimport.Keyserver; +import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver; import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; @@ -86,7 +86,7 @@ public class ImportKeysListKeybaseLoader */ private void queryServer(String query) { - KeybaseKeyServer server = new KeybaseKeyServer(); + KeybaseKeyserver server = new KeybaseKeyserver(); try { ArrayList<ImportKeysListEntry> searchResult = server.search(query); @@ -94,11 +94,11 @@ public class ImportKeysListKeybaseLoader mEntryList.addAll(searchResult); mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null); - } catch (KeyServer.InsufficientQuery e) { + } catch (Keyserver.InsufficientQuery e) { mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e); - } catch (KeyServer.QueryException e) { + } catch (Keyserver.QueryException e) { mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e); - } catch (KeyServer.TooManyResponses e) { + } catch (Keyserver.TooManyResponses e) { mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e); } 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 index 4175592d6..1b8d7d30e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java @@ -21,9 +21,9 @@ import android.content.Context; import android.support.v4.content.AsyncTaskLoader; import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.keyimport.HkpKeyServer; +import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; -import org.sufficientlysecure.keychain.keyimport.KeyServer; +import org.sufficientlysecure.keychain.keyimport.Keyserver; import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; @@ -92,7 +92,7 @@ public class ImportKeysListServerLoader * Query keyserver */ private void queryServer(String query, String keyServer, boolean enforceFingerprint) { - HkpKeyServer server = new HkpKeyServer(keyServer); + HkpKeyserver server = new HkpKeyserver(keyServer); try { ArrayList<ImportKeysListEntry> searchResult = server.search(query); @@ -108,7 +108,7 @@ public class ImportKeysListServerLoader * set fingerprint explicitly after query * to enforce a check when the key is imported by KeychainIntentService */ - uniqueEntry.setFingerPrintHex(fingerprint); + uniqueEntry.setFingerprintHex(fingerprint); uniqueEntry.setSelected(true); mEntryList.add(uniqueEntry); } @@ -116,11 +116,11 @@ public class ImportKeysListServerLoader mEntryList.addAll(searchResult); } mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null); - } catch (KeyServer.InsufficientQuery e) { + } catch (Keyserver.InsufficientQuery e) { mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e); - } catch (KeyServer.QueryException e) { + } catch (Keyserver.QueryException e) { mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e); - } catch (KeyServer.TooManyResponses e) { + } catch (Keyserver.TooManyResponses e) { mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e); } } 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 index cde008175..00cd554b9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java @@ -19,6 +19,7 @@ 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; @@ -29,6 +30,7 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.util.Highlighter; import java.util.Date; @@ -36,8 +38,9 @@ import java.util.Date; /** * Yes this class is abstract! */ -abstract public class SelectKeyCursorAdapter extends HighlightQueryCursorAdapter { +abstract public class SelectKeyCursorAdapter extends CursorAdapter { + private String mQuery; private LayoutInflater mInflater; protected int mIndexUserId, mIndexMasterKeyId, mIndexRevoked, mIndexExpiry; @@ -48,6 +51,10 @@ abstract public class SelectKeyCursorAdapter extends HighlightQueryCursorAdapter initIndex(c); } + public void setSearchQuery(String query) { + mQuery = query; + } + @Override public Cursor swapCursor(Cursor newCursor) { initIndex(newCursor); @@ -101,19 +108,20 @@ abstract public class SelectKeyCursorAdapter extends HighlightQueryCursorAdapter @Override public void bindView(View view, Context context, Cursor cursor) { + Highlighter highlighter = new Highlighter(context, mQuery); ViewHolderItem h = (ViewHolderItem) view.getTag(); String userId = cursor.getString(mIndexUserId); String[] userIdSplit = PgpKeyHelper.splitUserId(userId); if (userIdSplit[0] != null) { - h.mainUserId.setText(highlightSearchQuery(userIdSplit[0])); + h.mainUserId.setText(highlighter.highlight(userIdSplit[0])); } else { h.mainUserId.setText(R.string.user_id_no_name); } if (userIdSplit[1] != null) { h.mainUserIdRest.setVisibility(View.VISIBLE); - h.mainUserIdRest.setText(highlightSearchQuery(userIdSplit[1])); + h.mainUserIdRest.setText(highlighter.highlight(userIdSplit[1])); } else { h.mainUserIdRest.setVisibility(View.GONE); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Highlighter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Highlighter.java new file mode 100644 index 000000000..eeeacf465 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Highlighter.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 Thialfihar <thi@thialfihar.org> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.util; + +import android.content.Context; +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 class Highlighter { + private Context mContext; + private String mQuery; + + public Highlighter(Context context, String query) { + mContext = context; + mQuery = query; + } + + public Spannable highlight(String text) { + Spannable highlight = Spannable.Factory.getInstance().newSpannable(text); + + if (mQuery == null) { + return highlight; + } + + Pattern pattern = Pattern.compile("(?i)(" + mQuery.trim().replaceAll("\\s+", "|") + ")"); + Matcher matcher = pattern.matcher(text); + while (matcher.find()) { + highlight.setSpan( + new ForegroundColorSpan(mContext.getResources().getColor(R.color.emphasis)), + matcher.start(), + matcher.end(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + + return highlight; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java index 7ae1d8fab..76797811d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java @@ -23,29 +23,77 @@ import org.json.JSONObject; /** * Minimal hierarchy selector + * + * This is for picking out an item in a large multilevel JSON object, for example look at + * the Keybase.io User object, documentation at https://keybase.io/__/api-docs/1.0#user-objects + * an example available via + * curl https://keybase.io/_/api/1.0/user/lookup.json?username=timbray + * + * If you want to retrieve the ascii-armored key, you'd say + * String key = JWalk.getString(match,"them", "public_keys", "primary", "bundle"); */ public class JWalk { + /** + * Returns an int member value from the JSON sub-object addressed by the path + * + * @param json The object + * @param path list of string object member selectors + * @return the int addressed by the path, assuming such a thing exists + * @throws JSONException if any step in the path doesn’t work + */ public static int getInt(JSONObject json, String... path) throws JSONException { json = walk(json, path); return json.getInt(path[path.length - 1]); } + /** + * Returns a long member value from the JSON sub-object addressed by the path + * + * @param json The object + * @param path list of string object member selectors + * @return the int addressed by the path, assuming such a thing exists + * @throws JSONException if any step in the path doesn’t work + */ public static long getLong(JSONObject json, String... path) throws JSONException { json = walk(json, path); return json.getLong(path[path.length - 1]); } + /** + * Returns a String member value from the JSON sub-object addressed by the path + * + * @param json The object + * @param path list of string object member selectors + * @return the int addressed by the path, assuming such a thing exists + * @throws JSONException if any step in the path doesn’t work + */ public static String getString(JSONObject json, String... path) throws JSONException { json = walk(json, path); return json.getString(path[path.length - 1]); } + /** + * Returns a JSONArray member value from the JSON sub-object addressed by the path + * + * @param json The object + * @param path list of string object member selectors + * @return the int addressed by the path, assuming such a thing exists + * @throws JSONException if any step in the path doesn’t work + */ public static JSONArray getArray(JSONObject json, String... path) throws JSONException { json = walk(json, path); return json.getJSONArray(path[path.length - 1]); } + /** + * Returns a JSONObject member value from the JSON sub-object addressed by the path, or null + * + * @param json The object + * @param path list of string object member selectors + * @return the int addressed by the path, assuming such a thing exists + * @throws JSONException if any step in the path, except for the last, doesn’t work + */ public static JSONObject optObject(JSONObject json, String... path) throws JSONException { json = walk(json, path); return json.optJSONObject(path[path.length - 1]); diff --git a/OpenKeychain/src/main/res/layout/import_keys_activity.xml b/OpenKeychain/src/main/res/layout/import_keys_activity.xml index 876374700..2a332823e 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_activity.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_activity.xml @@ -1,20 +1,16 @@ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" android:id="@+id/content_frame" android:layout_marginLeft="@dimen/drawer_content_padding" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerHorizontal="true"> + android:layout_width="match_parent" + android:layout_height="match_parent"> <FrameLayout android:id="@+id/import_navigation_fragment" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" - android:orientation="vertical" - android:paddingLeft="4dp" - android:paddingRight="4dp" /> + android:orientation="vertical" /> <LinearLayout android:id="@+id/import_footer" @@ -56,6 +52,7 @@ android:layout_alignParentLeft="true" android:layout_below="@+id/import_navigation_fragment" android:orientation="vertical" - android:paddingLeft="4dp" - android:paddingRight="4dp" /> + android:paddingTop="8dp" + android:paddingLeft="16dp" + android:paddingRight="16dp" /> </RelativeLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/import_keys_clipboard_fragment.xml b/OpenKeychain/src/main/res/layout/import_keys_clipboard_fragment.xml index 046768495..739c34fba 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_clipboard_fragment.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_clipboard_fragment.xml @@ -3,13 +3,15 @@ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" + android:paddingTop="8dp" + android:paddingLeft="16dp" + android:paddingRight="16dp" android:orientation="horizontal" > <com.beardedhen.androidbootstrap.BootstrapButton android:id="@+id/import_clipboard_button" android:layout_width="match_parent" android:layout_height="70dp" - android:layout_margin="10dp" android:text="@string/import_clipboard_button" bootstrapbutton:bb_icon_left="fa-clipboard" bootstrapbutton:bb_size="default" diff --git a/OpenKeychain/src/main/res/layout/import_keys_file_fragment.xml b/OpenKeychain/src/main/res/layout/import_keys_file_fragment.xml index 0a49571d1..c07d2bb40 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_file_fragment.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_file_fragment.xml @@ -1,15 +1,17 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" - android:layout_width="fill_parent" + android:layout_width="match_parent" android:layout_height="wrap_content" + android:paddingTop="8dp" + android:paddingLeft="16dp" + android:paddingRight="16dp" android:orientation="vertical"> <com.beardedhen.androidbootstrap.BootstrapButton android:id="@+id/import_keys_file_browse" android:layout_width="match_parent" android:layout_height="70dp" - android:layout_margin="10dp" android:text="@string/filemanager_title_open" android:contentDescription="@string/filemanager_title_open" bootstrapbutton:bb_icon_left="fa-folder-open" diff --git a/OpenKeychain/src/main/res/layout/import_keys_keybase_fragment.xml b/OpenKeychain/src/main/res/layout/import_keys_keybase_fragment.xml index 248581342..ceba0e1ce 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_keybase_fragment.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_keybase_fragment.xml @@ -3,49 +3,36 @@ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal" > + android:paddingTop="8dp" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:orientation="horizontal"> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal"> - - <EditText - android:id="@+id/import_keybase_query" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="1" - android:gravity="top|left" - android:hint="@string/hint_keybase_search" - android:imeOptions="actionSearch" - android:inputType="textNoSuggestions" - android:singleLine="true" - android:lines="1" - android:maxLines="1" - android:minLines="1" - android:layout_gravity="center_vertical" /> - <com.beardedhen.androidbootstrap.BootstrapButton - android:id="@+id/import_keybase_search" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:layout_marginLeft="10dp" - bootstrapbutton:bb_icon_left="fa-search" - bootstrapbutton:bb_roundedCorners="true" - bootstrapbutton:bb_size="default" - bootstrapbutton:bb_type="default" /> - </LinearLayout> + <EditText + android:id="@+id/import_keybase_query" + android:layout_width="0dip" + android:layout_height="wrap_content" + android:layout_weight="1" + android:gravity="top|left" + android:hint="@string/hint_keybase_search" + android:imeOptions="actionSearch" + android:inputType="textNoSuggestions" + android:singleLine="true" + android:lines="1" + android:maxLines="1" + android:minLines="1" + android:layout_gravity="center_vertical" /> - <!-- <com.beardedhen.androidbootstrap.BootstrapButton - android:id="@+id/import_keybase_button" - android:layout_width="match_parent" - android:layout_height="70dp" - android:layout_margin="10dp" - android:text="@string/import_keybase_button" + android:id="@+id/import_keybase_search" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_marginLeft="8dp" + bootstrapbutton:bb_icon_left="fa-search" + bootstrapbutton:bb_roundedCorners="true" bootstrapbutton:bb_size="default" bootstrapbutton:bb_type="default" /> - --> </LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/import_keys_list_entry.xml b/OpenKeychain/src/main/res/layout/import_keys_list_entry.xml index f5ec71abe..c91335a5b 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_list_entry.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_list_entry.xml @@ -25,8 +25,7 @@ android:id="@+id/selected" android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_marginLeft="4dp" - android:layout_marginRight="4dp" + android:paddingRight="8dp" android:clickable="false" android:focusable="false" android:focusableInTouchMode="false" /> @@ -46,8 +45,7 @@ android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="1" - android:orientation="vertical" - android:paddingRight="4dip"> + android:orientation="vertical"> <TextView android:id="@+id/mainUserId" diff --git a/OpenKeychain/src/main/res/layout/import_keys_nfc_fragment.xml b/OpenKeychain/src/main/res/layout/import_keys_nfc_fragment.xml index 2a8e74fc2..8c0a80e4e 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_nfc_fragment.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_nfc_fragment.xml @@ -3,8 +3,10 @@ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:padding="10dp" - android:orientation="horizontal" > + android:paddingTop="8dp" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:orientation="horizontal"> <com.beardedhen.androidbootstrap.BootstrapButton android:id="@+id/import_nfc_button" @@ -12,7 +14,7 @@ android:layout_height="70dp" android:layout_alignParentRight="true" android:layout_alignParentTop="true" - android:layout_marginLeft="10dp" + android:layout_marginLeft="8dp" android:text="@string/import_nfc_help_button" bootstrapbutton:bb_icon_left="fa-question" bootstrapbutton:bb_size="default" diff --git a/OpenKeychain/src/main/res/layout/import_keys_qr_code_fragment.xml b/OpenKeychain/src/main/res/layout/import_keys_qr_code_fragment.xml index 472c05e65..590f7f797 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_qr_code_fragment.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_qr_code_fragment.xml @@ -3,13 +3,15 @@ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:orientation="vertical" > + android:paddingTop="8dp" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:orientation="vertical"> <com.beardedhen.androidbootstrap.BootstrapButton android:id="@+id/import_qrcode_button" android:layout_width="match_parent" android:layout_height="70dp" - android:layout_margin="10dp" android:text="@string/import_qr_scan_button" bootstrapbutton:bb_icon_left="fa-barcode" bootstrapbutton:bb_size="default" @@ -19,8 +21,9 @@ android:id="@+id/import_qrcode_text" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:paddingLeft="10dp" - android:paddingRight="10dp" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:paddingTop="8dp" android:visibility="gone" /> <ProgressBar @@ -28,8 +31,8 @@ style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingLeft="10dp" - android:paddingRight="10dp" + android:paddingLeft="16dp" + android:paddingRight="16dp" android:progress="0" android:visibility="gone" /> diff --git a/OpenKeychain/src/main/res/layout/import_keys_server_fragment.xml b/OpenKeychain/src/main/res/layout/import_keys_server_fragment.xml index 2438dd785..e17dbe783 100644 --- a/OpenKeychain/src/main/res/layout/import_keys_server_fragment.xml +++ b/OpenKeychain/src/main/res/layout/import_keys_server_fragment.xml @@ -2,7 +2,9 @@ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:padding="10dp" + android:paddingTop="8dp" + android:paddingLeft="16dp" + android:paddingRight="16dp" android:orientation="vertical"> <Spinner @@ -35,7 +37,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:layout_marginLeft="10dp" + android:layout_marginLeft="8dp" bootstrapbutton:bb_icon_left="fa-search" bootstrapbutton:bb_roundedCorners="true" bootstrapbutton:bb_size="default" diff --git a/OpenKeychain/src/main/res/layout/view_key_userids_item.xml b/OpenKeychain/src/main/res/layout/view_key_userids_item.xml index 2f4041f8c..03e619aff 100644 --- a/OpenKeychain/src/main/res/layout/view_key_userids_item.xml +++ b/OpenKeychain/src/main/res/layout/view_key_userids_item.xml @@ -4,7 +4,6 @@ android:layout_height="wrap_content" android:minHeight="?android:attr/listPreferredItemHeight" android:orientation="horizontal" - android:paddingRight="3dip" android:singleLine="true"> <CheckBox diff --git a/OpenKeychain/src/main/res/raw/help_start.html b/OpenKeychain/src/main/res/raw/help_start.html index 26386d3d4..58d1340c9 100644 --- a/OpenKeychain/src/main/res/raw/help_start.html +++ b/OpenKeychain/src/main/res/raw/help_start.html @@ -14,7 +14,7 @@ And don't add newlines before or after p tags because of transifex --> <p>Several applications support OpenKeychain to encrypt/sign your private communication: <br/><img src="apps_k9"/><br/>K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>! <br/><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"/><br/>Conversations</a>: Jabber/XMPP client -<br/><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"/><br/>PGPAuth</a>: App to send a PGP-signed request to a server to open or close $thing</p> +<br/><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"/><br/>PGPAuth</a>: App to send a PGP-signed request to a server to open or close something, e.g. a door</p> <h2>I found a bug in OpenKeychain!</h2> <p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p> |