diff --git a/F-Droid/res/layout/swap_wifi_qr.xml b/F-Droid/res/layout/swap_wifi_qr.xml
index 44967077a..3e461aeaa 100644
--- a/F-Droid/res/layout/swap_wifi_qr.xml
+++ b/F-Droid/res/layout/swap_wifi_qr.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 
-<ScrollView
+<org.fdroid.fdroid.views.swap.views.WifiQrView
     xmlns:tools="http://schemas.android.com/tools"
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="match_parent"
@@ -50,4 +50,4 @@
 
     </LinearLayout>
 
-</ScrollView>
\ No newline at end of file
+</org.fdroid.fdroid.views.swap.views.WifiQrView>
\ No newline at end of file
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java b/F-Droid/src/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java
index 8580e1b2d..f8fc37ace 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java
@@ -1,7 +1,9 @@
 package org.fdroid.fdroid.views.swap;
 
+import android.app.Activity;
 import android.app.ProgressDialog;
 import android.content.Context;
+import android.content.Intent;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -15,11 +17,16 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Toast;
 
+import com.google.zxing.integration.android.IntentIntegrator;
+import com.google.zxing.integration.android.IntentResult;
+
+import org.fdroid.fdroid.FDroid;
 import org.fdroid.fdroid.FDroidApp;
 import org.fdroid.fdroid.NfcHelper;
 import org.fdroid.fdroid.Preferences;
 import org.fdroid.fdroid.R;
 import org.fdroid.fdroid.Utils;
+import org.fdroid.fdroid.data.NewRepoConfig;
 import org.fdroid.fdroid.localrepo.LocalRepoManager;
 
 import java.util.Set;
@@ -39,6 +46,8 @@ public class SwapWorkflowActivity extends FragmentActivity {
         boolean buildMenu(Menu menu, @NonNull MenuInflater inflater);
     }
 
+    private static final int CONNECT_TO_SWAP = 1;
+
     private State currentState = State.INTRO;
     private InnerView currentView;
     private boolean hasPreparedLocalRepo = false;
@@ -115,10 +124,14 @@ public class SwapWorkflowActivity extends FragmentActivity {
     public void onJoinWifiComplete() {
         ensureLocalRepoRunning();
         if (!attemptToShowNfc()) {
-            // showWifiQr();
+            showWifiQr();
         }
     }
 
+    public void showWifiQr() {
+        inflateInnerView(R.layout.swap_wifi_qr);
+    }
+
     private boolean attemptToShowNfc() {
         // TODO: What if NFC is disabled? Hook up with NfcNotEnabledActivity? Or maybe only if they
         // click a relevant button?
@@ -169,6 +182,23 @@ public class SwapWorkflowActivity extends FragmentActivity {
         finish();
     }
 
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
+        IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
+        if (scanResult != null) {
+            if (scanResult.getContents() != null) {
+                NewRepoConfig repoConfig = new NewRepoConfig(this, scanResult.getContents());
+                if (repoConfig.isValidRepo()) {
+                    startActivityForResult(new Intent(FDroid.ACTION_ADD_REPO, Uri.parse(scanResult.getContents()), this, ConnectSwapActivity.class), CONNECT_TO_SWAP);
+                } else {
+                    Toast.makeText(this, "The QR code you scanned doesn't look like a swap code.", Toast.LENGTH_SHORT).show();
+                }
+            }
+        } else if (requestCode == CONNECT_TO_SWAP && resultCode == Activity.RESULT_OK) {
+            finish();
+        }
+    }
+
     class UpdateAsyncTask extends AsyncTask<Void, String, Void> {
 
         @SuppressWarnings("UnusedDeclaration")
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/views/NfcView.java b/F-Droid/src/org/fdroid/fdroid/views/swap/views/NfcView.java
index 8faf61da3..63ce23c61 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/swap/views/NfcView.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/views/NfcView.java
@@ -36,6 +36,11 @@ public class NfcView extends RelativeLayout implements SwapWorkflowActivity.Inne
         super(context, attrs, defStyleAttr, defStyleRes);
     }
 
+    private SwapWorkflowActivity getActivity() {
+        // TODO: Try and find a better way to get to the SwapActivity, which makes less asumptions.
+        return (SwapWorkflowActivity)getContext();
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
@@ -56,7 +61,7 @@ public class NfcView extends RelativeLayout implements SwapWorkflowActivity.Inne
         next.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
             @Override
             public boolean onMenuItemClick(MenuItem item) {
-                // TODO: Show QR Code.
+                getActivity().showWifiQr();
                 return true;
             }
         });
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/views/WifiQrView.java b/F-Droid/src/org/fdroid/fdroid/views/swap/views/WifiQrView.java
new file mode 100644
index 000000000..09b5d66c6
--- /dev/null
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/views/WifiQrView.java
@@ -0,0 +1,159 @@
+package org.fdroid.fdroid.views.swap.views;
+
+import android.annotation.TargetApi;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.LightingColorFilter;
+import android.net.Uri;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.v4.content.LocalBroadcastManager;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import com.google.zxing.integration.android.IntentIntegrator;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.client.utils.URLEncodedUtils;
+import org.fdroid.fdroid.FDroidApp;
+import org.fdroid.fdroid.Preferences;
+import org.fdroid.fdroid.QrGenAsyncTask;
+import org.fdroid.fdroid.R;
+import org.fdroid.fdroid.Utils;
+import org.fdroid.fdroid.net.WifiStateChangeService;
+import org.fdroid.fdroid.views.swap.SwapWorkflowActivity;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Locale;
+
+public class WifiQrView extends ScrollView implements SwapWorkflowActivity.InnerView {
+
+    private static final String TAG = "WifiQrView";
+
+    public WifiQrView(Context context) {
+        super(context);
+    }
+
+    public WifiQrView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public WifiQrView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public WifiQrView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    private SwapWorkflowActivity getActivity() {
+        // TODO: Try and find a better way to get to the SwapActivity, which makes less asumptions.
+        return (SwapWorkflowActivity)getContext();
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        setUIFromWifi();
+
+        ImageView qrImage = (ImageView)findViewById(R.id.wifi_qr_code);
+
+        // Replace all blacks with the background blue.
+        qrImage.setColorFilter(new LightingColorFilter(0xffffffff, getResources().getColor(R.color.swap_blue)));
+
+        Button openQr = (Button)findViewById(R.id.btn_qr_scanner);
+        openQr.setOnClickListener(new Button.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                IntentIntegrator integrator = new IntentIntegrator(getActivity());
+                integrator.initiateScan();
+            }
+        });
+
+        Button cancel = (Button)findViewById(R.id.btn_cancel_swap);
+        cancel.setOnClickListener(new Button.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                getActivity().stopSwapping();
+            }
+        });
+
+        LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        setUIFromWifi();
+                    }
+                },
+                new IntentFilter(WifiStateChangeService.BROADCAST)
+        );
+    }
+
+    @Override
+    public boolean buildMenu(Menu menu, @NonNull MenuInflater inflater) {
+        return false;
+    }
+
+    private void setUIFromWifi() {
+
+        if (TextUtils.isEmpty(FDroidApp.repo.address))
+            return;
+
+        String scheme = Preferences.get().isLocalRepoHttpsEnabled() ? "https://" : "http://";
+
+        // the fingerprint is not useful on the button label
+        String buttonLabel = scheme + FDroidApp.ipAddressString + ":" + FDroidApp.port;
+        TextView ipAddressView = (TextView) findViewById(R.id.device_ip_address);
+        ipAddressView.setText(buttonLabel);
+
+        /*
+         * Set URL to UPPER for compact QR Code, FDroid will translate it back.
+         * Remove the SSID from the query string since SSIDs are case-sensitive.
+         * Instead the receiver will have to rely on the BSSID to find the right
+         * wifi AP to join. Lots of QR Scanners are buggy and do not respect
+         * custom URI schemes, so we have to use http:// or https:// :-(
+         */
+        Uri sharingUri = Utils.getSharingUri(FDroidApp.repo);
+        String qrUriString = (scheme + sharingUri.getHost()).toUpperCase(Locale.ENGLISH);
+        if (sharingUri.getPort() != 80) {
+            qrUriString += ":" + sharingUri.getPort();
+        }
+        qrUriString += sharingUri.getPath().toUpperCase(Locale.ENGLISH);
+        boolean first = true;
+
+        // Andorid provides an API for getting the query parameters and iterating over them:
+        //   Uri.getQueryParameterNames()
+        // But it is only available on later Android versions. As such we use URLEncodedUtils instead.
+        List<NameValuePair> parameters = URLEncodedUtils.parse(URI.create(sharingUri.toString()), "UTF-8");
+        for (NameValuePair parameter : parameters) {
+            if (!parameter.getName().equals("ssid")) {
+                if (first) {
+                    qrUriString += "?";
+                    first = false;
+                } else {
+                    qrUriString += "&";
+                }
+                qrUriString += parameter.getName().toUpperCase(Locale.ENGLISH) + "=" +
+                        parameter.getValue().toUpperCase(Locale.ENGLISH);
+            }
+        }
+
+        Log.i(TAG, "Encoded swap URI in QR Code: " + qrUriString);
+
+        new QrGenAsyncTask(getActivity(), R.id.wifi_qr_code).execute(qrUriString);
+
+    }
+
+}