From f29280bbb268d112426c6662e1227118819fb904 Mon Sep 17 00:00:00 2001
From: Adithya Abraham Philip <adithyaphilip@gmail.com>
Date: Wed, 25 Nov 2015 01:35:41 +0530
Subject: added Facebook links support, reworked Preferences

---
 OpenKeychain/src/main/AndroidManifest.xml          |  16 ++
 .../keychain/keyimport/CloudSearch.java            |   3 +
 .../keychain/keyimport/FacebookKeyserver.java      | 197 ++++++++++++++++++
 .../keychain/keyimport/ImportKeysList.java         |  12 +-
 .../keychain/keyimport/ImportKeysListEntry.java    |  25 ++-
 .../keychain/keyimport/KeybaseKeyserver.java       |   5 +-
 .../keychain/keyimport/Keyserver.java              |   1 -
 .../keychain/keyimport/ParcelableKeyRing.java      |  27 ++-
 .../keychain/operations/ImportOperation.java       |  46 +++++
 .../operations/results/OperationResult.java        |  13 ++
 .../service/KeyserverSyncAdapterService.java       |   2 +-
 .../ui/CreateYubiKeyImportResetFragment.java       |   9 +-
 .../keychain/ui/DecryptFragment.java               |   9 +-
 .../keychain/ui/DecryptListFragment.java           |  10 +-
 .../keychain/ui/ImportKeysActivity.java            |  94 +++++----
 .../keychain/ui/ImportKeysCloudFragment.java       |  37 ++--
 .../keychain/ui/ImportKeysListFragment.java        |  45 ++--
 .../keychain/ui/ImportKeysProxyActivity.java       |  11 +-
 .../keychain/ui/KeyListFragment.java               |   9 +-
 .../keychain/ui/SettingsActivity.java              | 228 +++++----------------
 .../keychain/ui/ViewKeyActivity.java               |  10 +-
 .../keychain/util/EmailKeyHelper.java              |   2 +-
 .../keychain/util/Preferences.java                 | 100 +++++----
 OpenKeychain/src/main/res/values/strings.xml       |   3 +
 .../src/main/res/xml/experimental_preferences.xml  |  10 +-
 .../src/main/res/xml/passphrase_preferences.xml    |   4 +-
 26 files changed, 554 insertions(+), 374 deletions(-)
 create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java

(limited to 'OpenKeychain/src')

diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml
index 337ad73e0..1ac9eb257 100644
--- a/OpenKeychain/src/main/AndroidManifest.xml
+++ b/OpenKeychain/src/main/AndroidManifest.xml
@@ -681,6 +681,22 @@
                 <data android:pathPattern="/pks/lookup.*" />
             </intent-filter>
 
+            <!-- VIEW from facebook public key urls opened in a browser -->
+            <intent-filter android:label="@string/intent_import_key">
+                <action android:name="android.intent.action.VIEW" />
+
+                <category android:name="android.intent.category.BROWSABLE" />
+                <category android:name="android.intent.category.DEFAULT" />
+
+                <data android:scheme="https" />
+                <data android:scheme="http" />
+
+                <data android:host="www.facebook.com" />
+                <data android:host="facebook.com" />
+
+                <data android:pathPattern="/..*/publickey/download" />
+            </intent-filter>
+
             <!-- IMPORT_KEY with files TODO: does this work? -->
             <intent-filter android:label="@string/intent_import_key">
                 <action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY" />
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java
index 869d107ab..df45de11f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java
@@ -48,6 +48,9 @@ public class CloudSearch {
         if (cloudPrefs.searchKeybase) {
             servers.add(new KeybaseKeyserver(proxy));
         }
+        if (cloudPrefs.searchFacebook) {
+            servers.add(new FacebookKeyserver(proxy));
+        }
         final ImportKeysList results = new ImportKeysList(servers.size());
 
         ArrayList<Thread> searchThreads = new ArrayList<>();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java
new file mode 100644
index 000000000..d87a82a24
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.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 android.net.Uri;
+import android.support.annotation.NonNull;
+
+import com.squareup.okhttp.OkHttpClient;
+import com.squareup.okhttp.Request;
+import com.squareup.okhttp.Response;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.PgpHelper;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.IOException;
+import java.net.Proxy;
+
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+
+public class FacebookKeyserver extends Keyserver {
+
+    private static final String FB_KEY_URL_FORMAT
+            = "https://www.facebook.com/%s/publickey/download";
+    private static final String FB_HOST = "facebook.com";
+    private static final String FB_HOST_WWW = "www." + FB_HOST;
+
+    public static final String FB_URL = "https://" + FB_HOST_WWW;
+
+    public static final String ORIGIN = FB_URL;
+
+    private final Proxy mProxy;
+
+    public FacebookKeyserver(Proxy proxy) {
+        mProxy = proxy;
+    }
+
+    @Override
+    public List<ImportKeysListEntry> search(String fbUsername)
+            throws QueryFailedException, QueryNeedsRepairException {
+        List<ImportKeysListEntry> entry = new ArrayList<>(1);
+
+        String data = get(fbUsername);
+        // if we're here that means key retrieval succeeded,
+        // would have thrown an exception otherwise
+        try {
+            UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(data.getBytes());
+            try {
+                entry.add(getEntry(keyRing, fbUsername));
+            } catch (UnsupportedOperationException e) {
+                Log.e(Constants.TAG, "Parsing retrieved Facebook key failed!");
+            }
+        } catch (PgpGeneralException | IOException e) {
+            Log.e(Constants.TAG, "Failed parsing key from Facebook during search", e);
+            throw new QueryFailedException("No valid key found on Facebook");
+        }
+        return entry;
+    }
+
+    @Override
+    public String get(String fbUsername) throws QueryFailedException {
+        Log.d(Constants.TAG, "FacebookKeyserver get: " + fbUsername + " using Proxy: " + mProxy);
+
+        String data = query(fbUsername);
+
+        if (data == null) {
+            throw new QueryFailedException("data is null");
+        }
+
+        Matcher matcher = PgpHelper.PGP_PUBLIC_KEY.matcher(data);
+        if (matcher.find()) {
+            return matcher.group(1);
+        }
+        throw new QueryFailedException("data is null");
+    }
+
+    private String query(String fbUsername) throws QueryFailedException {
+        try {
+            String request = String.format(FB_KEY_URL_FORMAT, fbUsername);
+            Log.d(Constants.TAG, "fetching from Facebook with: " + request + " proxy: " + mProxy);
+
+            OkHttpClient client = new OkHttpClient();
+            client.setProxy(mProxy);
+
+            URL url = new URL(request);
+
+            Response response = client.newCall(new Request.Builder().url(url).build()).execute();
+
+            // contains body both in case of success or failure
+            String responseBody = response.body().string();
+
+            if (response.isSuccessful()) {
+                return responseBody;
+            } else {
+                // probably a 404 indicating that the key does not exist
+                throw new QueryFailedException("key for " + fbUsername + " not found on Facebook");
+            }
+
+        } catch (IOException e) {
+            Log.e(Constants.TAG, "IOException at Facebook key download", e);
+            throw new QueryFailedException("Cannot connect to Facebook. "
+                    + "Check your Internet connection!"
+                    + (mProxy == Proxy.NO_PROXY ? "" : " Using proxy " + mProxy));
+        }
+    }
+
+    @Override
+    public void add(String armoredKey) throws AddKeyException {
+        // Implementing will require usage of FB API
+        throw new UnsupportedOperationException("Uploading keys not supported yet");
+    }
+
+    /**
+     * Facebook returns the entire key even during our searching phase.
+     *
+     * @throws UnsupportedOperationException if the key could not be parsed
+     */
+    @NonNull
+    public static ImportKeysListEntry getEntry(UncachedKeyRing ring, String fbUsername)
+            throws UnsupportedOperationException {
+        ImportKeysListEntry entry = new ImportKeysListEntry();
+        entry.setSecretKey(false); // keys imported from Facebook must be public
+        entry.addOrigin(ORIGIN);
+
+        // so we can query for the Facebook username directly, and to identify the source to
+        // download the key from
+        entry.setFbUsername(fbUsername);
+
+        UncachedPublicKey key = ring.getPublicKey();
+
+        entry.setPrimaryUserId(key.getPrimaryUserIdWithFallback());
+        entry.setUserIds(key.getUnorderedUserIds());
+        entry.updateMergedUserIds();
+
+        entry.setPrimaryUserId(key.getPrimaryUserIdWithFallback());
+
+        entry.setKeyId(key.getKeyId());
+        entry.setKeyIdHex(KeyFormattingUtils.convertKeyIdToHex(key.getKeyId()));
+
+        entry.setFingerprintHex(KeyFormattingUtils.convertFingerprintToHex(key.getFingerprint()));
+
+
+        try {
+            if (key.isEC()) { // unsupported key format (ECDH or ECDSA)
+                Log.e(Constants.TAG, "ECDH/ECDSA key - not supported.");
+                throw new UnsupportedOperationException(
+                        "ECDH/ECDSA keys not supported yet");
+            }
+            entry.setBitStrength(key.getBitStrength());
+            final int algorithm = key.getAlgorithm();
+            entry.setAlgorithm(KeyFormattingUtils.getAlgorithmInfo(algorithm, key.getBitStrength(),
+                    key.getCurveOid()));
+        } catch (NumberFormatException | NullPointerException e) {
+            Log.e(Constants.TAG, "Conversion for bit size, algorithm, or creation date failed.", e);
+            // can't use this key
+            throw new UnsupportedOperationException(
+                    "Conversion for bit size, algorithm, or creation date failed.");
+        }
+
+        return entry;
+    }
+
+    public static String getUsernameFromUri(Uri uri) {
+        // path pattern is /username/publickey/download
+        return uri.getPathSegments().get(0);
+    }
+
+    public static boolean isFacebookHost(Uri uri) {
+        String host = uri.getHost();
+        return host.equalsIgnoreCase(FB_HOST) || host.equalsIgnoreCase(FB_HOST_WWW);
+    }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java
index 03439228b..75a219191 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java
@@ -77,9 +77,15 @@ public class ImportKeysList extends ArrayList<ImportKeysListEntry> {
         for (String origin : incoming.getOrigins()) {
             existing.addOrigin(origin);
 
-            // to work properly, Keybase-sourced entries need to pass along the extra
-            if (KeybaseKeyserver.ORIGIN.equals(origin)) {
-                existing.setExtraData(incoming.getExtraData());
+            // to work properly, Keybase-sourced/Facebook-sourced entries need to pass along the
+            // identifying name/id
+            if (incoming.getKeybaseName() != null) {
+                existing.setKeybaseName(incoming.getKeybaseName());
+                // one of the origins is not a HKP keyserver
+                incomingFromHkpServer = false;
+            }
+            if (incoming.getFbUsername() != null) {
+                existing.setFbUsername(incoming.getFbUsername());
                 // one of the origins is not a HKP keyserver
                 incomingFromHkpServer = false;
             }
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
index bb86d272f..b3cf70c9f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
@@ -49,7 +49,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
     private String mAlgorithm;
     private boolean mSecretKey;
     private String mPrimaryUserId;
-    private String mExtraData;
+    private String mKeybaseName;
+    private String mFbUsername;
     private String mQuery;
     private ArrayList<String> mOrigins;
     private Integer mHashCode = null;
@@ -81,7 +82,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
         dest.writeString(mAlgorithm);
         dest.writeByte((byte) (mSecretKey ? 1 : 0));
         dest.writeByte((byte) (mSelected ? 1 : 0));
-        dest.writeString(mExtraData);
+        dest.writeString(mKeybaseName);
+        dest.writeString(mFbUsername);
         dest.writeStringList(mOrigins);
     }
 
@@ -102,7 +104,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
             vr.mAlgorithm = source.readString();
             vr.mSecretKey = source.readByte() == 1;
             vr.mSelected = source.readByte() == 1;
-            vr.mExtraData = source.readString();
+            vr.mKeybaseName = source.readString();
+            vr.mFbUsername = source.readString();
             vr.mOrigins = new ArrayList<>();
             source.readStringList(vr.mOrigins);
 
@@ -229,12 +232,20 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
         mPrimaryUserId = uid;
     }
 
-    public String getExtraData() {
-        return mExtraData;
+    public String getKeybaseName() {
+        return mKeybaseName;
     }
 
-    public void setExtraData(String extraData) {
-        mExtraData = extraData;
+    public String getFbUsername() {
+        return mFbUsername;
+    }
+
+    public void setKeybaseName(String keybaseName) {
+        mKeybaseName = keybaseName;
+    }
+
+    public void setFbUsername(String fbUsername) {
+        mFbUsername = fbUsername;
     }
 
     public String getQuery() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java
index e4cd6738b..9243926df 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java
@@ -81,8 +81,9 @@ public class KeybaseKeyserver extends Keyserver {
         entry.setFingerprintHex(fingerprint);
 
         entry.setKeyIdHex("0x" + match.getKeyID());
-        // store extra info, so we can query for the keybase id directly
-        entry.setExtraData(username);
+        // so we can query for the keybase id directly, and to identify the location from which the
+        // key is to be retrieved
+        entry.setKeybaseName(username);
 
         final int bitStrength = match.getBitStrength();
         entry.setBitStrength(bitStrength);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java
index 00e8d6ac5..53e71f7a1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java
@@ -21,7 +21,6 @@ package org.sufficientlysecure.keychain.keyimport;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.Proxy;
 import java.util.List;
 
 public abstract class Keyserver {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java
index 6f6c816ea..a94ce0dce 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java
@@ -32,24 +32,39 @@ public class ParcelableKeyRing implements Parcelable {
     public final String mExpectedFingerprint;
     public final String mKeyIdHex;
     public final String mKeybaseName;
+    public final String mFbUsername;
 
     public ParcelableKeyRing(byte[] bytes) {
+        this(null, bytes, false);
+    }
+
+    /**
+     * @param disAmbiguator useless parameter intended to distinguish this overloaded constructor
+     *                      for when null is passed as first two arguments
+     */
+    public ParcelableKeyRing(String expectedFingerprint, byte[] bytes, boolean disAmbiguator) {
         mBytes = bytes;
-        mExpectedFingerprint = null;
+        mExpectedFingerprint = expectedFingerprint;
         mKeyIdHex = null;
         mKeybaseName = null;
+        mFbUsername = null;
     }
-    public ParcelableKeyRing(String expectedFingerprint, byte[] bytes) {
-        mBytes = bytes;
+
+    public ParcelableKeyRing(String expectedFingerprint, String keyIdHex) {
+        mBytes = null;
         mExpectedFingerprint = expectedFingerprint;
-        mKeyIdHex = null;
+        mKeyIdHex = keyIdHex;
         mKeybaseName = null;
+        mFbUsername = null;
     }
-    public ParcelableKeyRing(String expectedFingerprint, String keyIdHex, String keybaseName) {
+
+    public ParcelableKeyRing(String expectedFingerprint, String keyIdHex, String keybaseName,
+                             String fbUsername) {
         mBytes = null;
         mExpectedFingerprint = expectedFingerprint;
         mKeyIdHex = keyIdHex;
         mKeybaseName = keybaseName;
+        mFbUsername = fbUsername;
     }
 
     private ParcelableKeyRing(Parcel source) {
@@ -58,6 +73,7 @@ public class ParcelableKeyRing implements Parcelable {
         mExpectedFingerprint = source.readString();
         mKeyIdHex = source.readString();
         mKeybaseName = source.readString();
+        mFbUsername = source.readString();
     }
 
     public void writeToParcel(Parcel dest, int flags) {
@@ -65,6 +81,7 @@ public class ParcelableKeyRing implements Parcelable {
         dest.writeString(mExpectedFingerprint);
         dest.writeString(mKeyIdHex);
         dest.writeString(mKeybaseName);
+        dest.writeString(mFbUsername);
     }
 
     public static final Creator<ParcelableKeyRing> CREATOR = new Creator<ParcelableKeyRing>() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java
index 70288123f..a20181a00 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java
@@ -38,6 +38,7 @@ import android.support.annotation.NonNull;
 
 import org.sufficientlysecure.keychain.Constants;
 import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.keyimport.FacebookKeyserver;
 import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
 import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver;
 import org.sufficientlysecure.keychain.keyimport.Keyserver;
@@ -156,6 +157,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
         double progSteps = 100.0 / num;
 
         KeybaseKeyserver keybaseServer = null;
+        FacebookKeyserver facebookServer = null;
         HkpKeyserver keyServer = null;
 
         // iterate over all entries
@@ -228,6 +230,12 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
                             byte[] data = keybaseServer.get(entry.mKeybaseName).getBytes();
                             UncachedKeyRing keybaseKey = UncachedKeyRing.decodeFromData(data);
 
+                            if (keybaseKey != null) {
+                                log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER_OK, 3);
+                            } else {
+                                log.add(LogType.MSG_IMPORT_FETCH_ERROR_DECODE, 3);
+                            }
+
                             // If there already is a key, merge the two
                             if (key != null && keybaseKey != null) {
                                 log.add(LogType.MSG_IMPORT_MERGE, 3);
@@ -247,6 +255,44 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
                             log.add(LogType.MSG_IMPORT_FETCH_ERROR_KEYSERVER, 3, e.getMessage());
                         }
                     }
+
+                    // if the key is from Facebook, fetch from there
+                    if (entry.mFbUsername != null) {
+                        // Make sure we have this cached
+                        if (facebookServer == null) {
+                            facebookServer = new FacebookKeyserver(proxy);
+                        }
+
+                        try {
+                            log.add(LogType.MSG_IMPORT_FETCH_FACEBOOK, 2, entry.mFbUsername);
+                            byte[] data = facebookServer.get(entry.mFbUsername).getBytes();
+                            UncachedKeyRing facebookKey = UncachedKeyRing.decodeFromData(data);
+
+                            if (facebookKey != null) {
+                                log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER_OK, 3);
+                            } else {
+                                log.add(LogType.MSG_IMPORT_FETCH_ERROR_DECODE, 3);
+                            }
+
+                            // If there already is a key, merge the two
+                            if (key != null && facebookKey != null) {
+                                log.add(LogType.MSG_IMPORT_MERGE, 3);
+                                facebookKey = key.merge(facebookKey, log, 4);
+                                // If the merge didn't fail, use the new merged key
+                                if (facebookKey != null) {
+                                    key = facebookKey;
+                                } else {
+                                    log.add(LogType.MSG_IMPORT_MERGE_ERROR, 4);
+                                }
+                            } else if (facebookKey != null) {
+                                key = facebookKey;
+                            }
+                        } catch (Keyserver.QueryFailedException e) {
+                            // download failed, too bad. just proceed
+                            Log.e(Constants.TAG, "query failed", e);
+                            log.add(LogType.MSG_IMPORT_FETCH_ERROR_KEYSERVER, 3, e.getMessage());
+                        }
+                    }
                 }
 
                 if (key == null) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
index 9877f2318..84bdb3eb6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
@@ -233,6 +233,18 @@ public abstract class OperationResult implements Parcelable {
             dest.writeParcelable(mSubResult, 0);
         }
 
+        public static final Parcelable.Creator<SubLogEntryParcel> CREATOR = new Parcelable.Creator<SubLogEntryParcel>() {
+            @Override
+            public SubLogEntryParcel createFromParcel(Parcel in) {
+                return new SubLogEntryParcel(in);
+            }
+
+            @Override
+            public SubLogEntryParcel[] newArray(int size) {
+                return new SubLogEntryParcel[size];
+            }
+        };
+
         @Override
         StringBuilder getPrintableLogEntry(Resources resources, int indent) {
 
@@ -757,6 +769,7 @@ public abstract class OperationResult implements Parcelable {
         MSG_IMPORT_FETCH_ERROR_KEYSERVER(LogLevel.ERROR, R.string.msg_import_fetch_error_keyserver),
         MSG_IMPORT_FETCH_ERROR_KEYSERVER_SECRET (LogLevel.ERROR, R.string.msg_import_fetch_error_keyserver_secret),
         MSG_IMPORT_FETCH_KEYBASE (LogLevel.INFO, R.string.msg_import_fetch_keybase),
+        MSG_IMPORT_FETCH_FACEBOOK (LogLevel.INFO, R.string.msg_import_fetch_facebook),
         MSG_IMPORT_FETCH_KEYSERVER (LogLevel.INFO, R.string.msg_import_fetch_keyserver),
         MSG_IMPORT_FETCH_KEYSERVER_OK (LogLevel.DEBUG, R.string.msg_import_fetch_keyserver_ok),
         MSG_IMPORT_KEYSERVER (LogLevel.DEBUG, R.string.msg_import_keyserver),
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java
index fc3224e39..35b4c2a7c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java
@@ -440,7 +440,7 @@ public class KeyserverSyncAdapterService extends Service {
             String hexKeyId = KeyFormattingUtils
                     .convertKeyIdToHex(keyId);
             // we aren't updating from keybase as of now
-            keyList.add(new ParcelableKeyRing(fingerprint, hexKeyId, null));
+            keyList.add(new ParcelableKeyRing(fingerprint, hexKeyId));
         }
         keyCursor.close();
 
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyImportResetFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyImportResetFragment.java
index 0a2d52617..5712f4452 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyImportResetFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyImportResetFragment.java
@@ -218,15 +218,10 @@ public class CreateYubiKeyImportResetFragment
     public void importKey() {
 
         ArrayList<ParcelableKeyRing> keyList = new ArrayList<>();
-        keyList.add(new ParcelableKeyRing(mNfcFingerprint, null, null));
+        keyList.add(new ParcelableKeyRing(mNfcFingerprint, null));
         mKeyList = keyList;
 
-        {
-            Preferences prefs = Preferences.getPreferences(getActivity());
-            Preferences.CloudSearchPrefs cloudPrefs =
-                    new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
-            mKeyserver = cloudPrefs.keyserver;
-        }
+        mKeyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
 
         super.setProgressMessageResource(R.string.progress_importing);
 
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 37dd6afad..351b62ba7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
@@ -138,16 +138,11 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
         final String keyserver;
 
         // search config
-        {
-            Preferences prefs = Preferences.getPreferences(getActivity());
-            Preferences.CloudSearchPrefs cloudPrefs =
-                    new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
-            keyserver = cloudPrefs.keyserver;
-        }
+        keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
 
         {
             ParcelableKeyRing keyEntry = new ParcelableKeyRing(null,
-                    KeyFormattingUtils.convertKeyIdToHex(unknownKeyId), null);
+                    KeyFormattingUtils.convertKeyIdToHex(unknownKeyId));
             ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();
             selectedEntries.add(keyEntry);
 
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java
index 922dd7307..9419cf8ce 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java
@@ -18,7 +18,6 @@
 package org.sufficientlysecure.keychain.ui;
 
 
-import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -746,16 +745,11 @@ public class DecryptListFragment
         final String keyserver;
 
         // search config
-        {
-            Preferences prefs = Preferences.getPreferences(getActivity());
-            Preferences.CloudSearchPrefs cloudPrefs =
-                    new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
-            keyserver = cloudPrefs.keyserver;
-        }
+        keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
 
         {
             ParcelableKeyRing keyEntry = new ParcelableKeyRing(null,
-                    KeyFormattingUtils.convertKeyIdToHex(unknownKeyId), null);
+                    KeyFormattingUtils.convertKeyIdToHex(unknownKeyId));
             ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();
             selectedEntries.add(keyEntry);
 
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 7f7532ddf..c54f55b6f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -30,6 +30,7 @@ import android.view.ViewGroup;
 import org.sufficientlysecure.keychain.Constants;
 import org.sufficientlysecure.keychain.R;
 import org.sufficientlysecure.keychain.intents.OpenKeychainIntents;
+import org.sufficientlysecure.keychain.keyimport.FacebookKeyserver;
 import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
 import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
 import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
@@ -41,6 +42,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify;
 import org.sufficientlysecure.keychain.util.Log;
 import org.sufficientlysecure.keychain.util.ParcelableFileCache;
 import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
+import org.sufficientlysecure.keychain.util.Preferences;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -50,6 +52,8 @@ public class ImportKeysActivity extends BaseNfcActivity
 
     public static final String ACTION_IMPORT_KEY = OpenKeychainIntents.IMPORT_KEY;
     public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER = OpenKeychainIntents.IMPORT_KEY_FROM_KEYSERVER;
+    public static final String ACTION_IMPORT_KEY_FROM_FACEBOOK
+            = Constants.INTENT_PREFIX + "IMPORT_KEY_FROM_FACEBOOK";
     public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT =
             Constants.INTENT_PREFIX + "IMPORT_KEY_FROM_KEY_SERVER_AND_RETURN_RESULT";
     public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE = Constants.INTENT_PREFIX
@@ -101,11 +105,6 @@ public class ImportKeysActivity extends BaseNfcActivity
         if (getIntent().hasExtra(EXTRA_PENDING_INTENT_DATA)) {
             mPendingIntentData = getIntent().getParcelableExtra(EXTRA_PENDING_INTENT_DATA);
         }
-
-        // if we aren't being restored, initialize fragments
-        if (savedInstanceState == null) {
-            handleActions(getIntent());
-        }
     }
 
     @Override
@@ -113,6 +112,18 @@ public class ImportKeysActivity extends BaseNfcActivity
         setContentView(R.layout.import_keys_activity);
     }
 
+    @Override
+    public void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        setIntent(intent);
+    }
+
+    @Override
+    protected void onResumeFragments() {
+        super.onResumeFragments();
+        handleActions(getIntent());
+    }
+
     protected void handleActions(Intent intent) {
         String action = intent.getAction();
         Bundle extras = intent.getExtras();
@@ -124,7 +135,9 @@ public class ImportKeysActivity extends BaseNfcActivity
         }
 
         if (Intent.ACTION_VIEW.equals(action)) {
-            if ("http".equals(scheme) || "https".equals(scheme)) {
+            if (FacebookKeyserver.isFacebookHost(dataUri)) {
+                action = ACTION_IMPORT_KEY_FROM_FACEBOOK;
+            } else if ("http".equals(scheme) || "https".equals(scheme)) {
                 action = ACTION_SEARCH_KEYSERVER_FROM_URL;
             } else {
                 // Android's Action when opening file associated to Keychain (see AndroidManifest.xml)
@@ -205,26 +218,31 @@ public class ImportKeysActivity extends BaseNfcActivity
                 }
                 break;
             }
+            case ACTION_IMPORT_KEY_FROM_FACEBOOK: {
+                String fbUsername = FacebookKeyserver.getUsernameFromUri(dataUri);
+
+                Preferences.CloudSearchPrefs cloudSearchPrefs =
+                        new Preferences.CloudSearchPrefs(false, true, true, null);
+                // we allow our users to edit the query if they wish
+                startTopCloudFragment(fbUsername, false, cloudSearchPrefs);
+                // search immediately
+                startListFragment(null, null, fbUsername, cloudSearchPrefs);
+                break;
+            }
             case ACTION_SEARCH_KEYSERVER_FROM_URL: {
                 // need to process URL to get search query and keyserver authority
                 String query = dataUri.getQueryParameter("search");
-                String keyserver = dataUri.getAuthority();
                 // if query not specified, we still allow users to search the keyserver in the link
                 if (query == null) {
                     Notify.create(this, R.string.import_url_warn_no_search_parameter, Notify.LENGTH_INDEFINITE,
                             Notify.Style.WARN).show();
-                    // we just set the keyserver
-                    startTopCloudFragment(null, false, keyserver);
-                    // we don't set the keyserver for ImportKeysListFragment since
-                    // it'll be set in the cloudSearchPrefs of ImportKeysCloudFragment
-                    // which is used when the user clicks on the search button
-                    startListFragment(null, null, null, null);
-                } else {
-                    // we allow our users to edit the query if they wish
-                    startTopCloudFragment(query, false, keyserver);
-                    // search immediately
-                    startListFragment(null, null, query, keyserver);
                 }
+                Preferences.CloudSearchPrefs cloudSearchPrefs = new Preferences.CloudSearchPrefs(
+                        true, true, true, dataUri.getAuthority());
+                // we allow our users to edit the query if they wish
+                startTopCloudFragment(query, false, cloudSearchPrefs);
+                // search immediately (if query is not null)
+                startListFragment(null, null, query, cloudSearchPrefs);
                 break;
             }
             case ACTION_IMPORT_KEY_FROM_FILE:
@@ -254,18 +272,21 @@ public class ImportKeysActivity extends BaseNfcActivity
     }
 
     /**
-     * if the fragment is started with non-null bytes/dataUri/serverQuery, it will immediately
-     * load content
+     * Shows the list of keys to be imported.
+     * If the fragment is started with non-null bytes/dataUri/serverQuery, it will immediately
+     * load content.
      *
-     * @param bytes              bytes containing list of keyrings to import
-     * @param dataUri            uri to file to import keyrings from
-     * @param serverQuery        query to search for on the keyserver
-     * @param keyserver          keyserver authority to search on. If null will use keyserver from
-     *                           user preferences
+     * @param bytes            bytes containing list of keyrings to import
+     * @param dataUri          uri to file to import keyrings from
+     * @param serverQuery      query to search for on the keyserver
+     * @param cloudSearchPrefs search specifications to use. If null will retrieve from user's
+     *                         preferences.
      */
-    private void startListFragment(byte[] bytes, Uri dataUri, String serverQuery, String keyserver) {
+    private void startListFragment(byte[] bytes, Uri dataUri, String serverQuery,
+                                   Preferences.CloudSearchPrefs cloudSearchPrefs) {
         Fragment listFragment =
-                ImportKeysListFragment.newInstance(bytes, dataUri, serverQuery, false, keyserver);
+                ImportKeysListFragment.newInstance(bytes, dataUri, serverQuery, false,
+                        cloudSearchPrefs);
         getSupportFragmentManager().beginTransaction()
                 .replace(R.id.import_keys_list_container, listFragment, TAG_FRAG_LIST)
                 .commit();
@@ -283,14 +304,16 @@ public class ImportKeysActivity extends BaseNfcActivity
      * loads the CloudFragment, which consists of the search bar, search button and settings icon
      * visually.
      *
-     * @param query              search query
-     * @param disableQueryEdit   if true, user will not be able to edit the search query
-     * @param keyserver          keyserver authority to use for search, if null will use keyserver
-     *                           specified in user preferences
+     * @param query            search query
+     * @param disableQueryEdit if true, user will not be able to edit the search query
+     * @param cloudSearchPrefs keyserver authority to use for search, if null will use keyserver
+     *                         specified in user preferences
      */
-    private void startTopCloudFragment(String query, boolean disableQueryEdit, String keyserver) {
+    private void startTopCloudFragment(String query, boolean disableQueryEdit,
+                                       Preferences.CloudSearchPrefs cloudSearchPrefs) {
         findViewById(R.id.import_keys_top_layout).setVisibility(View.VISIBLE);
-        Fragment importCloudFragment = ImportKeysCloudFragment.newInstance(query, disableQueryEdit, keyserver);
+        Fragment importCloudFragment = ImportKeysCloudFragment.newInstance(query, disableQueryEdit,
+                cloudSearchPrefs);
         getSupportFragmentManager().beginTransaction()
                 .replace(R.id.import_keys_top_container, importCloudFragment, TAG_FRAG_TOP)
                 .commit();
@@ -363,9 +386,8 @@ public class ImportKeysActivity extends BaseNfcActivity
                 // change the format into ParcelableKeyRing
                 ArrayList<ImportKeysListEntry> entries = keyListFragment.getSelectedEntries();
                 for (ImportKeysListEntry entry : entries) {
-                    keys.add(new ParcelableKeyRing(
-                                    entry.getFingerprintHex(), entry.getKeyIdHex(), entry.getExtraData())
-                    );
+                    keys.add(new ParcelableKeyRing(entry.getFingerprintHex(),
+                            entry.getKeyIdHex(), entry.getKeybaseName(), entry.getFbUsername()));
                 }
             }
 
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java
index 1cd5c24f3..0701dbb91 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysCloudFragment.java
@@ -42,33 +42,35 @@ import org.sufficientlysecure.keychain.util.Preferences;
 
 import java.util.List;
 
+/**
+ * Consists of the search bar, search button, and search settings button
+ */
 public class ImportKeysCloudFragment extends Fragment {
     public static final String ARG_QUERY = "query";
     public static final String ARG_DISABLE_QUERY_EDIT = "disable_query_edit";
-    public static final String ARG_KEYSERVER = "keyserver";
+    public static final String ARG_CLOUD_SEARCH_PREFS = "cloud_search_prefs";
 
     private ImportKeysActivity mImportActivity;
 
-    private View mSearchButton;
     private AutoCompleteTextView mQueryEditText;
-    private View mConfigButton;
 
     /**
      * Creates new instance of this fragment
      *
      * @param query            query to search for
      * @param disableQueryEdit if true, user cannot edit query
-     * @param keyserver        specified keyserver authority to use. If null, will use keyserver
-     *                         specified in user preferences
+     * @param cloudSearchPrefs search parameters to use. If null will retrieve from user's
+     *                         preferences.
      */
     public static ImportKeysCloudFragment newInstance(String query, boolean disableQueryEdit,
-                                                      String keyserver) {
+                                                      Preferences.CloudSearchPrefs
+                                                              cloudSearchPrefs) {
         ImportKeysCloudFragment frag = new ImportKeysCloudFragment();
 
         Bundle args = new Bundle();
         args.putString(ARG_QUERY, query);
         args.putBoolean(ARG_DISABLE_QUERY_EDIT, disableQueryEdit);
-        args.putString(ARG_KEYSERVER, keyserver);
+        args.putParcelable(ARG_CLOUD_SEARCH_PREFS, cloudSearchPrefs);
 
         frag.setArguments(args);
 
@@ -82,9 +84,7 @@ public class ImportKeysCloudFragment extends Fragment {
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         View view = inflater.inflate(R.layout.import_keys_cloud_fragment, container, false);
 
-        mSearchButton = view.findViewById(R.id.cloud_import_server_search);
         mQueryEditText = (AutoCompleteTextView) view.findViewById(R.id.cloud_import_server_query);
-        mConfigButton = view.findViewById(R.id.cloud_import_server_config_button);
 
         List<String> namesAndEmails = ContactHelper.getContactNames(getActivity());
         namesAndEmails.addAll(ContactHelper.getContactMails(getActivity()));
@@ -96,7 +96,8 @@ public class ImportKeysCloudFragment extends Fragment {
                         )
         );
 
-        mSearchButton.setOnClickListener(new OnClickListener() {
+        View searchButton = view.findViewById(R.id.cloud_import_server_search);
+        searchButton.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
                 search(mQueryEditText.getText().toString());
@@ -116,7 +117,8 @@ public class ImportKeysCloudFragment extends Fragment {
             }
         });
 
-        mConfigButton.setOnClickListener(new OnClickListener() {
+        View configButton = view.findViewById(R.id.cloud_import_server_config_button);
+        configButton.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
                 Intent intent = new Intent(mImportActivity, SettingsActivity.class);
@@ -159,15 +161,14 @@ public class ImportKeysCloudFragment extends Fragment {
     }
 
     private void search(String query) {
-        Preferences.CloudSearchPrefs cloudSearchPrefs;
-        String explicitKeyserver = getArguments().getString(ARG_KEYSERVER);
-        // no explicit keyserver passed
-        if (explicitKeyserver == null) {
+        Preferences.CloudSearchPrefs cloudSearchPrefs
+                = getArguments().getParcelable(ARG_CLOUD_SEARCH_PREFS);
+
+        // no explicit search preferences passed
+        if (cloudSearchPrefs == null) {
             cloudSearchPrefs = Preferences.getPreferences(getActivity()).getCloudSearchPrefs();
-        } else {
-            // assume we are also meant to search keybase.io
-            cloudSearchPrefs = new Preferences.CloudSearchPrefs(true, true, explicitKeyserver);
         }
+
         mImportActivity.loadCallback(
                 new ImportKeysListFragment.CloudLoaderState(query, cloudSearchPrefs));
         toggleKeyboard(false);
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 4955bad6e..b399af950 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
@@ -65,7 +65,7 @@ public class ImportKeysListFragment extends ListFragment implements
     private static final String ARG_BYTES = "bytes";
     public static final String ARG_SERVER_QUERY = "query";
     public static final String ARG_NON_INTERACTIVE = "non_interactive";
-    public static final String ARG_KEYSERVER_URL = "keyserver_url";
+    public static final String ARG_CLOUD_SEARCH_PREFS = "cloud_search_prefs";
 
     private static final int REQUEST_PERMISSION_READ_EXTERNAL_STORAGE = 12;
 
@@ -140,32 +140,35 @@ public class ImportKeysListFragment extends ListFragment implements
      * by dataUri, or searches a keyserver for serverQuery, if parameter is not null, in that order
      * Will immediately load data if non-null bytes/dataUri/serverQuery
      *
-     * @param bytes       byte data containing list of keyrings to be imported
-     * @param dataUri     file from which keyrings are to be imported
-     * @param serverQuery query to search for on keyserver
-     * @param keyserver   if not null, will perform search on specified keyserver. Else, uses
-     *                    keyserver specified in user preferences
+     * @param bytes            byte data containing list of keyrings to be imported
+     * @param dataUri          file from which keyrings are to be imported
+     * @param serverQuery      query to search for on keyserver
+     * @param cloudSearchPrefs search parameters to use. If null will retrieve from user's
+     *                         preferences.
      * @return fragment with arguments set based on passed parameters
      */
     public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, String serverQuery,
-                                                     String keyserver) {
-        return newInstance(bytes, dataUri, serverQuery, false, keyserver);
+                                                     Preferences.CloudSearchPrefs cloudSearchPrefs) {
+        return newInstance(bytes, dataUri, serverQuery, false, cloudSearchPrefs);
     }
 
     /**
      * Visually consists of a list of keyrings with checkboxes to specify which are to be imported
      * Will immediately load data if non-null bytes/dataUri/serverQuery is supplied
      *
-     * @param bytes          byte data containing list of keyrings to be imported
-     * @param dataUri        file from which keyrings are to be imported
-     * @param serverQuery    query to search for on keyserver
-     * @param nonInteractive if true, users will not be able to check/uncheck items in the list
-     * @param keyserver      if set, will perform search on specified keyserver. If null, falls back
-     *                       to keyserver specified in user preferences
+     * @param bytes            byte data containing list of keyrings to be imported
+     * @param dataUri          file from which keyrings are to be imported
+     * @param serverQuery      query to search for on keyserver
+     * @param nonInteractive   if true, users will not be able to check/uncheck items in the list
+     * @param cloudSearchPrefs search parameters to use. If null will retrieve from user's
+     *                         preferences.
      * @return fragment with arguments set based on passed parameters
      */
-    public static ImportKeysListFragment newInstance(byte[] bytes, Uri dataUri, String serverQuery,
-                                                     boolean nonInteractive, String keyserver) {
+    public static ImportKeysListFragment newInstance(byte[] bytes,
+                                                     Uri dataUri,
+                                                     String serverQuery,
+                                                     boolean nonInteractive,
+                                                     Preferences.CloudSearchPrefs cloudSearchPrefs) {
         ImportKeysListFragment frag = new ImportKeysListFragment();
 
         Bundle args = new Bundle();
@@ -173,7 +176,7 @@ public class ImportKeysListFragment extends ListFragment implements
         args.putParcelable(ARG_DATA_URI, dataUri);
         args.putString(ARG_SERVER_QUERY, serverQuery);
         args.putBoolean(ARG_NON_INTERACTIVE, nonInteractive);
-        args.putString(ARG_KEYSERVER_URL, keyserver);
+        args.putParcelable(ARG_CLOUD_SEARCH_PREFS, cloudSearchPrefs);
 
         frag.setArguments(args);
 
@@ -223,7 +226,6 @@ public class ImportKeysListFragment extends ListFragment implements
         Uri dataUri = args.getParcelable(ARG_DATA_URI);
         byte[] bytes = args.getByteArray(ARG_BYTES);
         String query = args.getString(ARG_SERVER_QUERY);
-        String keyserver = args.getString(ARG_KEYSERVER_URL);
         mNonInteractive = args.getBoolean(ARG_NON_INTERACTIVE, false);
 
         getListView().setOnTouchListener(new OnTouchListener() {
@@ -241,11 +243,10 @@ public class ImportKeysListFragment extends ListFragment implements
         if (dataUri != null || bytes != null) {
             mLoaderState = new BytesLoaderState(bytes, dataUri);
         } else if (query != null) {
-            Preferences.CloudSearchPrefs cloudSearchPrefs;
-            if (keyserver == null) {
+            Preferences.CloudSearchPrefs cloudSearchPrefs
+                    = args.getParcelable(ARG_CLOUD_SEARCH_PREFS);
+            if (cloudSearchPrefs == null) {
                 cloudSearchPrefs = Preferences.getPreferences(getActivity()).getCloudSearchPrefs();
-            } else {
-                cloudSearchPrefs = new Preferences.CloudSearchPrefs(true, true, keyserver);
             }
 
             mLoaderState = new CloudLoaderState(query, cloudSearchPrefs);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java
index 45ce604c3..3969f4039 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java
@@ -19,7 +19,6 @@ package org.sufficientlysecure.keychain.ui;
 
 import android.annotation.TargetApi;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.net.Uri;
 import android.nfc.NdefMessage;
 import android.nfc.NfcAdapter;
@@ -130,7 +129,6 @@ public class ImportKeysProxyActivity extends FragmentActivity
             String scannedContent = scanResult.getContents();
             processScannedContent(scannedContent);
 
-            return;
         }
     }
 
@@ -199,7 +197,7 @@ public class ImportKeysProxyActivity extends FragmentActivity
     }
 
     public void importKeys(String fingerprint) {
-        ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);
+        ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null);
         ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();
         selectedEntries.add(keyEntry);
 
@@ -209,12 +207,7 @@ public class ImportKeysProxyActivity extends FragmentActivity
     private void startImportService(ArrayList<ParcelableKeyRing> keyRings) {
 
         // search config
-        {
-            Preferences prefs = Preferences.getPreferences(this);
-            Preferences.CloudSearchPrefs cloudPrefs =
-                    new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
-            mKeyserver = cloudPrefs.keyserver;
-        }
+        mKeyserver = Preferences.getPreferences(this).getPreferredKeyserver();
 
         mKeyList = keyRings;
 
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 db31bd0a1..dd8107304 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -588,7 +588,7 @@ public class KeyListFragment extends LoaderFragment
             while (cursor.moveToNext()) {
                 byte[] blob = cursor.getBlob(0);//fingerprint column is 0
                 String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob);
-                ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);
+                ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null);
                 keyList.add(keyEntry);
             }
             mKeyList = keyList;
@@ -597,12 +597,7 @@ public class KeyListFragment extends LoaderFragment
         }
 
         // search config
-        {
-            Preferences prefs = Preferences.getPreferences(getActivity());
-            Preferences.CloudSearchPrefs cloudPrefs =
-                    new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
-            mKeyserver = cloudPrefs.keyserver;
-        }
+        mKeyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
 
         mImportOpHelper = new CryptoOperationHelper<>(1, this, this, R.string.progress_updating);
         mImportOpHelper.setProgressCancellable(true);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java
index 9e962fa0d..2b994e11c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java
@@ -29,11 +29,9 @@ import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.Bundle;
-import android.preference.CheckBoxPreference;
 import android.preference.EditTextPreference;
 import android.preference.ListPreference;
 import android.preference.Preference;
-import android.preference.PreferenceActivity;
 import android.preference.PreferenceFragment;
 import android.preference.PreferenceScreen;
 import android.preference.SwitchPreference;
@@ -115,6 +113,15 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
         });
     }
 
+    public static abstract class PresetPreferenceFragment extends PreferenceFragment {
+        @Override
+        public void addPreferencesFromResource(int preferencesResId) {
+            // so that preferences are written to our preference file, not the default
+            Preferences.setPreferenceManagerFileAndMode(this.getPreferenceManager());
+            super.addPreferencesFromResource(preferencesResId);
+        }
+    }
+
     @Override
     public void onBuildHeaders(List<Header> target) {
         super.onBuildHeaders(target);
@@ -124,7 +131,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
     /**
      * This fragment shows the Cloud Search preferences
      */
-    public static class CloudSearchPrefsFragment extends PreferenceFragment {
+    public static class CloudSearchPrefsFragment extends PresetPreferenceFragment {
 
         private PreferenceScreen mKeyServerPreference = null;
 
@@ -149,12 +156,6 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
                             return false;
                         }
                     });
-            initializeSearchKeyserver(
-                    (SwitchPreference) findPreference(Constants.Pref.SEARCH_KEYSERVER)
-            );
-            initializeSearchKeybase(
-                    (SwitchPreference) findPreference(Constants.Pref.SEARCH_KEYBASE)
-            );
         }
 
         @Override
@@ -172,12 +173,20 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
                 }
             }
         }
+
+        public static String keyserverSummary(Context context) {
+            String[] servers = sPreferences.getKeyServers();
+            String serverSummary = context.getResources().getQuantityString(
+                    R.plurals.n_keyservers, servers.length, servers.length);
+            return serverSummary + "; " + context.getString(R.string.label_preferred) + ": " + sPreferences
+                    .getPreferredKeyserver();
+        }
     }
 
     /**
      * This fragment shows the PIN/password preferences
      */
-    public static class PassphrasePrefsFragment extends PreferenceFragment {
+    public static class PassphrasePrefsFragment extends PresetPreferenceFragment {
 
         @Override
         public void onCreate(Bundle savedInstanceState) {
@@ -186,18 +195,27 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
             // Load the preferences from an XML resource
             addPreferencesFromResource(R.xml.passphrase_preferences);
 
-            initializePassphraseCacheSubs(
-                    (CheckBoxPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_SUBS));
-
             initializePassphraseCacheTtl(
                     (IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL));
+        }
 
-            initializeUseNumKeypadForYubiKeyPin(
-                    (CheckBoxPreference) findPreference(Constants.Pref.USE_NUMKEYPAD_FOR_YUBIKEY_PIN));
+        private static void initializePassphraseCacheTtl(
+                final IntegerListPreference passphraseCacheTtl) {
+            passphraseCacheTtl.setValue("" + sPreferences.getPassphraseCacheTtl());
+            passphraseCacheTtl.setSummary(passphraseCacheTtl.getEntry());
+            passphraseCacheTtl
+                    .setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+                        public boolean onPreferenceChange(Preference preference, Object newValue) {
+                            passphraseCacheTtl.setValue(newValue.toString());
+                            passphraseCacheTtl.setSummary(passphraseCacheTtl.getEntry());
+                            sPreferences.setPassphraseCacheTtl(Integer.parseInt(newValue.toString()));
+                            return false;
+                        }
+                    });
         }
     }
 
-    public static class ProxyPrefsFragment extends PreferenceFragment {
+    public static class ProxyPrefsFragment extends PresetPreferenceFragment {
 
         @Override
         public void onCreate(Bundle savedInstanceState) {
@@ -213,37 +231,18 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
             private EditTextPreference mProxyHost;
             private EditTextPreference mProxyPort;
             private ListPreference mProxyType;
-            private PreferenceActivity mActivity;
-            private PreferenceFragment mFragment;
+            private PresetPreferenceFragment mFragment;
 
-            public Initializer(PreferenceFragment fragment) {
+            public Initializer(PresetPreferenceFragment fragment) {
                 mFragment = fragment;
             }
 
-            public Initializer(PreferenceActivity activity) {
-                mActivity = activity;
-            }
-
             public Preference automaticallyFindPreference(String key) {
-                if (mFragment != null) {
-                    return mFragment.findPreference(key);
-                } else {
-                    return mActivity.findPreference(key);
-                }
+                return mFragment.findPreference(key);
             }
 
             public void initialize() {
-                // makes android's preference framework write to our file instead of default
-                // This allows us to use the "persistent" attribute to simplify code
-                if (mFragment != null) {
-                    Preferences.setPreferenceManagerFileAndMode(mFragment.getPreferenceManager());
-                    // Load the preferences from an XML resource
-                    mFragment.addPreferencesFromResource(R.xml.proxy_preferences);
-                } else {
-                    Preferences.setPreferenceManagerFileAndMode(mActivity.getPreferenceManager());
-                    // Load the preferences from an XML resource
-                    mActivity.addPreferencesFromResource(R.xml.proxy_preferences);
-                }
+                mFragment.addPreferencesFromResource(R.xml.proxy_preferences);
 
                 mUseTor = (SwitchPreference) automaticallyFindPreference(Constants.Pref.USE_TOR_PROXY);
                 mUseNormalProxy = (SwitchPreference) automaticallyFindPreference(Constants.Pref.USE_NORMAL_PROXY);
@@ -268,7 +267,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
                 mUseTor.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
                     @Override
                     public boolean onPreferenceChange(Preference preference, Object newValue) {
-                        Activity activity = mFragment != null ? mFragment.getActivity() : mActivity;
+                        Activity activity = mFragment.getActivity();
                         if ((Boolean) newValue) {
                             boolean installed = OrbotHelper.isOrbotInstalled(activity);
                             if (!installed) {
@@ -314,7 +313,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
                 mProxyHost.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
                     @Override
                     public boolean onPreferenceChange(Preference preference, Object newValue) {
-                        Activity activity = mFragment != null ? mFragment.getActivity() : mActivity;
+                        Activity activity = mFragment.getActivity();
                         if (TextUtils.isEmpty((String) newValue)) {
                             Notify.create(
                                     activity,
@@ -332,7 +331,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
                 mProxyPort.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
                     @Override
                     public boolean onPreferenceChange(Preference preference, Object newValue) {
-                        Activity activity = mFragment != null ? mFragment.getActivity() : mActivity;
+                        Activity activity = mFragment.getActivity();
                         try {
                             int port = Integer.parseInt((String) newValue);
                             if (port < 0 || port > 65535) {
@@ -407,7 +406,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
     /**
      * This fragment shows the keyserver/contacts sync preferences
      */
-    public static class SyncPrefsFragment extends PreferenceFragment {
+    public static class SyncPrefsFragment extends PresetPreferenceFragment {
 
         @Override
         public void onCreate(Bundle savedInstanceState) {
@@ -542,7 +541,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
     /**
      * This fragment shows experimental features
      */
-    public static class ExperimentalPrefsFragment extends PreferenceFragment {
+    public static class ExperimentalPrefsFragment extends PresetPreferenceFragment {
 
         @Override
         public void onCreate(Bundle savedInstanceState) {
@@ -551,17 +550,23 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
             // Load the preferences from an XML resource
             addPreferencesFromResource(R.xml.experimental_preferences);
 
-            initializeExperimentalEnableWordConfirm(
-                    (SwitchPreference) findPreference(Constants.Pref.EXPERIMENTAL_ENABLE_WORD_CONFIRM));
+            initializeTheme((ListPreference) findPreference(Constants.Pref.THEME));
 
-            initializeExperimentalEnableLinkedIdentities(
-                    (SwitchPreference) findPreference(Constants.Pref.EXPERIMENTAL_ENABLE_LINKED_IDENTITIES));
+        }
 
-            initializeExperimentalEnableKeybase(
-                    (SwitchPreference) findPreference(Constants.Pref.EXPERIMENTAL_ENABLE_KEYBASE));
+        private static void initializeTheme(final ListPreference themePref) {
+            themePref.setSummary(themePref.getEntry() + "\n"
+                    + themePref.getContext().getString(R.string.label_experimental_settings_theme_summary));
+            themePref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+                public boolean onPreferenceChange(Preference preference, Object newValue) {
+                    themePref.setSummary(newValue + "\n"
+                            + themePref.getContext().getString(R.string.label_experimental_settings_theme_summary));
 
-            initializeTheme((ListPreference) findPreference(Constants.Pref.THEME));
+                    ((SettingsActivity) themePref.getContext()).recreate();
 
+                    return true;
+                }
+            });
         }
     }
 
@@ -573,125 +578,4 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
                 || ExperimentalPrefsFragment.class.getName().equals(fragmentName)
                 || super.isValidFragment(fragmentName);
     }
-
-    private static void initializePassphraseCacheSubs(final CheckBoxPreference mPassphraseCacheSubs) {
-        mPassphraseCacheSubs.setChecked(sPreferences.getPassphraseCacheSubs());
-        mPassphraseCacheSubs.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                mPassphraseCacheSubs.setChecked((Boolean) newValue);
-                sPreferences.setPassphraseCacheSubs((Boolean) newValue);
-                return false;
-            }
-        });
-    }
-
-    private static void initializePassphraseCacheTtl(final IntegerListPreference mPassphraseCacheTtl) {
-        mPassphraseCacheTtl.setValue("" + sPreferences.getPassphraseCacheTtl());
-        mPassphraseCacheTtl.setSummary(mPassphraseCacheTtl.getEntry());
-        mPassphraseCacheTtl
-                .setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-                    public boolean onPreferenceChange(Preference preference, Object newValue) {
-                        mPassphraseCacheTtl.setValue(newValue.toString());
-                        mPassphraseCacheTtl.setSummary(mPassphraseCacheTtl.getEntry());
-                        sPreferences.setPassphraseCacheTtl(Integer.parseInt(newValue.toString()));
-                        return false;
-                    }
-                });
-    }
-
-    private static void initializeTheme(final ListPreference mTheme) {
-        mTheme.setValue(sPreferences.getTheme());
-        mTheme.setSummary(mTheme.getEntry() + "\n"
-                + mTheme.getContext().getString(R.string.label_experimental_settings_theme_summary));
-        mTheme.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                mTheme.setValue((String) newValue);
-                mTheme.setSummary(mTheme.getEntry() + "\n"
-                        + mTheme.getContext().getString(R.string.label_experimental_settings_theme_summary));
-                sPreferences.setTheme((String) newValue);
-
-                ((SettingsActivity) mTheme.getContext()).recreate();
-
-                return false;
-            }
-        });
-    }
-
-    private static void initializeSearchKeyserver(final SwitchPreference mSearchKeyserver) {
-        Preferences.CloudSearchPrefs prefs = sPreferences.getCloudSearchPrefs();
-        mSearchKeyserver.setChecked(prefs.searchKeyserver);
-        mSearchKeyserver.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                mSearchKeyserver.setChecked((Boolean) newValue);
-                sPreferences.setSearchKeyserver((Boolean) newValue);
-                return false;
-            }
-        });
-    }
-
-    private static void initializeSearchKeybase(final SwitchPreference mSearchKeybase) {
-        Preferences.CloudSearchPrefs prefs = sPreferences.getCloudSearchPrefs();
-        mSearchKeybase.setChecked(prefs.searchKeybase);
-        mSearchKeybase.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                mSearchKeybase.setChecked((Boolean) newValue);
-                sPreferences.setSearchKeybase((Boolean) newValue);
-                return false;
-            }
-        });
-    }
-
-    public static String keyserverSummary(Context context) {
-        String[] servers = sPreferences.getKeyServers();
-        String serverSummary = context.getResources().getQuantityString(
-                R.plurals.n_keyservers, servers.length, servers.length);
-        return serverSummary + "; " + context.getString(R.string.label_preferred) + ": " + sPreferences
-                .getPreferredKeyserver();
-    }
-
-    private static void initializeUseNumKeypadForYubiKeyPin(final CheckBoxPreference mUseNumKeypadForYubiKeyPin) {
-        mUseNumKeypadForYubiKeyPin.setChecked(sPreferences.useNumKeypadForYubiKeyPin());
-        mUseNumKeypadForYubiKeyPin.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                mUseNumKeypadForYubiKeyPin.setChecked((Boolean) newValue);
-                sPreferences.setUseNumKeypadForYubiKeyPin((Boolean) newValue);
-                return false;
-            }
-        });
-    }
-
-    private static void initializeExperimentalEnableWordConfirm(final SwitchPreference mExperimentalEnableWordConfirm) {
-        mExperimentalEnableWordConfirm.setChecked(sPreferences.getExperimentalEnableWordConfirm());
-        mExperimentalEnableWordConfirm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                mExperimentalEnableWordConfirm.setChecked((Boolean) newValue);
-                sPreferences.setExperimentalEnableWordConfirm((Boolean) newValue);
-                return false;
-            }
-        });
-    }
-
-    private static void initializeExperimentalEnableLinkedIdentities(final SwitchPreference mExperimentalEnableLinkedIdentities) {
-        mExperimentalEnableLinkedIdentities.setChecked(sPreferences.getExperimentalEnableLinkedIdentities());
-        mExperimentalEnableLinkedIdentities.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                mExperimentalEnableLinkedIdentities.setChecked((Boolean) newValue);
-                sPreferences.setExperimentalEnableLinkedIdentities((Boolean) newValue);
-                return false;
-            }
-        });
-    }
-
-    private static void initializeExperimentalEnableKeybase(final SwitchPreference mExperimentalKeybase) {
-        mExperimentalKeybase.setChecked(sPreferences.getExperimentalEnableKeybase());
-        mExperimentalKeybase.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                mExperimentalKeybase.setChecked((Boolean) newValue);
-                sPreferences.setExperimentalEnableKeybase((Boolean) newValue);
-                return false;
-            }
-        });
-    }
 }
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 e1b796f38..ea87aa113 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -1038,18 +1038,12 @@ public class ViewKeyActivity extends BaseNfcActivity implements
                 KeychainContract.Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
         String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob);
 
-        ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);
+        ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null);
         ArrayList<ParcelableKeyRing> entries = new ArrayList<>();
         entries.add(keyEntry);
         mKeyList = entries;
 
-        // search config
-        {
-            Preferences prefs = Preferences.getPreferences(this);
-            Preferences.CloudSearchPrefs cloudPrefs =
-                    new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
-            mKeyserver = cloudPrefs.keyserver;
-        }
+        mKeyserver = Preferences.getPreferences(this).getPreferredKeyserver();
 
         mOperationHelper.cryptoOperation();
     }
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java
index a55249842..d3cf74d16 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java
@@ -57,7 +57,7 @@ public class EmailKeyHelper {
             // Put them in a list and import
             ArrayList<ParcelableKeyRing> keys = new ArrayList<>(entries.size());
             for (ImportKeysListEntry entry : entries) {
-                keys.add(new ParcelableKeyRing(entry.getFingerprintHex(), entry.getKeyIdHex(), null));
+                keys.add(new ParcelableKeyRing(entry.getFingerprintHex(), entry.getKeyIdHex()));
             }
             mKeyList = keys;
             mKeyserver = keyserver;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
index 559c5556f..ce81bbcac 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
@@ -21,10 +21,10 @@ package org.sufficientlysecure.keychain.util;
 import android.content.Context;
 import android.content.SharedPreferences;
 
-import android.content.res.Resources;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.preference.PreferenceManager;
 import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
 
 import org.sufficientlysecure.keychain.Constants;
 import org.sufficientlysecure.keychain.Constants.Pref;
@@ -42,7 +42,6 @@ import java.util.Vector;
 public class Preferences {
     private static Preferences sPreferences;
     private SharedPreferences mSharedPreferences;
-    private Resources mResources;
 
     private static String PREF_FILE_NAME = "APG.main";
     private static int PREF_FILE_MODE = Context.MODE_MULTI_PROCESS;
@@ -62,10 +61,15 @@ public class Preferences {
     }
 
     private Preferences(Context context) {
-        mResources = context.getResources();
         updateSharedPreferences(context);
     }
 
+    /**
+     * Makes android's preference framework write to our file instead of default.
+     * This allows us to use the "persistent" attribute to simplify code, which automatically
+     * writes and reads preference values.
+     * @param manager
+     */
     public static void setPreferenceManagerFileAndMode(PreferenceManager manager) {
         manager.setSharedPreferencesName(PREF_FILE_NAME);
         manager.setSharedPreferencesMode(PREF_FILE_MODE);
@@ -130,12 +134,6 @@ public class Preferences {
         return mSharedPreferences.getBoolean(Pref.USE_NUMKEYPAD_FOR_YUBIKEY_PIN, true);
     }
 
-    public void setUseNumKeypadForYubiKeyPin(boolean useNumKeypadForYubikeyPin) {
-        SharedPreferences.Editor editor = mSharedPreferences.edit();
-        editor.putBoolean(Pref.USE_NUMKEYPAD_FOR_YUBIKEY_PIN, useNumKeypadForYubikeyPin);
-        editor.commit();
-    }
-
     public void setFirstTime(boolean value) {
         SharedPreferences.Editor editor = mSharedPreferences.edit();
         editor.putBoolean(Constants.Pref.FIRST_TIME, value);
@@ -181,18 +179,6 @@ public class Preferences {
         editor.commit();
     }
 
-    public void setSearchKeyserver(boolean searchKeyserver) {
-        SharedPreferences.Editor editor = mSharedPreferences.edit();
-        editor.putBoolean(Pref.SEARCH_KEYSERVER, searchKeyserver);
-        editor.commit();
-    }
-
-    public void setSearchKeybase(boolean searchKeybase) {
-        SharedPreferences.Editor editor = mSharedPreferences.edit();
-        editor.putBoolean(Pref.SEARCH_KEYBASE, searchKeybase);
-        editor.commit();
-    }
-
     public void setFilesUseCompression(boolean compress) {
         SharedPreferences.Editor editor = mSharedPreferences.edit();
         editor.putBoolean(Pref.FILE_USE_COMPRESSION, compress);
@@ -266,17 +252,6 @@ public class Preferences {
         return Integer.parseInt(mSharedPreferences.getString(Pref.PROXY_PORT, "-1"));
     }
 
-    /**
-     * we store port as String for easy interfacing with EditTextPreference, but return it as an integer
-     *
-     * @param port proxy port
-     */
-    public void setProxyPort(String port) {
-        SharedPreferences.Editor editor = mSharedPreferences.edit();
-        editor.putString(Pref.PROXY_PORT, port);
-        editor.commit();
-    }
-
     public Proxy.Type getProxyType() {
         final String typeHttp = Pref.ProxyType.TYPE_HTTP;
         final String typeSocks = Pref.ProxyType.TYPE_SOCKS;
@@ -338,12 +313,14 @@ public class Preferences {
     public CloudSearchPrefs getCloudSearchPrefs() {
         return new CloudSearchPrefs(mSharedPreferences.getBoolean(Pref.SEARCH_KEYSERVER, true),
                 mSharedPreferences.getBoolean(Pref.SEARCH_KEYBASE, true),
+                false,
                 getPreferredKeyserver());
     }
 
-    public static class CloudSearchPrefs {
+    public static class CloudSearchPrefs implements Parcelable {
         public final boolean searchKeyserver;
         public final boolean searchKeybase;
+        public final boolean searchFacebook;
         public final String keyserver;
 
         /**
@@ -351,41 +328,58 @@ public class Preferences {
          * @param searchKeybase   should keybase.io be searched
          * @param keyserver       the keyserver url authority to search on
          */
-        public CloudSearchPrefs(boolean searchKeyserver, boolean searchKeybase, String keyserver) {
+        public CloudSearchPrefs(boolean searchKeyserver, boolean searchKeybase,
+                                boolean searchFacebook, String keyserver) {
             this.searchKeyserver = searchKeyserver;
             this.searchKeybase = searchKeybase;
+            this.searchFacebook = searchFacebook;
             this.keyserver = keyserver;
         }
-    }
 
-    // experimental prefs
+        protected CloudSearchPrefs(Parcel in) {
+            searchKeyserver = in.readByte() != 0x00;
+            searchKeybase = in.readByte() != 0x00;
+            searchFacebook = in.readByte() != 0x00;
+            keyserver = in.readString();
+        }
 
-    public void setExperimentalEnableWordConfirm(boolean enableWordConfirm) {
-        SharedPreferences.Editor editor = mSharedPreferences.edit();
-        editor.putBoolean(Pref.EXPERIMENTAL_ENABLE_WORD_CONFIRM, enableWordConfirm);
-        editor.commit();
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeByte((byte) (searchKeyserver ? 0x01 : 0x00));
+            dest.writeByte((byte) (searchKeybase ? 0x01 : 0x00));
+            dest.writeByte((byte) (searchFacebook ? 0x01 : 0x00));
+            dest.writeString(keyserver);
+        }
+
+        public static final Parcelable.Creator<CloudSearchPrefs> CREATOR
+                = new Parcelable.Creator<CloudSearchPrefs>() {
+            @Override
+            public CloudSearchPrefs createFromParcel(Parcel in) {
+                return new CloudSearchPrefs(in);
+            }
+
+            @Override
+            public CloudSearchPrefs[] newArray(int size) {
+                return new CloudSearchPrefs[size];
+            }
+        };
     }
 
+    // experimental prefs
+
     public boolean getExperimentalEnableWordConfirm() {
         return mSharedPreferences.getBoolean(Pref.EXPERIMENTAL_ENABLE_WORD_CONFIRM, false);
     }
 
-    public void setExperimentalEnableLinkedIdentities(boolean enableLinkedIdentities) {
-        SharedPreferences.Editor editor = mSharedPreferences.edit();
-        editor.putBoolean(Pref.EXPERIMENTAL_ENABLE_LINKED_IDENTITIES, enableLinkedIdentities);
-        editor.commit();
-    }
-
     public boolean getExperimentalEnableLinkedIdentities() {
         return mSharedPreferences.getBoolean(Pref.EXPERIMENTAL_ENABLE_LINKED_IDENTITIES, false);
     }
 
-    public void setExperimentalEnableKeybase(boolean enableKeybase) {
-        SharedPreferences.Editor editor = mSharedPreferences.edit();
-        editor.putBoolean(Pref.EXPERIMENTAL_ENABLE_KEYBASE, enableKeybase);
-        editor.commit();
-    }
-
     public boolean getExperimentalEnableKeybase() {
         return mSharedPreferences.getBoolean(Pref.EXPERIMENTAL_ENABLE_KEYBASE, false);
     }
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index c9eba5db2..b94c09273 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -193,6 +193,8 @@
     <string name="pref_keyserver_summary">"Search keys on selected OpenPGP keyservers (HKP protocol)"</string>
     <string name="pref_keybase">"keybase.io"</string>
     <string name="pref_keybase_summary">"Search keys on keybase.io"</string>
+    <string name="pref_facebook">"Facebook"</string>
+    <string name="pref_facebook_summary">"Search keys on Facebook by username"</string>
 
     <string name="label_sync_settings_keyserver_title">"Automatic key updates"</string>
     <string name="label_sync_settings_keyserver_summary_on">"Every three days, keys are updated from the preferred keyserver"</string>
@@ -1294,6 +1296,7 @@
     <string name="msg_import_fetch_error_keyserver">"Could not retrieve key from keyservers: %s"</string>
     <string name="msg_import_fetch_error_keyserver_secret">"Cannot import secret key from keyserver!"</string>
     <string name="msg_import_fetch_keybase">"Retrieving from keybase.io: %s"</string>
+    <string name="msg_import_fetch_facebook">"Retrieving from Facebook: %s"</string>
     <string name="msg_import_fetch_keyserver">"Retrieving from keyserver: %s"</string>
     <string name="msg_import_fetch_keyserver_ok">"Key retrieval successful"</string>
     <string name="msg_import_keyserver">"Using keyserver %s"</string>
diff --git a/OpenKeychain/src/main/res/xml/experimental_preferences.xml b/OpenKeychain/src/main/res/xml/experimental_preferences.xml
index ff1fa5a95..1c8fae46f 100644
--- a/OpenKeychain/src/main/res/xml/experimental_preferences.xml
+++ b/OpenKeychain/src/main/res/xml/experimental_preferences.xml
@@ -1,6 +1,6 @@
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
     <Preference
-        android:persistent="false"
+        android:persistent="true"
         android:selectable="false"
         android:title="@string/label_experimental_settings_desc_title"
         android:summary="@string/label_experimental_settings_desc_summary" />
@@ -8,21 +8,21 @@
     <SwitchPreference
         android:defaultValue="false"
         android:key="experimentalEnableWordConfirm"
-        android:persistent="false"
+        android:persistent="true"
         android:summary="@string/label_experimental_settings_word_confirm_summary"
         android:title="@string/label_experimental_settings_word_confirm_title" />
 
     <SwitchPreference
         android:defaultValue="false"
         android:key="experimentalEnableLinkedIdentities"
-        android:persistent="false"
+        android:persistent="true"
         android:summary="@string/label_experimental_settings_linked_identities_summary"
         android:title="@string/label_experimental_settings_linked_identities_title" />
 
     <SwitchPreference
         android:defaultValue="false"
         android:key="experimentalEnableKeybase"
-        android:persistent="false"
+        android:persistent="true"
         android:summary="@string/label_experimental_settings_keybase_summary"
         android:title="@string/label_experimental_settings_keybase_title" />
 
@@ -31,7 +31,7 @@
         android:entries="@array/theme_entries"
         android:entryValues="@array/theme_values"
         android:key="theme"
-        android:persistent="false"
+        android:persistent="true"
         android:title="@string/label_theme" />
 
 </PreferenceScreen>
diff --git a/OpenKeychain/src/main/res/xml/passphrase_preferences.xml b/OpenKeychain/src/main/res/xml/passphrase_preferences.xml
index 86de8a4b7..75f293c43 100644
--- a/OpenKeychain/src/main/res/xml/passphrase_preferences.xml
+++ b/OpenKeychain/src/main/res/xml/passphrase_preferences.xml
@@ -7,11 +7,11 @@
         android:title="@string/label_passphrase_cache_ttl" />
     <CheckBoxPreference
         android:key="passphraseCacheSubs"
-        android:persistent="false"
+        android:persistent="true"
         android:title="@string/label_passphrase_cache_subs" />
     <CheckBoxPreference
         android:defaultValue="false"
         android:key="useNumKeypadForYubikeyPin"
-        android:persistent="false"
+        android:persistent="true"
         android:title="@string/label_use_num_keypad_for_yubikey_pin" />
 </PreferenceScreen>
-- 
cgit v1.2.3