aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2014-05-14 16:02:28 +0200
committerVincent Breitmoser <valodim@mugenguild.com>2014-05-14 16:02:28 +0200
commit2f95100d88954db389cba8e615390795d121c1c8 (patch)
tree1bf4ffb29bb83fac33ba2ed7c84fb1d6a8af2bd4 /OpenKeychain/src/main/java
parent90ac60b6db0637fbefbce5cb2cd80a64f5bb708d (diff)
parent638554f2560b3c5fd36ca9b4ba205cb5a999b84b (diff)
downloadopen-keychain-2f95100d88954db389cba8e615390795d121c1c8.tar.gz
open-keychain-2f95100d88954db389cba8e615390795d121c1c8.tar.bz2
open-keychain-2f95100d88954db389cba8e615390795d121c1c8.zip
Merge remote-tracking branch 'origin/master' into wrapped-key-ring
Conflicts: OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java OpenKeychain/src/main/res/values/strings.xml
Diffstat (limited to 'OpenKeychain/src/main/java')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java20
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java19
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyServer.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java)20
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java)5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeyServer.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java)22
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyServer.java161
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java65
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java243
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java43
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java28
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java66
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java39
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java30
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java15
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java50
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpHtmlFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java50
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java120
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java54
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java79
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LoaderFragment.java78
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java11
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java25
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java85
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java274
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java35
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeysFragment.java128
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java255
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java308
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListKeybaseLoader.java107
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java44
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java49
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java40
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java147
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java69
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java73
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java22
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SlidingTabLayout.java318
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SlidingTabStrip.java211
72 files changed, 2531 insertions, 1048 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
index 866888bb2..f911318a0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
@@ -18,6 +18,9 @@
package org.sufficientlysecure.keychain;
import android.app.Application;
+import android.content.Context;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
import android.os.Environment;
import org.spongycastle.jce.provider.BouncyCastleProvider;
@@ -70,5 +73,22 @@ public class KeychainApplication extends Application {
// that the directory doesn't exist at this point
}
}
+
+ brandGlowEffect(getApplicationContext(),
+ getApplicationContext().getResources().getColor(R.color.emphasis));
+ }
+
+ static void brandGlowEffect(Context context, int brandColor) {
+ // terrible hack to brand the edge overscroll glow effect
+ // https://gist.github.com/menny/7878762#file-brandgloweffect_full-java
+
+ //glow
+ int glowDrawableId = context.getResources().getIdentifier("overscroll_glow", "drawable", "android");
+ Drawable androidGlow = context.getResources().getDrawable(glowDrawableId);
+ androidGlow.setColorFilter(brandColor, PorterDuff.Mode.SRC_IN);
+ //edge
+ int edgeDrawableId = context.getResources().getIdentifier("overscroll_edge", "drawable", "android");
+ Drawable androidEdge = context.getResources().getDrawable(edgeDrawableId);
+ androidEdge.setColorFilter(brandColor, PorterDuff.Mode.SRC_IN);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java
index 72b6d9e1d..46e7936ce 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java
@@ -33,25 +33,6 @@ import org.sufficientlysecure.keychain.util.Log;
public class ActionBarHelper {
/**
- * Set actionbar without home button if called from another app
- *
- * @param activity
- */
- public static void setBackButton(ActionBarActivity activity) {
- final ActionBar actionBar = activity.getSupportActionBar();
- Log.d(Constants.TAG, "calling package (only set when using startActivityForResult)="
- + activity.getCallingPackage());
- if (activity.getCallingPackage() != null
- && activity.getCallingPackage().equals(Constants.PACKAGE_NAME)) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setHomeButtonEnabled(true);
- } else {
- actionBar.setDisplayHomeAsUpEnabled(false);
- actionBar.setHomeButtonEnabled(false);
- }
- }
-
- /**
* Sets custom view on ActionBar for Done/Cancel activities
*
* @param actionBar
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java
index f22fcd4b8..d7d73cf3d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/FileHelper.java
@@ -112,16 +112,18 @@ public class FileHelper {
if ("content".equalsIgnoreCase(uri.getScheme())) {
String[] projection = {"_data"};
- Cursor cursor = null;
-
+ Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
try {
- cursor = context.getContentResolver().query(uri, projection, null, null, null);
- int columnIndex = cursor.getColumnIndexOrThrow("_data");
- if (cursor.moveToFirst()) {
+ if (cursor != null && cursor.moveToFirst()) {
+ int columnIndex = cursor.getColumnIndexOrThrow("_data");
return cursor.getString(columnIndex);
}
} catch (Exception e) {
// Eat it
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
}
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyServer.java
index cf658b0b6..85ce6bfcc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyServer.java
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-package org.sufficientlysecure.keychain.util;
+package org.sufficientlysecure.keychain.keyimport;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
@@ -32,9 +32,8 @@ import org.apache.http.util.EntityUtils;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
+import org.sufficientlysecure.keychain.util.Log;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
@@ -167,21 +166,6 @@ public class HkpKeyServer extends KeyServer {
mPort = port;
}
- private static String readAll(InputStream in, String encoding) throws IOException {
- ByteArrayOutputStream raw = new ByteArrayOutputStream();
-
- byte buffer[] = new byte[1 << 16];
- int n = 0;
- while ((n = in.read(buffer)) != -1) {
- raw.write(buffer, 0, n);
- }
-
- if (encoding == null) {
- encoding = "utf8";
- }
- return raw.toString(encoding);
- }
-
private String query(String request) throws QueryException, HttpError {
InetAddress ips[];
try {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
index b06852af4..1199290e0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
@@ -15,12 +15,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.ui.adapter;
+package org.sufficientlysecure.keychain.keyimport;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.SparseArray;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.openpgp.PGPKeyRing;
@@ -203,7 +202,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
* Constructor for later querying from keyserver
*/
public ImportKeysListEntry() {
- // keys from keyserver are always public keys
+ // keys from keyserver are always public keys; from keybase too
secretKey = false;
// do not select by default
mSelected = false;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeyServer.java
index 2b97165ac..d6ebca5a6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeyServer.java
@@ -16,10 +16,11 @@
* limitations under the License.
*/
-package org.sufficientlysecure.keychain.util;
-
-import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
+package org.sufficientlysecure.keychain.keyimport;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.List;
public abstract class KeyServer {
@@ -49,4 +50,19 @@ public abstract class KeyServer {
abstract String get(String keyIdHex) throws QueryException;
abstract void add(String armoredKey) throws AddKeyException;
+
+ public static String readAll(InputStream in, String encoding) throws IOException {
+ ByteArrayOutputStream raw = new ByteArrayOutputStream();
+
+ byte buffer[] = new byte[1 << 16];
+ int n = 0;
+ while ((n = in.read(buffer)) != -1) {
+ raw.write(buffer, 0, n);
+ }
+
+ if (encoding == null) {
+ encoding = "utf8";
+ }
+ return raw.toString(encoding);
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyServer.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyServer.java
new file mode 100644
index 000000000..7ffe123c0
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyServer.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2014 Tim Bray <tbray@textuality.com>
+ *
+ * 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.keyimport;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.util.JWalk;
+import org.sufficientlysecure.keychain.util.Log;
+
+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>();
+
+ @Override
+ public ArrayList<ImportKeysListEntry> search(String query) throws QueryException, TooManyResponses,
+ InsufficientQuery {
+ ArrayList<ImportKeysListEntry> results = new ArrayList<ImportKeysListEntry>();
+
+ JSONObject fromQuery = getFromKeybase("_/api/1.0/user/autocomplete.json?q=", query);
+ try {
+
+ JSONArray matches = JWalk.getArray(fromQuery, "completions");
+ for (int i = 0; i < matches.length(); i++) {
+ JSONObject match = matches.getJSONObject(i);
+
+ // only list them if they have a key
+ if (JWalk.optObject(match, "components", "key_fingerprint") != null) {
+ results.add(makeEntry(match));
+ }
+ }
+ } catch (Exception e) {
+ throw new QueryException("Unexpected structure in keybase search result: " + e.getMessage());
+ }
+
+ return results;
+ }
+
+ private JSONObject getUser(String keybaseID) throws QueryException {
+ try {
+ return getFromKeybase("_/api/1.0/user/lookup.json?username=", keybaseID);
+ } catch (Exception e) {
+ String detail = "";
+ 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();
+
+ // 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);
+
+ // 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());
+
+ // 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"));
+
+ // String displayName = JWalk.getString(match, "them", "profile", "full_name");
+ ArrayList<String> userIds = new ArrayList<String>();
+ String name = "keybase.io/" + keybaseID + " <" + keybaseID + "@keybase.io>";
+ userIds.add(name);
+ userIds.add(keybaseID);
+ entry.setUserIds(userIds);
+ entry.setPrimaryUserId(name);
+ return entry;
+ }
+
+ private JSONObject getFromKeybase(String path, String query) throws QueryException {
+ try {
+ String url = "https://keybase.io/" + path + URLEncoder.encode(query, "utf8");
+ Log.d(Constants.TAG, "keybase query: " + url);
+
+ URL realUrl = new URL(url);
+ HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
+ conn.setConnectTimeout(5000); // TODO: Reasonable values for keybase
+ conn.setReadTimeout(25000);
+ conn.connect();
+ int response = conn.getResponseCode();
+ if (response >= 200 && response < 300) {
+ String text = readAll(conn.getInputStream(), conn.getContentEncoding());
+ try {
+ JSONObject json = new JSONObject(text);
+ if (JWalk.getInt(json, "status", "code") != 0) {
+ throw new QueryException("Keybase autocomplete search failed");
+ }
+ return json;
+ } catch (JSONException e) {
+ throw new QueryException("Keybase.io query returned broken JSON");
+ }
+ } else {
+ String message = readAll(conn.getErrorStream(), conn.getContentEncoding());
+ throw new QueryException("Keybase.io query error (status=" + response +
+ "): " + message);
+ }
+ } catch (Exception e) {
+ throw new QueryException("Keybase.io query error");
+ }
+ }
+
+ @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());
+ }
+ }
+ 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/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
index a7e1bd603..a0d2d5cea 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
@@ -36,11 +36,10 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
-import org.sufficientlysecure.keychain.util.HkpKeyServer;
+import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
+import org.sufficientlysecure.keychain.keyimport.HkpKeyServer;
import org.sufficientlysecure.keychain.util.IterableIterator;
-import org.sufficientlysecure.keychain.util.KeyServer.AddKeyException;
-import org.sufficientlysecure.keychain.util.KeychainServiceListener;
+import org.sufficientlysecure.keychain.keyimport.KeyServer.AddKeyException;
import org.sufficientlysecure.keychain.util.Log;
import java.io.ByteArrayOutputStream;
@@ -51,6 +50,11 @@ import java.util.List;
public class PgpImportExport {
+ // TODO: is this really used?
+ public interface KeychainServiceListener {
+ boolean hasServiceStopped();
+ }
+
private Context mContext;
private Progressable mProgressable;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
index ccbbb3719..96ee3f10a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
@@ -181,8 +181,6 @@ public class PgpSignEncrypt {
}
/**
- * TODO: test this option!
- *
* @param encryptToSigner
* @return
*/
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
index 061f91176..68726d3e0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
@@ -255,53 +255,60 @@ public class KeychainDatabase extends SQLiteOpenHelper {
}
}.getReadableDatabase();
- Cursor c = null;
+ Cursor cursor = null;
try {
// we insert in two steps: first, all public keys that have secret keys
- c = db.rawQuery("SELECT key_ring_data FROM key_rings WHERE type = 1 OR EXISTS ("
+ cursor = db.rawQuery("SELECT key_ring_data FROM key_rings WHERE type = 1 OR EXISTS ("
+ " SELECT 1 FROM key_rings d2 WHERE key_rings.master_key_id = d2.master_key_id"
+ " AND d2.type = 1) ORDER BY type ASC", null);
- Log.d(Constants.TAG, "Importing " + c.getCount() + " secret keyrings from apg.db...");
- for (int i = 0; i < c.getCount(); i++) {
- c.moveToPosition(i);
- byte[] data = c.getBlob(0);
- PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
- ProviderHelper providerHelper = new ProviderHelper(context);
- if (ring instanceof PGPPublicKeyRing)
- providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
- else if (ring instanceof PGPSecretKeyRing)
- providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
- else {
- Log.e(Constants.TAG, "Unknown blob data type!");
+ Log.d(Constants.TAG, "Importing " + cursor.getCount() + " secret keyrings from apg.db...");
+ if (cursor != null) {
+ for (int i = 0; i < cursor.getCount(); i++) {
+ cursor.moveToPosition(i);
+ byte[] data = cursor.getBlob(0);
+ PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
+ ProviderHelper providerHelper = new ProviderHelper(context);
+ if (ring instanceof PGPPublicKeyRing)
+ providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
+ else if (ring instanceof PGPSecretKeyRing)
+ providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
+ else {
+ Log.e(Constants.TAG, "Unknown blob data type!");
+ }
}
}
+ if (cursor != null) {
+ cursor.close();
+ }
// afterwards, insert all keys, starting with public keys that have secret keys, then
// secret keys, then all others. this order is necessary to ensure all certifications
// are recognized properly.
- c = db.rawQuery("SELECT key_ring_data FROM key_rings ORDER BY (type = 0 AND EXISTS ("
+ cursor = db.rawQuery("SELECT key_ring_data FROM key_rings ORDER BY (type = 0 AND EXISTS ("
+ " SELECT 1 FROM key_rings d2 WHERE key_rings.master_key_id = d2.master_key_id AND"
+ " d2.type = 1)) DESC, type DESC", null);
// import from old database
- Log.d(Constants.TAG, "Importing " + c.getCount() + " keyrings from apg.db...");
- for (int i = 0; i < c.getCount(); i++) {
- c.moveToPosition(i);
- byte[] data = c.getBlob(0);
- PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
- ProviderHelper providerHelper = new ProviderHelper(context);
- if (ring instanceof PGPPublicKeyRing) {
- providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
- } else if (ring instanceof PGPSecretKeyRing) {
- providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
- } else {
- Log.e(Constants.TAG, "Unknown blob data type!");
+ Log.d(Constants.TAG, "Importing " + cursor.getCount() + " keyrings from apg.db...");
+ if (cursor != null) {
+ for (int i = 0; i < cursor.getCount(); i++) {
+ cursor.moveToPosition(i);
+ byte[] data = cursor.getBlob(0);
+ PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
+ ProviderHelper providerHelper = new ProviderHelper(context);
+ if (ring instanceof PGPPublicKeyRing) {
+ providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
+ } else if (ring instanceof PGPSecretKeyRing) {
+ providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
+ } else {
+ Log.e(Constants.TAG, "Unknown blob data type!");
+ }
}
}
} catch (IOException e) {
Log.e(Constants.TAG, "Error importing apg.db!", e);
} finally {
- if (c != null) {
- c.close();
+ if (cursor != null) {
+ cursor.close();
}
if (db != null) {
db.close();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
index c67c2eca4..9f6314329 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -567,20 +567,21 @@ public class KeychainProvider extends ContentProvider {
}
SQLiteDatabase db = getDb().getReadableDatabase();
- Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, having, orderBy);
-
- // Tell the cursor what uri to watch, so it knows when its source data changes
- c.setNotificationUri(getContext().getContentResolver(), uri);
+ Cursor cursor = qb.query(db, projection, selection, selectionArgs, groupBy, having, orderBy);
+ if (cursor != null) {
+ // Tell the cursor what uri to watch, so it knows when its source data changes
+ cursor.setNotificationUri(getContext().getContentResolver(), uri);
+ }
if (Constants.DEBUG) {
Log.d(Constants.TAG,
"Query: "
+ qb.buildQuery(projection, selection, selectionArgs, null, null,
orderBy, null));
- Log.d(Constants.TAG, "Cursor: " + DatabaseUtils.dumpCursorToString(c));
+ Log.d(Constants.TAG, "Cursor: " + DatabaseUtils.dumpCursorToString(cursor));
}
- return c;
+ return cursor;
}
/**
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index 0784a0f33..18035eefe 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -26,6 +26,7 @@ import android.database.Cursor;
import android.database.DatabaseUtils;
import android.net.Uri;
import android.os.RemoteException;
+import android.support.v4.util.LongSparseArray;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.S2K;
@@ -103,36 +104,38 @@ public class ProviderHelper {
throws NotFoundException {
Cursor cursor = mContentResolver.query(uri, proj, null, null, null);
- HashMap<String, Object> result = new HashMap<String, Object>(proj.length);
- if (cursor != null && cursor.moveToFirst()) {
- int pos = 0;
- for (String p : proj) {
- switch (types[pos]) {
- case FIELD_TYPE_NULL:
- result.put(p, cursor.isNull(pos));
- break;
- case FIELD_TYPE_INTEGER:
- result.put(p, cursor.getLong(pos));
- break;
- case FIELD_TYPE_FLOAT:
- result.put(p, cursor.getFloat(pos));
- break;
- case FIELD_TYPE_STRING:
- result.put(p, cursor.getString(pos));
- break;
- case FIELD_TYPE_BLOB:
- result.put(p, cursor.getBlob(pos));
- break;
+ try {
+ HashMap<String, Object> result = new HashMap<String, Object>(proj.length);
+ if (cursor != null && cursor.moveToFirst()) {
+ int pos = 0;
+ for (String p : proj) {
+ switch (types[pos]) {
+ case FIELD_TYPE_NULL:
+ result.put(p, cursor.isNull(pos));
+ break;
+ case FIELD_TYPE_INTEGER:
+ result.put(p, cursor.getLong(pos));
+ break;
+ case FIELD_TYPE_FLOAT:
+ result.put(p, cursor.getFloat(pos));
+ break;
+ case FIELD_TYPE_STRING:
+ result.put(p, cursor.getString(pos));
+ break;
+ case FIELD_TYPE_BLOB:
+ result.put(p, cursor.getBlob(pos));
+ break;
+ }
+ pos += 1;
}
- pos += 1;
}
- }
- if (cursor != null) {
- cursor.close();
+ return result;
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
}
-
- return result;
}
public Object getUnifiedData(long masterKeyId, String column, int type)
@@ -172,22 +175,24 @@ public class ProviderHelper {
}
@Deprecated
- public Map<Long, PGPKeyRing> getPGPKeyRings(Uri queryUri) {
+ public LongSparseArray<PGPKeyRing> getPGPKeyRings(Uri queryUri) {
Cursor cursor = mContentResolver.query(queryUri,
new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA},
null, null, null);
- Map<Long, PGPKeyRing> result = new HashMap<Long, PGPKeyRing>(cursor.getCount());
- if (cursor != null && cursor.moveToFirst()) do {
- long masterKeyId = cursor.getLong(0);
- byte[] data = cursor.getBlob(1);
- if (data != null) {
- result.put(masterKeyId, PgpConversionHelper.BytesToPGPKeyRing(data));
+ LongSparseArray<PGPKeyRing> result = new LongSparseArray<PGPKeyRing>(cursor.getCount());
+ try {
+ if (cursor != null && cursor.moveToFirst()) do {
+ long masterKeyId = cursor.getLong(0);
+ byte[] data = cursor.getBlob(1);
+ if (data != null) {
+ result.put(masterKeyId, PgpConversionHelper.BytesToPGPKeyRing(data));
+ }
+ } while (cursor.moveToNext());
+ } finally {
+ if (cursor != null) {
+ cursor.close();
}
- } while (cursor.moveToNext());
-
- if (cursor != null) {
- cursor.close();
}
return result;
@@ -255,11 +260,11 @@ public class ProviderHelper {
@Deprecated
public PGPKeyRing getPGPKeyRing(Uri queryUri) throws NotFoundException {
- Map<Long, PGPKeyRing> result = getPGPKeyRings(queryUri);
- if (result.isEmpty()) {
+ LongSparseArray<PGPKeyRing> result = getPGPKeyRings(queryUri);
+ if (result.size() == 0) {
throw new NotFoundException("PGPKeyRing object not found!");
} else {
- return result.values().iterator().next();
+ return result.valueAt(0);
}
}
@@ -312,7 +317,7 @@ public class ProviderHelper {
}
// get a list of owned secret keys, for verification filtering
- Map<Long, PGPKeyRing> allKeyRings = getPGPKeyRings(KeyRingData.buildSecretKeyRingUri());
+ LongSparseArray<PGPKeyRing> allKeyRings = getPGPKeyRings(KeyRingData.buildSecretKeyRingUri());
// special case: available secret keys verify themselves!
if (secretRing != null)
allKeyRings.put(secretRing.getSecretKey().getKeyID(), secretRing);
@@ -350,7 +355,7 @@ public class ProviderHelper {
}
}
// verify signatures from known private keys
- if (allKeyRings.containsKey(certId)) {
+ if (allKeyRings.indexOfKey(certId) >= 0) {
// mark them as verified
cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME),
@@ -634,27 +639,29 @@ public class ProviderHelper {
}, inMasterKeyList, null, null);
}
- if (cursor != null) {
- int masterIdCol = cursor.getColumnIndex(KeyRingData.MASTER_KEY_ID);
- int dataCol = cursor.getColumnIndex(KeyRingData.KEY_RING_DATA);
- if (cursor.moveToFirst()) {
- do {
- Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol));
-
- byte[] data = cursor.getBlob(dataCol);
-
- // get actual keyring data blob and write it to ByteArrayOutputStream
- try {
- output.add(getKeyRingAsArmoredString(data));
- } catch (IOException e) {
- Log.e(Constants.TAG, "IOException", e);
- }
- } while (cursor.moveToNext());
+ try {
+ if (cursor != null) {
+ int masterIdCol = cursor.getColumnIndex(KeyRingData.MASTER_KEY_ID);
+ int dataCol = cursor.getColumnIndex(KeyRingData.KEY_RING_DATA);
+ if (cursor.moveToFirst()) {
+ do {
+ Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol));
+
+ byte[] data = cursor.getBlob(dataCol);
+
+ // get actual keyring data blob and write it to ByteArrayOutputStream
+ try {
+ output.add(getKeyRingAsArmoredString(data));
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IOException", e);
+ }
+ } while (cursor.moveToNext());
+ }
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
}
- }
-
- if (cursor != null) {
- cursor.close();
}
if (output.size() > 0) {
@@ -668,17 +675,19 @@ public class ProviderHelper {
Cursor cursor = mContentResolver.query(ApiApps.CONTENT_URI, null, null, null, null);
ArrayList<String> packageNames = new ArrayList<String>();
- if (cursor != null) {
- int packageNameCol = cursor.getColumnIndex(ApiApps.PACKAGE_NAME);
- if (cursor.moveToFirst()) {
- do {
- packageNames.add(cursor.getString(packageNameCol));
- } while (cursor.moveToNext());
+ try {
+ if (cursor != null) {
+ int packageNameCol = cursor.getColumnIndex(ApiApps.PACKAGE_NAME);
+ if (cursor.moveToFirst()) {
+ do {
+ packageNames.add(cursor.getString(packageNameCol));
+ } while (cursor.moveToNext());
+ }
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
}
- }
-
- if (cursor != null) {
- cursor.close();
}
return packageNames;
@@ -726,13 +735,19 @@ public class ProviderHelper {
public AppSettings getApiAppSettings(Uri uri) {
AppSettings settings = null;
- Cursor cur = mContentResolver.query(uri, null, null, null, null);
- if (cur != null && cur.moveToFirst()) {
- settings = new AppSettings();
- settings.setPackageName(cur.getString(
- cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
- settings.setPackageSignature(cur.getBlob(
- cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE)));
+ Cursor cursor = mContentResolver.query(uri, null, null, null, null);
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ settings = new AppSettings();
+ settings.setPackageName(cursor.getString(
+ cursor.getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
+ settings.setPackageSignature(cursor.getBlob(
+ cursor.getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE)));
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
}
return settings;
@@ -741,20 +756,26 @@ public class ProviderHelper {
public AccountSettings getApiAccountSettings(Uri accountUri) {
AccountSettings settings = null;
- Cursor cur = mContentResolver.query(accountUri, null, null, null, null);
- if (cur != null && cur.moveToFirst()) {
- settings = new AccountSettings();
-
- settings.setAccountName(cur.getString(
- cur.getColumnIndex(KeychainContract.ApiAccounts.ACCOUNT_NAME)));
- settings.setKeyId(cur.getLong(
- cur.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID)));
- settings.setCompression(cur.getInt(
- cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.COMPRESSION)));
- settings.setHashAlgorithm(cur.getInt(
- cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.HASH_ALORITHM)));
- settings.setEncryptionAlgorithm(cur.getInt(
- cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM)));
+ Cursor cursor = mContentResolver.query(accountUri, null, null, null, null);
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ settings = new AccountSettings();
+
+ settings.setAccountName(cursor.getString(
+ cursor.getColumnIndex(KeychainContract.ApiAccounts.ACCOUNT_NAME)));
+ settings.setKeyId(cursor.getLong(
+ cursor.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID)));
+ settings.setCompression(cursor.getInt(
+ cursor.getColumnIndexOrThrow(KeychainContract.ApiAccounts.COMPRESSION)));
+ settings.setHashAlgorithm(cursor.getInt(
+ cursor.getColumnIndexOrThrow(KeychainContract.ApiAccounts.HASH_ALORITHM)));
+ settings.setEncryptionAlgorithm(cursor.getInt(
+ cursor.getColumnIndexOrThrow(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM)));
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
}
return settings;
@@ -764,10 +785,16 @@ public class ProviderHelper {
Set<Long> keyIds = new HashSet<Long>();
Cursor cursor = mContentResolver.query(uri, null, null, null, null);
- if (cursor != null) {
- int keyIdColumn = cursor.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID);
- while (cursor.moveToNext()) {
- keyIds.add(cursor.getLong(keyIdColumn));
+ try {
+ if (cursor != null) {
+ int keyIdColumn = cursor.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID);
+ while (cursor.moveToNext()) {
+ keyIds.add(cursor.getLong(keyIdColumn));
+ }
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
}
}
@@ -780,18 +807,18 @@ public class ProviderHelper {
String[] projection = new String[]{ApiApps.PACKAGE_SIGNATURE};
Cursor cursor = mContentResolver.query(queryUri, projection, null, null, null);
+ try {
+ byte[] signature = null;
+ if (cursor != null && cursor.moveToFirst()) {
+ int signatureCol = 0;
- byte[] signature = null;
- if (cursor != null && cursor.moveToFirst()) {
- int signatureCol = 0;
-
- signature = cursor.getBlob(signatureCol);
- }
-
- if (cursor != null) {
- cursor.close();
+ signature = cursor.getBlob(signatureCol);
+ }
+ return signature;
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
}
-
- return signature;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index 8a247a11b..58aa2ece8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -42,6 +42,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.ImportKeysActivity;
+import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
@@ -69,19 +70,25 @@ public class OpenPgpService extends RemoteService {
for (String email : encryptionUserIds) {
Uri uri = KeyRings.buildUnifiedKeyRingsFindByEmailUri(email);
- Cursor cur = getContentResolver().query(uri, null, null, null, null);
- if (cur.moveToFirst()) {
- long id = cur.getLong(cur.getColumnIndex(KeyRings.MASTER_KEY_ID));
- keyIds.add(id);
- } else {
- missingUserIdsCheck = true;
- missingUserIds.add(email);
- Log.d(Constants.TAG, "user id missing");
- }
- if (cur.moveToNext()) {
- duplicateUserIdsCheck = true;
- duplicateUserIds.add(email);
- Log.d(Constants.TAG, "more than one user id with the same email");
+ Cursor cursor = getContentResolver().query(uri, null, null, null, null);
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ long id = cursor.getLong(cursor.getColumnIndex(KeyRings.MASTER_KEY_ID));
+ keyIds.add(id);
+ } else {
+ missingUserIdsCheck = true;
+ missingUserIds.add(email);
+ Log.d(Constants.TAG, "user id missing");
+ }
+ if (cursor != null && cursor.moveToNext()) {
+ duplicateUserIdsCheck = true;
+ duplicateUserIds.add(email);
+ Log.d(Constants.TAG, "more than one user id with the same email");
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
}
}
@@ -417,7 +424,15 @@ public class OpenPgpService extends RemoteService {
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
- // TODO: also return PendingIntent that opens the key view activity
+ // also return PendingIntent that opens the key view activity
+ Intent intent = new Intent(getBaseContext(), ViewKeyActivity.class);
+ intent.setData(KeyRings.buildGenericKeyRingUri(Long.toString(masterKeyId)));
+
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
+
+ result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
return result;
} catch (ProviderHelper.NotFoundException e) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
index c6637c058..8df341f9e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
@@ -40,6 +40,9 @@ public class AppSettingsActivity extends ActionBarActivity {
private AppSettingsFragment mSettingsFragment;
private AccountsListFragment mAccountsListFragment;
+ // model
+ AppSettings mAppSettings;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -80,22 +83,39 @@ public class AppSettingsActivity extends ActionBarActivity {
case R.id.menu_api_settings_revoke:
revokeAccess();
return true;
+ case R.id.menu_api_settings_start:
+ startApp();
+ return true;
}
return super.onOptionsItemSelected(item);
}
+ private void startApp() {
+ Intent i;
+ PackageManager manager = getPackageManager();
+ try {
+ i = manager.getLaunchIntentForPackage(mAppSettings.getPackageName());
+ if (i == null)
+ throw new PackageManager.NameNotFoundException();
+ i.addCategory(Intent.CATEGORY_LAUNCHER);
+ startActivity(i);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(Constants.TAG, "startApp", e);
+ }
+ }
+
private void loadData(Bundle savedInstanceState, Uri appUri) {
- AppSettings settings = new ProviderHelper(this).getApiAppSettings(appUri);
- mSettingsFragment.setAppSettings(settings);
+ mAppSettings = new ProviderHelper(this).getApiAppSettings(appUri);
+ mSettingsFragment.setAppSettings(mAppSettings);
String appName;
PackageManager pm = getPackageManager();
try {
- ApplicationInfo ai = pm.getApplicationInfo(settings.getPackageName(), 0);
+ ApplicationInfo ai = pm.getApplicationInfo(mAppSettings.getPackageName(), 0);
appName = (String) pm.getApplicationLabel(ai);
} catch (PackageManager.NameNotFoundException e) {
// fallback
- appName = settings.getPackageName();
+ appName = mAppSettings.getPackageName();
}
setTitle(appName);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
index 2fd1ad3b5..d0b958844 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
@@ -250,7 +250,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
// set text on view
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_select_pub_keys_text);
- textView.setHtmlFromString(text);
+ textView.setHtmlFromString(text, true);
/* Load select pub keys fragment */
// Check that the activity is using the layout version with
@@ -292,7 +292,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
// set text on view
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_app_error_message_text);
- textView.setHtmlFromString(text);
+ textView.setHtmlFromString(text, true);
} else {
Log.e(Constants.TAG, "Action does not exist!");
setResult(RESULT_CANCELED);
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 5365ed62c..ff599aaa1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -50,10 +50,10 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
-import org.sufficientlysecure.keychain.util.HkpKeyServer;
+import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
+import org.sufficientlysecure.keychain.keyimport.HkpKeyServer;
import org.sufficientlysecure.keychain.util.InputData;
-import org.sufficientlysecure.keychain.util.KeychainServiceListener;
+import org.sufficientlysecure.keychain.keyimport.KeybaseKeyServer;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler;
@@ -75,7 +75,7 @@ import java.util.List;
* after doing them.
*/
public class KeychainIntentService extends IntentService
- implements Progressable, KeychainServiceListener {
+ implements Progressable, PgpImportExport.KeychainServiceListener {
/* extras that can be given by intent */
public static final String EXTRA_MESSENGER = "messenger";
@@ -99,6 +99,7 @@ public class KeychainIntentService extends IntentService
public static final String ACTION_UPLOAD_KEYRING = Constants.INTENT_PREFIX + "UPLOAD_KEYRING";
public static final String ACTION_DOWNLOAD_AND_IMPORT_KEYS = Constants.INTENT_PREFIX + "QUERY_KEYRING";
+ public static final String ACTION_IMPORT_KEYBASE_KEYS = Constants.INTENT_PREFIX + "DOWNLOAD_KEYBASE";
public static final String ACTION_CERTIFY_KEYRING = Constants.INTENT_PREFIX + "SIGN_KEYRING";
@@ -319,6 +320,7 @@ public class KeychainIntentService extends IntentService
.setEncryptionMasterKeyIds(encryptionKeyIds)
.setSymmetricPassphrase(symmetricPassphrase)
.setSignatureMasterKeyId(signatureKeyId)
+ .setEncryptToSigner(true)
.setSignatureHashAlgorithm(
Preferences.getPreferences(this).getDefaultHashAlgorithm())
.setSignaturePassphrase(
@@ -681,8 +683,7 @@ public class KeychainIntentService extends IntentService
new String[]{KeyRings.MASTER_KEY_ID, KeyRings.HAS_ANY_SECRET},
selection, null, null);
try {
- cursor.moveToFirst();
- do {
+ if (cursor != null && cursor.moveToFirst()) do {
// export public either way
publicMasterKeyIds.add(cursor.getLong(0));
// add secret if available (and requested)
@@ -690,7 +691,9 @@ public class KeychainIntentService extends IntentService
secretMasterKeyIds.add(cursor.getLong(0));
} while (cursor.moveToNext());
} finally {
- cursor.close();
+ if (cursor != null) {
+ cursor.close();
+ }
}
PgpImportExport pgpImportExport = new PgpImportExport(this, this, this);
@@ -729,6 +732,55 @@ public class KeychainIntentService extends IntentService
} catch (Exception e) {
sendErrorToHandler(e);
}
+ } else if (ACTION_IMPORT_KEYBASE_KEYS.equals(action)) {
+ ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST);
+
+ try {
+ 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();
+
+ // create PGPKeyRing object based on downloaded armored key
+ PGPKeyRing downloadedKey = null;
+ BufferedInputStream bufferedInput =
+ new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
+ if (bufferedInput.available() > 0) {
+ InputStream in = PGPUtil.getDecoderStream(bufferedInput);
+ PGPObjectFactory objectFactory = new PGPObjectFactory(in);
+
+ // get first object in block
+ Object obj;
+ if ((obj = objectFactory.nextObject()) != null) {
+
+ if (obj instanceof PGPKeyRing) {
+ downloadedKey = (PGPKeyRing) obj;
+ } else {
+ throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
+ }
+ }
+ }
+
+ // save key bytes in entry object for doing the
+ // actual import afterwards
+ entry.setBytes(downloadedKey.getEncoded());
+ }
+
+ Intent importIntent = new Intent(this, KeychainIntentService.class);
+ importIntent.setAction(ACTION_IMPORT_KEYRING);
+ Bundle importData = new Bundle();
+ importData.putParcelableArrayList(IMPORT_KEY_LIST, entries);
+ importIntent.putExtra(EXTRA_DATA, importData);
+ importIntent.putExtra(EXTRA_MESSENGER, mMessenger);
+
+ // now import it with this service
+ onHandleIntent(importIntent);
+
+ // result is handled in ACTION_IMPORT_KEYRING
+ } catch (Exception e) {
+ sendErrorToHandler(e);
+ }
} else if (ACTION_DOWNLOAD_AND_IMPORT_KEYS.equals(action)) {
try {
ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
index 8f0a3ab8d..d5d02081a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
@@ -24,7 +24,8 @@ import android.os.Handler;
import android.os.Message;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
-import android.widget.Toast;
+
+import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
@@ -99,9 +100,9 @@ public class KeychainIntentServiceHandler extends Handler {
// show error from service
if (data.containsKey(DATA_ERROR)) {
- Toast.makeText(mActivity,
+ AppMsg.makeText(mActivity,
mActivity.getString(R.string.error_message, data.getString(DATA_ERROR)),
- Toast.LENGTH_SHORT).show();
+ AppMsg.STYLE_ALERT).show();
}
break;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
index eb8ef003c..6c74818a5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
@@ -26,10 +26,11 @@ import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.support.v4.app.LoaderManager;
+import android.support.v4.app.NavUtils;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
-import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
+import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
@@ -40,7 +41,6 @@ import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
-import com.beardedhen.androidbootstrap.BootstrapButton;
import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
@@ -64,7 +64,7 @@ import java.util.ArrayList;
*/
public class CertifyKeyActivity extends ActionBarActivity implements
SelectSecretKeyLayoutFragment.SelectSecretKeyCallback, LoaderManager.LoaderCallbacks<Cursor> {
- private BootstrapButton mSignButton;
+ private View mSignButton;
private CheckBox mUploadKeyCheckbox;
private Spinner mSelectKeyserverSpinner;
@@ -86,20 +86,16 @@ public class CertifyKeyActivity extends ActionBarActivity implements
setContentView(R.layout.certify_key_activity);
- final ActionBar actionBar = getSupportActionBar();
- actionBar.setDisplayShowTitleEnabled(true);
- actionBar.setDisplayHomeAsUpEnabled(false);
- actionBar.setHomeButtonEnabled(false);
-
mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getSupportFragmentManager()
.findFragmentById(R.id.sign_key_select_key_fragment);
mSelectKeyFragment.setCallback(this);
mSelectKeyFragment.setFilterCertify(true);
- mSelectKeyserverSpinner = (Spinner) findViewById(R.id.sign_key_keyserver);
+ mSelectKeyserverSpinner = (Spinner) findViewById(R.id.upload_key_keyserver);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, Preferences.getPreferences(this)
- .getKeyServers());
+ .getKeyServers()
+ );
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSelectKeyserverSpinner.setAdapter(adapter);
@@ -122,14 +118,14 @@ public class CertifyKeyActivity extends ActionBarActivity implements
}
});
- mSignButton = (BootstrapButton) findViewById(R.id.sign_key_sign_button);
+ mSignButton = findViewById(R.id.sign_key_sign_button);
mSignButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mPubKeyId != 0) {
if (mMasterKeyId == 0) {
- mSelectKeyFragment.setError(getString(R.string.select_key_to_sign));
+ mSelectKeyFragment.setError(getString(R.string.select_key_to_certify));
} else {
initiateSigning();
}
@@ -145,7 +141,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
}
Log.e(Constants.TAG, "uri: " + mDataUri);
- mUserIds = (ListView) findViewById(R.id.user_ids);
+ mUserIds = (ListView) findViewById(R.id.view_key_user_ids);
mUserIdsAdapter = new ViewKeyUserIdsAdapter(this, null, 0, true);
mUserIds.setAdapter(mUserIdsAdapter);
@@ -201,7 +197,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
byte[] fingerprintBlob = data.getBlob(INDEX_FINGERPRINT);
String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);
- ((TextView) findViewById(R.id.fingerprint))
+ ((TextView) findViewById(R.id.view_key_fingerprint))
.setText(PgpKeyHelper.colorizeFingerprint(fingerprint));
}
break;
@@ -318,7 +314,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
// fill values for this action
Bundle data = new Bundle();
- Spinner keyServer = (Spinner) findViewById(R.id.sign_key_keyserver);
+ Spinner keyServer = (Spinner) findViewById(R.id.upload_key_keyserver);
String server = (String) keyServer.getSelectedItem();
data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, server);
@@ -359,4 +355,17 @@ public class CertifyKeyActivity extends ActionBarActivity implements
public void onKeySelected(long secretKeyId) {
mMasterKeyId = secretKeyId;
}
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home: {
+ Intent viewIntent = NavUtils.getParentActivityIntent(this);
+ viewIntent.setData(KeyRings.buildGenericKeyRingUri(mDataUri));
+ NavUtils.navigateUpTo(this, viewIntent);
+ return true;
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
index 1877a43b5..5b21be6e4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
@@ -27,7 +27,6 @@ import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
@@ -68,9 +67,6 @@ public class DecryptActivity extends DrawerActivity {
setContentView(R.layout.decrypt_activity);
- // set actionbar without home button if called from another app
- ActionBarHelper.setBackButton(this);
-
initView();
setupDrawerNavigation(savedInstanceState);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
index 0f88ee753..d953e2591 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
@@ -54,7 +54,7 @@ public class DecryptFileFragment extends DecryptFragment {
private EditText mFilename;
private CheckBox mDeleteAfter;
private BootstrapButton mBrowse;
- private BootstrapButton mDecryptButton;
+ private View mDecryptButton;
private String mInputFilename = null;
private String mOutputFilename = null;
@@ -71,7 +71,7 @@ public class DecryptFileFragment extends DecryptFragment {
mFilename = (EditText) view.findViewById(R.id.decrypt_file_filename);
mBrowse = (BootstrapButton) view.findViewById(R.id.decrypt_file_browse);
mDeleteAfter = (CheckBox) view.findViewById(R.id.decrypt_file_delete_after_decryption);
- mDecryptButton = (BootstrapButton) view.findViewById(R.id.decrypt_file_action_decrypt);
+ mDecryptButton = view.findViewById(R.id.decrypt_file_action_decrypt);
mBrowse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FileHelper.openFile(DecryptFileFragment.this, mFilename.getText().toString(), "*/*",
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
index 6bb2209ea..955f8324e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
@@ -192,7 +192,7 @@ public class DecryptFragment extends Fragment {
mLookupKey.setVisibility(View.GONE);
// successful decryption-only
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_blue));
+ mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_purple));
mResultText.setText(R.string.decrypt_result_decrypted);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java
index 454ee4415..daa48b526 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java
@@ -47,8 +47,8 @@ public class DecryptMessageFragment extends DecryptFragment {
// view
private EditText mMessage;
- private BootstrapButton mDecryptButton;
- private BootstrapButton mDecryptFromCLipboardButton;
+ private View mDecryptButton;
+ private View mDecryptFromCLipboardButton;
// model
private String mCiphertext;
@@ -61,8 +61,8 @@ public class DecryptMessageFragment extends DecryptFragment {
View view = inflater.inflate(R.layout.decrypt_message_fragment, container, false);
mMessage = (EditText) view.findViewById(R.id.message);
- mDecryptButton = (BootstrapButton) view.findViewById(R.id.action_decrypt);
- mDecryptFromCLipboardButton = (BootstrapButton) view.findViewById(R.id.action_decrypt_from_clipboard);
+ mDecryptButton = view.findViewById(R.id.action_decrypt);
+ mDecryptFromCLipboardButton = view.findViewById(R.id.action_decrypt_from_clipboard);
mDecryptButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@@ -108,11 +108,11 @@ public class DecryptMessageFragment extends DecryptFragment {
mCiphertext = matcher.group(1);
decryptStart(null);
} else {
- AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_INFO)
+ AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_ALERT)
.show();
}
} else {
- AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_INFO)
+ AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_ALERT)
.show();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
index 529ea62a3..fdcb98976 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -60,6 +60,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.widget.Editor;
@@ -502,7 +503,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
int curID = 0;
for (String userID : userIDs) {
if (userID.equals("") && (!userID.equals(originalIDs.get(curID)) || newIDs.get(curID))) {
- AlertDialog.Builder alert = new AlertDialog.Builder(
+ CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(
EditKeyActivity.this);
alert.setIcon(R.drawable.ic_dialog_alert_holo_light);
@@ -525,7 +526,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
}
);
alert.setCancelable(false);
- alert.create().show();
+ alert.show();
return;
}
curID++;
@@ -614,7 +615,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
private void cancelClicked() {
if (needsSaving()) { //ask if we want to save
- AlertDialog.Builder alert = new AlertDialog.Builder(
+ CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(
EditKeyActivity.this);
alert.setIcon(R.drawable.ic_dialog_alert_holo_light);
@@ -637,7 +638,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
}
});
alert.setCancelable(false);
- alert.create().show();
+ alert.show();
} else {
setResult(RESULT_CANCELED);
finish();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
index 6c71c641f..39d4a09bc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
@@ -27,7 +27,6 @@ import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.util.Log;
@@ -145,26 +144,28 @@ public class EncryptActivity extends DrawerActivity implements
setContentView(R.layout.encrypt_activity);
- // set actionbar without home button if called from another app
- ActionBarHelper.setBackButton(this);
-
initView();
- setupDrawerNavigation(savedInstanceState);
+ // if called with an intent action, do not init drawer navigation
+ if (ACTION_ENCRYPT.equals(getIntent().getAction())) {
+ // TODO: back button to key?
+ } else {
+ setupDrawerNavigation(savedInstanceState);
+ }
// Handle intent actions
handleActions(getIntent());
mTabsAdapterMode.addTab(EncryptAsymmetricFragment.class,
- mAsymmetricFragmentBundle, getString(R.string.label_asymmetric));
+ mAsymmetricFragmentBundle, getString(R.string.label_asymmetric));
mTabsAdapterMode.addTab(EncryptSymmetricFragment.class,
- mSymmetricFragmentBundle, getString(R.string.label_symmetric));
+ mSymmetricFragmentBundle, getString(R.string.label_symmetric));
mViewPagerMode.setCurrentItem(mSwitchToMode);
mTabsAdapterContent.addTab(EncryptMessageFragment.class,
- mMessageFragmentBundle, getString(R.string.label_message));
+ mMessageFragmentBundle, getString(R.string.label_message));
mTabsAdapterContent.addTab(EncryptFileFragment.class,
- mFileFragmentBundle, getString(R.string.label_file));
+ mFileFragmentBundle, getString(R.string.label_file));
mViewPagerContent.setCurrentItem(mSwitchToContent);
}
@@ -217,9 +218,9 @@ public class EncryptActivity extends DrawerActivity implements
// preselect keys given by intent
mAsymmetricFragmentBundle.putLongArray(EncryptAsymmetricFragment.ARG_ENCRYPTION_KEY_IDS,
- encryptionKeyIds);
+ encryptionKeyIds);
mAsymmetricFragmentBundle.putLong(EncryptAsymmetricFragment.ARG_SIGNATURE_KEY_ID,
- signatureKeyId);
+ signatureKeyId);
mSwitchToMode = PAGER_MODE_ASYMMETRIC;
/**
@@ -241,9 +242,10 @@ public class EncryptActivity extends DrawerActivity implements
} else {
Log.e(Constants.TAG,
"Direct binary data without actual file in filesystem is not supported " +
- "by Intents. Please use the Remote Service API!");
- Toast.makeText(this, R.string.error_only_files_are_supported, Toast.LENGTH_LONG)
- .show();
+ "by Intents. Please use the Remote Service API!"
+ );
+ Toast.makeText(this, R.string.error_only_files_are_supported,
+ Toast.LENGTH_LONG).show();
// end activity
finish();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
index 0b6449cda..2b0c94f22 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
@@ -220,9 +220,6 @@ public class EncryptAsymmetricFragment extends Fragment {
private void selectPublicKeys() {
Intent intent = new Intent(getActivity(), SelectPublicKeyActivity.class);
Vector<Long> keyIds = new Vector<Long>();
- if (mSecretKeyId != 0) {
- keyIds.add(mSecretKeyId);
- }
if (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) {
for (int i = 0; i < mEncryptionKeyIds.length; ++i) {
keyIds.add(mEncryptionKeyIds[i]);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
index b8ac59dac..d150abdeb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
@@ -67,7 +67,7 @@ public class EncryptFileFragment extends Fragment {
private CheckBox mDeleteAfter = null;
private CheckBox mShareAfter = null;
private BootstrapButton mBrowse = null;
- private BootstrapButton mEncryptFile;
+ private View mEncryptFile;
private FileDialogFragment mFileDialog;
@@ -92,7 +92,7 @@ public class EncryptFileFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.encrypt_file_fragment, container, false);
- mEncryptFile = (BootstrapButton) view.findViewById(R.id.action_encrypt_file);
+ mEncryptFile = view.findViewById(R.id.action_encrypt_file);
mEncryptFile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java
index 0a1a3474a..4c35806e5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java
@@ -28,9 +28,8 @@ import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.EditText;
+import android.widget.TextView;
-import com.beardedhen.androidbootstrap.BootstrapButton;
import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
@@ -46,9 +45,9 @@ import org.sufficientlysecure.keychain.util.Log;
public class EncryptMessageFragment extends Fragment {
public static final String ARG_TEXT = "text";
- private EditText mMessage = null;
- private BootstrapButton mEncryptShare;
- private BootstrapButton mEncryptClipboard;
+ private TextView mMessage = null;
+ private View mEncryptShare;
+ private View mEncryptClipboard;
private EncryptActivityInterface mEncryptInterface;
@@ -70,9 +69,9 @@ public class EncryptMessageFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.encrypt_message_fragment, container, false);
- mMessage = (EditText) view.findViewById(R.id.message);
- mEncryptClipboard = (BootstrapButton) view.findViewById(R.id.action_encrypt_clipboard);
- mEncryptShare = (BootstrapButton) view.findViewById(R.id.action_encrypt_share);
+ mMessage = (TextView) view.findViewById(R.id.message);
+ mEncryptClipboard = view.findViewById(R.id.action_encrypt_clipboard);
+ mEncryptShare = view.findViewById(R.id.action_encrypt_share);
mEncryptClipboard.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java
index ec9f3a759..5909970af 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpAboutFragment.java
@@ -45,7 +45,7 @@ public class HelpAboutFragment extends Fragment {
HtmlTextView aboutTextView = (HtmlTextView) view.findViewById(R.id.help_about_text);
// load html from raw resource (Parsing handled by HtmlTextView library)
- aboutTextView.setHtmlFromRawResource(getActivity(), R.raw.help_about);
+ aboutTextView.setHtmlFromRawResource(getActivity(), R.raw.help_about, true);
// no flickering when clicking textview for Android < 4
aboutTextView.setTextColor(getResources().getColor(android.R.color.black));
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java
index e81bc1563..cf7446a58 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpActivity.java
@@ -24,7 +24,9 @@ import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter;
+import org.sufficientlysecure.keychain.util.SlidingTabLayout;
public class HelpActivity extends ActionBarActivity {
public static final String EXTRA_SELECTED_TAB = "selected_tab";
@@ -37,25 +39,27 @@ public class HelpActivity extends ActionBarActivity {
public static final int TAB_ABOUT = 5;
ViewPager mViewPager;
- TabsAdapter mTabsAdapter;
+ private PagerTabStripAdapter mTabsAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.help_activity);
-
- mViewPager = (ViewPager) findViewById(R.id.pager);
-
final ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setHomeButtonEnabled(false);
- actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
- mTabsAdapter = new TabsAdapter(this, mViewPager);
+ setContentView(R.layout.help_activity);
+
+ mViewPager = (ViewPager) findViewById(R.id.pager);
+ SlidingTabLayout slidingTabLayout =
+ (SlidingTabLayout) findViewById(R.id.sliding_tab_layout);
+
+ mTabsAdapter = new PagerTabStripAdapter(this);
+ mViewPager.setAdapter(mTabsAdapter);
- int selectedTab = 0;
+ int selectedTab = TAB_START;
Intent intent = getIntent();
if (intent.getExtras() != null && intent.getExtras().containsKey(EXTRA_SELECTED_TAB)) {
selectedTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB);
@@ -63,30 +67,36 @@ public class HelpActivity extends ActionBarActivity {
Bundle startBundle = new Bundle();
startBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_start);
- mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_start)),
- HelpHtmlFragment.class, startBundle, (selectedTab == TAB_START));
+ mTabsAdapter.addTab(HelpHtmlFragment.class, startBundle,
+ getString(R.string.help_tab_start));
Bundle faqBundle = new Bundle();
faqBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_faq);
- mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_faq)),
- HelpHtmlFragment.class, faqBundle, (selectedTab == TAB_FAQ));
+ mTabsAdapter.addTab(HelpHtmlFragment.class, faqBundle,
+ getString(R.string.help_tab_faq));
Bundle wotBundle = new Bundle();
wotBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_wot);
- mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_wot)),
- HelpHtmlFragment.class, wotBundle, (selectedTab == TAB_WOT));
+ mTabsAdapter.addTab(HelpHtmlFragment.class, wotBundle,
+ getString(R.string.help_tab_wot));
Bundle nfcBundle = new Bundle();
nfcBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_nfc_beam);
- mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_nfc_beam)),
- HelpHtmlFragment.class, nfcBundle, (selectedTab == TAB_NFC));
+ mTabsAdapter.addTab(HelpHtmlFragment.class, nfcBundle,
+ getString(R.string.help_tab_nfc_beam));
Bundle changelogBundle = new Bundle();
changelogBundle.putInt(HelpHtmlFragment.ARG_HTML_FILE, R.raw.help_changelog);
- mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_changelog)),
- HelpHtmlFragment.class, changelogBundle, (selectedTab == TAB_CHANGELOG));
+ mTabsAdapter.addTab(HelpHtmlFragment.class, changelogBundle,
+ getString(R.string.help_tab_changelog));
+
+ mTabsAdapter.addTab(HelpAboutFragment.class, null,
+ getString(R.string.help_tab_about));
+
+ // NOTE: must be after adding the tabs!
+ slidingTabLayout.setViewPager(mViewPager);
- mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_about)),
- HelpAboutFragment.class, null, (selectedTab == TAB_ABOUT));
+ // switch to tab selected by extra
+ mViewPager.setCurrentItem(selectedTab);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpHtmlFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpHtmlFragment.java
index 98c84e5e3..a3f0ef614 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpHtmlFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/HelpHtmlFragment.java
@@ -66,7 +66,7 @@ public class HelpHtmlFragment extends Fragment {
scroller.addView(text);
// load html from raw resource (Parsing handled by HtmlTextView library)
- text.setHtmlFromRawResource(getActivity(), mHtmlFile);
+ text.setHtmlFromRawResource(getActivity(), mHtmlFile, true);
// no flickering when clicking textview for Android < 4
text.setTextColor(getResources().getColor(android.R.color.black));
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 0fccd668f..35076287b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -38,16 +38,14 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
-import com.beardedhen.androidbootstrap.BootstrapButton;
import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
+import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.ui.dialog.BadImportKeyDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
@@ -62,6 +60,8 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
+ "IMPORT_KEY_FROM_KEYSERVER";
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_KEY_SERVER_AND_RETURN";
+ public static final String ACTION_IMPORT_KEY_FROM_KEYBASE = Constants.INTENT_PREFIX
+ + "IMPORT_KEY_FROM_KEYBASE";
// Actions for internal use only:
public static final String ACTION_IMPORT_KEY_FROM_FILE = Constants.INTENT_PREFIX
@@ -85,20 +85,22 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
private ImportKeysListFragment mListFragment;
private String[] mNavigationStrings;
private Fragment mCurrentFragment;
- private BootstrapButton mImportButton;
+ private View mImportButton;
private static final Class[] NAVIGATION_CLASSES = new Class[]{
ImportKeysServerFragment.class,
ImportKeysFileFragment.class,
ImportKeysQrCodeFragment.class,
ImportKeysClipboardFragment.class,
- ImportKeysNFCFragment.class
+ ImportKeysNFCFragment.class,
+ ImportKeysKeybaseFragment.class
};
private static final int NAV_SERVER = 0;
private static final int NAV_FILE = 1;
private static final int NAV_QR_CODE = 2;
private static final int NAV_CLIPBOARD = 3;
private static final int NAV_NFC = 4;
+ private static final int NAV_KEYBASE = 5;
private int mCurrentNavPosition = -1;
@@ -108,7 +110,7 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
setContentView(R.layout.import_keys_activity);
- mImportButton = (BootstrapButton) findViewById(R.id.import_import);
+ mImportButton = findViewById(R.id.import_import);
mImportButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@@ -121,7 +123,6 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) {
setTitle(R.string.nav_import);
} else {
- ActionBarHelper.setBackButton(this);
getSupportActionBar().setDisplayShowTitleEnabled(false);
// set drop down navigation
@@ -238,6 +239,12 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
// no immediate actions!
startListFragment(savedInstanceState, null, null, null);
+ } else if (ACTION_IMPORT_KEY_FROM_KEYBASE.equals(action)) {
+ // NOTE: this only displays the appropriate fragment, no actions are taken
+ loadNavFragment(NAV_KEYBASE, null);
+
+ // no immediate actions!
+ startListFragment(savedInstanceState, null, null, null);
} else {
startListFragment(savedInstanceState, null, null, null);
}
@@ -340,8 +347,8 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
startListFragment(savedInstanceState, null, null, query);
}
- public void loadCallback(byte[] importData, Uri dataUri, String serverQuery, String keyServer) {
- mListFragment.loadNew(importData, dataUri, serverQuery, keyServer);
+ public void loadCallback(byte[] importData, Uri dataUri, String serverQuery, String keyServer, String keybaseQuery) {
+ mListFragment.loadNew(importData, dataUri, serverQuery, keyServer, keybaseQuery);
}
/**
@@ -449,6 +456,31 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
// start service with intent
startService(intent);
+ } else if (mListFragment.getKeybaseQuery() != null) {
+ // Send all information needed to service to query keys in other thread
+ Intent intent = new Intent(this, KeychainIntentService.class);
+
+ intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYBASE_KEYS);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ // get selected key entries
+ ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedData();
+ data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries);
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ saveHandler.showProgressDialog(this);
+
+ // start service with intent
+ startService(intent);
+
} else {
AppMsg.makeText(this, R.string.error_nothing_import, AppMsg.STYLE_ALERT).show();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
index 412fbddd8..f331358fa 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
@@ -71,7 +71,7 @@ public class ImportKeysClipboardFragment extends Fragment {
return;
}
}
- mImportActivity.loadCallback(sendText.getBytes(), null, null, null);
+ mImportActivity.loadCallback(sendText.getBytes(), null, null, null, null);
}
});
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
index dc5333a8f..51f961aab 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
@@ -85,7 +85,7 @@ public class ImportKeysFileFragment extends Fragment {
if (resultCode == Activity.RESULT_OK && data != null) {
// load data
- mImportActivity.loadCallback(null, data.getData(), null, null);
+ mImportActivity.loadCallback(null, data.getData(), null, null, null);
}
break;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java
new file mode 100644
index 000000000..a996079c9
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysKeybaseFragment.java
@@ -0,0 +1,120 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.support.v4.app.Fragment;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.util.Log;
+
+/**
+ * Import public keys from the Keybase.io directory. First cut: just raw search.
+ * TODO: make a pick list of the people you’re following on keybase
+ */
+public class ImportKeysKeybaseFragment extends Fragment {
+
+ private ImportKeysActivity mImportActivity;
+ private BootstrapButton mSearchButton;
+ private EditText mQueryEditText;
+
+ public static final String ARG_QUERY = "query";
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static ImportKeysKeybaseFragment newInstance() {
+ ImportKeysKeybaseFragment frag = new ImportKeysKeybaseFragment();
+
+ Bundle args = new Bundle();
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.import_keys_keybase_fragment, container, false);
+
+ mQueryEditText = (EditText) view.findViewById(R.id.import_keybase_query);
+
+ mSearchButton = (BootstrapButton) view.findViewById(R.id.import_keybase_search);
+ mSearchButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String query = mQueryEditText.getText().toString();
+ search(query);
+
+ // close keyboard after pressing search
+ InputMethodManager imm =
+ (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(mQueryEditText.getWindowToken(), 0);
+ }
+ });
+
+ mQueryEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (actionId == EditorInfo.IME_ACTION_SEARCH) {
+ String query = mQueryEditText.getText().toString();
+ search(query);
+
+ // Don't return true to let the keyboard close itself after pressing search
+ return false;
+ }
+ return false;
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ mImportActivity = (ImportKeysActivity) getActivity();
+
+ // set displayed values
+ if (getArguments() != null) {
+ if (getArguments().containsKey(ARG_QUERY)) {
+ String query = getArguments().getString(ARG_QUERY);
+ mQueryEditText.setText(query, TextView.BufferType.EDITABLE);
+ }
+ }
+ }
+
+ private void search(String query) {
+ mImportActivity.loadCallback(null, null, null, null, query);
+ }
+}
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 3a6c384e8..bf6abcd6f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
@@ -33,11 +33,12 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter;
-import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
+import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
+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.util.KeyServer;
+import org.sufficientlysecure.keychain.keyimport.KeyServer;
import org.sufficientlysecure.keychain.util.Log;
import java.io.ByteArrayInputStream;
@@ -60,9 +61,11 @@ public class ImportKeysListFragment extends ListFragment implements
private Uri mDataUri;
private String mServerQuery;
private String mKeyServer;
+ private String mKeybaseQuery;
private static final int LOADER_ID_BYTES = 0;
private static final int LOADER_ID_SERVER_QUERY = 1;
+ private static final int LOADER_ID_KEYBASE = 2;
public byte[] getKeyBytes() {
return mKeyBytes;
@@ -76,6 +79,10 @@ public class ImportKeysListFragment extends ListFragment implements
return mServerQuery;
}
+ public String getKeybaseQuery() {
+ return mKeybaseQuery;
+ }
+
public String getKeyServer() {
return mKeyServer;
}
@@ -148,6 +155,16 @@ public class ImportKeysListFragment extends ListFragment implements
// give arguments to onCreateLoader()
getLoaderManager().initLoader(LOADER_ID_SERVER_QUERY, null, this);
}
+
+ if (mKeybaseQuery != null) {
+ // Start out with a progress indicator.
+ setListShown(false);
+
+ // Prepare the loader. Either re-connect with an existing one,
+ // or start a new one.
+ // give arguments to onCreateLoader()
+ getLoaderManager().initLoader(LOADER_ID_KEYBASE, null, this);
+ }
}
@Override
@@ -157,16 +174,18 @@ public class ImportKeysListFragment extends ListFragment implements
// Select checkbox!
// Update underlying data and notify adapter of change. The adapter will
// update the view automatically.
+
ImportKeysListEntry entry = mAdapter.getItem(position);
entry.setSelected(!entry.isSelected());
mAdapter.notifyDataSetChanged();
}
- public void loadNew(byte[] keyBytes, Uri dataUri, String serverQuery, String keyServer) {
+ public void loadNew(byte[] keyBytes, Uri dataUri, String serverQuery, String keyServer, String keybaseQuery) {
mKeyBytes = keyBytes;
mDataUri = dataUri;
mServerQuery = serverQuery;
mKeyServer = keyServer;
+ mKeybaseQuery = keybaseQuery;
if (mKeyBytes != null || mDataUri != null) {
// Start out with a progress indicator.
@@ -181,6 +200,13 @@ public class ImportKeysListFragment extends ListFragment implements
getLoaderManager().restartLoader(LOADER_ID_SERVER_QUERY, null, this);
}
+
+ if (mKeybaseQuery != null) {
+ // Start out with a progress indicator.
+ setListShown(false);
+
+ getLoaderManager().restartLoader(LOADER_ID_KEYBASE, null, this);
+ }
}
@Override
@@ -194,6 +220,9 @@ public class ImportKeysListFragment extends ListFragment implements
case LOADER_ID_SERVER_QUERY: {
return new ImportKeysListServerLoader(getActivity(), mServerQuery, mKeyServer);
}
+ case LOADER_ID_KEYBASE: {
+ return new ImportKeysListKeybaseLoader(getActivity(), mKeybaseQuery);
+ }
default:
return null;
@@ -248,7 +277,7 @@ public class ImportKeysListFragment extends ListFragment implements
if (error == null) {
AppMsg.makeText(
getActivity(), getResources().getQuantityString(R.plurals.keys_found,
- mAdapter.getCount(), mAdapter.getCount()),
+ mAdapter.getCount(), mAdapter.getCount()),
AppMsg.STYLE_INFO
).show();
} else if (error instanceof KeyServer.InsufficientQuery) {
@@ -263,6 +292,19 @@ public class ImportKeysListFragment extends ListFragment implements
}
break;
+ case LOADER_ID_KEYBASE:
+
+ if (error == null) {
+ AppMsg.makeText(
+ getActivity(), getResources().getQuantityString(R.plurals.keys_found,
+ mAdapter.getCount(), mAdapter.getCount()),
+ AppMsg.STYLE_INFO
+ ).show();
+ } else if (error instanceof KeyServer.QueryException) {
+ AppMsg.makeText(getActivity(), R.string.error_keyserver_query,
+ AppMsg.STYLE_ALERT).show();
+ }
+
default:
break;
}
@@ -279,6 +321,10 @@ public class ImportKeysListFragment extends ListFragment implements
// Clear the data in the adapter.
mAdapter.clear();
break;
+ case LOADER_ID_KEYBASE:
+ // Clear the data in the adapter.
+ mAdapter.clear();
+ break;
default:
break;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
index 65d463456..22b56e1ab 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
@@ -117,7 +117,7 @@ public class ImportKeysQrCodeFragment extends Fragment {
// is this a full key encoded as qr code?
if (scannedContent.startsWith("-----BEGIN PGP")) {
- mImportActivity.loadCallback(scannedContent.getBytes(), null, null, null);
+ mImportActivity.loadCallback(scannedContent.getBytes(), null, null, null, null);
return;
}
@@ -197,7 +197,7 @@ public class ImportKeysQrCodeFragment extends Fragment {
for (String in : mScannedContent) {
result += in;
}
- mImportActivity.loadCallback(result.getBytes(), null, null, null);
+ mImportActivity.loadCallback(result.getBytes(), null, null, null, null);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
index 82fe2fc4c..9e3d88ff5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
@@ -151,7 +151,7 @@ public class ImportKeysServerFragment extends Fragment {
}
private void search(String query, String keyServer) {
- mImportActivity.loadCallback(null, null, query, keyServer);
+ mImportActivity.loadCallback(null, null, query, keyServer, null);
}
}
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 82f65a962..3fd958bcc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -29,7 +29,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
-import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -45,11 +44,10 @@ import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.AdapterView;
-import android.widget.Button;
import android.widget.FrameLayout;
+import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
@@ -77,18 +75,13 @@ import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
* Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
* StickyListHeaders library which does not extend upon ListView.
*/
-public class KeyListFragment extends Fragment
+public class KeyListFragment extends LoaderFragment
implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
LoaderManager.LoaderCallbacks<Cursor> {
private KeyListAdapter mAdapter;
private StickyListHeadersListView mStickyList;
- // rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
- boolean mListShown;
- View mProgressContainer;
- View mListContainer;
-
private String mCurQuery;
private SearchView mSearchView;
// empty list layout
@@ -100,14 +93,15 @@ public class KeyListFragment extends Fragment
* Load custom layout with StickyListView from library
*/
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View root = inflater.inflate(R.layout.key_list_fragment, container, false);
+ public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
+ View root = super.onCreateView(inflater, superContainer, savedInstanceState);
+ View view = inflater.inflate(R.layout.key_list_fragment, getContainer());
- mStickyList = (StickyListHeadersListView) root.findViewById(R.id.key_list_list);
+ mStickyList = (StickyListHeadersListView) view.findViewById(R.id.key_list_list);
mStickyList.setOnItemClickListener(this);
// empty view
- mButtonEmptyCreate = (BootstrapButton) root.findViewById(R.id.key_list_empty_button_create);
+ mButtonEmptyCreate = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_create);
mButtonEmptyCreate.setOnClickListener(new OnClickListener() {
@Override
@@ -119,7 +113,7 @@ public class KeyListFragment extends Fragment
startActivityForResult(intent, 0);
}
});
- mButtonEmptyImport = (BootstrapButton) root.findViewById(R.id.key_list_empty_button_import);
+ mButtonEmptyImport = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_import);
mButtonEmptyImport.setOnClickListener(new OnClickListener() {
@Override
@@ -130,11 +124,6 @@ public class KeyListFragment extends Fragment
}
});
- // rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
- mListContainer = root.findViewById(R.id.key_list_list_container);
- mProgressContainer = root.findViewById(R.id.key_list_progress_container);
- mListShown = true;
-
return root;
}
@@ -233,9 +222,8 @@ public class KeyListFragment extends Fragment
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
- // NOTE: Not supported by StickyListHeader, but reimplemented here
// Start out with a progress indicator.
- setListShown(false);
+ setContentShown(false);
// Create an empty adapter we will use to display the loaded data.
mAdapter = new KeyListAdapter(getActivity(), null, 0);
@@ -297,12 +285,11 @@ public class KeyListFragment extends Fragment
// this view is made visible if no data is available
mStickyList.setEmptyView(getActivity().findViewById(R.id.key_list_empty));
- // NOTE: Not supported by StickyListHeader, but reimplemented here
// The list should now be shown.
if (isResumed()) {
- setListShown(true);
+ setContentShown(true);
} else {
- setListShownNoAnimation(true);
+ setContentShownNoAnimation(true);
}
}
@@ -380,6 +367,9 @@ public class KeyListFragment extends Fragment
// Execute this when searching
mSearchView.setOnQueryTextListener(this);
+ View searchPlate = mSearchView.findViewById(android.support.v7.appcompat.R.id.search_plate);
+ searchPlate.setBackgroundResource(R.drawable.keychaintheme_searchview_holo_light);
+
// Erase search result without focus
MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() {
@Override
@@ -414,43 +404,6 @@ public class KeyListFragment extends Fragment
return true;
}
- // rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
- public void setListShown(boolean shown, boolean animate) {
- if (mListShown == shown) {
- return;
- }
- mListShown = shown;
- if (shown) {
- if (animate) {
- mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
- getActivity(), android.R.anim.fade_out));
- mListContainer.startAnimation(AnimationUtils.loadAnimation(
- getActivity(), android.R.anim.fade_in));
- }
- mProgressContainer.setVisibility(View.GONE);
- mListContainer.setVisibility(View.VISIBLE);
- } else {
- if (animate) {
- mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
- getActivity(), android.R.anim.fade_in));
- mListContainer.startAnimation(AnimationUtils.loadAnimation(
- getActivity(), android.R.anim.fade_out));
- }
- mProgressContainer.setVisibility(View.VISIBLE);
- mListContainer.setVisibility(View.INVISIBLE);
- }
- }
-
- // rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
- public void setListShown(boolean shown) {
- setListShown(shown, true);
- }
-
- // rebuild functionality of ListFragment, http://stackoverflow.com/a/12504097
- public void setListShownNoAnimation(boolean shown) {
- setListShown(shown, false);
- }
-
/**
* Implements StickyListHeadersAdapter from library
*/
@@ -475,7 +428,7 @@ public class KeyListFragment extends Fragment
TextView mMainUserIdRest;
View mStatusDivider;
FrameLayout mStatusLayout;
- Button mButton;
+ ImageButton mButton;
TextView mRevoked;
ImageView mVerified;
}
@@ -488,7 +441,7 @@ public class KeyListFragment extends Fragment
holder.mMainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
holder.mStatusDivider = (View) view.findViewById(R.id.status_divider);
holder.mStatusLayout = (FrameLayout) view.findViewById(R.id.status_layout);
- holder.mButton = (Button) view.findViewById(R.id.edit);
+ holder.mButton = (ImageButton) view.findViewById(R.id.edit);
holder.mRevoked = (TextView) view.findViewById(R.id.revoked);
holder.mVerified = (ImageView) view.findViewById(R.id.verified);
view.setTag(holder);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LoaderFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LoaderFragment.java
new file mode 100644
index 000000000..87ab1bb8c
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LoaderFragment.java
@@ -0,0 +1,78 @@
+package org.sufficientlysecure.keychain.ui;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+
+import org.sufficientlysecure.keychain.R;
+
+/** This is a fragment helper class, which implements a generic
+ * progressbar/container view.
+ *
+ * To use it in a fragment, simply subclass, use onCreateView to create the
+ * layout's root view, and ues getContainer() as root view of your subclass.
+ * The layout shows a progress bar by default, and can be switched to the
+ * actual contents by calling setContentShown().
+ *
+ */
+public class LoaderFragment extends Fragment {
+ private boolean mContentShown;
+ private View mProgressContainer;
+ private ViewGroup mContainer;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View root = inflater.inflate(R.layout.loader_layout, container, false);
+
+ mContentShown = true;
+ mContainer = (ViewGroup) root.findViewById(R.id.loader_container);
+ mProgressContainer = root.findViewById(R.id.loader_progress);
+
+ // content is not shown (by visibility statuses in the layout files)
+ mContentShown = false;
+
+ return root;
+
+ }
+
+ protected ViewGroup getContainer() {
+ return mContainer;
+ }
+
+ public void setContentShown(boolean shown, boolean animate) {
+ if (mContentShown == shown) {
+ return;
+ }
+ mContentShown = shown;
+ if (shown) {
+ if (animate) {
+ mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
+ getActivity(), android.R.anim.fade_out));
+ mContainer.startAnimation(AnimationUtils.loadAnimation(
+ getActivity(), android.R.anim.fade_in));
+ }
+ mProgressContainer.setVisibility(View.GONE);
+ mContainer.setVisibility(View.VISIBLE);
+ } else {
+ if (animate) {
+ mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
+ getActivity(), android.R.anim.fade_in));
+ mContainer.startAnimation(AnimationUtils.loadAnimation(
+ getActivity(), android.R.anim.fade_out));
+ }
+ mProgressContainer.setVisibility(View.VISIBLE);
+ mContainer.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ public void setContentShown(boolean shown) {
+ setContentShown(shown, true);
+ }
+
+ public void setContentShownNoAnimation(boolean shown) {
+ setContentShown(shown, false);
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
index 6de03198e..38a0c8478 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
@@ -56,7 +56,7 @@ public class SelectSecretKeyFragment extends ListFragment implements
Bundle args = new Bundle();
args.putBoolean(ARG_FILTER_CERTIFY, filterCertify);
- args.putBoolean(ARG_FILTER_CERTIFY, filterSign);
+ args.putBoolean(ARG_FILTER_SIGN, filterSign);
frag.setArguments(args);
return frag;
@@ -124,7 +124,8 @@ public class SelectSecretKeyFragment extends ListFragment implements
KeyRings.CAN_CERTIFY,
// has sign may be any subkey
KeyRings.HAS_SIGN,
- KeyRings.HAS_ANY_SECRET
+ KeyRings.HAS_ANY_SECRET,
+ KeyRings.HAS_SECRET
};
String where = KeyRings.HAS_ANY_SECRET + " = 1";
@@ -158,7 +159,7 @@ public class SelectSecretKeyFragment extends ListFragment implements
private class SelectSecretKeyCursorAdapter extends SelectKeyCursorAdapter {
- private int mIndexHasSign, mIndexCanCertify;
+ private int mIndexHasSign, mIndexCanCertify, mIndexHasSecret;
public SelectSecretKeyCursorAdapter(Context context, Cursor c, int flags, ListView listView) {
super(context, c, flags, listView);
@@ -170,6 +171,7 @@ public class SelectSecretKeyFragment extends ListFragment implements
if (cursor != null) {
mIndexCanCertify = cursor.getColumnIndexOrThrow(KeyRings.CAN_CERTIFY);
mIndexHasSign = cursor.getColumnIndexOrThrow(KeyRings.HAS_SIGN);
+ mIndexHasSecret = cursor.getColumnIndexOrThrow(KeyRings.HAS_SECRET);
}
}
@@ -187,7 +189,8 @@ public class SelectSecretKeyFragment extends ListFragment implements
// Check if key is viable for our purposes (certify or sign)
if(mFilterCertify) {
// Only enable if can certify
- if (cursor.getInt(mIndexCanCertify) == 0) {
+ if (cursor.getInt(mIndexCanCertify) == 0
+ || cursor.getInt(mIndexHasSecret) == 0) {
h.status.setText(R.string.can_certify_not);
} else {
h.status.setText(R.string.can_certify);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
index 97a2af645..dbd1b7507 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
@@ -23,15 +23,15 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
+import android.support.v4.app.NavUtils;
import android.support.v7.app.ActionBarActivity;
+import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.Toast;
-import com.beardedhen.androidbootstrap.BootstrapButton;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
@@ -44,7 +44,7 @@ import org.sufficientlysecure.keychain.util.Log;
* Sends the selected public key to a keyserver
*/
public class UploadKeyActivity extends ActionBarActivity {
- private BootstrapButton mUploadButton;
+ private View mUploadButton;
private Spinner mKeyServerSpinner;
private Uri mDataUri;
@@ -53,10 +53,10 @@ public class UploadKeyActivity extends ActionBarActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.key_server_export);
+ setContentView(R.layout.upload_key_activity);
- mUploadButton = (BootstrapButton) findViewById(R.id.btn_export_to_server);
- mKeyServerSpinner = (Spinner) findViewById(R.id.sign_key_keyserver);
+ mUploadButton = findViewById(R.id.upload_key_action_upload);
+ mKeyServerSpinner = (Spinner) findViewById(R.id.upload_key_keyserver);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, Preferences.getPreferences(this)
@@ -129,4 +129,17 @@ public class UploadKeyActivity extends ActionBarActivity {
// start service with intent
startService(intent);
}
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home: {
+ Intent viewIntent = NavUtils.getParentActivityIntent(this);
+ viewIntent.setData(KeychainContract.KeyRings.buildGenericKeyRingUri(mDataUri));
+ NavUtils.navigateUpTo(this, viewIntent);
+ return true;
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
index 1d2b50faa..cf7fdcd89 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
@@ -28,7 +28,6 @@ import android.support.v4.content.Loader;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.text.format.DateFormat;
-import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
@@ -73,11 +72,12 @@ public class ViewCertActivity extends ActionBarActivity
private Uri mDataUri;
- private long mSignerKeyId;
+ private long mCertifierKeyId;
- private TextView mSigneeKey, mSigneeUid, mAlgorithm, mType, mRReason, mCreation;
- private TextView mSignerKey, mSignerUid, mStatus;
+ private TextView mSigneeKey, mSigneeUid, mAlgorithm, mType, mReason, mCreation;
+ private TextView mCertifierKey, mCertifierUid, mStatus;
private View mRowReason;
+ private View mViewCertifierButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -93,14 +93,16 @@ public class ViewCertActivity extends ActionBarActivity
mSigneeUid = (TextView) findViewById(R.id.signee_uid);
mAlgorithm = (TextView) findViewById(R.id.algorithm);
mType = (TextView) findViewById(R.id.signature_type);
- mRReason = (TextView) findViewById(R.id.reason);
+ mReason = (TextView) findViewById(R.id.reason);
mCreation = (TextView) findViewById(R.id.creation);
- mSignerKey = (TextView) findViewById(R.id.signer_key_id);
- mSignerUid = (TextView) findViewById(R.id.signer_uid);
+ mCertifierKey = (TextView) findViewById(R.id.signer_key_id);
+ mCertifierUid = (TextView) findViewById(R.id.signer_uid);
mRowReason = findViewById(R.id.row_reason);
+ mViewCertifierButton = findViewById(R.id.view_cert_view_cert_key);
+
mDataUri = getIntent().getData();
if (mDataUri == null) {
Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!");
@@ -121,7 +123,7 @@ public class ViewCertActivity extends ActionBarActivity
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if (data.moveToFirst()) {
- String signeeKey = "0x" + PgpKeyHelper.convertKeyIdToHex(data.getLong(INDEX_MASTER_KEY_ID));
+ String signeeKey = PgpKeyHelper.convertKeyIdToHex(data.getLong(INDEX_MASTER_KEY_ID));
mSigneeKey.setText(signeeKey);
String signeeUid = data.getString(INDEX_USER_ID);
@@ -130,22 +132,24 @@ public class ViewCertActivity extends ActionBarActivity
Date creationDate = new Date(data.getLong(INDEX_CREATION) * 1000);
mCreation.setText(DateFormat.getDateFormat(getApplicationContext()).format(creationDate));
- mSignerKeyId = data.getLong(INDEX_KEY_ID_CERTIFIER);
- String signerKey = "0x" + PgpKeyHelper.convertKeyIdToHex(mSignerKeyId);
- mSignerKey.setText(signerKey);
+ mCertifierKeyId = data.getLong(INDEX_KEY_ID_CERTIFIER);
+ String certifierKey = PgpKeyHelper.convertKeyIdToHex(mCertifierKeyId);
+ mCertifierKey.setText(certifierKey);
- String signerUid = data.getString(INDEX_SIGNER_UID);
- if (signerUid != null) {
- mSignerUid.setText(signerUid);
+ String certifierUid = data.getString(INDEX_SIGNER_UID);
+ if (certifierUid != null) {
+ mCertifierUid.setText(certifierUid);
} else {
- mSignerUid.setText(R.string.unknown_uid);
+ mCertifierUid.setText(R.string.unknown_uid);
}
PGPSignature sig = PgpConversionHelper.BytesToPGPSignature(data.getBlob(INDEX_DATA));
try {
ProviderHelper providerHelper = new ProviderHelper(this);
+
CachedPublicKeyRing signeeRing = providerHelper.getCachedPublicKeyRing(data.getLong(INDEX_MASTER_KEY_ID));
CachedPublicKeyRing signerRing = providerHelper.getCachedPublicKeyRing(sig.getKeyID());
+
try {
signerRing.getSubkey().initSignature(sig);
if (signeeRing.getSubkey().verifySignature(sig, signeeUid)) {
@@ -191,25 +195,39 @@ public class ViewCertActivity extends ActionBarActivity
p = new RevocationReason(false, p.getData());
}
String reason = ((RevocationReason) p).getRevocationDescription();
- mRReason.setText(reason);
+ mReason.setText(reason);
mRowReason.setVisibility(View.VISIBLE);
}
break;
}
}
}
- }
- @Override
- public void onLoaderReset(Loader<Cursor> loader) {
- }
+ // can't do this before the data is initialized
+ mViewCertifierButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent viewIntent = new Intent(ViewCertActivity.this, ViewKeyActivity.class);
+ try {
+ ProviderHelper providerHelper = new ProviderHelper(ViewCertActivity.this);
+ long signerMasterKeyId = providerHelper.getMasterKeyId(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(mCertifierKeyId))
+ );
+ viewIntent.setData(KeyRings.buildGenericKeyRingUri(
+ Long.toString(signerMasterKeyId))
+ );
+ startActivity(viewIntent);
+ } catch (ProviderHelper.NotFoundException e) {
+ // TODO notify user of this, maybe offer download?
+ Log.e(Constants.TAG, "key not found!", e);
+ }
+ }
+ });
+ }
@Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.view_cert, menu);
- return true;
+ public void onLoaderReset(Loader<Cursor> loader) {
}
@Override
@@ -221,25 +239,6 @@ public class ViewCertActivity extends ActionBarActivity
NavUtils.navigateUpTo(this, viewIntent);
return true;
}
- case R.id.menu_view_cert_view_signer:
- // can't do this before the data is initialized
- Intent viewIntent = new Intent(this, ViewKeyActivity.class);
-
- try {
- ProviderHelper providerHelper = new ProviderHelper(this);
- long signerMasterKeyId = providerHelper.getMasterKeyId(
- KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(mSignerKeyId))
- );
- viewIntent.setData(KeyRings.buildGenericKeyRingUri(
- Long.toString(signerMasterKeyId))
- );
- startActivity(viewIntent);
- } catch (ProviderHelper.NotFoundException e) {
- // TODO notify user of this, maybe offer download?
- Log.e(Constants.TAG, "key not found!", e);
- }
-
- return true;
}
return super.onOptionsItemSelected(item);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
index 79f409663..bed116f5f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.ui;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
+import android.database.Cursor;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
@@ -31,41 +32,54 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.View;
import android.view.Window;
import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter;
-import org.sufficientlysecure.keychain.ui.dialog.ShareNfcDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.ShareQrCodeDialogFragment;
+import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.SlidingTabLayout;
import java.io.IOException;
+import java.util.Date;
import java.util.HashMap;
-public class ViewKeyActivity extends ActionBarActivity {
+public class ViewKeyActivity extends ActionBarActivity implements
+ LoaderManager.LoaderCallbacks<Cursor> {
ExportHelper mExportHelper;
ProviderHelper mProviderHelper;
protected Uri mDataUri;
- public static final String EXTRA_SELECTED_TAB = "selectedTab";
+ public static final String EXTRA_SELECTED_TAB = "selected_tab";
+ public static final int TAB_MAIN = 0;
+ public static final int TAB_SHARE = 1;
+ public static final int TAB_KEYS = 2;
+ public static final int TAB_CERTS = 3;
- ViewPager mViewPager;
- TabsAdapter mTabsAdapter;
+ // view
+ private ViewPager mViewPager;
+ private SlidingTabLayout mSlidingTabLayout;
+ private PagerTabStripAdapter mTabsAdapter;
+ private View mStatusDivider;
+ private View mStatusRevoked;
+ private View mStatusExpired;
public static final int REQUEST_CODE_LOOKUP_KEY = 0x00007006;
@@ -76,6 +90,9 @@ public class ViewKeyActivity extends ActionBarActivity {
private byte[] mNfcKeyringBytes;
private static final int NFC_SENT = 1;
+ private static final int LOADER_ID_UNIFIED = 0;
+
+
@Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
@@ -89,33 +106,71 @@ public class ViewKeyActivity extends ActionBarActivity {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setIcon(android.R.color.transparent);
actionBar.setHomeButtonEnabled(true);
- actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
setContentView(R.layout.view_key_activity);
- mViewPager = (ViewPager) findViewById(R.id.pager);
+ mStatusDivider = findViewById(R.id.status_divider);
+ mStatusRevoked = findViewById(R.id.view_key_revoked);
+ mStatusExpired = findViewById(R.id.view_key_expired);
- mTabsAdapter = new TabsAdapter(this, mViewPager);
+ mViewPager = (ViewPager) findViewById(R.id.view_key_pager);
+ mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.view_key_sliding_tab_layout);
- int selectedTab = 0;
+ mTabsAdapter = new PagerTabStripAdapter(this);
+ mViewPager.setAdapter(mTabsAdapter);
+
+ int switchToTab = TAB_MAIN;
Intent intent = getIntent();
if (intent.getExtras() != null && intent.getExtras().containsKey(EXTRA_SELECTED_TAB)) {
- selectedTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB);
+ switchToTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB);
+ }
+
+ Uri dataUri = getIntent().getData();
+ if (dataUri == null) {
+ Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
+ finish();
+ return;
}
- mDataUri = getIntent().getData();
+ loadData(dataUri);
- initNfc(mDataUri);
+ initNfc(dataUri);
Bundle mainBundle = new Bundle();
- mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, mDataUri);
- mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.key_view_tab_main)),
- ViewKeyMainFragment.class, mainBundle, (selectedTab == 0));
+ mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri);
+ mTabsAdapter.addTab(ViewKeyMainFragment.class,
+ mainBundle, getString(R.string.key_view_tab_main));
+
+ Bundle shareBundle = new Bundle();
+ shareBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, dataUri);
+ mTabsAdapter.addTab(ViewKeyShareFragment.class,
+ mainBundle, getString(R.string.key_view_tab_share));
+
+ Bundle keyDetailsBundle = new Bundle();
+ keyDetailsBundle.putParcelable(ViewKeyKeysFragment.ARG_DATA_URI, dataUri);
+ mTabsAdapter.addTab(ViewKeyKeysFragment.class,
+ keyDetailsBundle, getString(R.string.key_view_tab_keys));
Bundle certBundle = new Bundle();
- certBundle.putParcelable(ViewKeyCertsFragment.ARG_DATA_URI, mDataUri);
- mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.key_view_tab_certs)),
- ViewKeyCertsFragment.class, certBundle, (selectedTab == 1));
+ certBundle.putParcelable(ViewKeyCertsFragment.ARG_DATA_URI, dataUri);
+ mTabsAdapter.addTab(ViewKeyCertsFragment.class,
+ certBundle, getString(R.string.key_view_tab_certs));
+
+ // NOTE: must be after adding the tabs!
+ mSlidingTabLayout.setViewPager(mViewPager);
+
+ // switch to tab selected by extra
+ mViewPager.setCurrentItem(switchToTab);
+ }
+
+ private void loadData(Uri dataUri) {
+ mDataUri = dataUri;
+
+ Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
+
+ // Prepare the loaders. Either re-connect with an existing ones,
+ // or start new ones.
+ getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
}
@Override
@@ -143,24 +198,6 @@ public class ViewKeyActivity extends ActionBarActivity {
case R.id.menu_key_view_export_file:
exportToFile(mDataUri, mExportHelper, mProviderHelper);
return true;
- case R.id.menu_key_view_share_default_fingerprint:
- shareKey(mDataUri, true, mProviderHelper);
- return true;
- case R.id.menu_key_view_share_default:
- shareKey(mDataUri, false, mProviderHelper);
- return true;
- case R.id.menu_key_view_share_qr_code_fingerprint:
- shareKeyQrCode(mDataUri, true);
- return true;
- case R.id.menu_key_view_share_qr_code:
- shareKeyQrCode(mDataUri, false);
- return true;
- case R.id.menu_key_view_share_nfc:
- shareNfc();
- return true;
- case R.id.menu_key_view_share_clipboard:
- copyToClipboard(mDataUri, mProviderHelper);
- return true;
case R.id.menu_key_view_delete: {
deleteKey(mDataUri, mExportHelper);
return true;
@@ -209,84 +246,6 @@ public class ViewKeyActivity extends ActionBarActivity {
startActivityForResult(queryIntent, REQUEST_CODE_LOOKUP_KEY);
}
- private void shareKey(Uri dataUri, boolean fingerprintOnly, ProviderHelper providerHelper)
- throws ProviderHelper.NotFoundException {
- String content = null;
- if (fingerprintOnly) {
- byte[] data = (byte[]) providerHelper.getGenericData(
- KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri),
- KeychainContract.Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
- if (data != null) {
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(data);
- content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
- } else {
- AppMsg.makeText(this, "Bad key selected!",
- AppMsg.STYLE_ALERT).show();
- return;
- }
- } else {
- // get public keyring as ascii armored string
- try {
- Uri uri = KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri);
- content = providerHelper.getKeyRingAsArmoredString(uri);
-
- // Android will fail with android.os.TransactionTooLargeException if key is too big
- // see http://www.lonestarprod.com/?p=34
- if (content.length() >= 86389) {
- AppMsg.makeText(this, R.string.key_too_big_for_sharing,
- AppMsg.STYLE_ALERT).show();
- return;
- }
- } catch (IOException e) {
- Log.e(Constants.TAG, "error processing key!", e);
- AppMsg.makeText(this, R.string.error_invalid_data, AppMsg.STYLE_ALERT).show();
- } catch (ProviderHelper.NotFoundException e) {
- Log.e(Constants.TAG, "key not found!", e);
- AppMsg.makeText(this, R.string.error_key_not_found, AppMsg.STYLE_ALERT).show();
- }
- }
-
- if (content != null) {
- // let user choose application
- Intent sendIntent = new Intent(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_TEXT, content);
- sendIntent.setType("text/plain");
- startActivity(Intent.createChooser(sendIntent,
- getResources().getText(R.string.action_share_key_with)));
- } else {
- Log.e(Constants.TAG, "content is null!");
- }
- }
-
- private void shareKeyQrCode(Uri dataUri, boolean fingerprintOnly) {
- ShareQrCodeDialogFragment dialog = ShareQrCodeDialogFragment.newInstance(dataUri,
- fingerprintOnly);
- dialog.show(getSupportFragmentManager(), "shareQrCodeDialog");
- }
-
- private void copyToClipboard(Uri dataUri, ProviderHelper providerHelper) {
- // get public keyring as ascii armored string
- try {
- Uri uri = KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri);
- String keyringArmored = providerHelper.getKeyRingAsArmoredString(uri);
-
- ClipboardReflection.copyToClipboard(this, keyringArmored);
- AppMsg.makeText(this, R.string.key_copied_to_clipboard, AppMsg.STYLE_INFO)
- .show();
- } catch (IOException e) {
- Log.e(Constants.TAG, "error processing key!", e);
- AppMsg.makeText(this, R.string.error_key_processing, AppMsg.STYLE_ALERT).show();
- } catch (ProviderHelper.NotFoundException e) {
- Log.e(Constants.TAG, "key not found!", e);
- AppMsg.makeText(this, R.string.error_key_not_found, AppMsg.STYLE_ALERT).show();
- }
- }
-
- private void shareNfc() {
- ShareNfcDialogFragment dialog = ShareNfcDialogFragment.newInstance();
- dialog.show(getSupportFragmentManager(), "shareNfcDialog");
- }
-
private void deleteKey(Uri dataUri, ExportHelper exportHelper) {
// Message is received after key is deleted
Handler returnHandler = new Handler() {
@@ -409,4 +368,85 @@ public class ViewKeyActivity extends ActionBarActivity {
}
};
+ static final String[] UNIFIED_PROJECTION = new String[]{
+ KeychainContract.KeyRings._ID,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.KeyRings.USER_ID,
+ KeychainContract.KeyRings.IS_REVOKED,
+ KeychainContract.KeyRings.EXPIRY,
+
+ };
+ static final int INDEX_UNIFIED_MASTER_KEY_ID = 1;
+ static final int INDEX_UNIFIED_USER_ID = 2;
+ static final int INDEX_UNIFIED_IS_REVOKED = 3;
+ static final int INDEX_UNIFIED_EXPIRY = 4;
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ switch (id) {
+ case LOADER_ID_UNIFIED: {
+ Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri);
+ return new CursorLoader(this, baseUri, UNIFIED_PROJECTION, null, null, null);
+ }
+
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ /* TODO better error handling? May cause problems when a key is deleted,
+ * because the notification triggers faster than the activity closes.
+ */
+ // Avoid NullPointerExceptions...
+ if (data.getCount() == 0) {
+ return;
+ }
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ switch (loader.getId()) {
+ case LOADER_ID_UNIFIED: {
+ if (data.moveToFirst()) {
+ // get name, email, and comment from USER_ID
+ String[] mainUserId = PgpKeyHelper.splitUserId(data.getString(INDEX_UNIFIED_USER_ID));
+ if (mainUserId[0] != null) {
+ setTitle(mainUserId[0]);
+ } else {
+ setTitle(R.string.user_id_no_name);
+ }
+
+ // get key id from MASTER_KEY_ID
+ long masterKeyId = data.getLong(INDEX_UNIFIED_MASTER_KEY_ID);
+ String keyIdStr = PgpKeyHelper.convertKeyIdToHex(masterKeyId);
+ getSupportActionBar().setSubtitle(keyIdStr);
+
+ // If this key is revoked, it cannot be used for anything!
+ if (data.getInt(INDEX_UNIFIED_IS_REVOKED) != 0) {
+ mStatusDivider.setVisibility(View.VISIBLE);
+ mStatusRevoked.setVisibility(View.VISIBLE);
+ mStatusExpired.setVisibility(View.GONE);
+ } else {
+ mStatusRevoked.setVisibility(View.GONE);
+
+ Date expiryDate = new Date(data.getLong(INDEX_UNIFIED_EXPIRY) * 1000);
+ if (!data.isNull(INDEX_UNIFIED_EXPIRY) && expiryDate.before(new Date())) {
+ mStatusDivider.setVisibility(View.VISIBLE);
+ mStatusExpired.setVisibility(View.VISIBLE);
+ } else {
+ mStatusDivider.setVisibility(View.GONE);
+ mStatusExpired.setVisibility(View.GONE);
+ }
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
index 3c4135715..d5658586d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
@@ -23,7 +23,6 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -46,7 +45,7 @@ import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
-public class ViewKeyCertsFragment extends Fragment
+public class ViewKeyCertsFragment extends LoaderFragment
implements LoaderManager.LoaderCallbacks<Cursor>, AdapterView.OnItemClickListener {
// These are the rows that we will retrieve.
@@ -75,19 +74,23 @@ public class ViewKeyCertsFragment extends Fragment
private Uri mDataUri;
+ // starting with 4 for this fragment
+ private static final int LOADER_ID = 4;
+
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.view_key_certs_fragment, container, false);
+ public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
+ View root = super.onCreateView(inflater, superContainer, savedInstanceState);
+ View view = inflater.inflate(R.layout.view_key_certs_fragment, getContainer());
+
+ mStickyList = (StickyListHeadersListView) view.findViewById(R.id.list);
- return view;
+ return root;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list);
-
if (!getArguments().containsKey(ARG_DATA_URI)) {
Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
getActivity().finish();
@@ -112,11 +115,12 @@ public class ViewKeyCertsFragment extends Fragment
mAdapter = new CertListAdapter(getActivity(), null);
mStickyList.setAdapter(mAdapter);
- getLoaderManager().initLoader(0, null, this);
+ getLoaderManager().initLoader(LOADER_ID, null, this);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ setContentShown(false);
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
return new CursorLoader(getActivity(), mDataUri, PROJECTION, null, null, SORT_ORDER);
@@ -129,6 +133,8 @@ public class ViewKeyCertsFragment extends Fragment
mAdapter.swapCursor(data);
mStickyList.setAdapter(mAdapter);
+
+ setContentShown(true);
}
/**
@@ -208,11 +214,18 @@ public class ViewKeyCertsFragment extends Fragment
// set name and stuff, common to both key types
TextView wSignerKeyId = (TextView) view.findViewById(R.id.signerKeyId);
- TextView wSignerUserId = (TextView) view.findViewById(R.id.signerUserId);
+ TextView wSignerName = (TextView) view.findViewById(R.id.signerName);
TextView wSignStatus = (TextView) view.findViewById(R.id.signStatus);
String signerKeyId = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexSignerKeyId));
- String signerUserId = cursor.getString(mIndexSignerUserId);
+ String[] userId = PgpKeyHelper.splitUserId(cursor.getString(mIndexSignerUserId));
+ if (userId[0] != null) {
+ wSignerName.setText(userId[0]);
+ } else {
+ wSignerName.setText(R.string.user_id_no_name);
+ }
+ wSignerKeyId.setText(signerKeyId);
+
switch (cursor.getInt(mIndexType)) {
case PGPSignature.DEFAULT_CERTIFICATION: // 0x10
wSignStatus.setText(R.string.cert_default);
@@ -231,8 +244,6 @@ public class ViewKeyCertsFragment extends Fragment
break;
}
- wSignerUserId.setText(signerUserId);
- wSignerKeyId.setText(signerKeyId);
view.setTag(R.id.tag_mki, cursor.getLong(mIndexMasterKeyId));
view.setTag(R.id.tag_rank, cursor.getLong(mIndexRank));
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeysFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeysFragment.java
new file mode 100644
index 000000000..fb228f032
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeysFragment.java
@@ -0,0 +1,128 @@
+/*
+ * 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;
+
+import android.database.Cursor;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.text.format.DateFormat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+import org.sufficientlysecure.keychain.ui.adapter.ViewKeyKeysAdapter;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.Date;
+
+
+public class ViewKeyKeysFragment extends LoaderFragment implements
+ LoaderManager.LoaderCallbacks<Cursor> {
+
+ public static final String ARG_DATA_URI = "uri";
+
+ private ListView mKeys;
+
+ private ViewKeyKeysAdapter mKeysAdapter;
+
+ private Uri mDataUri;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
+ View root = super.onCreateView(inflater, superContainer, savedInstanceState);
+ View view = inflater.inflate(R.layout.view_key_keys_fragment, getContainer());
+
+ mKeys = (ListView) view.findViewById(R.id.keys);
+
+ return root;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ Uri dataUri = getArguments().getParcelable(ARG_DATA_URI);
+ if (dataUri == null) {
+ Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
+ getActivity().finish();
+ return;
+ }
+
+ loadData(dataUri);
+ }
+
+ private void loadData(Uri dataUri) {
+ mDataUri = dataUri;
+
+ Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
+
+ mKeysAdapter = new ViewKeyKeysAdapter(getActivity(), null, 0);
+ mKeys.setAdapter(mKeysAdapter);
+
+ // Prepare the loaders. Either re-connect with an existing ones,
+ // or start new ones.
+ getLoaderManager().initLoader(0, null, this);
+ }
+
+ static final String[] KEYS_PROJECTION = new String[] {
+ Keys._ID,
+ Keys.KEY_ID, Keys.RANK, Keys.ALGORITHM, Keys.KEY_SIZE, Keys.HAS_SECRET,
+ Keys.CAN_CERTIFY, Keys.CAN_ENCRYPT, Keys.CAN_SIGN, Keys.IS_REVOKED,
+ Keys.CREATION, Keys.EXPIRY, Keys.FINGERPRINT
+ };
+
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ setContentShown(false);
+ Uri baseUri = Keys.buildKeysUri(mDataUri);
+ return new CursorLoader(getActivity(), baseUri, KEYS_PROJECTION, null, null, null);
+ }
+
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ // Avoid NullPointerExceptions, if we get an empty result set.
+ if(data.getCount() == 0) {
+ return;
+ }
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ mKeysAdapter.swapCursor(data);
+
+ setContentShown(true);
+ }
+
+ /**
+ * This is called when the last Cursor provided to onLoadFinished() above is about to be closed.
+ * We need to make sure we are no longer using it.
+ */
+ public void onLoaderReset(Loader<Cursor> loader) {
+ mKeysAdapter.swapCursor(null);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
index ef4da3010..026417776 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
@@ -19,90 +19,64 @@ package org.sufficientlysecure.keychain.ui;
import android.content.Intent;
import android.database.Cursor;
-import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
-import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.LinearLayout;
import android.widget.ListView;
-import android.widget.TextView;
-import com.beardedhen.androidbootstrap.BootstrapButton;
+import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
+import org.sufficientlysecure.keychain.R;import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.ui.adapter.ViewKeyKeysAdapter;
import org.sufficientlysecure.keychain.ui.adapter.ViewKeyUserIdsAdapter;
import org.sufficientlysecure.keychain.util.Log;
import java.util.Date;
-
-public class ViewKeyMainFragment extends Fragment implements
+public class ViewKeyMainFragment extends LoaderFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
public static final String ARG_DATA_URI = "uri";
- private LinearLayout mContainer;
- private TextView mName;
- private TextView mEmail;
- private TextView mComment;
- private TextView mAlgorithm;
- private TextView mKeyId;
- private TextView mExpiry;
- private TextView mCreation;
- private TextView mFingerprint;
- private TextView mSecretKey;
- private BootstrapButton mActionEdit;
- private BootstrapButton mActionEncrypt;
- private BootstrapButton mActionCertify;
+ private View mActionEdit;
+ private View mActionEditDivider;
+ private View mActionEncrypt;
+ private View mActionCertify;
+ private View mActionCertifyDivider;
private ListView mUserIds;
- private ListView mKeys;
private static final int LOADER_ID_UNIFIED = 0;
private static final int LOADER_ID_USER_IDS = 1;
- private static final int LOADER_ID_KEYS = 2;
+
+ // conservative attitude
+ private boolean mHasEncrypt = true;
private ViewKeyUserIdsAdapter mUserIdsAdapter;
- private ViewKeyKeysAdapter mKeysAdapter;
private Uri mDataUri;
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.view_key_main_fragment, container, false);
-
- mContainer = (LinearLayout) view.findViewById(R.id.container);
- mName = (TextView) view.findViewById(R.id.name);
- mEmail = (TextView) view.findViewById(R.id.email);
- mComment = (TextView) view.findViewById(R.id.comment);
- mKeyId = (TextView) view.findViewById(R.id.key_id);
- mAlgorithm = (TextView) view.findViewById(R.id.algorithm);
- mCreation = (TextView) view.findViewById(R.id.creation);
- mExpiry = (TextView) view.findViewById(R.id.expiry);
- mFingerprint = (TextView) view.findViewById(R.id.fingerprint);
- mSecretKey = (TextView) view.findViewById(R.id.secret_key);
- mUserIds = (ListView) view.findViewById(R.id.user_ids);
- mKeys = (ListView) view.findViewById(R.id.keys);
- mActionEdit = (BootstrapButton) view.findViewById(R.id.action_edit);
- mActionEncrypt = (BootstrapButton) view.findViewById(R.id.action_encrypt);
- mActionCertify = (BootstrapButton) view.findViewById(R.id.action_certify);
-
- return view;
+ public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
+ View root = super.onCreateView(inflater, superContainer, savedInstanceState);
+ View view = inflater.inflate(R.layout.view_key_main_fragment, getContainer());
+
+ mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids);
+ mActionEdit = view.findViewById(R.id.view_key_action_edit);
+ mActionEditDivider = view.findViewById(R.id.view_key_action_edit_divider);
+ mActionEncrypt = view.findViewById(R.id.view_key_action_encrypt);
+ mActionCertify = view.findViewById(R.id.view_key_action_certify);
+ mActionCertifyDivider = view.findViewById(R.id.view_key_action_certify_divider);
+
+ return root;
}
@Override
@@ -120,14 +94,6 @@ public class ViewKeyMainFragment extends Fragment implements
}
private void loadData(Uri dataUri) {
- if (dataUri.equals(mDataUri)) {
- Log.d(Constants.TAG, "Same URI, no need to load the data again!");
- return;
- }
-
- getActivity().setProgressBarIndeterminateVisibility(true);
- mContainer.setVisibility(View.GONE);
-
mDataUri = dataUri;
Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
@@ -135,52 +101,42 @@ public class ViewKeyMainFragment extends Fragment implements
mActionEncrypt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- encryptToContact(mDataUri);
+ encrypt(mDataUri);
}
});
mActionCertify.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
- certifyKey(mDataUri);
+ certify(mDataUri);
+ }
+ });
+ mActionEdit.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View view) {
+ editKey(mDataUri);
}
});
mUserIdsAdapter = new ViewKeyUserIdsAdapter(getActivity(), null, 0);
mUserIds.setAdapter(mUserIdsAdapter);
- mKeysAdapter = new ViewKeyKeysAdapter(getActivity(), null, 0);
- mKeys.setAdapter(mKeysAdapter);
-
// Prepare the loaders. Either re-connect with an existing ones,
// or start new ones.
- getActivity().getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
- getActivity().getSupportLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this);
- getActivity().getSupportLoaderManager().initLoader(LOADER_ID_KEYS, null, this);
+ getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
+ getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this);
}
- static final String[] UNIFIED_PROJECTION = new String[] {
- KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.HAS_ANY_SECRET,
- KeyRings.USER_ID, KeyRings.FINGERPRINT,
- KeyRings.ALGORITHM, KeyRings.KEY_SIZE, KeyRings.CREATION, KeyRings.EXPIRY,
-
+ static final String[] UNIFIED_PROJECTION = new String[]{
+ KeyRings._ID, KeyRings.MASTER_KEY_ID,
+ KeyRings.HAS_ANY_SECRET, KeyRings.IS_REVOKED, KeyRings.EXPIRY, KeyRings.HAS_ENCRYPT
};
- static final int INDEX_UNIFIED_MKI = 1;
+ static final int INDEX_UNIFIED_MASTER_KEY_ID = 1;
static final int INDEX_UNIFIED_HAS_ANY_SECRET = 2;
- static final int INDEX_UNIFIED_UID = 3;
- static final int INDEX_UNIFIED_FINGERPRINT = 4;
- static final int INDEX_UNIFIED_ALGORITHM = 5;
- static final int INDEX_UNIFIED_KEY_SIZE = 6;
- static final int INDEX_UNIFIED_CREATION = 7;
- static final int INDEX_UNIFIED_EXPIRY = 8;
-
- static final String[] KEYS_PROJECTION = new String[] {
- Keys._ID,
- Keys.KEY_ID, Keys.RANK, Keys.ALGORITHM, Keys.KEY_SIZE, Keys.HAS_SECRET,
- Keys.CAN_CERTIFY, Keys.CAN_ENCRYPT, Keys.CAN_SIGN, Keys.IS_REVOKED,
- Keys.CREATION, Keys.EXPIRY, Keys.FINGERPRINT
- };
- static final int KEYS_INDEX_CAN_ENCRYPT = 7;
+ static final int INDEX_UNIFIED_IS_REVOKED = 3;
+ static final int INDEX_UNIFIED_EXPIRY = 4;
+ static final int INDEX_UNIFIED_HAS_ENCRYPT = 5;
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ setContentShown(false);
+
switch (id) {
case LOADER_ID_UNIFIED: {
Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
@@ -188,11 +144,8 @@ public class ViewKeyMainFragment extends Fragment implements
}
case LOADER_ID_USER_IDS: {
Uri baseUri = UserIds.buildUserIdsUri(mDataUri);
- return new CursorLoader(getActivity(), baseUri, ViewKeyUserIdsAdapter.USER_IDS_PROJECTION, null, null, null);
- }
- case LOADER_ID_KEYS: {
- Uri baseUri = Keys.buildKeysUri(mDataUri);
- return new CursorLoader(getActivity(), baseUri, KEYS_PROJECTION, null, null, null);
+ return new CursorLoader(getActivity(), baseUri,
+ ViewKeyUserIdsAdapter.USER_IDS_PROJECTION, null, null, null);
}
default:
@@ -205,7 +158,7 @@ public class ViewKeyMainFragment extends Fragment implements
* because the notification triggers faster than the activity closes.
*/
// Avoid NullPointerExceptions...
- if(data.getCount() == 0) {
+ if (data.getCount() == 0) {
return;
}
// Swap the new cursor in. (The framework will take care of closing the
@@ -213,80 +166,43 @@ public class ViewKeyMainFragment extends Fragment implements
switch (loader.getId()) {
case LOADER_ID_UNIFIED: {
if (data.moveToFirst()) {
- // get name, email, and comment from USER_ID
- String[] mainUserId = PgpKeyHelper.splitUserId(data.getString(INDEX_UNIFIED_UID));
- if (mainUserId[0] != null) {
- getActivity().setTitle(mainUserId[0]);
- mName.setText(mainUserId[0]);
- } else {
- getActivity().setTitle(R.string.user_id_no_name);
- mName.setText(R.string.user_id_no_name);
- }
- mEmail.setText(mainUserId[1]);
- mComment.setText(mainUserId[2]);
-
if (data.getInt(INDEX_UNIFIED_HAS_ANY_SECRET) != 0) {
- mSecretKey.setTextColor(getResources().getColor(R.color.emphasis));
- mSecretKey.setText(R.string.secret_key_yes);
+ // certify button
+ mActionCertify.setVisibility(View.GONE);
+ mActionCertifyDivider.setVisibility(View.GONE);
// edit button
mActionEdit.setVisibility(View.VISIBLE);
- mActionEdit.setOnClickListener(new View.OnClickListener() {
- public void onClick(View view) {
- Intent editIntent = new Intent(getActivity(), EditKeyActivity.class);
- editIntent.setData(
- KeyRingData.buildSecretKeyRingUri(mDataUri));
- editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
- startActivityForResult(editIntent, 0);
- }
- });
+ mActionEditDivider.setVisibility(View.VISIBLE);
} else {
- mSecretKey.setTextColor(Color.BLACK);
- mSecretKey.setText(getResources().getString(R.string.secret_key_no));
-
// certify button
mActionCertify.setVisibility(View.VISIBLE);
+ mActionCertifyDivider.setVisibility(View.VISIBLE);
+
// edit button
mActionEdit.setVisibility(View.GONE);
+ mActionEditDivider.setVisibility(View.GONE);
}
- // get key id from MASTER_KEY_ID
- long masterKeyId = data.getLong(INDEX_UNIFIED_MKI);
- String keyIdStr = PgpKeyHelper.convertKeyIdToHex(masterKeyId);
- mKeyId.setText(keyIdStr);
-
- // get creation date from CREATION
- if (data.isNull(INDEX_UNIFIED_CREATION)) {
- mCreation.setText(R.string.none);
+ // If this key is revoked, it cannot be used for anything!
+ if (data.getInt(INDEX_UNIFIED_IS_REVOKED) != 0) {
+ mActionEdit.setEnabled(false);
+ mActionCertify.setEnabled(false);
+ mActionEncrypt.setEnabled(false);
} else {
- Date creationDate = new Date(data.getLong(INDEX_UNIFIED_CREATION) * 1000);
-
- mCreation.setText(
- DateFormat.getDateFormat(getActivity().getApplicationContext()).format(
- creationDate));
- }
+ mActionEdit.setEnabled(true);
- // get expiry date from EXPIRY
- if (data.isNull(INDEX_UNIFIED_EXPIRY)) {
- mExpiry.setText(R.string.none);
- } else {
Date expiryDate = new Date(data.getLong(INDEX_UNIFIED_EXPIRY) * 1000);
-
- mExpiry.setText(
- DateFormat.getDateFormat(getActivity().getApplicationContext()).format(
- expiryDate));
+ if (!data.isNull(INDEX_UNIFIED_EXPIRY) && expiryDate.before(new Date())) {
+ mActionCertify.setEnabled(false);
+ mActionEncrypt.setEnabled(false);
+ } else {
+ mActionCertify.setEnabled(true);
+ mActionEncrypt.setEnabled(true);
+ }
}
- String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
- getActivity(),
- data.getInt(INDEX_UNIFIED_ALGORITHM),
- data.getInt(INDEX_UNIFIED_KEY_SIZE)
- );
- mAlgorithm.setText(algorithmStr);
-
- byte[] fingerprintBlob = data.getBlob(INDEX_UNIFIED_FINGERPRINT);
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);
- mFingerprint.setText(PgpKeyHelper.colorizeFingerprint(fingerprint));
+ mHasEncrypt = data.getInt(INDEX_UNIFIED_HAS_ENCRYPT) != 0;
break;
}
@@ -296,26 +212,8 @@ public class ViewKeyMainFragment extends Fragment implements
mUserIdsAdapter.swapCursor(data);
break;
- case LOADER_ID_KEYS:
- // hide encrypt button if no encryption key is available
- // TODO: do with subquery!
- boolean canEncrypt = false;
- data.moveToFirst();
- do {
- if (data.getInt(KEYS_INDEX_CAN_ENCRYPT) == 1) {
- canEncrypt = true;
- break;
- }
- } while (data.moveToNext());
- if (!canEncrypt) {
- mActionEncrypt.setVisibility(View.GONE);
- }
-
- mKeysAdapter.swapCursor(data);
- break;
}
- getActivity().setProgressBarIndeterminateVisibility(false);
- mContainer.setVisibility(View.VISIBLE);
+ setContentShown(true);
}
/**
@@ -327,16 +225,18 @@ public class ViewKeyMainFragment extends Fragment implements
case LOADER_ID_USER_IDS:
mUserIdsAdapter.swapCursor(null);
break;
- case LOADER_ID_KEYS:
- mKeysAdapter.swapCursor(null);
- break;
}
}
- private void encryptToContact(Uri dataUri) {
+ private void encrypt(Uri dataUri) {
+ // If there is no encryption key, don't bother.
+ if (!mHasEncrypt) {
+ AppMsg.makeText(getActivity(), R.string.error_no_encrypt_subkey, AppMsg.STYLE_ALERT).show();
+ return;
+ }
try {
long keyId = new ProviderHelper(getActivity()).extractOrGetMasterKeyId(dataUri);
- long[] encryptionKeyIds = new long[]{ keyId };
+ long[] encryptionKeyIds = new long[]{keyId};
Intent intent = new Intent(getActivity(), EncryptActivity.class);
intent.setAction(EncryptActivity.ACTION_ENCRYPT);
intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
@@ -347,10 +247,17 @@ public class ViewKeyMainFragment extends Fragment implements
}
}
- private void certifyKey(Uri dataUri) {
+ private void certify(Uri dataUri) {
Intent signIntent = new Intent(getActivity(), CertifyKeyActivity.class);
signIntent.setData(dataUri);
startActivity(signIntent);
}
+ private void editKey(Uri dataUri) {
+ Intent editIntent = new Intent(getActivity(), EditKeyActivity.class);
+ editIntent.setData(KeychainContract.KeyRingData.buildSecretKeyRingUri(dataUri));
+ editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
+ startActivityForResult(editIntent, 0);
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
new file mode 100644
index 000000000..b3655133d
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
@@ -0,0 +1,308 @@
+/*
+ * 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;
+
+import android.annotation.TargetApi;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.devspark.appmsg.AppMsg;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.dialog.ShareNfcDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.ShareQrCodeDialogFragment;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.QrCodeUtils;
+
+import java.io.IOException;
+
+
+public class ViewKeyShareFragment extends LoaderFragment implements
+ LoaderManager.LoaderCallbacks<Cursor> {
+
+ public static final String ARG_DATA_URI = "uri";
+
+ private TextView mFingerprint;
+ private ImageView mFingerprintQrCode;
+ private View mFingerprintShareButton;
+ private View mFingerprintClipboardButton;
+ private View mKeyShareButton;
+ private View mKeyClipboardButton;
+ private View mNfcHelpButton;
+ private View mNfcPrefsButton;
+
+ ProviderHelper mProviderHelper;
+
+ private static final int QR_CODE_SIZE = 1000;
+
+ private static final int LOADER_ID_UNIFIED = 0;
+
+ private Uri mDataUri;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
+ View root = super.onCreateView(inflater, superContainer, savedInstanceState);
+ View view = inflater.inflate(R.layout.view_key_share_fragment, getContainer());
+
+ mProviderHelper = new ProviderHelper(ViewKeyShareFragment.this.getActivity());
+
+ mFingerprint = (TextView) view.findViewById(R.id.view_key_fingerprint);
+ mFingerprintQrCode = (ImageView) view.findViewById(R.id.view_key_fingerprint_qr_code_image);
+ mFingerprintShareButton = view.findViewById(R.id.view_key_action_fingerprint_share);
+ mFingerprintClipboardButton = view.findViewById(R.id.view_key_action_fingerprint_clipboard);
+ mKeyShareButton = view.findViewById(R.id.view_key_action_key_share);
+ mKeyClipboardButton = view.findViewById(R.id.view_key_action_key_clipboard);
+ mNfcHelpButton = view.findViewById(R.id.view_key_action_nfc_help);
+ mNfcPrefsButton = view.findViewById(R.id.view_key_action_nfc_prefs);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ mNfcPrefsButton.setVisibility(View.VISIBLE);
+ } else {
+ mNfcPrefsButton.setVisibility(View.GONE);
+ }
+
+ mFingerprintQrCode.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showQrCodeDialog();
+ }
+ });
+
+ mFingerprintShareButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ share(mDataUri, mProviderHelper, true, false);
+ }
+ });
+ mFingerprintClipboardButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ share(mDataUri, mProviderHelper, true, true);
+ }
+ });
+ mKeyShareButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ share(mDataUri, mProviderHelper, false, false);
+ }
+ });
+ mKeyClipboardButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ share(mDataUri, mProviderHelper, false, true);
+ }
+ });
+ mNfcHelpButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showNfcHelpDialog();
+ }
+ });
+ mNfcPrefsButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showNfcPrefs();
+ }
+ });
+
+ return root;
+ }
+
+ private void share(Uri dataUri, ProviderHelper providerHelper, boolean fingerprintOnly,
+ boolean toClipboard) {
+ try {
+ String content;
+ if (fingerprintOnly) {
+ byte[] data = (byte[]) providerHelper.getGenericData(
+ KeyRings.buildUnifiedKeyRingUri(dataUri),
+ Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(data);
+ content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
+ } else {
+ // get public keyring as ascii armored string
+ Uri uri = KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri);
+ content = providerHelper.getKeyRingAsArmoredString(uri);
+ }
+
+ if (toClipboard) {
+ ClipboardReflection.copyToClipboard(getActivity(), content);
+ String message;
+ if (fingerprintOnly) {
+ message = getResources().getString(R.string.fingerprint_copied_to_clipboard);
+ } else {
+ message = getResources().getString(R.string.key_copied_to_clipboard);
+ }
+ AppMsg.makeText(getActivity(), message, AppMsg.STYLE_INFO).show();
+ } else {
+ // Android will fail with android.os.TransactionTooLargeException if key is too big
+ // see http://www.lonestarprod.com/?p=34
+ if (content.length() >= 86389) {
+ AppMsg.makeText(getActivity(), R.string.key_too_big_for_sharing,
+ AppMsg.STYLE_ALERT).show();
+ return;
+ }
+
+ // let user choose application
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_TEXT, content);
+ sendIntent.setType("text/plain");
+ String title;
+ if (fingerprintOnly) {
+ title = getResources().getString(R.string.title_share_fingerprint_with);
+ } else {
+ title = getResources().getString(R.string.title_share_key);
+ }
+ startActivity(Intent.createChooser(sendIntent, title));
+ }
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "error processing key!", e);
+ AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show();
+ } catch (ProviderHelper.NotFoundException e) {
+ Log.e(Constants.TAG, "key not found!", e);
+ AppMsg.makeText(getActivity(), R.string.error_key_not_found, AppMsg.STYLE_ALERT).show();
+ }
+ }
+
+ private void showQrCodeDialog() {
+ ShareQrCodeDialogFragment dialog = ShareQrCodeDialogFragment.newInstance(mDataUri);
+ dialog.show(ViewKeyShareFragment.this.getActivity().getSupportFragmentManager(), "shareQrCodeDialog");
+ }
+
+ private void showNfcHelpDialog() {
+ ShareNfcDialogFragment dialog = ShareNfcDialogFragment.newInstance();
+ dialog.show(getActivity().getSupportFragmentManager(), "shareNfcDialog");
+ }
+
+ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ private void showNfcPrefs() {
+ Intent intentSettings = new Intent(
+ Settings.ACTION_NFCSHARING_SETTINGS);
+ startActivity(intentSettings);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ Uri dataUri = getArguments().getParcelable(ARG_DATA_URI);
+ if (dataUri == null) {
+ Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
+ getActivity().finish();
+ return;
+ }
+
+ loadData(dataUri);
+ }
+
+ private void loadData(Uri dataUri) {
+ mDataUri = dataUri;
+
+ Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
+
+ // Prepare the loaders. Either re-connect with an existing ones,
+ // or start new ones.
+ getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
+ }
+
+ static final String[] UNIFIED_PROJECTION = new String[]{
+ KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.HAS_ANY_SECRET,
+ KeyRings.USER_ID, KeyRings.FINGERPRINT,
+ KeyRings.ALGORITHM, KeyRings.KEY_SIZE, KeyRings.CREATION, KeyRings.EXPIRY,
+
+ };
+ static final int INDEX_UNIFIED_MASTER_KEY_ID = 1;
+ static final int INDEX_UNIFIED_HAS_ANY_SECRET = 2;
+ static final int INDEX_UNIFIED_USER_ID = 3;
+ static final int INDEX_UNIFIED_FINGERPRINT = 4;
+ static final int INDEX_UNIFIED_ALGORITHM = 5;
+ static final int INDEX_UNIFIED_KEY_SIZE = 6;
+ static final int INDEX_UNIFIED_CREATION = 7;
+ static final int INDEX_UNIFIED_EXPIRY = 8;
+
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ setContentShown(false);
+ switch (id) {
+ case LOADER_ID_UNIFIED: {
+ Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
+ return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null);
+ }
+
+ default:
+ return null;
+ }
+ }
+
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ /* TODO better error handling? May cause problems when a key is deleted,
+ * because the notification triggers faster than the activity closes.
+ */
+ // Avoid NullPointerExceptions...
+ if (data.getCount() == 0) {
+ return;
+ }
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ switch (loader.getId()) {
+ case LOADER_ID_UNIFIED: {
+ if (data.moveToFirst()) {
+
+ byte[] fingerprintBlob = data.getBlob(INDEX_UNIFIED_FINGERPRINT);
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);
+ mFingerprint.setText(PgpKeyHelper.colorizeFingerprint(fingerprint));
+
+ String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
+ mFingerprintQrCode.setImageBitmap(
+ QrCodeUtils.getQRCodeBitmap(qrCodeContent, QR_CODE_SIZE)
+ );
+
+ break;
+ }
+ }
+
+ }
+ setContentShown(true);
+ }
+
+ /**
+ * This is called when the last Cursor provided to onLoadFinished() above is about to be closed.
+ * We need to make sure we are no longer using it.
+ */
+ public void onLoaderReset(Loader<Cursor> loader) {
+ }
+}
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 f4fa7f3bf..9d323c822 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
@@ -28,10 +28,10 @@ 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.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import java.util.ArrayList;
@@ -106,7 +106,7 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
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.fingerprint = (TextView) convertView.findViewById(R.id.view_key_fingerprint);
holder.algorithm = (TextView) convertView.findViewById(R.id.algorithm);
holder.status = (TextView) convertView.findViewById(R.id.status);
holder.userIdsList = (LinearLayout) convertView.findViewById(R.id.user_ids_list);
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
new file mode 100644
index 000000000..420880522
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListKeybaseLoader.java
@@ -0,0 +1,107 @@
+/*
+ * 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.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.util.Log;
+
+import java.util.ArrayList;
+
+public class ImportKeysListKeybaseLoader
+ extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
+ Context mContext;
+
+ String mKeybaseQuery;
+
+ private ArrayList<ImportKeysListEntry> mEntryList = new ArrayList<ImportKeysListEntry>();
+ private AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper;
+
+ public ImportKeysListKeybaseLoader(Context context, String keybaseQuery) {
+ super(context);
+ mContext = context;
+ mKeybaseQuery = keybaseQuery;
+ }
+
+ @Override
+ public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() {
+
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null);
+
+ if (mKeybaseQuery == null) {
+ Log.e(Constants.TAG, "mKeybaseQery is null!");
+ return mEntryListWrapper;
+ }
+
+ queryServer(mKeybaseQuery);
+
+ 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<ArrayList<ImportKeysListEntry>> data) {
+ super.deliverResult(data);
+ }
+
+ /**
+ * Query keybase
+ */
+ private void queryServer(String query) {
+
+ KeybaseKeyServer server = new KeybaseKeyServer();
+ try {
+ ArrayList<ImportKeysListEntry> searchResult = server.search(query);
+
+ mEntryList.clear();
+
+ mEntryList.addAll(searchResult);
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null);
+ } catch (KeyServer.InsufficientQuery e) {
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e);
+ } catch (KeyServer.QueryException e) {
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e);
+ } catch (KeyServer.TooManyResponses e) {
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e);
+ }
+
+ }
+
+}
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
index 3fd5d5daf..b6c829677 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
@@ -24,6 +24,7 @@ import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
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 838aeefee..4175592d6 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,8 +21,9 @@ 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.keyimport.HkpKeyServer;
+import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
+import org.sufficientlysecure.keychain.keyimport.KeyServer;
import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
@@ -116,13 +117,10 @@ public class ImportKeysListServerLoader
}
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null);
} catch (KeyServer.InsufficientQuery e) {
- Log.e(Constants.TAG, "InsufficientQuery", e);
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e);
} catch (KeyServer.QueryException e) {
- Log.e(Constants.TAG, "QueryException", e);
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e);
} catch (KeyServer.TooManyResponses e) {
- Log.e(Constants.TAG, "TooManyResponses", e);
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e);
}
}
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
index fd864eb09..977740567 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java
@@ -17,7 +17,7 @@
package org.sufficientlysecure.keychain.ui.adapter;
-import android.content.Context;
+import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentPagerAdapter;
@@ -26,8 +26,8 @@ import android.support.v7.app.ActionBarActivity;
import java.util.ArrayList;
public class PagerTabStripAdapter extends FragmentPagerAdapter {
- private final Context mContext;
- private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
+ protected final Activity mActivity;
+ protected final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
static final class TabInfo {
public final Class<?> clss;
@@ -43,7 +43,7 @@ public class PagerTabStripAdapter extends FragmentPagerAdapter {
public PagerTabStripAdapter(ActionBarActivity activity) {
super(activity.getSupportFragmentManager());
- mContext = activity;
+ mActivity = activity;
}
public void addTab(Class<?> clss, Bundle args, String title) {
@@ -60,7 +60,7 @@ public class PagerTabStripAdapter extends FragmentPagerAdapter {
@Override
public Fragment getItem(int position) {
TabInfo info = mTabs.get(position);
- return Fragment.instantiate(mContext, info.clss.getName(), info.args);
+ return Fragment.instantiate(mActivity, info.clss.getName(), info.args);
}
@Override
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
index 9e26e559f..f4942a2a0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java
@@ -121,35 +121,17 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
keyId.setText(keyIdStr);
// may be set with additional "stripped" later on
if (hasAnySecret && cursor.getInt(mIndexHasSecret) == 0) {
- keyDetails.setText("(" + algorithmStr + ", " +
- context.getString(R.string.key_stripped) + ")");
+ keyDetails.setText(algorithmStr + ", " +
+ context.getString(R.string.key_stripped));
} else {
- keyDetails.setText("(" + algorithmStr + ")");
+ 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);
- }
+ // Set icons according to properties
+ masterKeyIcon.setVisibility(cursor.getInt(mIndexRank) == 0 ? View.VISIBLE : View.INVISIBLE);
+ certifyIcon.setVisibility(cursor.getInt(mIndexCanCertify) != 0 ? View.VISIBLE : View.GONE);
+ encryptIcon.setVisibility(cursor.getInt(mIndexCanEncrypt) != 0 ? View.VISIBLE : View.GONE);
+ signIcon.setVisibility(cursor.getInt(mIndexCanSign) != 0 ? View.VISIBLE : View.GONE);
boolean valid = true;
if (cursor.getInt(mIndexRevokedKey) > 0) {
@@ -168,13 +150,13 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
Date expiryDate = new Date(cursor.getLong(mIndexExpiry) * 1000);
valid = valid && expiryDate.after(new Date());
- keyExpiry.setText("(" +
+ keyExpiry.setText(
context.getString(R.string.label_expiry) + ": " +
- DateFormat.getDateFormat(context).format(expiryDate) + ")");
-
- keyExpiry.setVisibility(View.VISIBLE);
+ DateFormat.getDateFormat(context).format(expiryDate));
} else {
- keyExpiry.setVisibility(View.GONE);
+ keyExpiry.setText(
+ context.getString(R.string.label_expiry) + ": " +
+ context.getString(R.string.none));
}
// if key is expired or revoked, strike through text
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
index 52e6dec92..64452e8b4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
@@ -27,6 +27,7 @@ import android.widget.AdapterView;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
@@ -106,46 +107,58 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter implements AdapterView.
@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 vName = (TextView) view.findViewById(R.id.userId);
TextView vAddress = (TextView) view.findViewById(R.id.address);
+ TextView vComment = (TextView) view.findViewById(R.id.comment);
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]);
+ vName.setText(userId[0]);
+ } else {
+ vName.setText(R.string.user_id_no_name);
+ }
+ if (userId[1] != null) {
+ vAddress.setText(userId[1]);
+ vAddress.setVisibility(View.VISIBLE);
} else {
- vUserId.setText(R.string.user_id_no_name);
+ vAddress.setVisibility(View.GONE);
+ }
+ if (userId[2] != null) {
+ vComment.setText(userId[2]);
+ vComment.setVisibility(View.VISIBLE);
+ } else {
+ vComment.setVisibility(View.GONE);
}
- vAddress.setText(userId[1]);
+
+ // show small star icon for primary user ids
+ boolean isPrimary = cursor.getInt(mIsPrimary) != 0;
if (cursor.getInt(mIsRevoked) > 0) {
- vRank.setText(" ");
+
+ // set revocation icon (can this even be primary?)
vVerified.setImageResource(R.drawable.key_certify_revoke);
// disable and strike through text for revoked user ids
- vUserId.setEnabled(false);
+ vName.setEnabled(false);
vAddress.setEnabled(false);
- vUserId.setText(OtherHelper.strikeOutText(vUserId.getText()));
+ vName.setText(OtherHelper.strikeOutText(vName.getText()));
vAddress.setText(OtherHelper.strikeOutText(vAddress.getText()));
} else {
- vUserId.setEnabled(true);
+ vName.setEnabled(true);
vAddress.setEnabled(true);
int verified = cursor.getInt(mVerifiedId);
- // TODO introduce own resources for this :)
switch (verified) {
case Certs.VERIFIED_SECRET:
- vVerified.setImageResource(R.drawable.key_certify_ok_depth0);
+ vVerified.setImageResource(isPrimary
+ ? R.drawable.key_certify_primary_ok_depth0
+ : R.drawable.key_certify_ok_depth0);
break;
case Certs.VERIFIED_SELF:
- vVerified.setImageResource(R.drawable.key_certify_ok_self);
+ vVerified.setImageResource(isPrimary
+ ? R.drawable.key_certify_primary_ok_self
+ : R.drawable.key_certify_ok_self);
break;
default:
vVerified.setImageResource(R.drawable.key_certify_error);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java
index 35e464423..19cf27259 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java
@@ -17,7 +17,6 @@
package org.sufficientlysecure.keychain.ui.dialog;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
@@ -50,7 +49,7 @@ public class BadImportKeyDialogFragment extends DialogFragment {
final FragmentActivity activity = getActivity();
final int badImport = getArguments().getInt(ARG_BAD_IMPORT);
- AlertDialog.Builder alert = new AlertDialog.Builder(activity);
+ CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
alert.setIcon(R.drawable.ic_dialog_alert_holo_light);
alert.setTitle(R.string.warning);
alert.setMessage(activity.getResources()
@@ -63,6 +62,6 @@ public class BadImportKeyDialogFragment extends DialogFragment {
});
alert.setCancelable(true);
- return alert.create();
+ return alert.show();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java
index c71bc160a..6c012cb94 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java
@@ -83,7 +83,7 @@ public class CreateKeyDialogFragment extends DialogFragment {
final int childCount = getArguments().getInt(ARG_EDITOR_CHILD_COUNT);
mInflater = context.getLayoutInflater();
- AlertDialog.Builder dialog = new AlertDialog.Builder(context);
+ CustomAlertDialogBuilder dialog = new CustomAlertDialogBuilder(context);
View view = mInflater.inflate(R.layout.create_key_dialog, null);
dialog.setView(view);
@@ -146,7 +146,7 @@ public class CreateKeyDialogFragment extends DialogFragment {
}
});
- final AlertDialog alertDialog = dialog.create();
+ final AlertDialog alertDialog = dialog.show();
mCustomKeyEditText.addTextChangedListener(new TextWatcher() {
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java
new file mode 100644
index 000000000..4b40b7ef1
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CustomAlertDialogBuilder.java
@@ -0,0 +1,40 @@
+package org.sufficientlysecure.keychain.ui.dialog;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.view.View;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.R;
+
+/** This class extends AlertDiaog.Builder, styling the header using emphasis color.
+ * Note that this class is a huge hack, because dialog boxes aren't easily stylable.
+ * Also, the dialog NEEDS to be called with show() directly, not create(), otherwise
+ * the order of internal operations will lead to a crash!
+ */
+public class CustomAlertDialogBuilder extends AlertDialog.Builder {
+
+ public CustomAlertDialogBuilder(Activity activity) {
+ super(activity);
+ }
+
+ @Override
+ public AlertDialog show() {
+ AlertDialog dialog = super.show();
+
+ int dividerId = dialog.getContext().getResources().getIdentifier("android:id/titleDivider", null, null);
+ View divider = dialog.findViewById(dividerId);
+ if (divider != null) {
+ divider.setBackgroundColor(dialog.getContext().getResources().getColor(R.color.emphasis));
+ }
+
+ int textViewId = dialog.getContext().getResources().getIdentifier("android:id/alertTitle", null, null);
+ TextView tv = (TextView) dialog.findViewById(textViewId);
+ if (tv != null) {
+ tv.setTextColor(dialog.getContext().getResources().getColor(R.color.emphasis));
+ }
+
+ return dialog;
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
index 37dec70cd..b42a79993 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
@@ -17,7 +17,6 @@
package org.sufficientlysecure.keychain.ui.dialog;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
@@ -59,7 +58,7 @@ public class DeleteFileDialogFragment extends DialogFragment {
final String deleteFile = getArguments().getString(ARG_DELETE_FILE);
- AlertDialog.Builder alert = new AlertDialog.Builder(activity);
+ CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
alert.setIcon(R.drawable.ic_dialog_alert_holo_light);
@@ -120,6 +119,6 @@ public class DeleteFileDialogFragment extends DialogFragment {
});
alert.setCancelable(true);
- return alert.create();
+ return alert.show();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
index 93cdef5e3..01d2fae6a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
@@ -17,7 +17,6 @@
package org.sufficientlysecure.keychain.ui.dialog;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
@@ -73,7 +72,7 @@ public class DeleteKeyDialogFragment extends DialogFragment {
final long[] masterKeyIds = getArguments().getLongArray(ARG_DELETE_MASTER_KEY_IDS);
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(activity);
// Setup custom View to display in AlertDialog
LayoutInflater inflater = activity.getLayoutInflater();
@@ -144,7 +143,7 @@ public class DeleteKeyDialogFragment extends DialogFragment {
}
});
- return builder.create();
+ return builder.show();
}
/**
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
index 6a5baf658..24f93bed7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
@@ -97,7 +97,7 @@ public class FileDialogFragment extends DialogFragment {
LayoutInflater inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- AlertDialog.Builder alert = new AlertDialog.Builder(activity);
+ CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
alert.setTitle(title);
View view = inflater.inflate(R.layout.file_dialog, null);
@@ -157,7 +157,7 @@ public class FileDialogFragment extends DialogFragment {
dismiss();
}
});
- return alert.create();
+ return alert.show();
}
/**
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
index 9b5581cc7..cc653e58c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
@@ -134,7 +134,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
final long secretKeyId = getArguments().getLong(ARG_SECRET_KEY_ID);
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
- AlertDialog.Builder alert = new AlertDialog.Builder(activity);
+ CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
alert.setTitle(R.string.title_authentication);
@@ -243,7 +243,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
});
mCanKB = true;
- return alert.create();
+ return alert.show();
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
index e5db22a04..04bec3282 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
@@ -81,7 +81,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
int title = getArguments().getInt(ARG_TITLE);
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
- AlertDialog.Builder alert = new AlertDialog.Builder(activity);
+ CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
alert.setTitle(title);
alert.setMessage(R.string.enter_passphrase_twice);
@@ -135,7 +135,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
}
});
- return alert.create();
+ return alert.show();
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java
index 15df53dcc..961f92f03 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java
@@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.ui.dialog;
import android.annotation.TargetApi;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
@@ -51,7 +50,7 @@ public class ShareNfcDialogFragment extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
final FragmentActivity activity = getActivity();
- AlertDialog.Builder alert = new AlertDialog.Builder(activity);
+ CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
alert.setTitle(R.string.share_nfc_dialog);
alert.setCancelable(true);
@@ -78,7 +77,7 @@ public class ShareNfcDialogFragment extends DialogFragment {
+ getString(R.string.error_nfc_needed));
} else {
// nfc works...
- textView.setHtmlFromRawResource(getActivity(), R.raw.nfc_beam_share);
+ textView.setHtmlFromRawResource(getActivity(), R.raw.nfc_beam_share, true);
alert.setNegativeButton(R.string.menu_beam_preferences,
new DialogInterface.OnClickListener() {
@@ -93,6 +92,6 @@ public class ShareNfcDialogFragment extends DialogFragment {
}
}
- return alert.create();
+ return alert.show();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
index fe50c759b..24608784b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
@@ -18,14 +18,12 @@
package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Activity;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
@@ -34,37 +32,26 @@ import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.QrCodeUtils;
-import java.io.IOException;
-import java.util.ArrayList;
-
public class ShareQrCodeDialogFragment extends DialogFragment {
private static final String ARG_KEY_URI = "uri";
- private static final String ARG_FINGERPRINT_ONLY = "fingerprint_only";
private ImageView mImage;
private TextView mText;
- private boolean mFingerprintOnly;
-
- private ArrayList<String> mContentList;
- private int mCounter;
-
private static final int QR_CODE_SIZE = 1000;
/**
* Creates new instance of this dialog fragment
*/
- public static ShareQrCodeDialogFragment newInstance(Uri dataUri, boolean fingerprintOnly) {
+ public static ShareQrCodeDialogFragment newInstance(Uri dataUri) {
ShareQrCodeDialogFragment frag = new ShareQrCodeDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_KEY_URI, dataUri);
- args.putBoolean(ARG_FINGERPRINT_ONLY, fingerprintOnly);
frag.setArguments(args);
@@ -79,9 +66,8 @@ public class ShareQrCodeDialogFragment extends DialogFragment {
final Activity activity = getActivity();
Uri dataUri = getArguments().getParcelable(ARG_KEY_URI);
- mFingerprintOnly = getArguments().getBoolean(ARG_FINGERPRINT_ONLY);
- AlertDialog.Builder alert = new AlertDialog.Builder(getActivity());
+ CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(getActivity());
alert.setTitle(R.string.share_qr_code_dialog_title);
LayoutInflater inflater = activity.getLayoutInflater();
@@ -94,131 +80,32 @@ public class ShareQrCodeDialogFragment extends DialogFragment {
ProviderHelper providerHelper = new ProviderHelper(getActivity());
String content;
try {
- if (mFingerprintOnly) {
- alert.setPositiveButton(R.string.btn_okay, null);
-
- byte[] blob = (byte[]) providerHelper.getGenericData(
- KeyRings.buildUnifiedKeyRingUri(dataUri),
- KeyRings.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
- if (blob == null) {
- Log.e(Constants.TAG, "key not found!");
- AppMsg.makeText(getActivity(), R.string.error_key_not_found, AppMsg.STYLE_ALERT).show();
- return null;
- }
-
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(blob);
- mText.setText(getString(R.string.share_qr_code_dialog_fingerprint_text) + " " + fingerprint);
- content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
- setQrCode(content);
- } else {
- mText.setText(R.string.share_qr_code_dialog_start);
-
- try {
- Uri uri = KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri);
- content = providerHelper.getKeyRingAsArmoredString(uri);
- } catch (IOException e) {
- Log.e(Constants.TAG, "error processing key!", e);
- AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_ALERT).show();
- return null;
- }
-
- // OnClickListener are set in onResume to prevent automatic dismissing of Dialogs
- // http://bit.ly/O5vfaR
- alert.setPositiveButton(R.string.btn_next, null);
- alert.setNegativeButton(android.R.string.cancel, null);
-
- mContentList = splitString(content, 1000);
-
- // start with first
- mCounter = 0;
- updatePartsQrCode();
+ alert.setPositiveButton(R.string.btn_okay, null);
+
+ byte[] blob = (byte[]) providerHelper.getGenericData(
+ KeyRings.buildUnifiedKeyRingUri(dataUri),
+ KeyRings.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
+ if (blob == null) {
+ Log.e(Constants.TAG, "key not found!");
+ AppMsg.makeText(getActivity(), R.string.error_key_not_found, AppMsg.STYLE_ALERT).show();
+ return null;
}
+
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(blob);
+ mText.setText(getString(R.string.share_qr_code_dialog_fingerprint_text) + " " + fingerprint);
+ content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
+ setQrCode(content);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
AppMsg.makeText(getActivity(), R.string.error_key_not_found, AppMsg.STYLE_ALERT).show();
return null;
}
- return alert.create();
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- if (!mFingerprintOnly) {
- AlertDialog alertDialog = (AlertDialog) getDialog();
- final Button backButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
- final Button nextButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
-
- backButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mCounter > 0) {
- mCounter--;
- updatePartsQrCode();
- updateDialog(backButton, nextButton);
- } else {
- dismiss();
- }
- }
- });
- nextButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
-
- if (mCounter < mContentList.size() - 1) {
- mCounter++;
- updatePartsQrCode();
- updateDialog(backButton, nextButton);
- } else {
- dismiss();
- }
- }
- });
- }
- }
-
- private void updatePartsQrCode() {
- // Content: <counter>,<size>,<content>
- setQrCode(mCounter + "," + mContentList.size() + "," + mContentList.get(mCounter));
+ return alert.show();
}
private void setQrCode(String data) {
mImage.setImageBitmap(QrCodeUtils.getQRCodeBitmap(data, QR_CODE_SIZE));
}
- private void updateDialog(Button backButton, Button nextButton) {
- if (mCounter == 0) {
- backButton.setText(android.R.string.cancel);
- } else {
- backButton.setText(R.string.btn_back);
- }
- if (mCounter == mContentList.size() - 1) {
- nextButton.setText(android.R.string.ok);
- } else {
- nextButton.setText(R.string.btn_next);
- }
-
- mText.setText(getResources().getString(R.string.share_qr_code_dialog_progress,
- mCounter + 1, mContentList.size()));
- }
-
- /**
- * Split String by number of characters
- *
- * @param text
- * @param size
- * @return
- */
- private ArrayList<String> splitString(String text, int size) {
- ArrayList<String> strings = new ArrayList<String>();
- int index = 0;
- while (index < text.length()) {
- strings.add(text.substring(index, Math.min(index + size, text.length())));
- index += size;
- }
-
- return strings;
- }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java
deleted file mode 100644
index 937a48e48..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2013 Eric Frohnhoefer
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.sufficientlysecure.keychain.ui.widget;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.widget.TextView;
-
-/**
- * Copied from StickyListHeaders lib example
- *
- * @author Eric Frohnhoefer
- */
-public class UnderlineTextView extends TextView {
- private final Paint mPaint = new Paint();
- private int mUnderlineHeight = 0;
-
- public UnderlineTextView(Context context) {
- this(context, null);
- }
-
- public UnderlineTextView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public UnderlineTextView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- init(context, attrs);
- }
-
- private void init(Context context, AttributeSet attrs) {
- Resources r = getResources();
- mUnderlineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2,
- r.getDisplayMetrics());
- }
-
- @Override
- public void setPadding(int left, int top, int right, int bottom) {
- super.setPadding(left, top, right, bottom + mUnderlineHeight);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- // Draw the underline the same color as the text
- mPaint.setColor(getTextColors().getDefaultColor());
- canvas.drawRect(0, getHeight() - mUnderlineHeight, getWidth(), getHeight(), mPaint);
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java
index 5793d7438..f89ffd139 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java
@@ -19,6 +19,9 @@ package org.sufficientlysecure.keychain.util;
import java.io.InputStream;
+/**
+ * Wrapper to include size besides an InputStream
+ */
public class InputData {
private PositionAwareInputStream mInputStream;
private long mSize;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java
index ad1aab146..68b402124 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java
@@ -22,6 +22,8 @@ import android.support.v4.app.Fragment;
import com.google.zxing.integration.android.IntentIntegrator;
/**
+ * Copied from older Barcode Scanner Integration library
+ *
* IntentIntegrator for the V4 Android compatibility package.
*
* @author Lachezar Dobrev
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java
new file mode 100644
index 000000000..7ae1d8fab
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 Tim Bray <tbray@textuality.com>
+ *
+ * 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 org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Minimal hierarchy selector
+ */
+public class JWalk {
+
+ public static int getInt(JSONObject json, String... path) throws JSONException {
+ json = walk(json, path);
+ return json.getInt(path[path.length - 1]);
+ }
+
+ public static long getLong(JSONObject json, String... path) throws JSONException {
+ json = walk(json, path);
+ return json.getLong(path[path.length - 1]);
+ }
+
+ public static String getString(JSONObject json, String... path) throws JSONException {
+ json = walk(json, path);
+ return json.getString(path[path.length - 1]);
+ }
+
+ public static JSONArray getArray(JSONObject json, String... path) throws JSONException {
+ json = walk(json, path);
+ return json.getJSONArray(path[path.length - 1]);
+ }
+
+ public static JSONObject optObject(JSONObject json, String... path) throws JSONException {
+ json = walk(json, path);
+ return json.optJSONObject(path[path.length - 1]);
+ }
+
+ private static JSONObject walk(JSONObject json, String... path) throws JSONException {
+ int len = path.length - 1;
+ int pathIndex = 0;
+ try {
+ while (pathIndex < len) {
+ json = json.getJSONObject(path[pathIndex]);
+ pathIndex++;
+ }
+ } catch (JSONException e) {
+ // try to give ’em a nice-looking error
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < len; i++) {
+ sb.append(path[i]).append('.');
+ }
+ sb.append(path[len]);
+ throw new JSONException("JWalk error at step " + pathIndex + " of " + sb);
+ }
+ return json;
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java
deleted file mode 100644
index b205bd556..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java
+++ /dev/null
@@ -1,22 +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.util;
-
-public interface KeychainServiceListener {
- boolean hasServiceStopped();
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java
index cd9e56394..28e567d76 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java
@@ -32,6 +32,9 @@ import org.sufficientlysecure.keychain.Constants;
import java.util.Hashtable;
+/**
+ * Copied from Bitcoin Wallet
+ */
public class QrCodeUtils {
public static final QRCodeWriter QR_CODE_WRITER = new QRCodeWriter();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SlidingTabLayout.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SlidingTabLayout.java
new file mode 100644
index 000000000..065034be1
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SlidingTabLayout.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.util;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.HorizontalScrollView;
+import android.widget.TextView;
+
+/**
+ * Copied from http://developer.android.com/samples/SlidingTabsColors/index.html
+ */
+
+/**
+ * To be used with ViewPager to provide a tab indicator component which give constant feedback as to
+ * the user's scroll progress.
+ * <p/>
+ * To use the component, simply add it to your view hierarchy. Then in your
+ * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
+ * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
+ * <p/>
+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors
+ * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
+ * alternative is via the {@link TabColorizer} interface which provides you complete control over
+ * which color is used for any individual position.
+ * <p/>
+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
+ * providing the layout ID of your custom layout.
+ */
+public class SlidingTabLayout extends HorizontalScrollView {
+
+ /**
+ * Allows complete control over the colors drawn in the tab layout. Set with
+ * {@link #setCustomTabColorizer(TabColorizer)}.
+ */
+ public interface TabColorizer {
+
+ /**
+ * @return return the color of the indicator used when {@code position} is selected.
+ */
+ int getIndicatorColor(int position);
+
+ /**
+ * @return return the color of the divider drawn to the right of {@code position}.
+ */
+ int getDividerColor(int position);
+
+ }
+
+ private static final int TITLE_OFFSET_DIPS = 24;
+ private static final int TAB_VIEW_PADDING_DIPS = 16;
+ private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
+
+ private int mTitleOffset;
+
+ private int mTabViewLayoutId;
+ private int mTabViewTextViewId;
+
+ private ViewPager mViewPager;
+ private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
+
+ private final SlidingTabStrip mTabStrip;
+
+ public SlidingTabLayout(Context context) {
+ this(context, null);
+ }
+
+ public SlidingTabLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ // Disable the Scroll Bar
+ setHorizontalScrollBarEnabled(false);
+ // Make sure that the Tab Strips fills this View
+ setFillViewport(true);
+
+ mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
+
+ mTabStrip = new SlidingTabStrip(context);
+ addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ }
+
+ /**
+ * Set the custom {@link TabColorizer} to be used.
+ * <p/>
+ * If you only require simple custmisation then you can use
+ * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
+ * similar effects.
+ */
+ public void setCustomTabColorizer(TabColorizer tabColorizer) {
+ mTabStrip.setCustomTabColorizer(tabColorizer);
+ }
+
+ /**
+ * Sets the colors to be used for indicating the selected tab. These colors are treated as a
+ * circular array. Providing one color will mean that all tabs are indicated with the same color.
+ */
+ public void setSelectedIndicatorColors(int... colors) {
+ mTabStrip.setSelectedIndicatorColors(colors);
+ }
+
+ /**
+ * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
+ * Providing one color will mean that all tabs are indicated with the same color.
+ */
+ public void setDividerColors(int... colors) {
+ mTabStrip.setDividerColors(colors);
+ }
+
+ /**
+ * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
+ * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
+ * that the layout can update it's scroll position correctly.
+ *
+ * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
+ */
+ public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
+ mViewPagerPageChangeListener = listener;
+ }
+
+ /**
+ * Set the custom layout to be inflated for the tab views.
+ *
+ * @param layoutResId Layout id to be inflated
+ * @param textViewId id of the {@link TextView} in the inflated view
+ */
+ public void setCustomTabView(int layoutResId, int textViewId) {
+ mTabViewLayoutId = layoutResId;
+ mTabViewTextViewId = textViewId;
+ }
+
+ /**
+ * Sets the associated view pager. Note that the assumption here is that the pager content
+ * (number of tabs and tab titles) does not change after this call has been made.
+ */
+ public void setViewPager(ViewPager viewPager) {
+ mTabStrip.removeAllViews();
+
+ mViewPager = viewPager;
+ if (viewPager != null) {
+ viewPager.setOnPageChangeListener(new InternalViewPagerListener());
+ populateTabStrip();
+ }
+ }
+
+ /**
+ * Create a default view to be used for tabs. This is called if a custom tab view is not set via
+ * {@link #setCustomTabView(int, int)}.
+ */
+ protected TextView createDefaultTabView(Context context) {
+ TextView textView = new TextView(context);
+ textView.setGravity(Gravity.CENTER);
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
+ textView.setTypeface(Typeface.DEFAULT_BOLD);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ // If we're running on Honeycomb or newer, then we can use the Theme's
+ // selectableItemBackground to ensure that the View has a pressed state
+ TypedValue outValue = new TypedValue();
+ getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
+ outValue, true);
+ textView.setBackgroundResource(outValue.resourceId);
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
+ textView.setAllCaps(true);
+ }
+
+ int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
+ textView.setPadding(padding, padding, padding, padding);
+
+ return textView;
+ }
+
+ private void populateTabStrip() {
+ final PagerAdapter adapter = mViewPager.getAdapter();
+ final View.OnClickListener tabClickListener = new TabClickListener();
+
+ for (int i = 0; i < adapter.getCount(); i++) {
+ View tabView = null;
+ TextView tabTitleView = null;
+
+ if (mTabViewLayoutId != 0) {
+ // If there is a custom tab view layout id set, try and inflate it
+ tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
+ false);
+ tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
+ }
+
+ if (tabView == null) {
+ tabView = createDefaultTabView(getContext());
+ }
+
+ if (tabTitleView == null && TextView.class.isInstance(tabView)) {
+ tabTitleView = (TextView) tabView;
+ }
+
+ tabTitleView.setText(adapter.getPageTitle(i));
+ tabView.setOnClickListener(tabClickListener);
+
+ mTabStrip.addView(tabView);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ if (mViewPager != null) {
+ scrollToTab(mViewPager.getCurrentItem(), 0);
+ }
+ }
+
+ private void scrollToTab(int tabIndex, int positionOffset) {
+ final int tabStripChildCount = mTabStrip.getChildCount();
+ if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
+ return;
+ }
+
+ View selectedChild = mTabStrip.getChildAt(tabIndex);
+ if (selectedChild != null) {
+ int targetScrollX = selectedChild.getLeft() + positionOffset;
+
+ if (tabIndex > 0 || positionOffset > 0) {
+ // If we're not at the first child and are mid-scroll, make sure we obey the offset
+ targetScrollX -= mTitleOffset;
+ }
+
+ scrollTo(targetScrollX, 0);
+ }
+ }
+
+ private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
+ private int mScrollState;
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ int tabStripChildCount = mTabStrip.getChildCount();
+ if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
+ return;
+ }
+
+ mTabStrip.onViewPagerPageChanged(position, positionOffset);
+
+ View selectedTitle = mTabStrip.getChildAt(position);
+ int extraOffset = (selectedTitle != null)
+ ? (int) (positionOffset * selectedTitle.getWidth())
+ : 0;
+ scrollToTab(position, extraOffset);
+
+ if (mViewPagerPageChangeListener != null) {
+ mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
+ positionOffsetPixels);
+ }
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ mScrollState = state;
+
+ if (mViewPagerPageChangeListener != null) {
+ mViewPagerPageChangeListener.onPageScrollStateChanged(state);
+ }
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
+ mTabStrip.onViewPagerPageChanged(position, 0f);
+ scrollToTab(position, 0);
+ }
+
+ if (mViewPagerPageChangeListener != null) {
+ mViewPagerPageChangeListener.onPageSelected(position);
+ }
+ }
+
+ }
+
+ private class TabClickListener implements View.OnClickListener {
+ @Override
+ public void onClick(View v) {
+ for (int i = 0; i < mTabStrip.getChildCount(); i++) {
+ if (v == mTabStrip.getChildAt(i)) {
+ mViewPager.setCurrentItem(i);
+ return;
+ }
+ }
+ }
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SlidingTabStrip.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SlidingTabStrip.java
new file mode 100644
index 000000000..9ce6f943e
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/SlidingTabStrip.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.util;
+
+import android.R;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * Copied from http://developer.android.com/samples/SlidingTabsColors/index.html
+ */
+class SlidingTabStrip extends LinearLayout {
+
+ private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
+ private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
+ private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
+ private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFFAA66CC;
+
+ private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
+ private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
+ private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
+
+ private final int mBottomBorderThickness;
+ private final Paint mBottomBorderPaint;
+
+ private final int mSelectedIndicatorThickness;
+ private final Paint mSelectedIndicatorPaint;
+
+ private final int mDefaultBottomBorderColor;
+
+ private final Paint mDividerPaint;
+ private final float mDividerHeight;
+
+ private int mSelectedPosition;
+ private float mSelectionOffset;
+
+ private SlidingTabLayout.TabColorizer mCustomTabColorizer;
+ private final SimpleTabColorizer mDefaultTabColorizer;
+
+ SlidingTabStrip(Context context) {
+ this(context, null);
+ }
+
+ SlidingTabStrip(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setWillNotDraw(false);
+
+ final float density = getResources().getDisplayMetrics().density;
+
+ TypedValue outValue = new TypedValue();
+ context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
+ final int themeForegroundColor = outValue.data;
+
+ mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
+ DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
+
+ mDefaultTabColorizer = new SimpleTabColorizer();
+ mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
+ mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
+ DEFAULT_DIVIDER_COLOR_ALPHA));
+
+ mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
+ mBottomBorderPaint = new Paint();
+ mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
+
+ mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
+ mSelectedIndicatorPaint = new Paint();
+
+ mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
+ mDividerPaint = new Paint();
+ mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
+ }
+
+ void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
+ mCustomTabColorizer = customTabColorizer;
+ invalidate();
+ }
+
+ void setSelectedIndicatorColors(int... colors) {
+ // Make sure that the custom colorizer is removed
+ mCustomTabColorizer = null;
+ mDefaultTabColorizer.setIndicatorColors(colors);
+ invalidate();
+ }
+
+ void setDividerColors(int... colors) {
+ // Make sure that the custom colorizer is removed
+ mCustomTabColorizer = null;
+ mDefaultTabColorizer.setDividerColors(colors);
+ invalidate();
+ }
+
+ void onViewPagerPageChanged(int position, float positionOffset) {
+ mSelectedPosition = position;
+ mSelectionOffset = positionOffset;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ final int height = getHeight();
+ final int childCount = getChildCount();
+ final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
+ final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
+ ? mCustomTabColorizer
+ : mDefaultTabColorizer;
+
+ // Thick colored underline below the current selection
+ if (childCount > 0) {
+ View selectedTitle = getChildAt(mSelectedPosition);
+ int left = selectedTitle.getLeft();
+ int right = selectedTitle.getRight();
+ int color = tabColorizer.getIndicatorColor(mSelectedPosition);
+
+ if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
+ int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
+ if (color != nextColor) {
+ color = blendColors(nextColor, color, mSelectionOffset);
+ }
+
+ // Draw the selection partway between the tabs
+ View nextTitle = getChildAt(mSelectedPosition + 1);
+ left = (int) (mSelectionOffset * nextTitle.getLeft() +
+ (1.0f - mSelectionOffset) * left);
+ right = (int) (mSelectionOffset * nextTitle.getRight() +
+ (1.0f - mSelectionOffset) * right);
+ }
+
+ mSelectedIndicatorPaint.setColor(color);
+
+ canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
+ height, mSelectedIndicatorPaint);
+ }
+
+ // Thin underline along the entire bottom edge
+ canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
+
+ // Vertical separators between the titles
+ int separatorTop = (height - dividerHeightPx) / 2;
+ for (int i = 0; i < childCount - 1; i++) {
+ View child = getChildAt(i);
+ mDividerPaint.setColor(tabColorizer.getDividerColor(i));
+ canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
+ separatorTop + dividerHeightPx, mDividerPaint);
+ }
+ }
+
+ /**
+ * Set the alpha value of the {@code color} to be the given {@code alpha} value.
+ */
+ private static int setColorAlpha(int color, byte alpha) {
+ return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
+ }
+
+ /**
+ * Blend {@code color1} and {@code color2} using the given ratio.
+ *
+ * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
+ * 0.0 will return {@code color2}.
+ */
+ private static int blendColors(int color1, int color2, float ratio) {
+ final float inverseRation = 1f - ratio;
+ float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
+ float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
+ float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
+ return Color.rgb((int) r, (int) g, (int) b);
+ }
+
+ private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
+ private int[] mIndicatorColors;
+ private int[] mDividerColors;
+
+ @Override
+ public final int getIndicatorColor(int position) {
+ return mIndicatorColors[position % mIndicatorColors.length];
+ }
+
+ @Override
+ public final int getDividerColor(int position) {
+ return mDividerColors[position % mDividerColors.length];
+ }
+
+ void setIndicatorColors(int... colors) {
+ mIndicatorColors = colors;
+ }
+
+ void setDividerColors(int... colors) {
+ mDividerColors = colors;
+ }
+ }
+} \ No newline at end of file