diff --git a/app/build.gradle b/app/build.gradle
index 389288879df8214d5dab193fbc21897b4751915c..88ed1bea0734099fcae924c09f33a024c9e6c70d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -11,8 +11,8 @@ android {
         applicationId "de.ccc.events.badge.card10"
         minSdkVersion 21
         targetSdkVersion 29
-        versionCode 1
-        versionName "0.1"
+        versionCode 2
+        versionName "0.2"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
     }
     buildTypes {
diff --git a/app/src/main/java/de/ccc/events/badge/card10/common/ConnectionService.kt b/app/src/main/java/de/ccc/events/badge/card10/common/ConnectionService.kt
index 007473bc65f86069a885fef7353d939849a4b972..a5d97c3b4d0bf0979160d021a33605fe7a928bf8 100644
--- a/app/src/main/java/de/ccc/events/badge/card10/common/ConnectionService.kt
+++ b/app/src/main/java/de/ccc/events/badge/card10/common/ConnectionService.kt
@@ -42,7 +42,7 @@ object ConnectionService {
     private var connection: BluetoothGatt? = null
 
     private var connectionState = BluetoothGatt.STATE_DISCONNECTED
-    private var gattListeners = mutableListOf<GattListener>()
+    private var gattListeners = mutableMapOf<String, GattListener>()
 
     private val fileServiceUuid = UUID.fromString("42230100-2342-2342-2342-234223422342")
 
@@ -58,8 +58,8 @@ object ConnectionService {
 
     fun isConnected() = connectionState == BluetoothGatt.STATE_CONNECTED
 
-    fun addGattListener(listener: GattListener) {
-        gattListeners.add(listener)
+    fun addGattListener(tag: String, listener: GattListener) {
+        gattListeners[tag] = listener
     }
 
     fun connect(context: Context) {
@@ -113,8 +113,6 @@ object ConnectionService {
             connectionState = newState
             connection = gatt
 
-            gattListeners.map { it.onConnectionStateChange(newState) }
-
             when (newState) {
                 BluetoothGatt.STATE_CONNECTED -> {
                     gatt?.discoverServices()
@@ -132,6 +130,8 @@ object ConnectionService {
             mtu = newMtu - 3 // Very precise science
 
             leService?.enableNotify(gatt)
+
+            gattListeners.values.map { it.onConnectionReady() }
         }
 
         override fun onCharacteristicWrite(
@@ -145,7 +145,7 @@ object ConnectionService {
 
             connection = gatt
 
-            gattListeners.map { it.onCharacteristicWrite(characteristic, status) }
+            gattListeners.values.map { it.onCharacteristicWrite(characteristic, status) }
         }
 
         override fun onCharacteristicChanged(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?) {
@@ -155,7 +155,7 @@ object ConnectionService {
                 throw IllegalStateException()
             }
 
-            gattListeners.map { it.onCharacteristicChanged(characteristic) }
+            gattListeners.values.map { it.onCharacteristicChanged(characteristic) }
         }
     }
 
diff --git a/app/src/main/java/de/ccc/events/badge/card10/common/GattListener.kt b/app/src/main/java/de/ccc/events/badge/card10/common/GattListener.kt
index a05fd2ca0ad85677df48c7a034b5c8bf211fc95e..0dc4b7504ac845cf039804f3ddb330ded7ffc96b 100644
--- a/app/src/main/java/de/ccc/events/badge/card10/common/GattListener.kt
+++ b/app/src/main/java/de/ccc/events/badge/card10/common/GattListener.kt
@@ -27,5 +27,5 @@ import android.bluetooth.BluetoothGattCharacteristic
 interface GattListener {
     fun onCharacteristicWrite(characteristic: BluetoothGattCharacteristic, status: Int) {}
     fun onCharacteristicChanged(characteristic: BluetoothGattCharacteristic) {}
-    fun onConnectionStateChange(state: Int) {}
+    fun onConnectionReady() {}
 }
\ No newline at end of file
diff --git a/app/src/main/java/de/ccc/events/badge/card10/filetransfer/BatchTransferFragment.kt b/app/src/main/java/de/ccc/events/badge/card10/filetransfer/BatchTransferFragment.kt
index c1503eaa04ca5d090707a9a63a479c11372dabfb..5fd1fd17b32920ed82c213085142f0e4f235c73a 100644
--- a/app/src/main/java/de/ccc/events/badge/card10/filetransfer/BatchTransferFragment.kt
+++ b/app/src/main/java/de/ccc/events/badge/card10/filetransfer/BatchTransferFragment.kt
@@ -61,8 +61,7 @@ class BatchTransferFragment : Fragment(), FileTransferListener, GattListener {
         progress.max = 5
 
         button_cancel.setOnClickListener {
-//            isCancelled = true
-            startTransfer()
+            isCancelled = true
         }
 
         button_done.setOnClickListener {
@@ -78,6 +77,7 @@ class BatchTransferFragment : Fragment(), FileTransferListener, GattListener {
 
     private fun initConnection() {
         val ctx = context ?: throw IllegalStateException()
+        ConnectionService.addGattListener("batchfiletransfer", this)
         ConnectionService.connect(ctx)
     }
 
@@ -91,6 +91,7 @@ class BatchTransferFragment : Fragment(), FileTransferListener, GattListener {
     }
 
     private fun transferNext() {
+        Thread.sleep(1000)
         val item = queue.dequeue()
 
         if (item == null || isCancelled) {
@@ -115,16 +116,15 @@ class BatchTransferFragment : Fragment(), FileTransferListener, GattListener {
             val reader = ChunkedReader(ctx, transferJob.sourceUri, ConnectionService.mtu)
             val service = ConnectionService.leService ?: throw IllegalStateException()
             transfer = FileTransfer(service, reader,this, transferJob.destPath)
+            transfer?.start()
         } catch (e: Exception) {
             Log.e(TAG, "Failed to initialize transfer")
             return
         }
     }
 
-    override fun onConnectionStateChange(state: Int) {
-        if (state == BluetoothGatt.STATE_CONNECTED) {
-            startTransfer()
-        }
+    override fun onConnectionReady() {
+        startTransfer()
     }
 
     override fun onError() {
diff --git a/app/src/main/java/de/ccc/events/badge/card10/filetransfer/FileTransfer.kt b/app/src/main/java/de/ccc/events/badge/card10/filetransfer/FileTransfer.kt
index 413fdc68ddd64fff4621ed2b84762f7141dc9daa..1ce612075333b58834f9addc38a63198727ccd02 100644
--- a/app/src/main/java/de/ccc/events/badge/card10/filetransfer/FileTransfer.kt
+++ b/app/src/main/java/de/ccc/events/badge/card10/filetransfer/FileTransfer.kt
@@ -41,7 +41,7 @@ class FileTransfer(
     private var currentState = TransferState.IDLE
 
     init {
-        service.addOnPacketReceivedListener(this)
+        service.setOnPacketReceivedListener(this)
     }
 
     override fun onPacketReceived(packet: Packet) {
@@ -98,13 +98,13 @@ class FileTransfer(
             throw IllegalStateException()
         }
 
+        currentState = TransferState.START_SENT
         service.sendPacket(
             Packet(
                 PacketType.START,
                 destinationPath.toByteArray(Charset.forName("ASCII"))
             )
         )
-        currentState = TransferState.START_SENT
     }
 
     private fun sendNext() {
diff --git a/app/src/main/java/de/ccc/events/badge/card10/filetransfer/FileTransferFragment.kt b/app/src/main/java/de/ccc/events/badge/card10/filetransfer/FileTransferFragment.kt
index acc94330a9f8959ff025f0fd89a5cec0cc8dda5f..0730711d4eeae9413d1b3e3290e6844803863ffd 100644
--- a/app/src/main/java/de/ccc/events/badge/card10/filetransfer/FileTransferFragment.kt
+++ b/app/src/main/java/de/ccc/events/badge/card10/filetransfer/FileTransferFragment.kt
@@ -34,11 +34,14 @@ import android.widget.EditText
 import android.widget.ProgressBar
 import android.widget.TextView
 import androidx.annotation.UiThread
+import androidx.appcompat.app.AlertDialog
 import androidx.fragment.app.Fragment
 import de.ccc.events.badge.card10.CARD10_BLUETOOTH_MAC_PREFIX
 import de.ccc.events.badge.card10.R
+import de.ccc.events.badge.card10.common.ConnectionException
 import de.ccc.events.badge.card10.common.ConnectionService
 import de.ccc.events.badge.card10.common.GattListener
+import de.ccc.events.badge.card10.main.MainFragment
 import java.lang.Exception
 import java.lang.IllegalStateException
 
@@ -76,8 +79,15 @@ class FileTransferFragment : Fragment(), GattListener, FileTransferListener{
 
         buttonStartStop = view.findViewById(R.id.button_start_stop_transfer)
 
-        initConnection()
-        toggleControls()
+        try {
+            initConnection()
+            toggleControls()
+        } catch (e: ConnectionException) {
+            showError(e.message)
+        } catch (e: Exception) {
+            showError(getString(R.string.connection_error_generic))
+        }
+
     }
 
     private fun initConnection() {
@@ -160,4 +170,21 @@ class FileTransferFragment : Fragment(), GattListener, FileTransferListener{
         }
         toggleControls()
     }
+
+    private fun showError(message: String?) {
+        val ctx = context ?: throw IllegalStateException()
+        val fm = fragmentManager ?: throw IllegalStateException()
+
+        val errorDialog =
+            AlertDialog.Builder(ctx)
+                .setMessage(message ?: getString(R.string.connection_error_generic))
+                .setPositiveButton(R.string.dialog_action_ok) {
+                    dialog, _ -> dialog.dismiss()
+                    fm.beginTransaction()
+                        .replace(R.id.fragment_container, MainFragment())
+                        .addToBackStack(null)
+                        .commit()
+                }
+                .show()
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/de/ccc/events/badge/card10/filetransfer/LowEffortService.kt b/app/src/main/java/de/ccc/events/badge/card10/filetransfer/LowEffortService.kt
index d76380ba4fa6c41e0ee789474dfda4fde116d53b..c77e23ca5f1cafe58afa437d4b80aa9f83641ac7 100644
--- a/app/src/main/java/de/ccc/events/badge/card10/filetransfer/LowEffortService.kt
+++ b/app/src/main/java/de/ccc/events/badge/card10/filetransfer/LowEffortService.kt
@@ -43,7 +43,7 @@ class LowEffortService(
     private val centralRxCharacteristicUuid = UUID.fromString("42230102-2342-2342-2342-234223422342")
 
     private var notifyEnabled = false
-    private val listeners = mutableListOf<OnPacketReceivedListener>()
+    private var listener: OnPacketReceivedListener? = null
 
     init {
         val tx = service.getCharacteristic(centralTxCharacteristicUuid)
@@ -58,7 +58,7 @@ class LowEffortService(
         centralTx = tx
         centralRx = rx
 
-        ConnectionService.addGattListener(this)
+        ConnectionService.addGattListener("filetransfer",this)
     }
 
     fun enableNotify(gatt: BluetoothGatt) {
@@ -89,8 +89,8 @@ class LowEffortService(
         return status
     }
 
-    fun addOnPacketReceivedListener(listener: OnPacketReceivedListener) {
-        listeners.add(listener)
+    fun setOnPacketReceivedListener(packetListener: OnPacketReceivedListener) {
+        listener = packetListener
     }
 
     // GattListener methods
@@ -99,6 +99,6 @@ class LowEffortService(
     }
 
     override fun onCharacteristicChanged(characteristic: BluetoothGattCharacteristic) {
-        listeners.map { it.onPacketReceived(Packet.fromBytes(characteristic.value)) }
+        listener?.onPacketReceived(Packet.fromBytes(characteristic.value))
     }
 }
\ No newline at end of file
diff --git a/app/src/main/java/de/ccc/events/badge/card10/hatchery/AppDetailFragment.kt b/app/src/main/java/de/ccc/events/badge/card10/hatchery/AppDetailFragment.kt
index fe19dd4faaf32081a13e898bb1fb5143f580977f..6bb2de7f646a17a7690c71bcab8061cf2f92bf90 100644
--- a/app/src/main/java/de/ccc/events/badge/card10/hatchery/AppDetailFragment.kt
+++ b/app/src/main/java/de/ccc/events/badge/card10/hatchery/AppDetailFragment.kt
@@ -116,7 +116,7 @@ class AppDetailFragment : Fragment() {
                     targetFile.createNewFile()
                     Log.d(TAG, "Extracting ${entry.name} to ${targetFile.absolutePath}")
                     tarStream.copyTo(targetFile.outputStream())
-                    appFiles.add(TransferJob(targetFile.toUri(), "apps/${entry.name}"))
+                    appFiles.add(TransferJob(targetFile.toUri(), "/apps/${entry.name}"))
                 }
 
                 val launcher = createLauncher(app.slug, cacheDir)
@@ -156,12 +156,12 @@ class AppDetailFragment : Fragment() {
             val src = """
                 # Launcher script for $slug
                 import os
-                os.exec("apps/$slug/__init__.py")
+                os.exec("/apps/$slug/__init__.py")
             """.trimIndent()
 
             file.writeText(src)
 
-            return TransferJob(file.toUri(), fileName)
+            return TransferJob(file.toUri(), "/$fileName")
         }
     }
 }
diff --git a/app/src/main/res/drawable-hdpi/ic_launcher.png b/app/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..b4ad371c074af03eb310d9a7ea1f6ff3733e89f1
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/layout/main_fragment.xml b/app/src/main/res/layout/main_fragment.xml
index 561d2d5deb6556fca58efa525d2776d3177fd42a..5151484721f2e56c89a3d5f19b0d16ba693e7d76 100644
--- a/app/src/main/res/layout/main_fragment.xml
+++ b/app/src/main/res/layout/main_fragment.xml
@@ -52,8 +52,7 @@
                 android:text="@string/main_button_browse_apps"
                 app:layout_constraintLeft_toLeftOf="parent"
                 app:layout_constraintRight_toRightOf="parent"
-                app:layout_constraintTop_toTopOf="parent"
-                android:enabled="false"/>
+                app:layout_constraintTop_toTopOf="parent"/>
 
         <Button android:layout_width="wrap_content"
                 android:layout_height="wrap_content"