diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index d113d3511..000000000
--- a/.gitmodules
+++ /dev/null
@@ -1,4 +0,0 @@
-[submodule "extern/Support"]
-	path = extern/Support
-	url = https://android.googlesource.com/platform/frameworks/support
-	ignore = dirty
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a71f271dd..458388b1b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,9 @@
-### Upcoming release
+### 0.95 (2015-08-04)
 
-* Adapt UI to use Material Design
+* Start porting UI to Material Design, including a new launcher icon
+
+* Add support for app changelog links, which will appear for apps that have
+  them once the repositories have been updated again
 
 * Redesign the App Details view with larger icons, expandable description and
   links with icons
@@ -11,11 +14,16 @@
 * Remove the root installer, since the system installer is safer, more stable
   and now easy to set up with root privileges
 
+* Speed up and simplify repo update process by streaming the data out of the
+  jar file directly
+
 * Can now manually add swap repo via "Repositories" screen
 
 * Using NFC during swap now initiates a proper swap, rather than redirecting to
   the "Repositories" screen
 
+* Drop ant support to greatly simplify the build process and its maintenance
+
 ### 0.92 (2015-06-08)
 
 * Make swap only in portrait mode to prevent crashes and issues where UI elements are obscured
diff --git a/F-Droid/AndroidManifest.xml b/F-Droid/AndroidManifest.xml
index b75b27df5..9344576ad 100644
--- a/F-Droid/AndroidManifest.xml
+++ b/F-Droid/AndroidManifest.xml
@@ -3,8 +3,8 @@
     xmlns:tools="http://schemas.android.com/tools"
     package="org.fdroid.fdroid"
     android:installLocation="auto"
-    android:versionCode="940"
-    android:versionName="0.94-test"
+    android:versionCode="95050"
+    android:versionName="0.95"
     >
 
     <uses-sdk
@@ -62,7 +62,7 @@
         android:label="@string/app_name"
         android:description="@string/app_description"
         android:allowBackup="true"
-        android:theme="@style/AppThemeDark"
+        android:theme="@style/AppThemeLight"
         android:supportsRtl="true"
         >
 
diff --git a/F-Droid/build.gradle b/F-Droid/build.gradle
index 1d994dc85..5f6240f5a 100644
--- a/F-Droid/build.gradle
+++ b/F-Droid/build.gradle
@@ -109,8 +109,7 @@ task binaryDeps(type: Copy, dependsOn: ':F-Droid:prepareReleaseDependencies') {
         include 'support-v4-preferencefragment/build/outputs/aar/support-v4-preferencefragment-release.aar',
                 'nanohttpd/core/build/libs/nanohttpd-2.1.0.jar',
                 'zipsigner/build/libs/zipsigner.jar',
-                'jmdns/build/libs/jmdns.jar',
-                'Support/v4/build/libs/support-v4.jar'
+                'jmdns/build/libs/jmdns.jar'
     }
 
     into 'libs/binaryDeps'
@@ -174,6 +173,9 @@ android {
     lintOptions {
         checkReleaseBuilds false
         abortOnError false
+
+        // Our translations are crowd-sourced
+        disable 'MissingTranslation'
     }
 
     // Enable all Android lint warnings
diff --git a/F-Droid/libs/README.md b/F-Droid/libs/README.md
index a338d672c..430057598 100644
--- a/F-Droid/libs/README.md
+++ b/F-Droid/libs/README.md
@@ -1,8 +1,10 @@
 # Licenses
 
-To see which license any one of these libraries is under, consult the extern/ directory.
-If you have checked out the source for all dependencies (`git submodule update --init` from the root directory),
-then you should be able to find the relevant LICENSE file for each, or else you can consult upstream.
+To see which license any one of these libraries is under, consult the extern/
+directory. If you have checked out the source for all dependencies (`git
+submodule update --init` from the root directory if there are any submodules),
+then you should be able to find the relevant LICENSE file for each, or else
+you can consult upstream.
 
 
 # Building libraries from source
diff --git a/F-Droid/libs/android-support-v4.jar b/F-Droid/libs/android-support-v4.jar
deleted file mode 100644
index 4ebdaa9ed..000000000
Binary files a/F-Droid/libs/android-support-v4.jar and /dev/null differ
diff --git a/F-Droid/lint.xml b/F-Droid/lint.xml
deleted file mode 100644
index 348e7f6ce..000000000
--- a/F-Droid/lint.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<lint>
-    <!-- Remove severity="ignore" to see the missing translations -->
-    <issue id="MissingTranslation" severity="ignore" />
-    <issue id="TrulyRandom" severity="ignore" />
-    <issue id="UnusedResources" severity="ignore">
-        <ignore path="res/values/default_repo.xml" />
-    </issue>
-</lint>
\ No newline at end of file
diff --git a/F-Droid/res/drawable-hdpi/ic_add_white.png b/F-Droid/res/drawable-hdpi/ic_add_white.png
index 72cedcad4..694179bd4 100644
Binary files a/F-Droid/res/drawable-hdpi/ic_add_white.png and b/F-Droid/res/drawable-hdpi/ic_add_white.png differ
diff --git a/F-Droid/res/drawable-hdpi/ic_bluetooth_white.png b/F-Droid/res/drawable-hdpi/ic_bluetooth_white.png
index bf5912471..fce188400 100644
Binary files a/F-Droid/res/drawable-hdpi/ic_bluetooth_white.png and b/F-Droid/res/drawable-hdpi/ic_bluetooth_white.png differ
diff --git a/F-Droid/res/drawable-hdpi/ic_bullet_key_permission.png b/F-Droid/res/drawable-hdpi/ic_bullet_key_permission.png
deleted file mode 100644
index 4cf50ade1..000000000
Binary files a/F-Droid/res/drawable-hdpi/ic_bullet_key_permission.png and /dev/null differ
diff --git a/F-Droid/res/drawable-hdpi/ic_clear.png b/F-Droid/res/drawable-hdpi/ic_clear.png
new file mode 100644
index 000000000..1a9cd75a0
Binary files /dev/null and b/F-Droid/res/drawable-hdpi/ic_clear.png differ
diff --git a/F-Droid/res/drawable-hdpi/ic_delete_white.png b/F-Droid/res/drawable-hdpi/ic_delete_white.png
index 0e95e9b1d..4a9f76947 100644
Binary files a/F-Droid/res/drawable-hdpi/ic_delete_white.png and b/F-Droid/res/drawable-hdpi/ic_delete_white.png differ
diff --git a/F-Droid/res/drawable-hdpi/ic_do_not_disturb_white.png b/F-Droid/res/drawable-hdpi/ic_do_not_disturb_white.png
index bb07825f1..34ccc229d 100644
Binary files a/F-Droid/res/drawable-hdpi/ic_do_not_disturb_white.png and b/F-Droid/res/drawable-hdpi/ic_do_not_disturb_white.png differ
diff --git a/F-Droid/res/drawable-hdpi/ic_edit_white.png b/F-Droid/res/drawable-hdpi/ic_edit_white.png
deleted file mode 100644
index 34ec7092f..000000000
Binary files a/F-Droid/res/drawable-hdpi/ic_edit_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable-hdpi/ic_help_white.png b/F-Droid/res/drawable-hdpi/ic_help_white.png
index 1173aed08..5664f9532 100644
Binary files a/F-Droid/res/drawable-hdpi/ic_help_white.png and b/F-Droid/res/drawable-hdpi/ic_help_white.png differ
diff --git a/F-Droid/res/drawable-hdpi/ic_menu_refresh.png b/F-Droid/res/drawable-hdpi/ic_menu_refresh.png
deleted file mode 100644
index 40ab9862b..000000000
Binary files a/F-Droid/res/drawable-hdpi/ic_menu_refresh.png and /dev/null differ
diff --git a/F-Droid/res/drawable-hdpi/ic_nfc_white.png b/F-Droid/res/drawable-hdpi/ic_nfc_white.png
index 78db0e17e..8aea57d31 100644
Binary files a/F-Droid/res/drawable-hdpi/ic_nfc_white.png and b/F-Droid/res/drawable-hdpi/ic_nfc_white.png differ
diff --git a/F-Droid/res/drawable-hdpi/ic_play_arrow_white.png b/F-Droid/res/drawable-hdpi/ic_play_arrow_white.png
index 043acd808..57c9fa546 100644
Binary files a/F-Droid/res/drawable-hdpi/ic_play_arrow_white.png and b/F-Droid/res/drawable-hdpi/ic_play_arrow_white.png differ
diff --git a/F-Droid/res/drawable-hdpi/ic_refresh_white.png b/F-Droid/res/drawable-hdpi/ic_refresh_white.png
index 72128fe69..ffa7be933 100644
Binary files a/F-Droid/res/drawable-hdpi/ic_refresh_white.png and b/F-Droid/res/drawable-hdpi/ic_refresh_white.png differ
diff --git a/F-Droid/res/drawable-hdpi/ic_search_white.png b/F-Droid/res/drawable-hdpi/ic_search_white.png
index 0bbeab150..bbfbc96cb 100644
Binary files a/F-Droid/res/drawable-hdpi/ic_search_white.png and b/F-Droid/res/drawable-hdpi/ic_search_white.png differ
diff --git a/F-Droid/res/drawable-hdpi/ic_settings_white.png b/F-Droid/res/drawable-hdpi/ic_settings_white.png
index 6bb8f6e08..97ded33b5 100644
Binary files a/F-Droid/res/drawable-hdpi/ic_settings_white.png and b/F-Droid/res/drawable-hdpi/ic_settings_white.png differ
diff --git a/F-Droid/res/drawable-hdpi/ic_share_white.png b/F-Droid/res/drawable-hdpi/ic_share_white.png
index 9963c6a05..b09a6926d 100644
Binary files a/F-Droid/res/drawable-hdpi/ic_share_white.png and b/F-Droid/res/drawable-hdpi/ic_share_white.png differ
diff --git a/F-Droid/res/drawable-hdpi/ic_toc_white.png b/F-Droid/res/drawable-hdpi/ic_toc_white.png
new file mode 100644
index 000000000..bb3adfa89
Binary files /dev/null and b/F-Droid/res/drawable-hdpi/ic_toc_white.png differ
diff --git a/F-Droid/res/drawable-hdpi/ic_view_headline_white.png b/F-Droid/res/drawable-hdpi/ic_view_headline_white.png
deleted file mode 100644
index 4f9458b1a..000000000
Binary files a/F-Droid/res/drawable-hdpi/ic_view_headline_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable-ldpi/ic_bullet_key_permission.png b/F-Droid/res/drawable-ldpi/ic_bullet_key_permission.png
deleted file mode 100644
index 4aff20ce9..000000000
Binary files a/F-Droid/res/drawable-ldpi/ic_bullet_key_permission.png and /dev/null differ
diff --git a/F-Droid/res/drawable-ldpi/ic_menu_refresh.png b/F-Droid/res/drawable-ldpi/ic_menu_refresh.png
deleted file mode 100644
index 8e3ff8927..000000000
Binary files a/F-Droid/res/drawable-ldpi/ic_menu_refresh.png and /dev/null differ
diff --git a/F-Droid/res/drawable-mdpi/ic_add_white.png b/F-Droid/res/drawable-mdpi/ic_add_white.png
index 67042105d..3856041d7 100644
Binary files a/F-Droid/res/drawable-mdpi/ic_add_white.png and b/F-Droid/res/drawable-mdpi/ic_add_white.png differ
diff --git a/F-Droid/res/drawable-mdpi/ic_bluetooth_white.png b/F-Droid/res/drawable-mdpi/ic_bluetooth_white.png
index 678678da4..27a8a719f 100644
Binary files a/F-Droid/res/drawable-mdpi/ic_bluetooth_white.png and b/F-Droid/res/drawable-mdpi/ic_bluetooth_white.png differ
diff --git a/F-Droid/res/drawable-mdpi/ic_bullet_key_permission.png b/F-Droid/res/drawable-mdpi/ic_bullet_key_permission.png
deleted file mode 100644
index 7fee56024..000000000
Binary files a/F-Droid/res/drawable-mdpi/ic_bullet_key_permission.png and /dev/null differ
diff --git a/F-Droid/res/drawable-mdpi/ic_clear.png b/F-Droid/res/drawable-mdpi/ic_clear.png
new file mode 100644
index 000000000..40a1a84e3
Binary files /dev/null and b/F-Droid/res/drawable-mdpi/ic_clear.png differ
diff --git a/F-Droid/res/drawable-mdpi/ic_delete_white.png b/F-Droid/res/drawable-mdpi/ic_delete_white.png
index cdb230c2f..e2f5f3555 100644
Binary files a/F-Droid/res/drawable-mdpi/ic_delete_white.png and b/F-Droid/res/drawable-mdpi/ic_delete_white.png differ
diff --git a/F-Droid/res/drawable-mdpi/ic_do_not_disturb_white.png b/F-Droid/res/drawable-mdpi/ic_do_not_disturb_white.png
index 4e4fa1e59..3e87dde72 100644
Binary files a/F-Droid/res/drawable-mdpi/ic_do_not_disturb_white.png and b/F-Droid/res/drawable-mdpi/ic_do_not_disturb_white.png differ
diff --git a/F-Droid/res/drawable-mdpi/ic_edit_white.png b/F-Droid/res/drawable-mdpi/ic_edit_white.png
deleted file mode 100644
index 7f0ea51bf..000000000
Binary files a/F-Droid/res/drawable-mdpi/ic_edit_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable-mdpi/ic_help_white.png b/F-Droid/res/drawable-mdpi/ic_help_white.png
index 9f8051b44..db699622b 100644
Binary files a/F-Droid/res/drawable-mdpi/ic_help_white.png and b/F-Droid/res/drawable-mdpi/ic_help_white.png differ
diff --git a/F-Droid/res/drawable-mdpi/ic_menu_refresh.png b/F-Droid/res/drawable-mdpi/ic_menu_refresh.png
deleted file mode 100644
index ceb6842af..000000000
Binary files a/F-Droid/res/drawable-mdpi/ic_menu_refresh.png and /dev/null differ
diff --git a/F-Droid/res/drawable-mdpi/ic_nfc_white.png b/F-Droid/res/drawable-mdpi/ic_nfc_white.png
index 428351e8e..cdadd9eef 100644
Binary files a/F-Droid/res/drawable-mdpi/ic_nfc_white.png and b/F-Droid/res/drawable-mdpi/ic_nfc_white.png differ
diff --git a/F-Droid/res/drawable-mdpi/ic_play_arrow_white.png b/F-Droid/res/drawable-mdpi/ic_play_arrow_white.png
index a55d19922..c61e948bb 100644
Binary files a/F-Droid/res/drawable-mdpi/ic_play_arrow_white.png and b/F-Droid/res/drawable-mdpi/ic_play_arrow_white.png differ
diff --git a/F-Droid/res/drawable-mdpi/ic_refresh_white.png b/F-Droid/res/drawable-mdpi/ic_refresh_white.png
index 5f89fc257..97e42b525 100644
Binary files a/F-Droid/res/drawable-mdpi/ic_refresh_white.png and b/F-Droid/res/drawable-mdpi/ic_refresh_white.png differ
diff --git a/F-Droid/res/drawable-mdpi/ic_search_white.png b/F-Droid/res/drawable-mdpi/ic_search_white.png
index 043759acd..faefc59c8 100644
Binary files a/F-Droid/res/drawable-mdpi/ic_search_white.png and b/F-Droid/res/drawable-mdpi/ic_search_white.png differ
diff --git a/F-Droid/res/drawable-mdpi/ic_settings_white.png b/F-Droid/res/drawable-mdpi/ic_settings_white.png
index 12e5d100d..8909c3553 100644
Binary files a/F-Droid/res/drawable-mdpi/ic_settings_white.png and b/F-Droid/res/drawable-mdpi/ic_settings_white.png differ
diff --git a/F-Droid/res/drawable-mdpi/ic_share_white.png b/F-Droid/res/drawable-mdpi/ic_share_white.png
index dd536bca2..e944fd70c 100644
Binary files a/F-Droid/res/drawable-mdpi/ic_share_white.png and b/F-Droid/res/drawable-mdpi/ic_share_white.png differ
diff --git a/F-Droid/res/drawable-mdpi/ic_toc_white.png b/F-Droid/res/drawable-mdpi/ic_toc_white.png
new file mode 100644
index 000000000..2a386add0
Binary files /dev/null and b/F-Droid/res/drawable-mdpi/ic_toc_white.png differ
diff --git a/F-Droid/res/drawable-mdpi/ic_view_headline_white.png b/F-Droid/res/drawable-mdpi/ic_view_headline_white.png
deleted file mode 100644
index e6ecf5a1d..000000000
Binary files a/F-Droid/res/drawable-mdpi/ic_view_headline_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_add_white.png b/F-Droid/res/drawable-xhdpi/ic_add_white.png
index 2bef05958..67bb598e5 100644
Binary files a/F-Droid/res/drawable-xhdpi/ic_add_white.png and b/F-Droid/res/drawable-xhdpi/ic_add_white.png differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_bluetooth_white.png b/F-Droid/res/drawable-xhdpi/ic_bluetooth_white.png
index e3fe3cfbb..920f5cae7 100644
Binary files a/F-Droid/res/drawable-xhdpi/ic_bluetooth_white.png and b/F-Droid/res/drawable-xhdpi/ic_bluetooth_white.png differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_bullet_key_permission.png b/F-Droid/res/drawable-xhdpi/ic_bullet_key_permission.png
deleted file mode 100644
index 6a0bdfcb1..000000000
Binary files a/F-Droid/res/drawable-xhdpi/ic_bullet_key_permission.png and /dev/null differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_clear.png b/F-Droid/res/drawable-xhdpi/ic_clear.png
new file mode 100644
index 000000000..6bc437298
Binary files /dev/null and b/F-Droid/res/drawable-xhdpi/ic_clear.png differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_delete_white.png b/F-Droid/res/drawable-xhdpi/ic_delete_white.png
index e69de29bb..388b5b060 100644
Binary files a/F-Droid/res/drawable-xhdpi/ic_delete_white.png and b/F-Droid/res/drawable-xhdpi/ic_delete_white.png differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_do_not_disturb_white.png b/F-Droid/res/drawable-xhdpi/ic_do_not_disturb_white.png
index 38f12a7b5..dc5e1ac59 100644
Binary files a/F-Droid/res/drawable-xhdpi/ic_do_not_disturb_white.png and b/F-Droid/res/drawable-xhdpi/ic_do_not_disturb_white.png differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_edit_white.png b/F-Droid/res/drawable-xhdpi/ic_edit_white.png
deleted file mode 100644
index 9380370f4..000000000
Binary files a/F-Droid/res/drawable-xhdpi/ic_edit_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_help_white.png b/F-Droid/res/drawable-xhdpi/ic_help_white.png
index 9b27847bb..2d11cf47a 100644
Binary files a/F-Droid/res/drawable-xhdpi/ic_help_white.png and b/F-Droid/res/drawable-xhdpi/ic_help_white.png differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_menu_refresh.png b/F-Droid/res/drawable-xhdpi/ic_menu_refresh.png
deleted file mode 100644
index b94ecf9b6..000000000
Binary files a/F-Droid/res/drawable-xhdpi/ic_menu_refresh.png and /dev/null differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_nfc_white.png b/F-Droid/res/drawable-xhdpi/ic_nfc_white.png
index 821029ce4..454a95024 100644
Binary files a/F-Droid/res/drawable-xhdpi/ic_nfc_white.png and b/F-Droid/res/drawable-xhdpi/ic_nfc_white.png differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_play_arrow_white.png b/F-Droid/res/drawable-xhdpi/ic_play_arrow_white.png
index 7cc008475..a3c80e73d 100644
Binary files a/F-Droid/res/drawable-xhdpi/ic_play_arrow_white.png and b/F-Droid/res/drawable-xhdpi/ic_play_arrow_white.png differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_refresh_white.png b/F-Droid/res/drawable-xhdpi/ic_refresh_white.png
index d271d8e03..1989184b1 100644
Binary files a/F-Droid/res/drawable-xhdpi/ic_refresh_white.png and b/F-Droid/res/drawable-xhdpi/ic_refresh_white.png differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_search_white.png b/F-Droid/res/drawable-xhdpi/ic_search_white.png
index 70c21baf7..bfc3e3939 100644
Binary files a/F-Droid/res/drawable-xhdpi/ic_search_white.png and b/F-Droid/res/drawable-xhdpi/ic_search_white.png differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_settings_white.png b/F-Droid/res/drawable-xhdpi/ic_settings_white.png
index 97e9ca945..5caedc8e5 100644
Binary files a/F-Droid/res/drawable-xhdpi/ic_settings_white.png and b/F-Droid/res/drawable-xhdpi/ic_settings_white.png differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_share_white.png b/F-Droid/res/drawable-xhdpi/ic_share_white.png
index bb521c141..22a8783e7 100644
Binary files a/F-Droid/res/drawable-xhdpi/ic_share_white.png and b/F-Droid/res/drawable-xhdpi/ic_share_white.png differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_toc_white.png b/F-Droid/res/drawable-xhdpi/ic_toc_white.png
new file mode 100644
index 000000000..7563e739c
Binary files /dev/null and b/F-Droid/res/drawable-xhdpi/ic_toc_white.png differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_view_headline_white.png b/F-Droid/res/drawable-xhdpi/ic_view_headline_white.png
deleted file mode 100644
index c66e9d234..000000000
Binary files a/F-Droid/res/drawable-xhdpi/ic_view_headline_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable-xxhdpi/ic_add_white.png b/F-Droid/res/drawable-xxhdpi/ic_add_white.png
index b12e040e0..0fdced8fc 100644
Binary files a/F-Droid/res/drawable-xxhdpi/ic_add_white.png and b/F-Droid/res/drawable-xxhdpi/ic_add_white.png differ
diff --git a/F-Droid/res/drawable-xxhdpi/ic_bluetooth_white.png b/F-Droid/res/drawable-xxhdpi/ic_bluetooth_white.png
index 6c8cb16af..860c75864 100644
Binary files a/F-Droid/res/drawable-xxhdpi/ic_bluetooth_white.png and b/F-Droid/res/drawable-xxhdpi/ic_bluetooth_white.png differ
diff --git a/F-Droid/res/drawable-xxhdpi/ic_bullet_key_permission.png b/F-Droid/res/drawable-xxhdpi/ic_bullet_key_permission.png
deleted file mode 100644
index a74c2862b..000000000
Binary files a/F-Droid/res/drawable-xxhdpi/ic_bullet_key_permission.png and /dev/null differ
diff --git a/F-Droid/res/drawable-xxhdpi/ic_clear.png b/F-Droid/res/drawable-xxhdpi/ic_clear.png
new file mode 100644
index 000000000..51b4401ca
Binary files /dev/null and b/F-Droid/res/drawable-xxhdpi/ic_clear.png differ
diff --git a/F-Droid/res/drawable-xxhdpi/ic_delete_white.png b/F-Droid/res/drawable-xxhdpi/ic_delete_white.png
index a8d8ca84d..3fcdfdb55 100644
Binary files a/F-Droid/res/drawable-xxhdpi/ic_delete_white.png and b/F-Droid/res/drawable-xxhdpi/ic_delete_white.png differ
diff --git a/F-Droid/res/drawable-xxhdpi/ic_do_not_disturb_white.png b/F-Droid/res/drawable-xxhdpi/ic_do_not_disturb_white.png
index 9ac3d7a02..349b03f6c 100644
Binary files a/F-Droid/res/drawable-xxhdpi/ic_do_not_disturb_white.png and b/F-Droid/res/drawable-xxhdpi/ic_do_not_disturb_white.png differ
diff --git a/F-Droid/res/drawable-xxhdpi/ic_edit_white.png b/F-Droid/res/drawable-xxhdpi/ic_edit_white.png
deleted file mode 100644
index fe5bd13fb..000000000
Binary files a/F-Droid/res/drawable-xxhdpi/ic_edit_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable-xxhdpi/ic_help_white.png b/F-Droid/res/drawable-xxhdpi/ic_help_white.png
index f34516ecc..d49181785 100644
Binary files a/F-Droid/res/drawable-xxhdpi/ic_help_white.png and b/F-Droid/res/drawable-xxhdpi/ic_help_white.png differ
diff --git a/F-Droid/res/drawable-xxhdpi/ic_nfc_white.png b/F-Droid/res/drawable-xxhdpi/ic_nfc_white.png
index 81b19ef04..48d8e11f6 100644
Binary files a/F-Droid/res/drawable-xxhdpi/ic_nfc_white.png and b/F-Droid/res/drawable-xxhdpi/ic_nfc_white.png differ
diff --git a/F-Droid/res/drawable-xxhdpi/ic_play_arrow_white.png b/F-Droid/res/drawable-xxhdpi/ic_play_arrow_white.png
index de7f9bdb5..547ef30aa 100644
Binary files a/F-Droid/res/drawable-xxhdpi/ic_play_arrow_white.png and b/F-Droid/res/drawable-xxhdpi/ic_play_arrow_white.png differ
diff --git a/F-Droid/res/drawable-xxhdpi/ic_refresh_white.png b/F-Droid/res/drawable-xxhdpi/ic_refresh_white.png
index 87ab2d640..1692d8a24 100644
Binary files a/F-Droid/res/drawable-xxhdpi/ic_refresh_white.png and b/F-Droid/res/drawable-xxhdpi/ic_refresh_white.png differ
diff --git a/F-Droid/res/drawable-xxhdpi/ic_search_white.png b/F-Droid/res/drawable-xxhdpi/ic_search_white.png
index 75d9aa69f..abbb98951 100644
Binary files a/F-Droid/res/drawable-xxhdpi/ic_search_white.png and b/F-Droid/res/drawable-xxhdpi/ic_search_white.png differ
diff --git a/F-Droid/res/drawable-xxhdpi/ic_settings_white.png b/F-Droid/res/drawable-xxhdpi/ic_settings_white.png
index 5940812da..eabb0a2ba 100644
Binary files a/F-Droid/res/drawable-xxhdpi/ic_settings_white.png and b/F-Droid/res/drawable-xxhdpi/ic_settings_white.png differ
diff --git a/F-Droid/res/drawable-xxhdpi/ic_share_white.png b/F-Droid/res/drawable-xxhdpi/ic_share_white.png
index cf3072ecf..a35b3cd14 100644
Binary files a/F-Droid/res/drawable-xxhdpi/ic_share_white.png and b/F-Droid/res/drawable-xxhdpi/ic_share_white.png differ
diff --git a/F-Droid/res/drawable-xxhdpi/ic_toc_white.png b/F-Droid/res/drawable-xxhdpi/ic_toc_white.png
new file mode 100644
index 000000000..108a026a9
Binary files /dev/null and b/F-Droid/res/drawable-xxhdpi/ic_toc_white.png differ
diff --git a/F-Droid/res/drawable-xxhdpi/ic_view_headline_white.png b/F-Droid/res/drawable-xxhdpi/ic_view_headline_white.png
deleted file mode 100644
index faccf3d23..000000000
Binary files a/F-Droid/res/drawable-xxhdpi/ic_view_headline_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable-xxxhdpi/ic_add_white.png b/F-Droid/res/drawable-xxxhdpi/ic_add_white.png
index cd32f0a82..d64c22e9e 100644
Binary files a/F-Droid/res/drawable-xxxhdpi/ic_add_white.png and b/F-Droid/res/drawable-xxxhdpi/ic_add_white.png differ
diff --git a/F-Droid/res/drawable-xxxhdpi/ic_bluetooth_white.png b/F-Droid/res/drawable-xxxhdpi/ic_bluetooth_white.png
index c0181e016..90d8a3413 100644
Binary files a/F-Droid/res/drawable-xxxhdpi/ic_bluetooth_white.png and b/F-Droid/res/drawable-xxxhdpi/ic_bluetooth_white.png differ
diff --git a/F-Droid/res/drawable-xxxhdpi/ic_clear.png b/F-Droid/res/drawable-xxxhdpi/ic_clear.png
new file mode 100644
index 000000000..df42feecb
Binary files /dev/null and b/F-Droid/res/drawable-xxxhdpi/ic_clear.png differ
diff --git a/F-Droid/res/drawable-xxxhdpi/ic_delete_white.png b/F-Droid/res/drawable-xxxhdpi/ic_delete_white.png
index 47fef5aab..8d322aa9b 100644
Binary files a/F-Droid/res/drawable-xxxhdpi/ic_delete_white.png and b/F-Droid/res/drawable-xxxhdpi/ic_delete_white.png differ
diff --git a/F-Droid/res/drawable-xxxhdpi/ic_do_not_disturb_white.png b/F-Droid/res/drawable-xxxhdpi/ic_do_not_disturb_white.png
index 446abf648..144695ec0 100644
Binary files a/F-Droid/res/drawable-xxxhdpi/ic_do_not_disturb_white.png and b/F-Droid/res/drawable-xxxhdpi/ic_do_not_disturb_white.png differ
diff --git a/F-Droid/res/drawable-xxxhdpi/ic_edit_white.png b/F-Droid/res/drawable-xxxhdpi/ic_edit_white.png
deleted file mode 100644
index 736806495..000000000
Binary files a/F-Droid/res/drawable-xxxhdpi/ic_edit_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable-xxxhdpi/ic_help_white.png b/F-Droid/res/drawable-xxxhdpi/ic_help_white.png
index c99653261..8eb7241da 100644
Binary files a/F-Droid/res/drawable-xxxhdpi/ic_help_white.png and b/F-Droid/res/drawable-xxxhdpi/ic_help_white.png differ
diff --git a/F-Droid/res/drawable-xxxhdpi/ic_nfc_white.png b/F-Droid/res/drawable-xxxhdpi/ic_nfc_white.png
index 12cd90eae..7051d7bd6 100644
Binary files a/F-Droid/res/drawable-xxxhdpi/ic_nfc_white.png and b/F-Droid/res/drawable-xxxhdpi/ic_nfc_white.png differ
diff --git a/F-Droid/res/drawable-xxxhdpi/ic_play_arrow_white.png b/F-Droid/res/drawable-xxxhdpi/ic_play_arrow_white.png
index f4f713e83..be5c062b5 100644
Binary files a/F-Droid/res/drawable-xxxhdpi/ic_play_arrow_white.png and b/F-Droid/res/drawable-xxxhdpi/ic_play_arrow_white.png differ
diff --git a/F-Droid/res/drawable-xxxhdpi/ic_refresh_white.png b/F-Droid/res/drawable-xxxhdpi/ic_refresh_white.png
index fe0ae13aa..f5beca251 100644
Binary files a/F-Droid/res/drawable-xxxhdpi/ic_refresh_white.png and b/F-Droid/res/drawable-xxxhdpi/ic_refresh_white.png differ
diff --git a/F-Droid/res/drawable-xxxhdpi/ic_search_white.png b/F-Droid/res/drawable-xxxhdpi/ic_search_white.png
index 7caf6c9b7..dd5adfc7f 100644
Binary files a/F-Droid/res/drawable-xxxhdpi/ic_search_white.png and b/F-Droid/res/drawable-xxxhdpi/ic_search_white.png differ
diff --git a/F-Droid/res/drawable-xxxhdpi/ic_settings_white.png b/F-Droid/res/drawable-xxxhdpi/ic_settings_white.png
index 4d36a012e..507c5edd4 100644
Binary files a/F-Droid/res/drawable-xxxhdpi/ic_settings_white.png and b/F-Droid/res/drawable-xxxhdpi/ic_settings_white.png differ
diff --git a/F-Droid/res/drawable-xxxhdpi/ic_share_white.png b/F-Droid/res/drawable-xxxhdpi/ic_share_white.png
index 4eeff94cf..e351c7beb 100644
Binary files a/F-Droid/res/drawable-xxxhdpi/ic_share_white.png and b/F-Droid/res/drawable-xxxhdpi/ic_share_white.png differ
diff --git a/F-Droid/res/drawable-xxxhdpi/ic_toc_white.png b/F-Droid/res/drawable-xxxhdpi/ic_toc_white.png
new file mode 100644
index 000000000..0ec6e3895
Binary files /dev/null and b/F-Droid/res/drawable-xxxhdpi/ic_toc_white.png differ
diff --git a/F-Droid/res/drawable-xxxhdpi/ic_view_headline_white.png b/F-Droid/res/drawable-xxxhdpi/ic_view_headline_white.png
deleted file mode 100644
index 7674b081f..000000000
Binary files a/F-Droid/res/drawable-xxxhdpi/ic_view_headline_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable/hotspot.png b/F-Droid/res/drawable/hotspot.png
deleted file mode 100644
index 88953cea6..000000000
Binary files a/F-Droid/res/drawable/hotspot.png and /dev/null differ
diff --git a/F-Droid/res/drawable/ic_add_white.png b/F-Droid/res/drawable/ic_add_white.png
deleted file mode 100644
index 67042105d..000000000
Binary files a/F-Droid/res/drawable/ic_add_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable/ic_bluetooth_white.png b/F-Droid/res/drawable/ic_bluetooth_white.png
deleted file mode 100644
index 678678da4..000000000
Binary files a/F-Droid/res/drawable/ic_bluetooth_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable/ic_delete_white.png b/F-Droid/res/drawable/ic_delete_white.png
deleted file mode 100644
index cdb230c2f..000000000
Binary files a/F-Droid/res/drawable/ic_delete_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable/ic_do_not_disturb_white.png b/F-Droid/res/drawable/ic_do_not_disturb_white.png
deleted file mode 100644
index 4e4fa1e59..000000000
Binary files a/F-Droid/res/drawable/ic_do_not_disturb_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable/ic_edit_white.png b/F-Droid/res/drawable/ic_edit_white.png
deleted file mode 100644
index 7f0ea51bf..000000000
Binary files a/F-Droid/res/drawable/ic_edit_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable/ic_help_white.png b/F-Droid/res/drawable/ic_help_white.png
deleted file mode 100644
index 9f8051b44..000000000
Binary files a/F-Droid/res/drawable/ic_help_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable/ic_nfc_white.png b/F-Droid/res/drawable/ic_nfc_white.png
deleted file mode 100644
index 428351e8e..000000000
Binary files a/F-Droid/res/drawable/ic_nfc_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable/ic_play_arrow_white.png b/F-Droid/res/drawable/ic_play_arrow_white.png
deleted file mode 100644
index a55d19922..000000000
Binary files a/F-Droid/res/drawable/ic_play_arrow_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable/ic_refresh_white.png b/F-Droid/res/drawable/ic_refresh_white.png
deleted file mode 100644
index 5f89fc257..000000000
Binary files a/F-Droid/res/drawable/ic_refresh_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable/ic_search_white.png b/F-Droid/res/drawable/ic_search_white.png
deleted file mode 100644
index 043759acd..000000000
Binary files a/F-Droid/res/drawable/ic_search_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable/ic_settings_white.png b/F-Droid/res/drawable/ic_settings_white.png
deleted file mode 100644
index 12e5d100d..000000000
Binary files a/F-Droid/res/drawable/ic_settings_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable/ic_share_white.png b/F-Droid/res/drawable/ic_share_white.png
deleted file mode 100644
index dd536bca2..000000000
Binary files a/F-Droid/res/drawable/ic_share_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable/ic_swap.png b/F-Droid/res/drawable/ic_swap.png
deleted file mode 100644
index 01e501ea8..000000000
Binary files a/F-Droid/res/drawable/ic_swap.png and /dev/null differ
diff --git a/F-Droid/res/drawable/ic_text_dot.xml b/F-Droid/res/drawable/ic_text_dot.xml
deleted file mode 100644
index f8f396453..000000000
--- a/F-Droid/res/drawable/ic_text_dot.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-    android:inset="10dp">
-    <shape android:shape="oval">
-        <solid android:color="?android:attr/textColorSecondary" />
-        <size android:width="4dp" android:height="4dp" />
-    </shape>
-</inset>
diff --git a/F-Droid/res/drawable/ic_view_headline_white.png b/F-Droid/res/drawable/ic_view_headline_white.png
deleted file mode 100644
index e6ecf5a1d..000000000
Binary files a/F-Droid/res/drawable/ic_view_headline_white.png and /dev/null differ
diff --git a/F-Droid/res/drawable/nfc_touch.png b/F-Droid/res/drawable/nfc_touch.png
deleted file mode 100644
index d24a29e1c..000000000
Binary files a/F-Droid/res/drawable/nfc_touch.png and /dev/null differ
diff --git a/F-Droid/res/drawable/swap_nfc_icon.png b/F-Droid/res/drawable/swap_nfc_icon.png
deleted file mode 100644
index d11ac74f6..000000000
Binary files a/F-Droid/res/drawable/swap_nfc_icon.png and /dev/null differ
diff --git a/F-Droid/res/drawable/swap_success.png b/F-Droid/res/drawable/swap_success.png
deleted file mode 100644
index 1d09bfd42..000000000
Binary files a/F-Droid/res/drawable/swap_success.png and /dev/null differ
diff --git a/F-Droid/res/drawable/wifi.png b/F-Droid/res/drawable/wifi.png
deleted file mode 100644
index d2769efb1..000000000
Binary files a/F-Droid/res/drawable/wifi.png and /dev/null differ
diff --git a/F-Droid/res/drawable/wifi_ap_personal.png b/F-Droid/res/drawable/wifi_ap_personal.png
deleted file mode 100644
index 56f3216cf..000000000
Binary files a/F-Droid/res/drawable/wifi_ap_personal.png and /dev/null differ
diff --git a/F-Droid/res/drawable/wifi_ap_private.png b/F-Droid/res/drawable/wifi_ap_private.png
deleted file mode 100644
index 62fc961fc..000000000
Binary files a/F-Droid/res/drawable/wifi_ap_private.png and /dev/null differ
diff --git a/F-Droid/res/drawable/wifi_ap_public.png b/F-Droid/res/drawable/wifi_ap_public.png
deleted file mode 100644
index 2eef507b8..000000000
Binary files a/F-Droid/res/drawable/wifi_ap_public.png and /dev/null differ
diff --git a/F-Droid/res/layout/about.xml b/F-Droid/res/layout/about.xml
index 909a8518d..671e6153e 100644
--- a/F-Droid/res/layout/about.xml
+++ b/F-Droid/res/layout/about.xml
@@ -2,12 +2,11 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 	android:layout_width="fill_parent"
 	android:layout_height="fill_parent"
-	android:paddingLeft="8dp"
-	android:paddingStart="8dp"
-	android:paddingRight="8dp"
-	android:paddingEnd="8dp"
-	android:paddingTop="6dp"
-	android:paddingBottom="6dp"
+	android:paddingLeft="24dp"
+	android:paddingStart="24dp"
+	android:paddingRight="24dp"
+	android:paddingEnd="24dp"
+	android:paddingTop="20dp"
 	android:baselineAligned="false"
 	android:orientation="vertical">
 
diff --git a/F-Droid/res/layout/addrepo.xml b/F-Droid/res/layout/addrepo.xml
index d94d86a20..c9d2d6522 100644
--- a/F-Droid/res/layout/addrepo.xml
+++ b/F-Droid/res/layout/addrepo.xml
@@ -3,7 +3,11 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:padding="6dp">
+    android:paddingLeft="24dp"
+    android:paddingStart="24dp"
+    android:paddingRight="24dp"
+    android:paddingEnd="24dp"
+    android:paddingTop="20dp">
 
     <LinearLayout
         android:id="@+id/add_repo_form"
diff --git a/F-Droid/res/layout/app_details_header.xml b/F-Droid/res/layout/app_details_header.xml
index e36319c54..99f2cb936 100644
--- a/F-Droid/res/layout/app_details_header.xml
+++ b/F-Droid/res/layout/app_details_header.xml
@@ -16,7 +16,7 @@
     along with this program; if not, write to the Free Software
     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/icon_and_title"
     android:layout_width="match_parent"
@@ -40,6 +40,7 @@
         android:layout_gravity="center_vertical"
         android:baselineAligned="false"
         android:orientation="vertical"
+        android:layout_toRightOf="@id/icon"
         android:paddingLeft="16dp"
         android:paddingStart="16dp">
 
@@ -109,7 +110,50 @@
 
         </LinearLayout>
     </LinearLayout>
-</LinearLayout>
+    <LinearLayout
+        android:id="@+id/holder"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/icon"
+        android:gravity="center">
+        <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1">
+            <ProgressBar
+                android:id="@+id/progress_bar"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal" />
+            <TextView
+                android:id="@+id/progress_size"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentStart="true"
+                android:layout_alignParentLeft="true"
+                android:layout_below="@id/progress_bar"
+                android:textSize="12sp"/>
+            <TextView
+                android:id="@+id/progress_percentage"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentEnd="true"
+                android:layout_alignParentRight="true"
+                android:layout_below="@id/progress_bar"
+                android:textSize="12sp"/>
+        </RelativeLayout>
+        <ImageButton
+            android:id="@+id/cancel"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:padding="5dp"
+            android:layout_weight="0"
+            android:visibility="gone"
+            android:src="@drawable/ic_clear"
+            android:background="@null"/>
+    </LinearLayout>
+</RelativeLayout>
 
 
 
diff --git a/F-Droid/res/layout/app_permission_item_old.xml b/F-Droid/res/layout/app_permission_item_old.xml
deleted file mode 100644
index a5b395ede..000000000
--- a/F-Droid/res/layout/app_permission_item_old.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<!--
-  Defines the layout of a single permission item.
-  Contains the group name and a list of permission labels under the group.
--->
-
-<RelativeLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content">
-
-    <ImageView
-        android:id="@+id/perm_icon"
-        android:layout_width="24dip"
-        android:layout_height="24dip"
-        android:layout_alignParentStart="true"
-        android:scaleType="fitCenter" />
-
-
-    <TextView
-        android:id="@+id/permission_group"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textStyle="bold"
-        android:paddingStart="6dip"
-        android:layout_toEndOf="@id/perm_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content" />
-
-    <TextView
-        android:id="@+id/permission_list"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:layout_marginTop="-4dip"
-        android:paddingBottom="8dip"
-        android:paddingStart="6dip"
-        android:layout_below="@id/permission_group"
-        android:layout_toEndOf="@id/perm_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content" />
-
-</RelativeLayout>
diff --git a/F-Droid/res/layout/appinfo.xml b/F-Droid/res/layout/appinfo.xml
deleted file mode 100644
index 389acf75b..000000000
--- a/F-Droid/res/layout/appinfo.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-	android:layout_width="fill_parent"
-	android:layout_height="wrap_content"
-	android:paddingTop="4dp"
-	android:paddingBottom="8dp"
-	android:baselineAligned="false"
-	android:orientation="vertical" >
-
-	<TextView
-		android:id="@+id/summary"
-		android:layout_width="fill_parent"
-		android:layout_height="wrap_content"
-		android:textStyle="bold" />
-
-	<TextView
-		android:id="@+id/appid"
-		android:layout_width="fill_parent"
-		android:layout_height="wrap_content"
-		android:textSize="12sp" />
-
-	<TextView
-		android:id="@+id/signature"
-		android:layout_width="fill_parent"
-		android:layout_height="wrap_content"
-		android:textSize="12sp" />
-
-	<TextView
-		android:id="@+id/antifeatures"
-		android:layout_width="fill_parent"
-		android:layout_height="wrap_content"
-		android:layout_marginTop="6sp"
-		android:textStyle="bold"
-		android:textColor="#ff0000" />
-
-	<TextView
-		android:id="@+id/description"
-		android:layout_width="fill_parent"
-		android:layout_height="wrap_content"
-		android:layout_marginTop="8sp"
-		android:textSize="13sp"
-		android:singleLine="false" />
-
-	<TextView
-		android:id="@+id/permissions"
-		android:layout_width="fill_parent"
-		android:layout_height="wrap_content"
-		android:layout_marginTop="8sp"
-		android:singleLine="true"
-		android:textStyle="bold" />
-
-	<TextView
-		android:id="@+id/permissions_list"
-		android:layout_width="fill_parent"
-		android:layout_height="wrap_content"
-		android:textSize="13sp"
-		android:singleLine="false" />
-
-</LinearLayout>
diff --git a/F-Droid/res/layout/local_repo_activity.xml b/F-Droid/res/layout/local_repo_activity.xml
deleted file mode 100644
index d7a8a6045..000000000
--- a/F-Droid/res/layout/local_repo_activity.xml
+++ /dev/null
@@ -1,95 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-    android:orientation="vertical" >
-
-    <Button
-        android:id="@+id/enable_wifi"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/enable_wifi" />
-
-    <CheckBox
-        android:id="@+id/repoSwitch"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center"
-        android:padding="15dp"
-        android:text="@string/touch_to_turn_on_local_repo" />
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content" >
-
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:text="@string/sharing_uri" />
-
-        <TextView
-            android:id="@+id/sharing_uri"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_marginLeft="15dp"
-            android:layout_marginStart="15dp"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:textStyle="bold"
-            android:typeface="monospace" />
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content" >
-
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:text="@string/wifi_network" />
-
-        <TextView
-            android:id="@+id/wifi_network"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_marginLeft="15dp"
-            android:layout_marginStart="15dp"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:textStyle="bold"
-            android:typeface="monospace" />
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content" >
-
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:text="@string/fingerprint" />
-
-        <TextView
-            android:id="@+id/fingerprint"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_marginLeft="15dp"
-            android:layout_marginStart="15dp"
-            android:typeface="monospace" />
-    </LinearLayout>
-
-    <TextView
-        android:id="@+id/instrucionsTextView"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginLeft="20dp"
-        android:layout_marginStart="20dp"
-        android:layout_marginRight="20dp"
-        android:layout_marginEnd="20dp"
-        android:text="@string/same_wifi_instructions" />
-
-    <ImageView
-        android:id="@+id/repoQrCode"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:contentDescription="@string/qr_content_description" />
-
-</LinearLayout>
diff --git a/F-Droid/res/layout/qr_wizard_activity.xml b/F-Droid/res/layout/qr_wizard_activity.xml
deleted file mode 100644
index b461f9eb8..000000000
--- a/F-Droid/res/layout/qr_wizard_activity.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-    android:orientation="vertical" >
-
-    <TextView
-        android:id="@+id/qrWizardInstructions"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content" />
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content" >
-
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:text="@string/wifi_network" />
-
-        <TextView
-            android:id="@+id/qrWifiNetworkName"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_marginLeft="15dp"
-            android:layout_marginStart="15dp"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:textStyle="bold"
-            android:typeface="monospace" />
-    </LinearLayout>
-
-    <ImageView
-        android:id="@+id/qrWizardImage"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:contentDescription="@string/qr_code" />
-
-    <Button
-        android:id="@+id/qrNextButton"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/next" />
-
-</LinearLayout>
diff --git a/F-Droid/res/layout/repodetails.xml b/F-Droid/res/layout/repodetails.xml
index 86cb9b1a2..921612d11 100644
--- a/F-Droid/res/layout/repodetails.xml
+++ b/F-Droid/res/layout/repodetails.xml
@@ -1,32 +1,32 @@
 <?xml version="1.0" encoding="utf-8"?>
 
-<RelativeLayout
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:focusable="true"
-    android:focusableInTouchMode="true"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
     android:paddingTop="@dimen/padding_top"
     android:paddingLeft="@dimen/padding_side"
     android:paddingStart="@dimen/padding_side"
     android:paddingRight="@dimen/padding_side"
     android:paddingEnd="@dimen/padding_side">
 
-    <!-- Editable URL of this repo -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+    <RelativeLayout
+        android:id="@+id/repoView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
     <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:id="@+id/label_repo_url"
-        android:text="@string/repo_url"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentStart="true"
-        android:layout_alignParentTop="true" />
-    <EditText
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:id="@+id/input_repo_url"
-        android:inputType="textUri"
-        android:layout_below="@id/label_repo_url" />
+        android:textStyle="bold"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentTop="true" />
 
     <!-- Name of this repo -->
     <TextView
@@ -138,4 +138,12 @@
         android:layout_height="wrap_content"
         android:layout_below="@id/text_not_yet_updated"/>
 
-</RelativeLayout>
+    </RelativeLayout>
+
+    <ImageView
+        android:id="@+id/qr_code"
+        android:src="@drawable/swap_qr_example"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+    </LinearLayout>
+</ScrollView>
diff --git a/F-Droid/res/layout/repodiscoverylist.xml b/F-Droid/res/layout/repodiscoverylist.xml
index cdd85147d..694d07897 100644
--- a/F-Droid/res/layout/repodiscoverylist.xml
+++ b/F-Droid/res/layout/repodiscoverylist.xml
@@ -1,12 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent" >
+    android:layout_height="match_parent"
+    android:paddingLeft="24dp"
+    android:paddingStart="24dp"
+    android:paddingRight="24dp"
+    android:paddingEnd="24dp"
+    android:paddingTop="20dp" >
 
 	<LinearLayout
 	    android:id="@+id/reposcanprogresslayout"
 	    android:layout_width="match_parent"
-	    android:layout_height="50dp"
+	    android:layout_height="wrap_content"
 	    android:layout_alignParentTop="true"
 	    android:layout_alignParentLeft="true"
 	    android:layout_alignParentStart="true"
@@ -42,4 +47,4 @@
 		android:padding="8sp"
 	/>
 
-</RelativeLayout>
\ No newline at end of file
+</RelativeLayout>
diff --git a/F-Droid/res/layout/repolisticons.xml b/F-Droid/res/layout/repolisticons.xml
deleted file mode 100644
index 18cb61e24..000000000
--- a/F-Droid/res/layout/repolisticons.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/vw1"
-    android:layout_width="fill_parent"
-    android:layout_height="wrap_content"
-    android:orientation="horizontal">
-
-	<ImageView
-		android:id="@+id/img"
-		android:contentDescription="@string/repo_icon"
-        android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		/>
-
-    <LinearLayout
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical">
-
-        <TextView android:id="@+id/uri"
-            android:textSize="21sp"
-            android:textStyle="bold"
-            android:singleLine="true"
-            android:ellipsize="marquee"
-            android:layout_width="fill_parent"
-    		android:layout_height="fill_parent"/>
-
-        <TextView android:id="@+id/fingerprint"
-            android:textSize="14sp"
-            android:typeface="monospace"
-            android:singleLine="false"
-            android:layout_width="fill_parent"
-            android:layout_height="fill_parent"/>
-
-    </LinearLayout>
-
-</LinearLayout>
-<!--
- * Copyright (C) 2009  Roberto Jacinto
- * roberto.jacinto@caixamagica.pt
- *
- * 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 2
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
--->
diff --git a/F-Droid/res/layout/swap_connecting.xml b/F-Droid/res/layout/swap_connecting.xml
index e729653db..40dc308fb 100644
--- a/F-Droid/res/layout/swap_connecting.xml
+++ b/F-Droid/res/layout/swap_connecting.xml
@@ -30,6 +30,19 @@
             tools:text="Downloading index from http://10.0.0.4:8888/fdroid/repo"
             android:padding="30dp" />
 
-    <!-- TODO: Add button for "Retry" and show an error message when failed connecting. -->
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:id="@+id/error"
+            android:text="@string/swap_connection_misc_error"
+            android:padding="30dp"
+            android:visibility="gone" />
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:id="@+id/back"
+            android:text="@string/back"
+            android:visibility="gone" />
 
 </org.fdroid.fdroid.views.swap.SwapConnecting>
\ No newline at end of file
diff --git a/F-Droid/res/menu/local_repo_activity.xml b/F-Droid/res/menu/local_repo_activity.xml
deleted file mode 100644
index 606090278..000000000
--- a/F-Droid/res/menu/local_repo_activity.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto" >
-
-    <item
-        android:id="@+id/menu_setup_repo"
-        android:icon="@android:drawable/ic_input_add"
-        android:title="@string/setup_repo"
-        app:showAsAction="ifRoom|withText"/>
-    <item
-        android:id="@+id/menu_send_fdroid_via_wifi"
-        android:icon="@android:drawable/arrow_up_float"
-        android:title="@string/send_fdroid_via_wifi"
-        app:showAsAction="never"/>
-    <item
-        android:id="@+id/menu_settings"
-        android:icon="@android:drawable/ic_menu_preferences"
-        android:title="@string/menu_preferences"
-        app:showAsAction="never"/>
-
-</menu>
\ No newline at end of file
diff --git a/F-Droid/res/menu/main.xml b/F-Droid/res/menu/main.xml
index b454adc54..9c8a76dc0 100644
--- a/F-Droid/res/menu/main.xml
+++ b/F-Droid/res/menu/main.xml
@@ -7,18 +7,13 @@
         android:icon="@drawable/ic_search_white"
         android:title="@string/menu_search"
         app:showAsAction="always"/>
-    <item
-        android:id="@+id/action_update_repo"
-        android:icon="@drawable/ic_refresh_white"
-        android:title="@string/menu_update_repo"
-        app:showAsAction="ifRoom"/>
     <item
         android:id="@+id/action_swap"
         android:title="@string/swap"
         app:showAsAction="ifRoom"/>
     <item
         android:id="@+id/action_manage_repos"
-        android:icon="@drawable/ic_view_headline_white"
+        android:icon="@drawable/ic_toc_white"
         android:title="@string/menu_manage"
         app:showAsAction="ifRoom"/>
     <item
@@ -26,6 +21,11 @@
         android:icon="@drawable/ic_bluetooth_white"
         android:title="@string/menu_send_apk_bt"
         app:showAsAction="ifRoom"/>
+    <item
+        android:id="@+id/action_update_repo"
+        android:icon="@drawable/ic_refresh_white"
+        android:title="@string/menu_update_repo"
+        app:showAsAction="ifRoom"/>
     <item
         android:id="@+id/action_settings"
         android:icon="@drawable/ic_settings_white"
@@ -37,4 +37,4 @@
         android:title="@string/menu_about"
         app:showAsAction="ifRoom"/>
 
-</menu>
\ No newline at end of file
+</menu>
diff --git a/F-Droid/res/menu/manage_repo_context.xml b/F-Droid/res/menu/manage_repo_context.xml
deleted file mode 100644
index 57506a395..000000000
--- a/F-Droid/res/menu/manage_repo_context.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <item
-        android:id="@+id/edit_repo"
-        android:title="@string/edit"
-        android:icon="@drawable/ic_edit_white" />
-
-    <item
-        android:id="@+id/delete_repo"
-        android:title="@string/delete"
-        android:icon="@drawable/ic_delete_white" />
-
-</menu>
\ No newline at end of file
diff --git a/F-Droid/res/menu/repo_details_activity.xml b/F-Droid/res/menu/repo_details_activity.xml
new file mode 100644
index 000000000..82a38938c
--- /dev/null
+++ b/F-Droid/res/menu/repo_details_activity.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <item
+        android:id="@+id/menu_update"
+        android:icon="@drawable/ic_refresh_white"
+        android:title="@string/repo_update"
+        app:showAsAction="always|withText" />
+    <item
+        android:id="@+id/menu_delete"
+        android:icon="@drawable/ic_delete_white"
+        android:title="@string/delete"
+        app:showAsAction="ifRoom|withText" />
+    <item
+        android:id="@+id/menu_enable_nfc"
+        android:icon="@drawable/ic_nfc_white"
+        android:title="@string/enable_nfc_send"
+        app:showAsAction="ifRoom|withText" />
+
+</menu>
\ No newline at end of file
diff --git a/F-Droid/res/menu/select_local_apps_action_mode.xml b/F-Droid/res/menu/select_local_apps_action_mode.xml
deleted file mode 100644
index f0c9bc6f1..000000000
--- a/F-Droid/res/menu/select_local_apps_action_mode.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto" >
-
-    <item
-        android:id="@+id/action_search"
-        android:icon="@android:drawable/ic_menu_search"
-        android:title="@string/menu_search"
-        app:actionViewClass="android.support.v7.widget.SearchView"
-        app:showAsAction="ifRoom"/>
-    <item
-        android:id="@+id/action_update_repo"
-        android:icon="@android:drawable/ic_input_add"
-        android:title="@string/update_repo"
-        app:showAsAction="always"/>
-
-</menu>
\ No newline at end of file
diff --git a/F-Droid/res/menu/select_local_apps_activity.xml b/F-Droid/res/menu/select_local_apps_activity.xml
deleted file mode 100644
index 172e907bf..000000000
--- a/F-Droid/res/menu/select_local_apps_activity.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto" >
-
-    <item
-        android:id="@+id/action_search"
-        android:icon="@android:drawable/ic_menu_search"
-        android:title="@string/menu_search"
-        app:actionViewClass="android.support.v7.widget.SearchView"
-        app:showAsAction="collapseActionView|always"/>
-    <item
-        android:id="@+id/action_settings"
-        android:icon="@android:drawable/ic_menu_preferences"
-        android:title="@string/menu_preferences"
-        app:showAsAction="ifRoom"/>
-
-</menu>
\ No newline at end of file
diff --git a/F-Droid/res/values-ca/strings.xml b/F-Droid/res/values-ca/strings.xml
index 7851230ea..ece623ba0 100644
--- a/F-Droid/res/values-ca/strings.xml
+++ b/F-Droid/res/values-ca/strings.xml
@@ -44,7 +44,6 @@ L\'adreça d\'un dipòsit té un aspecte com ara: https://f-droid.org/repo</stri
   <string name="enable">Permès</string>
   <string name="add_key">Afegeix clau</string>
   <string name="overwrite">Sobreescriu</string>
-  <string name="repo_delete_title">Trieu el dipòsit que voleu suprimir</string>
   <string name="repo_update_title">Actualitza els dipòsits</string>
   <string name="tab_noninstalled">Disponible</string>
   <string name="tab_updates">Actualitzacions</string>
@@ -104,7 +103,6 @@ La voleu actualitzar?</string>
   <string name="status_checking_compatibility">S\'està comprovant la compatibilitat de les aplicacions amb el vostre dispositiu…</string>
   <string name="no_permissions">No es fa servir cap permís.</string>
   <string name="permissions_for_long">Permisos de la versió %s</string>
-  <string name="showPermissions">Mostra els permisos</string>
   <string name="no_handler_app">No teniu cap aplicació disponible que pugui gestionar %s</string>
   <string name="compactlayout">Vista compacta</string>
   <string name="theme">Tema</string>
diff --git a/F-Droid/res/values-cs/strings.xml b/F-Droid/res/values-cs/strings.xml
index e7dd4bef2..2a2f07eca 100644
--- a/F-Droid/res/values-cs/strings.xml
+++ b/F-Droid/res/values-cs/strings.xml
@@ -48,7 +48,6 @@ Uvolněno pod GPLv3 licencí.</string>
   <string name="enable">Povolit</string>
   <string name="add_key">Přidat klíč</string>
   <string name="overwrite">Přepsat</string>
-  <string name="repo_delete_title">Vybrat repozitář k odebrání</string>
   <string name="repo_update_title">Aktualizovat repozitáře</string>
   <string name="tab_noninstalled">Dostupné</string>
   <string name="tab_updates">Aktualizace</string>
@@ -143,8 +142,6 @@ Uvolněno pod GPLv3 licencí.</string>
   <string name="global_error_updating_repos">Chyba při aktualizaci:</string>
   <string name="no_permissions">Žádná oprávnění nejsou použita.</string>
   <string name="permissions_for_long">Oprávnění pro verzi %s</string>
-  <string name="showPermissions">Zobrazit oprávnění</string>
-  <string name="showPermissions_on">Zobrazit seznam oprávnění požadovaný aplikací</string>
   <string name="compactlayout">Úsporné rozvržení</string>
   <string name="compactlayout_on">Zobrazit menší ikonky</string>
   <string name="theme">Téma</string>
diff --git a/F-Droid/res/values-de/strings.xml b/F-Droid/res/values-de/strings.xml
index 60e2881f4..440cddcce 100644
--- a/F-Droid/res/values-de/strings.xml
+++ b/F-Droid/res/values-de/strings.xml
@@ -58,7 +58,6 @@ Die Adresse einer Paketquelle könnte wie folgt aussehen: https://f-droid.org/re
   <string name="enable">Aktivieren</string>
   <string name="add_key">Schlüssel hinzufügen</string>
   <string name="overwrite">Überschreiben</string>
-  <string name="repo_delete_title">Zu entfernende Paketquelle auswählen</string>
   <string name="repo_update_title">Paketquellen aktualisieren</string>
   <string name="tab_noninstalled">Verfügbar</string>
   <string name="tab_updates">Aktualisierungen</string>
@@ -179,8 +178,6 @@ wird verbunden</string>
   <string name="global_error_updating_repos">Fehler während der Aktualisierung:</string>
   <string name="no_permissions">Es werden keine Berechtigungen verwendet.</string>
   <string name="permissions_for_long">Berechtigungen für Version %s</string>
-  <string name="showPermissions">Berechtigungen anzeigen</string>
-  <string name="showPermissions_on">Eine Liste mit Berechtigungen, die von einer Anwendung verlangt werden, anzeigen</string>
   <string name="no_handler_app">Es ist keine Anwendung installiert, die mit %s umgehen kann</string>
   <string name="compactlayout">Kompakte Ansicht</string>
   <string name="compactlayout_on">Symbole in einer kleineren Größe anzeigen</string>
diff --git a/F-Droid/res/values-el/strings.xml b/F-Droid/res/values-el/strings.xml
index 1129a0120..9a4323044 100644
--- a/F-Droid/res/values-el/strings.xml
+++ b/F-Droid/res/values-el/strings.xml
@@ -51,7 +51,6 @@
   <string name="enable">Ενεργοποίηση</string>
   <string name="add_key">Προσθήκη κλειδιού</string>
   <string name="overwrite">Αντικατάσταση</string>
-  <string name="repo_delete_title">Επιλογή αποθετηρίου για διαγραφή</string>
   <string name="repo_update_title">Ενημέρωση αποθετηρίων</string>
   <string name="tab_noninstalled">Διαθέσιμα</string>
   <string name="tab_updates">Ενημερώσεις</string>
@@ -130,8 +129,6 @@
   <string name="status_inserting">Αποθήκευση λεπτομερειών των εφαρμογών</string>
   <string name="no_permissions">Δεν χρησιμοποιείται καμία άδεια.</string>
   <string name="permissions_for_long">Άδειες για την έκδοση %s</string>
-  <string name="showPermissions">Εμφάνιση αδειών</string>
-  <string name="showPermissions_on">Εμφάνιση μιας λίστας με τις άδειες που απαιτεί η εφαρμογή</string>
   <string name="no_handler_app">Δεν έχεται καμία διαθέσιμη εφαρμογή που να μπορεί να χειριστεί %s</string>
   <string name="compactlayout">Συμπτυγμένη Διάταξη</string>
   <string name="compactlayout_on">Εμφάνιση εικονιδίων σε μικρότερο μέγεθος</string>
diff --git a/F-Droid/res/values-es/strings.xml b/F-Droid/res/values-es/strings.xml
index 3fd280b3f..57e18ea72 100644
--- a/F-Droid/res/values-es/strings.xml
+++ b/F-Droid/res/values-es/strings.xml
@@ -57,7 +57,6 @@ La dirección de un repositorio es algo similar a esto: https://f-droid.org/repo
   <string name="enable">Habilitar</string>
   <string name="add_key">Añadir clave</string>
   <string name="overwrite">Sobreescribir</string>
-  <string name="repo_delete_title">Elige el repositorio a eliminar</string>
   <string name="repo_update_title">Actualizar repositorios</string>
   <string name="tab_noninstalled">Disponible</string>
   <string name="tab_updates">Actualizaciones</string>
@@ -178,8 +177,6 @@ La dirección de un repositorio es algo similar a esto: https://f-droid.org/repo
   <string name="global_error_updating_repos">Error en la Actualización:</string>
   <string name="no_permissions">No se usan permisos.</string>
   <string name="permissions_for_long">Permisos para la versión %s</string>
-  <string name="showPermissions">Mostrar permisos</string>
-  <string name="showPermissions_on">Mostrar una lista de los permisos que requiere una aplicación</string>
   <string name="no_handler_app">No tienes instalada ninguna aplicación que pueda manejar %s</string>
   <string name="compactlayout">Diseño compacto</string>
   <string name="compactlayout_on">Mostrar iconos en tamaño menor</string>
diff --git a/F-Droid/res/values-eu/strings.xml b/F-Droid/res/values-eu/strings.xml
index 645347278..1e5182296 100644
--- a/F-Droid/res/values-eu/strings.xml
+++ b/F-Droid/res/values-eu/strings.xml
@@ -40,7 +40,6 @@ GNU GPLv3 lizentziapean argitaratua.</string>
   <string name="enable">Gaitu</string>
   <string name="add_key">Gehitu gakoa</string>
   <string name="overwrite">Gainidatzi</string>
-  <string name="repo_delete_title">Aukeratu biltegia ezabatzeko</string>
   <string name="repo_update_title">Eguneratu biltegiak</string>
   <string name="tab_noninstalled">Eskuragarri</string>
   <string name="tab_updates">Eguneraketak</string>
@@ -96,7 +95,6 @@ konektatzen</string>
   <string name="status_checking_compatibility">Aplikazioak zure gailuarekin bateragarriak diren egiaztatzen…</string>
   <string name="no_permissions">Ez da baimenik erabiltzen.</string>
   <string name="permissions_for_long">%s bertsioarentzako baimenak</string>
-  <string name="showPermissions">Erakutsi baimenak</string>
   <string name="compactlayout">Diseinu trinkoa</string>
   <string name="theme">Gaia</string>
   <string name="unsigned">Sinatu gabea</string>
diff --git a/F-Droid/res/values-fa/strings.xml b/F-Droid/res/values-fa/strings.xml
index 4474ab384..9ad98b117 100644
--- a/F-Droid/res/values-fa/strings.xml
+++ b/F-Droid/res/values-fa/strings.xml
@@ -38,7 +38,6 @@
   <string name="enable">توانا ساختن</string>
   <string name="add_key">فزودن کلید</string>
   <string name="overwrite">بازنویسی</string>
-  <string name="repo_delete_title">انتخاب مخزن برای حذف</string>
   <string name="repo_update_title">به‌روزرسانی مخازن</string>
   <string name="tab_updates">به‌روزرسانی‌ها</string>
   <string name="fdroid_updates_available">به‌روزرسانی‌های F-Droid موجود هستند</string>
@@ -87,8 +86,6 @@
 %1$s</string>
   <string name="status_checking_compatibility">بررسی سازگاری برنامه‌ها با دستگاه شما…</string>
   <string name="no_permissions">دسترسی‌ای استفاده نشده‌است.</string>
-  <string name="showPermissions">نمایش دسترسی‌ها</string>
-  <string name="showPermissions_on">نمایش یک فهرست از دسترسی‌هایی که یک برنامه نیاز دارد</string>
   <string name="compactlayout">طرح‌بندی فشرده</string>
   <string name="compactlayout_on">نمایش شکلک‌ها در اندازه کوچکتر</string>
   <string name="theme">پوسته</string>
diff --git a/F-Droid/res/values-fi/strings.xml b/F-Droid/res/values-fi/strings.xml
index 0ce2a9616..177cbaa8a 100644
--- a/F-Droid/res/values-fi/strings.xml
+++ b/F-Droid/res/values-fi/strings.xml
@@ -49,7 +49,6 @@
   <string name="enable">Ota käyttöön</string>
   <string name="add_key">Lisää avain</string>
   <string name="overwrite">Korvaa</string>
-  <string name="repo_delete_title">Valitse sovelluslähde, jonka tahdot poistaa</string>
   <string name="repo_update_title">Päivitä sovelluslähteet</string>
   <string name="tab_noninstalled">Saatavilla</string>
   <string name="tab_updates">Päivityksiä</string>
@@ -142,7 +141,6 @@ Tahdotko päivittää ne?</string>
   <string name="repos_unchanged">Yhdessäkään säilössä ei ole pakettipäivityksiä</string>
   <string name="all_other_repos_fine">Muut säilöt eivät luoneet virheitä.</string>
   <string name="global_error_updating_repos">Päivityksenaikainen virhe:</string>
-  <string name="showPermissions">Näytä oikeudet</string>
   <string name="theme">Teema</string>
   <string name="unverified">Vahvistamaton</string>
   <string name="repo_not_yet_updated">Tätä säilöä ei ole vielä käytetty.
diff --git a/F-Droid/res/values-fr/strings.xml b/F-Droid/res/values-fr/strings.xml
index 9cf626580..08533f61c 100644
--- a/F-Droid/res/values-fr/strings.xml
+++ b/F-Droid/res/values-fr/strings.xml
@@ -54,7 +54,6 @@ L\'adresse d\'un dépôt ressemble à ceci: https://f-droid.org/repo</string>
   <string name="enable">Activer</string>
   <string name="add_key">Ajouter une clé</string>
   <string name="overwrite">Écraser</string>
-  <string name="repo_delete_title">Choisissez le dépôt à supprimer</string>
   <string name="repo_update_title">Mettre à jour les dépôts</string>
   <string name="tab_noninstalled">Disponibles</string>
   <string name="tab_updates">Mises à jour</string>
@@ -175,8 +174,6 @@ Voulez-vous les mettre à jour?</string>
   <string name="global_error_updating_repos">Erreur durant la mise à jour:</string>
   <string name="no_permissions">Aucune autorisation n\'est utilisée.</string>
   <string name="permissions_for_long">Permissions pour la version %s</string>
-  <string name="showPermissions">Afficher les permissions</string>
-  <string name="showPermissions_on">Afficher la liste des permissions qu\'une app requiert</string>
   <string name="no_handler_app">Vous n\'avez aucune application installée pour gérer %s</string>
   <string name="compactlayout">Affichage compact</string>
   <string name="compactlayout_on">Montrer les icônes à une taille plus petite</string>
diff --git a/F-Droid/res/values-gl/strings.xml b/F-Droid/res/values-gl/strings.xml
index c4e3032ea..b499edfef 100644
--- a/F-Droid/res/values-gl/strings.xml
+++ b/F-Droid/res/values-gl/strings.xml
@@ -43,7 +43,6 @@ Un enderezo a un repositorio sería algo
   <string name="repo_add_title">Engadir un novo repositorio</string>
   <string name="repo_add_add">Engadir</string>
   <string name="cancel">Cancelar</string>
-  <string name="repo_delete_title">Escoller o repositorio que retirar</string>
   <string name="repo_update_title">Actualizar repositorios</string>
   <string name="tab_noninstalled">Dispoñíbel</string>
   <string name="tab_updates">Actualizacións</string>
@@ -101,7 +100,6 @@ Quere actualizalos?</string>
   <string name="status_checking_compatibility">Comprobando a compatibilidade das aps con seu aparello</string>
   <string name="no_permissions">Non se usan permisos</string>
   <string name="permissions_for_long">Permisos para a versión %s</string>
-  <string name="showPermissions">Amosar permisos</string>
   <string name="no_handler_app">Non ten ningunha ap dispoñíbel que poida manexar %s</string>
   <string name="compactlayout">Deseño compacto</string>
   <string name="theme">Tema</string>
diff --git a/F-Droid/res/values-hu/strings.xml b/F-Droid/res/values-hu/strings.xml
index 59f1f864b..2d566d0f4 100644
--- a/F-Droid/res/values-hu/strings.xml
+++ b/F-Droid/res/values-hu/strings.xml
@@ -50,7 +50,6 @@ A repo cím valahogy így néz ki: https://f-droid.org/repo\"</string>
   <string name="enable">Engedélyez</string>
   <string name="add_key">Kulcs hozzáadás</string>
   <string name="overwrite">Felülírás</string>
-  <string name="repo_delete_title">Válasszon ki repo-t az eltávolításhoz</string>
   <string name="repo_update_title">Repo-k frissítése</string>
   <string name="tab_noninstalled">Elérhető</string>
   <string name="tab_updates">Frissítések</string>
@@ -119,8 +118,6 @@ Szeretné ezeket frissíteni?\"</string>
   <string name="status_checking_compatibility">Alkalmazások eszközkompatibilitási ellenőrzése…</string>
   <string name="no_permissions">Nincsenek engedélyei.</string>
   <string name="permissions_for_long">Engedélyek erre a verzióra - %s</string>
-  <string name="showPermissions">Engedélyek megjelenítése</string>
-  <string name="showPermissions_on">Mutassa meg egy listában az app által igényelt engedélyeket</string>
   <string name="no_handler_app">\"Nincs semmilyen app, ami képes ezt kezelni %s\"</string>
   <string name="compactlayout">Kompakt elrendezés</string>
   <string name="compactlayout_on">Ikonok kisebb méretben</string>
diff --git a/F-Droid/res/values-it/strings.xml b/F-Droid/res/values-it/strings.xml
index ac80682cb..30639d2c5 100644
--- a/F-Droid/res/values-it/strings.xml
+++ b/F-Droid/res/values-it/strings.xml
@@ -57,7 +57,6 @@ Un indirizzo URL di esempio è: https://f-droid.org/repo</string>
   <string name="enable">Abilita</string>
   <string name="add_key">Aggiungi chiave</string>
   <string name="overwrite">Sovrascrivi</string>
-  <string name="repo_delete_title">Rimuovi repository</string>
   <string name="repo_update_title">Aggiorna i repository</string>
   <string name="tab_noninstalled">Disponibile</string>
   <string name="tab_updates">Aggiornamenti</string>
@@ -178,8 +177,6 @@ Vuoi aggiornarlo?</string>
   <string name="global_error_updating_repos">Errore nell\'aggiornamento:</string>
   <string name="no_permissions">Non viene usata alcuna autorizzazione.</string>
   <string name="permissions_for_long">Autorizzazioni per la versione %s</string>
-  <string name="showPermissions">Mostra autorizzazioni</string>
-  <string name="showPermissions_on">Mostra l\'elenco dei permessi richiesti da un\'applicazione</string>
   <string name="no_handler_app">Non hai alcuna app disponibile che può gestire %s</string>
   <string name="compactlayout">Layout Compatto</string>
   <string name="compactlayout_on">Mostra le icone a dimensione ridotta</string>
diff --git a/F-Droid/res/values-ja/strings.xml b/F-Droid/res/values-ja/strings.xml
index 8125fdf8f..31bed2bca 100644
--- a/F-Droid/res/values-ja/strings.xml
+++ b/F-Droid/res/values-ja/strings.xml
@@ -59,7 +59,6 @@ GNU GPLv3 ライセンスに基づいてリリースされました.</string>
   <string name="enable">有効</string>
   <string name="add_key">キーを追加</string>
   <string name="overwrite">上書き</string>
-  <string name="repo_delete_title">削除するリポジトリを選択してください</string>
   <string name="repo_update_title">リポジトリを更新</string>
   <string name="tab_noninstalled">インストール可能</string>
   <string name="tab_updates">更新</string>
@@ -180,8 +179,6 @@ GNU GPLv3 ライセンスに基づいてリリースされました.</string>
   <string name="global_error_updating_repos">更新中に問題発生:</string>
   <string name="no_permissions">使用される権限はありません。</string>
   <string name="permissions_for_long">バージョン %s の権限</string>
-  <string name="showPermissions">権限を表示</string>
-  <string name="showPermissions_on">アプリケーションが要求する権限リストを表示します</string>
   <string name="no_handler_app">取り扱うアプリケーションはありません %s</string>
   <string name="compactlayout">コンパクト・レイアウト</string>
   <string name="compactlayout_on">アイコンを小さいサイズで表示します</string>
diff --git a/F-Droid/res/values-ko/strings.xml b/F-Droid/res/values-ko/strings.xml
index 8b3eba2d6..96be92651 100644
--- a/F-Droid/res/values-ko/strings.xml
+++ b/F-Droid/res/values-ko/strings.xml
@@ -32,7 +32,6 @@
   <string name="repo_add_title">새로운 저장소 추가</string>
   <string name="repo_add_add">추가</string>
   <string name="cancel">취소</string>
-  <string name="repo_delete_title">제거할 저장소 선택</string>
   <string name="repo_update_title">저장소 업데이트</string>
   <string name="tab_updates">업데이트</string>
   <string name="one_update_available">1개의 업데이트를 사용할 수 있습니다.</string>
@@ -82,7 +81,6 @@
   <string name="status_checking_compatibility">장치와 응용프로그램의 호환성 확인중…</string>
   <string name="no_permissions">사용된 권한이 없습니다.</string>
   <string name="permissions_for_long">%s 버전에 대한 권한</string>
-  <string name="showPermissions">권한 표시</string>
   <string name="no_handler_app">%s을(를) 처리할 수 있는 응용프로그램이 없습니다.</string>
   <string name="compactlayout">컴팩트 레이아웃</string>
   <string name="theme">테마</string>
diff --git a/F-Droid/res/values-lt/strings.xml b/F-Droid/res/values-lt/strings.xml
index 4a3300129..024f1c8ec 100644
--- a/F-Droid/res/values-lt/strings.xml
+++ b/F-Droid/res/values-lt/strings.xml
@@ -32,7 +32,6 @@
   <string name="enable">Įjungti</string>
   <string name="add_key">Pridėti raktą</string>
   <string name="overwrite">Perrašyti</string>
-  <string name="repo_delete_title">Pasirinkite saugyklą, kurią pašalinti</string>
   <string name="repo_update_title">Atnaujinti saugyklas</string>
   <string name="tab_noninstalled">Prieinamos programos</string>
   <string name="tab_updates">Atnaujinimai</string>
@@ -85,7 +84,6 @@ Ar norite atnaujinti
 %1$s</string>
   <string name="repos_unchanged">Įjungtose saugyklose nėra nieko naujo</string>
   <string name="global_error_updating_repos">Klaida naujinant:</string>
-  <string name="showPermissions">Rodyti reikalaujamus leidimus</string>
   <string name="theme">Tema</string>
   <string name="repo_num_apps">Programų kiekis</string>
   <string name="repo_description">Aprašymas</string>
diff --git a/F-Droid/res/values-nb/strings.xml b/F-Droid/res/values-nb/strings.xml
index 2c6a15001..221d80989 100644
--- a/F-Droid/res/values-nb/strings.xml
+++ b/F-Droid/res/values-nb/strings.xml
@@ -55,7 +55,6 @@ Lisensiert GNU GPLv3.</string>
   <string name="enable">Skru på</string>
   <string name="add_key">Legg til nøkkel</string>
   <string name="overwrite">Overskriv</string>
-  <string name="repo_delete_title">Velg pakkebrønnen du vil fjerne</string>
   <string name="repo_update_title">Oppdater registerne</string>
   <string name="tab_noninstalled">Tilgjengelig</string>
   <string name="tab_updates">Oppdateringer</string>
@@ -172,8 +171,6 @@ Lisensiert GNU GPLv3.</string>
   <string name="global_error_updating_repos">Feil under oppdatering:</string>
   <string name="no_permissions">Krever ingen tillatelser.</string>
   <string name="permissions_for_long">Tillatelser for versjon %s</string>
-  <string name="showPermissions">Vis tillatelser</string>
-  <string name="showPermissions_on">Vis en liste over rettighetene en app krever</string>
   <string name="no_handler_app">Du har ingen tilgjengelige applikasjoner som kan håndtere %s</string>
   <string name="compactlayout">Kompakt layout</string>
   <string name="compactlayout_on">Vis ikoner i mindre størrelse</string>
diff --git a/F-Droid/res/values-nl/strings.xml b/F-Droid/res/values-nl/strings.xml
index 495cb9a79..3d7868cd6 100644
--- a/F-Droid/res/values-nl/strings.xml
+++ b/F-Droid/res/values-nl/strings.xml
@@ -54,7 +54,6 @@ Een bron-adres ziet er ongeveer
   <string name="enable">Activeer</string>
   <string name="add_key">Voeg sleutel toe</string>
   <string name="overwrite">Overschrijven</string>
-  <string name="repo_delete_title">Kies bron om te verwijderen</string>
   <string name="repo_update_title">Vernieuw bronnen</string>
   <string name="tab_noninstalled">Beschikbaar</string>
   <string name="tab_updates">Updates</string>
@@ -130,8 +129,6 @@ Wilt u ze vernieuwen?</string>
   <string name="status_inserting">Applicatiedetails opslaan (%1$d%%)</string>
   <string name="no_permissions">Geen permissies worden gebruikt</string>
   <string name="permissions_for_long">Permissies voor versie %s</string>
-  <string name="showPermissions">Laat permissies zien</string>
-  <string name="showPermissions_on">Laat een lijst zien van benodigde permissies voor een app</string>
   <string name="no_handler_app">U hebt geen beschikbare app die %s kan verwerken</string>
   <string name="compactlayout">Compacte Layout</string>
   <string name="compactlayout_on">Iconen weergeven in klein formaat</string>
diff --git a/F-Droid/res/values-pl/strings.xml b/F-Droid/res/values-pl/strings.xml
index 11a25ba8f..2865801d8 100644
--- a/F-Droid/res/values-pl/strings.xml
+++ b/F-Droid/res/values-pl/strings.xml
@@ -134,8 +134,6 @@ Czy chcesz je zaktualizować?</string>
   <string name="all_other_repos_fine">Pozostałe repozytoria nie zgłosiły błędów.</string>
   <string name="global_error_updating_repos">Błąd podczas aktualizacji:</string>
   <string name="permissions_for_long">Uprawnienia dla wersji %s</string>
-  <string name="showPermissions">Wyświetl uprawnienia</string>
-  <string name="showPermissions_on">Wyświetl listę uprawnień wymaganych przez aplikację</string>
   <string name="no_handler_app">Nie masz żadnej dostępnej aplikacji, która może obsłużyć %s</string>
   <string name="compactlayout">Widok kompaktowy</string>
   <string name="compactlayout_on">Pokaż ikony w mniejszym rozmiarze</string>
diff --git a/F-Droid/res/values-pt-rBR/strings.xml b/F-Droid/res/values-pt-rBR/strings.xml
index e98cc525a..bcdcaac2d 100644
--- a/F-Droid/res/values-pt-rBR/strings.xml
+++ b/F-Droid/res/values-pt-rBR/strings.xml
@@ -45,7 +45,6 @@ Um endereço do repositório é algo similar a isto: https://f-droid.org/repo</s
   <string name="repo_add_title">Adicionar novo repositório</string>
   <string name="repo_add_add">Adicionar</string>
   <string name="cancel">Cancelar</string>
-  <string name="repo_delete_title">Escolha o repositório para remover</string>
   <string name="repo_update_title">Atualizar repositórios</string>
   <string name="tab_noninstalled">Disponível</string>
   <string name="tab_updates">Atualizações</string>
@@ -145,7 +144,6 @@ Você deseja atualizá-los?</string>
   <string name="global_error_updating_repos">Erro durante a atualização:</string>
   <string name="no_permissions">Nenhuma permissão utilizada.</string>
   <string name="permissions_for_long">Permissões para a versão %s</string>
-  <string name="showPermissions">Mostrar permissões</string>
   <string name="no_handler_app">Você não tem aplicativo instalado que lide com %s</string>
   <string name="compactlayout">Leiaute compacto</string>
   <string name="theme">Tema</string>
diff --git a/F-Droid/res/values-ro/strings.xml b/F-Droid/res/values-ro/strings.xml
index 2ff95a46c..71bf5cfd0 100644
--- a/F-Droid/res/values-ro/strings.xml
+++ b/F-Droid/res/values-ro/strings.xml
@@ -28,7 +28,6 @@ Distribuit sub licenta GNU GPLv3.</string>
   <string name="repo_add_add">Adauga</string>
   <string name="cancel">Anuleaza</string>
   <string name="enable">Activează</string>
-  <string name="repo_delete_title">Alegeti depozitul pentru stergere</string>
   <string name="repo_update_title">Actualizare depozit aplicatii</string>
   <string name="tab_noninstalled">Disponibil</string>
   <string name="tab_updates">Actualizare</string>
@@ -55,7 +54,6 @@ Distribuit sub licenta GNU GPLv3.</string>
   <string name="category_all">Tot</string>
   <string name="category_whatsnew">Ce este nou</string>
   <string name="category_recentlyupdated">Actualizat recent</string>
-  <string name="showPermissions">Arată permisiunile</string>
   <string name="theme">Temă</string>
   <string name="repo_url">URL</string>
   <string name="repo_num_apps">Număr de aplicaţii</string>
diff --git a/F-Droid/res/values-ru/strings.xml b/F-Droid/res/values-ru/strings.xml
index 86c952d51..45c77c90c 100644
--- a/F-Droid/res/values-ru/strings.xml
+++ b/F-Droid/res/values-ru/strings.xml
@@ -59,7 +59,6 @@
   <string name="enable">Включить</string>
   <string name="add_key">Добавить ключ</string>
   <string name="overwrite">Перезаписать</string>
-  <string name="repo_delete_title">Удалить репозиторий</string>
   <string name="repo_update_title">Обновить репозитории</string>
   <string name="tab_noninstalled">Доступно</string>
   <string name="tab_updates">Обновления</string>
@@ -179,8 +178,6 @@
   <string name="global_error_updating_repos">Ошибка во время обновления:</string>
   <string name="no_permissions">Разрешений не требуется.</string>
   <string name="permissions_for_long">Разрешения для версии %s</string>
-  <string name="showPermissions">Показывать разрешения</string>
-  <string name="showPermissions_on">Отображение списка разрешений, которые требуются приложению</string>
   <string name="no_handler_app">У вас нет установленного приложения для обработки %s</string>
   <string name="compactlayout">Компактный вид</string>
   <string name="compactlayout_on">Показать иконки в меньшем размере</string>
diff --git a/F-Droid/res/values-sl/strings.xml b/F-Droid/res/values-sl/strings.xml
index 6d83a3572..f72decdb7 100644
--- a/F-Droid/res/values-sl/strings.xml
+++ b/F-Droid/res/values-sl/strings.xml
@@ -23,7 +23,6 @@ Izdan z licenco GNU GPLv3.</string>
   <string name="repo_add_title">Dodaj novo skladišče</string>
   <string name="repo_add_add">Dodaj</string>
   <string name="cancel">Prekliči</string>
-  <string name="repo_delete_title">Odstrani skladišče</string>
   <string name="repo_update_title">Posodobi skladišča</string>
   <string name="tab_noninstalled">Na razpolago</string>
   <string name="tab_updates">Posodobitve</string>
diff --git a/F-Droid/res/values-sr/strings.xml b/F-Droid/res/values-sr/strings.xml
index c8b85e627..a7c4e99ef 100644
--- a/F-Droid/res/values-sr/strings.xml
+++ b/F-Droid/res/values-sr/strings.xml
@@ -58,7 +58,6 @@
   <string name="enable">Омогући</string>
   <string name="add_key">Додај кључ</string>
   <string name="overwrite">Пребриши</string>
-  <string name="repo_delete_title">Изаберите ризницу за уклањање</string>
   <string name="repo_update_title">Ажурирај ризнице</string>
   <string name="tab_noninstalled">Доступно</string>
   <string name="tab_updates">Надоградње</string>
@@ -179,8 +178,6 @@
   <string name="global_error_updating_repos">Грешка приликом ажурирања:</string>
   <string name="no_permissions">Не захтевају се никакве дозволе.</string>
   <string name="permissions_for_long">Дозволе за издање %s</string>
-  <string name="showPermissions">Прикажи дозволе</string>
-  <string name="showPermissions_on">Прикажи списак дозвола које апликација захтева</string>
   <string name="no_handler_app">Немате инсталирану апликацију за %s</string>
   <string name="compactlayout">Компактни распоред</string>
   <string name="compactlayout_on">Прикажи иконе мање величине</string>
diff --git a/F-Droid/res/values-sv/strings.xml b/F-Droid/res/values-sv/strings.xml
index 894bde6f9..c54c98905 100644
--- a/F-Droid/res/values-sv/strings.xml
+++ b/F-Droid/res/values-sv/strings.xml
@@ -57,7 +57,6 @@ En förrådsadress ser ut så här: https://f-droid.org/repo</string>
   <string name="enable">Aktivera</string>
   <string name="add_key">Lägg till nyckel</string>
   <string name="overwrite">Skriv över</string>
-  <string name="repo_delete_title">Välj förråd att ta bort</string>
   <string name="repo_update_title">Uppdatera förråd</string>
   <string name="tab_noninstalled">Tillgängliga</string>
   <string name="tab_updates">Uppdateringar</string>
@@ -178,8 +177,6 @@ Vill du uppdatera dem?</string>
   <string name="global_error_updating_repos">Fel under uppdatering:</string>
   <string name="no_permissions">Inga behörigheter används.</string>
   <string name="permissions_for_long">Behörigheter för version %s</string>
-  <string name="showPermissions">Visa behörigheter</string>
-  <string name="showPermissions_on">Visa en lista över behörigheter en app behöver</string>
   <string name="no_handler_app">Du har inte någon app tillgänglig för hantering av %s</string>
   <string name="compactlayout">Kompakt layout</string>
   <string name="compactlayout_on">Visa ikoner i en mindre storlek</string>
diff --git a/F-Droid/res/values-tr/strings.xml b/F-Droid/res/values-tr/strings.xml
index 405ac04a5..42ba57953 100644
--- a/F-Droid/res/values-tr/strings.xml
+++ b/F-Droid/res/values-tr/strings.xml
@@ -54,7 +54,6 @@ Bir depo adresi şuna benzer: https://f-droid.org/repo</string>
   <string name="enable">Etkinleştir</string>
   <string name="add_key">Anahtar ekle</string>
   <string name="overwrite">Üzerine yaz</string>
-  <string name="repo_delete_title">Kaldırılacak depoyu seç</string>
   <string name="repo_update_title">Depoları güncelle</string>
   <string name="tab_noninstalled">Mevcut</string>
   <string name="tab_updates">Güncellemeler</string>
@@ -159,8 +158,6 @@ bağlanılıyor</string>
   <string name="status_inserting">Uygulama detayları kaydediliyor (%1$d%%)</string>
   <string name="no_permissions">Hiçbir izin kullanılmıyor.</string>
   <string name="permissions_for_long">%s sürümü için izinler</string>
-  <string name="showPermissions">İzinleri göster</string>
-  <string name="showPermissions_on">Uygulamaların gerektirdiği izinlerin listesini göster</string>
   <string name="no_handler_app">%s unsurunu yönetecek hiçbir mevcut uygulamanız yok</string>
   <string name="compactlayout">Yoğun düzen</string>
   <string name="compactlayout_on">İkonları daha küçük boyutta görüntüle</string>
diff --git a/F-Droid/res/values-ug/strings.xml b/F-Droid/res/values-ug/strings.xml
index 0cf70f83d..a2ab7d593 100644
--- a/F-Droid/res/values-ug/strings.xml
+++ b/F-Droid/res/values-ug/strings.xml
@@ -39,7 +39,6 @@
   <string name="repo_add_title">يېڭى خەزىنە قوش</string>
   <string name="repo_add_add">قوش</string>
   <string name="cancel">ۋاز كەچ</string>
-  <string name="repo_delete_title">چىقىرىۋېتىدىغان خەزىنەنى تاللاڭ</string>
   <string name="repo_update_title">خەزىنە يېڭىلا</string>
   <string name="tab_noninstalled">ئىشلىتىشچان</string>
   <string name="tab_updates">يېڭىلانمىلار</string>
@@ -97,7 +96,6 @@
   <string name="status_checking_compatibility">ئەپلەرنىڭ ئۈسكۈنىڭىز بىلەن ماسلىشىشچانلىقىنى تەكشۈرۈۋاتىدۇ…</string>
   <string name="no_permissions">ھېچقانداق ھوقۇق ئىشلەتمەيدۇ.</string>
   <string name="permissions_for_long">%s نەشرىنىڭ ھوقۇقلىرى</string>
-  <string name="showPermissions">ھوقۇقلارنى كۆرسەت</string>
   <string name="no_handler_app">سىز %s نى بىر تەرەپ قىلالايدىغان ھېچقانداق ئەپ ئورناتمىغان</string>
   <string name="compactlayout">ئىخچام جايلاشتۇرۇش</string>
   <string name="theme">ئۆرنەك</string>
diff --git a/F-Droid/res/values-uk/strings.xml b/F-Droid/res/values-uk/strings.xml
index 73322fe72..be511cdc3 100644
--- a/F-Droid/res/values-uk/strings.xml
+++ b/F-Droid/res/values-uk/strings.xml
@@ -23,7 +23,6 @@
   <string name="repo_add_title">Новий репозиторій</string>
   <string name="repo_add_add">Додати</string>
   <string name="cancel">Назад</string>
-  <string name="repo_delete_title">Видалити репозиторій</string>
   <string name="repo_update_title">Оновити репозиторії?</string>
   <string name="tab_noninstalled">Наявне</string>
   <string name="tab_updates">Оновлення</string>
diff --git a/F-Droid/res/values-v11/styles.xml b/F-Droid/res/values-v11/styles.xml
index 1b9ea4093..7e19f4d8a 100644
--- a/F-Droid/res/values-v11/styles.xml
+++ b/F-Droid/res/values-v11/styles.xml
@@ -5,4 +5,14 @@
         <item name="android:actionButtonStyle">@style/SwapTheme.Wizard.ActionButton</item>
     </style>
 
-</resources>
\ No newline at end of file
+    <style name="AlertDialogBaseThemeDark" parent="Theme.AppCompat.Dialog.Alert">
+        <item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
+        <item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
+    </style>
+
+    <style name="AlertDialogBaseThemeLight" parent="Theme.AppCompat.Light.Dialog.Alert">
+        <item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
+        <item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
+    </style>
+
+</resources>
diff --git a/F-Droid/res/values-v21/styles.xml b/F-Droid/res/values-v21/styles.xml
index 9b2ebc734..04093aaad 100644
--- a/F-Droid/res/values-v21/styles.xml
+++ b/F-Droid/res/values-v21/styles.xml
@@ -3,6 +3,7 @@
 
     <style name="AppThemeDark" parent="AppBaseThemeDark">
         <!-- customizations that are not API-level specific go here. -->
+        <item name="android:alertDialogTheme">@style/AlertDialogThemeDark</item>
         <item name="android:colorEdgeEffect">@color/fdroid_blue</item>
         <item name="android:colorControlActivated">@color/fdroid_green</item>
         <item name="android:colorControlHighlight">@color/fdroid_green</item>
@@ -10,6 +11,7 @@
 
     <style name="AppThemeLight" parent="AppBaseThemeLight">
         <!-- customizations that are not API-level specific go here. -->
+        <item name="android:alertDialogTheme">@style/AlertDialogThemeLight</item>
         <item name="android:colorEdgeEffect">@color/fdroid_blue</item>
     </style>
 
@@ -17,4 +19,4 @@
         <item name="android:colorButtonNormal">#04b9e6</item>
     </style>
 
-</resources>
\ No newline at end of file
+</resources>
diff --git a/F-Droid/res/values-zh-rCN/strings.xml b/F-Droid/res/values-zh-rCN/strings.xml
index d9cd34c5f..018c3fc78 100644
--- a/F-Droid/res/values-zh-rCN/strings.xml
+++ b/F-Droid/res/values-zh-rCN/strings.xml
@@ -54,7 +54,6 @@ https://f-droid.org/repo</string>
   <string name="enable">启用</string>
   <string name="add_key">添加密钥</string>
   <string name="overwrite">覆盖</string>
-  <string name="repo_delete_title">选择要移除的软件源</string>
   <string name="repo_update_title">更新软件源</string>
   <string name="tab_noninstalled">可安装</string>
   <string name="tab_updates">更新</string>
@@ -146,8 +145,6 @@ https://f-droid.org/repo</string>
   <string name="status_inserting">正在保存应用程序的细节 (%1$d%%)</string>
   <string name="no_permissions">未使用任何权限。</string>
   <string name="permissions_for_long">版本 %s 的权限</string>
-  <string name="showPermissions">显示权限</string>
-  <string name="showPermissions_on">显示应用程序所需的权限清单</string>
   <string name="no_handler_app">你没有可处理 %s 的应用程序</string>
   <string name="compactlayout">紧凑布局</string>
   <string name="compactlayout_on">显示小图标</string>
diff --git a/F-Droid/res/values-zh-rHK/strings.xml b/F-Droid/res/values-zh-rHK/strings.xml
index d2926b696..eafcd2024 100644
--- a/F-Droid/res/values-zh-rHK/strings.xml
+++ b/F-Droid/res/values-zh-rHK/strings.xml
@@ -86,8 +86,6 @@
   <string name="status_inserting">儲存應用程式詳情中(%1$d%%)</string>
   <string name="no_permissions">沒有使用任何存取權。</string>
   <string name="permissions_for_long">版本%s的存取權</string>
-  <string name="showPermissions">顯示存取權</string>
-  <string name="showPermissions_on">顯示應用程式所需的存取權</string>
   <string name="compactlayout">精簡設計</string>
   <string name="compactlayout_on">顯示小型圖示</string>
   <string name="theme">主題</string>
diff --git a/F-Droid/res/values/strings.xml b/F-Droid/res/values/strings.xml
index b009e5154..7a27fd60e 100644
--- a/F-Droid/res/values/strings.xml
+++ b/F-Droid/res/values/strings.xml
@@ -69,11 +69,11 @@
 	<string name="links">Links</string>
 	<string name="content_description_view_more">View more</string>
 
+	<string name="back">Back</string>
 	<string name="cancel">Cancel</string>
 	<string name="enable">Enable</string>
 	<string name="add_key">Add Key</string>
 	<string name="overwrite">Overwrite</string>
-	<string name="repo_delete_title">Choose repository to remove</string>
 
 	<string name="repo_update_title">Update repositories</string>
 	<string name="tab_noninstalled">Available</string>
@@ -209,6 +209,7 @@
 	  - Percentage complete (int between 0-100)
 	-->
 	<string name="status_download">Downloading\n%2$s / %3$s (%4$d%%) from\n%1$s</string>
+	<string name="update_notification_title">Updating repositories</string>
 	<string name="status_processing_xml_percent">Processing %2$s / %3$s (%4$d%%) from %1$s</string>
 	<string name="status_connecting_to_repo">Connecting to\n%1$s</string>
 	<string name="status_checking_compatibility">Checking apps compatibility with your device…</string>
@@ -218,8 +219,6 @@
         <string name="global_error_updating_repos">Error during update:</string>
 	<string name="no_permissions">No permissions are used.</string>
 	<string name="permissions_for_long">Permissions for version %s</string>
-	<string name="showPermissions">Show permissions</string>
-	<string name="showPermissions_on">Display a list of permissions an app requires</string>
 	<string name="no_handler_app">You don\'t have any available app that can handle %s</string>
 	<string name="compactlayout">Compact Layout</string>
 	<string name="compactlayout_on">Show icons at a smaller size</string>
@@ -368,10 +367,11 @@
 	<string name="swap_bluetooth_your_device">Your device is</string>
 	<string name="swap_bluetooth_select_or_scan">Select from devices below, or press \"Scan\" from the menu to find more devices.</string>
 	<string name="swap_bluetooth_bonded_device">Bonded</string>
-	<string name="swap_bluetooth_bonding_device">Currently bonding...</string>
+	<string name="swap_bluetooth_bonding_device">Currently bonding…</string>
 	<string name="swap_bluetooth_unknown_device">Unknown device</string>
-	<string name="loading">Loading...</string>
+	<string name="loading">Loading…</string>
 	<string name="swap_attempt_install">TRY TO INSTALL</string>
+	<string name="swap_connection_misc_error">An error occurred while connecting to device, we can\'t seem to swap with it :(</string>
 
     <string name="install_confirm">Do you want to install this application?
             It will get access to:</string>
@@ -399,4 +399,13 @@
 
     <string name="perms_new_perm_prefix"><font size="12" fgcolor="#ff33b5e5">NEW: </font></string>
     <string name="perms_description_app">Provided by %1$s.</string>
+    <string name="downloading">Downloading…</string>
+
+    <string-array name="file_size_units">
+        <item>B</item>
+        <item>KiB</item>
+        <item>MiB</item>
+        <item>GiB</item>
+        <item>TiB</item>
+    </string-array>
 </resources>
diff --git a/F-Droid/res/values/styles.xml b/F-Droid/res/values/styles.xml
index 15a1c5821..68b49c53c 100644
--- a/F-Droid/res/values/styles.xml
+++ b/F-Droid/res/values/styles.xml
@@ -6,6 +6,8 @@
         <item name="colorPrimary">@color/fdroid_blue</item>
         <item name="colorPrimaryDark">@color/fdroid_blue_dark</item>
         <item name="colorAccent">@color/fdroid_green</item>
+        <item name="android:textColorLink">@color/fdroid_green</item>
+        <item name="alertDialogTheme">@style/AlertDialogThemeDark</item>
     </style>
 
     <style name="AppBaseThemeLight" parent="Theme.AppCompat.Light.DarkActionBar">
@@ -14,17 +16,14 @@
         <item name="colorPrimary">@color/fdroid_blue</item>
         <item name="colorPrimaryDark">@color/fdroid_blue_dark</item>
         <item name="colorAccent">@color/fdroid_green</item>
+        <item name="android:textColorLink">@color/fdroid_green</item>
+        <item name="alertDialogTheme">@style/AlertDialogThemeLight</item>
     </style>
 
     <color name="black">#FF000000</color>
     <color name="white">#FFFFFFFF</color>
     <color name="red">#FFFF0000</color>
 
-    <style name="AboutDialogLight" parent="@android:style/Theme.Dialog">
-        <item name="@android:windowBackground">@color/black</item>
-        <item name="@android:textColor">@color/white</item>
-    </style>
-
     <style name="AppThemeDark" parent="AppBaseThemeDark">
         <!-- customizations that are not API-level specific go here. -->
     </style>
@@ -33,6 +32,16 @@
         <!-- customizations that are not API-level specific go here. -->
     </style>
 
+    <style name="AlertDialogBaseThemeDark" parent="Theme.AppCompat.Dialog.Alert" />
+    <style name="AlertDialogThemeDark" parent="AlertDialogBaseThemeDark">
+        <item name="colorAccent">@color/fdroid_green</item>
+    </style>
+
+    <style name="AlertDialogBaseThemeLight" parent="Theme.AppCompat.Light.Dialog.Alert" />
+    <style name="AlertDialogThemeLight" parent="AlertDialogBaseThemeLight">
+        <item name="colorAccent">@color/fdroid_green</item>
+    </style>
+
     <style name="SwapTheme.Wizard" parent="Theme.AppCompat.Light.NoActionBar">
         <item name="colorButtonNormal">@color/swap_bright_blue</item>
         <item name="android:actionBarStyle">@style/Widget.AppCompat.ActionBar.Solid</item>
diff --git a/F-Droid/src/org/fdroid/fdroid/AppDetails.java b/F-Droid/src/org/fdroid/fdroid/AppDetails.java
index f0a4d2fc4..6500551b0 100644
--- a/F-Droid/src/org/fdroid/fdroid/AppDetails.java
+++ b/F-Droid/src/org/fdroid/fdroid/AppDetails.java
@@ -21,28 +21,30 @@
 package org.fdroid.fdroid;
 
 import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
 import android.bluetooth.BluetoothAdapter;
 import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.Signature;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.support.annotation.NonNull;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.ListFragment;
 import android.support.v4.app.NavUtils;
+import android.support.v4.content.LocalBroadcastManager;
 import android.support.v4.view.MenuItemCompat;
-import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
 import android.text.Html;
 import android.text.Layout;
 import android.text.Selection;
@@ -63,9 +65,11 @@ import android.view.Window;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.FrameLayout;
+import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ListView;
+import android.widget.ProgressBar;
 import android.widget.TextView;
 import android.widget.Toast;
 
@@ -90,6 +94,7 @@ import org.fdroid.fdroid.net.Downloader;
 
 import java.io.File;
 import java.security.NoSuchAlgorithmException;
+import java.text.DecimalFormat;
 import java.util.Iterator;
 import java.util.List;
 
@@ -113,7 +118,7 @@ interface AppInstallListener {
     void removeApk(String packageName);
 }
 
-public class AppDetails extends ActionBarActivity implements ProgressListener, AppDetailsData, AppInstallListener {
+public class AppDetails extends AppCompatActivity implements ProgressListener, AppDetailsData, AppInstallListener {
 
     private static final String TAG = "AppDetails";
 
@@ -124,7 +129,6 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
 
     private FDroidApp fdroidApp;
     private ApkListAdapter adapter;
-    private ProgressDialog progressDialog;
 
     private static class ViewHolder {
         TextView version;
@@ -316,6 +320,7 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
     private App app;
     private PackageManager mPm;
     private ApkDownloader downloadHandler;
+    private LocalBroadcastManager localBroadcastManager;
 
     private boolean startingIgnoreAll;
     private int startingIgnoreThis;
@@ -323,11 +328,14 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
     private final Context mctx = this;
     private Installer installer;
 
+
+    private AppDetailsHeaderFragment mHeaderFragment;
+
     /**
      * Stores relevant data that we want to keep track of when destroying the activity
      * with the expectation of it being recreated straight away (e.g. after an
      * orientation change). One of the major things is that we want the download thread
-     * to stay active, but for it not to trigger any UI stuff (e.g. progress dialogs)
+     * to stay active, but for it not to trigger any UI stuff (e.g. progress bar)
      * between the activity being destroyed and recreated.
      */
     private static class ConfigurationChangeHelper {
@@ -418,13 +426,7 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
             listFragment.removeSummaryHeader();
         }
 
-        // Spinner seems to default to visible on Android 4.0.3 and 4.0.4
-        // https://gitlab.com/fdroid/fdroidclient/issues/75
-        // Can't put this in onResume(), because that is called on return from asking
-        // the user permission to use su (in which case we still want to show the
-        // progress indicator after returning from that prompt).
-        setSupportProgressBarIndeterminateVisibility(false);
-
+        localBroadcastManager = LocalBroadcastManager.getInstance(this);
     }
 
     // The signature of the installed version.
@@ -442,39 +444,35 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
                 myAppObserver);
     }
 
-    @Override
-    protected void onResume() {
-        super.onResume();
-        if (downloadHandler != null) {
-            if (downloadHandler.isComplete()) {
-                downloadCompleteInstallApk();
-            } else {
-                downloadHandler.setProgressListener(this);
-
-                // Show the progress dialog, if for no other reason than to prevent them attempting
-                // to download again (i.e. we force them to touch 'cancel' before they can access
-                // the rest of the activity).
-                Log.d(TAG, "Showing dialog to user after resuming app details view, because a download was previously in progress");
-                updateProgressDialog();
-            }
-        }
-    }
-
     @Override
     protected void onResumeFragments() {
         super.onResumeFragments();
         refreshApkList();
         refreshHeader();
         supportInvalidateOptionsMenu();
+        if (downloadHandler != null) {
+            if (downloadHandler.isComplete()) {
+                downloadCompleteInstallApk();
+            } else {
+                localBroadcastManager.registerReceiver(downloaderProgressReceiver,
+                        new IntentFilter(Downloader.LOCAL_ACTION_PROGRESS));
+                downloadHandler.setProgressListener(this);
+
+                if (downloadHandler.getTotalBytes() == 0)
+                    mHeaderFragment.startProgress();
+                else
+                    mHeaderFragment.updateProgress(downloadHandler.getBytesRead(), downloadHandler.getTotalBytes());
+            }
+        }
     }
 
     /**
-     * Remove progress listener, suppress progress dialog, set downloadHandler to null.
+     * Remove progress listener, suppress progress bar, set downloadHandler to null.
      */
     private void cleanUpFinishedDownload() {
         if (downloadHandler != null) {
             downloadHandler.removeProgressListener();
-            removeProgressDialog();
+            mHeaderFragment.removeProgress();
             downloadHandler = null;
         }
     }
@@ -485,7 +483,7 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
      */
     private void downloadCompleteInstallApk() {
         if (downloadHandler != null) {
-            installApk(downloadHandler.localFile(), downloadHandler.getApk().id);
+            installApk(downloadHandler.localFile());
             cleanUpFinishedDownload();
         }
     }
@@ -504,13 +502,23 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
             setIgnoreUpdates(app.id, app.ignoreAllUpdates, app.ignoreThisUpdate);
         }
 
+        localBroadcastManager.unregisterReceiver(downloaderProgressReceiver);
         if (downloadHandler != null) {
             downloadHandler.removeProgressListener();
         }
 
-        removeProgressDialog();
+        mHeaderFragment.removeProgress();
     }
 
+    private final BroadcastReceiver downloaderProgressReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (mHeaderFragment != null)
+                mHeaderFragment.updateProgress(intent.getIntExtra(Downloader.EXTRA_BYTES_READ, -1),
+                    intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, -1));
+        }
+    };
+
     private void onAppChanged() {
         if (!reset(app.id)) {
             AppDetails.this.finish();
@@ -553,13 +561,6 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
         super.onDestroy();
     }
 
-    private void removeProgressDialog() {
-        if (progressDialog != null) {
-            progressDialog.dismiss();
-            progressDialog = null;
-        }
-    }
-
     // Reset the display and list contents. Used when entering the activity, and
     // also when something has been installed/uninstalled.
     // Return true if the app was found, false otherwise.
@@ -619,9 +620,9 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
     }
 
     private void refreshHeader() {
-        AppDetailsHeaderFragment headerFragment = (AppDetailsHeaderFragment)
+        mHeaderFragment = (AppDetailsHeaderFragment)
                 getSupportFragmentManager().findFragmentById(R.id.header);
-        headerFragment.refresh();
+        mHeaderFragment.updateViews();
     }
 
     @Override
@@ -725,8 +726,8 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
         }
 
         @Override
-        public boolean onTouchEvent(TextView widget, Spannable buffer,
-                MotionEvent event) {
+        public boolean onTouchEvent(@NonNull TextView widget, @NonNull Spannable buffer,
+                @NonNull MotionEvent event) {
             try {
                 return super.onTouchEvent(widget, buffer, event);
             } catch (ActivityNotFoundException ex) {
@@ -805,6 +806,10 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
     // Install the version of this app denoted by 'app.curApk'.
     @Override
     public void install(final Apk apk) {
+        // Ignore call if another download is running.
+        if (downloadHandler != null && !downloadHandler.isComplete())
+            return;
+
         final String[] projection = { RepoProvider.DataColumns.ADDRESS };
         Repo repo = RepoProvider.Helper.findById(this, apk.repo, projection);
         if (repo == null || repo.address == null) {
@@ -854,32 +859,28 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
 
     private void startDownload(Apk apk, String repoAddress) {
         downloadHandler = new ApkDownloader(getBaseContext(), apk, repoAddress);
+        localBroadcastManager.registerReceiver(downloaderProgressReceiver,
+                new IntentFilter(Downloader.LOCAL_ACTION_PROGRESS));
         downloadHandler.setProgressListener(this);
         if (downloadHandler.download()) {
-            updateProgressDialog();
+            mHeaderFragment.startProgress();
         }
     }
 
-    private void installApk(File file, String packageName) {
-        setSupportProgressBarIndeterminateVisibility(true);
-
+    private void installApk(File file) {
         try {
             installer.installPackage(file);
         } catch (AndroidNotCompatibleException e) {
             Log.e(TAG, "Android not compatible with this Installer!", e);
-            setSupportProgressBarIndeterminateVisibility(false);
         }
     }
 
     @Override
     public void removeApk(String packageName) {
-        setSupportProgressBarIndeterminateVisibility(true);
-
         try {
             installer.deletePackage(packageName);
         } catch (AndroidNotCompatibleException e) {
             Log.e(TAG, "Android not compatible with this Installer!", e);
-            setSupportProgressBarIndeterminateVisibility(false);
         }
     }
 
@@ -894,7 +895,6 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
                         PackageManagerCompat.setInstaller(mPm, app.id);
                     }
 
-                    setSupportProgressBarIndeterminateVisibility(false);
                     onAppChanged();
                 }
             });
@@ -906,7 +906,6 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
                 runOnUiThread(new Runnable() {
                     @Override
                     public void run() {
-                        setSupportProgressBarIndeterminateVisibility(false);
                         onAppChanged();
                     }
                 });
@@ -914,7 +913,6 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
                 runOnUiThread(new Runnable() {
                     @Override
                     public void run() {
-                        setSupportProgressBarIndeterminateVisibility(false);
                         onAppChanged();
 
                         Log.e(TAG, "Installer aborted with errorCode: " + errorCode);
@@ -945,79 +943,6 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
         startActivity(Intent.createChooser(shareIntent, getString(R.string.menu_share)));
     }
 
-    private ProgressDialog getProgressDialog(String file) {
-        if (progressDialog == null) {
-            final ProgressDialog pd = new ProgressDialog(this);
-            pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
-            if (Build.VERSION.SDK_INT >= 11) {
-                pd.setProgressNumberFormat("%1d/%2d KiB");
-            }
-            pd.setMessage(getString(R.string.download_server) + ":\n " + file);
-            pd.setCancelable(true);
-            pd.setCanceledOnTouchOutside(false);
-
-            // The indeterminate-ness will get overridden on the first progress event we receive.
-            pd.setIndeterminate(true);
-
-            pd.setOnCancelListener(new DialogInterface.OnCancelListener() {
-                @Override
-                public void onCancel(DialogInterface dialog) {
-                    Log.d(TAG, "User clicked 'cancel' on download, attempting to interrupt download thread.");
-                    if (downloadHandler !=  null) {
-                        downloadHandler.cancel();
-                        cleanUpFinishedDownload();
-                    } else {
-                        Log.e(TAG, "Tried to cancel, but the downloadHandler doesn't exist.");
-                    }
-                    progressDialog = null;
-                    Toast.makeText(AppDetails.this, getString(R.string.download_cancelled), Toast.LENGTH_LONG).show();
-                }
-            });
-            pd.setButton(DialogInterface.BUTTON_NEUTRAL,
-                    getString(R.string.cancel),
-                    new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            pd.cancel();
-                        }
-                    }
-            );
-            progressDialog = pd;
-        }
-        return progressDialog;
-    }
-
-    /**
-     * Looks at the current <code>downloadHandler</code> and finds it's size and progress.
-     * This is in comparison to {@link org.fdroid.fdroid.AppDetails#updateProgressDialog(int, int)},
-     * which is used when you have the details from a freshly received
-     * {@link org.fdroid.fdroid.ProgressListener.Event}.
-     */
-    private void updateProgressDialog() {
-        if (downloadHandler != null) {
-            updateProgressDialog(downloadHandler.getProgress(), downloadHandler.getTotalSize());
-        }
-    }
-
-    private void updateProgressDialog(int progress, int total) {
-        if (downloadHandler != null) {
-            ProgressDialog pd = getProgressDialog(downloadHandler.getRemoteAddress());
-            if (total > 0) {
-                pd.setIndeterminate(false);
-                pd.setProgress(progress/1024);
-                pd.setMax(total/1024);
-            } else {
-                pd.setIndeterminate(true);
-                pd.setProgress(progress/1024);
-                pd.setMax(0);
-            }
-            if (!pd.isShowing()) {
-                Log.d(TAG, "Showing progress dialog for download.");
-                pd.show();
-            }
-        }
-    }
-
     @Override
     public void onProgress(Event event) {
         if (downloadHandler == null || !downloadHandler.isEventFromThis(event)) {
@@ -1034,9 +959,6 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
 
         boolean finished = false;
         switch (event.type) {
-        case Downloader.EVENT_PROGRESS:
-            updateProgressDialog(event.progress, event.total);
-            break;
         case ApkDownloader.EVENT_ERROR:
             final String text;
             if (event.getData().getInt(ApkDownloader.EVENT_DATA_ERROR_TYPE) == ApkDownloader.ERROR_HASH_MISMATCH)
@@ -1054,7 +976,8 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
         }
 
         if (finished) {
-            removeProgressDialog();
+            if (mHeaderFragment != null)
+                mHeaderFragment.removeProgress();
             downloadHandler = null;
         }
     }
@@ -1447,9 +1370,14 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
         }
     }
 
-    public static class AppDetailsHeaderFragment extends Fragment {
+    public static class AppDetailsHeaderFragment extends Fragment implements View.OnClickListener {
 
         private AppDetailsData data;
+        private Button btMain;
+        private ProgressBar progressBar;
+        private TextView progressSize;
+        private TextView progressPercent;
+        private ImageButton cancelButton;
         protected final DisplayImageOptions displayImageOptions;
         public static boolean installed = false;
         public static boolean updateWanted = false;
@@ -1493,35 +1421,120 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
             TextView tv = (TextView) view.findViewById(R.id.title);
             tv.setText(getApp().name);
 
+            btMain   = (Button) view.findViewById(R.id.btn_main);
+            progressBar     = (ProgressBar) view.findViewById(R.id.progress_bar);
+            progressSize    = (TextView) view.findViewById(R.id.progress_size);
+            progressPercent = (TextView) view.findViewById(R.id.progress_percentage);
+            cancelButton    = (ImageButton) view.findViewById(R.id.cancel);
+            progressBar.setIndeterminate(false);
+            cancelButton.setOnClickListener(this);
+
             updateViews(view);
         }
 
         @Override
         public void onResume() {
             super.onResume();
-            refresh();
+            updateViews();
         }
 
-        public void refresh() {
+        /**
+         * Displays empty, indeterminate progress bar and related views.
+         */
+        public void startProgress() {
+            setProgressVisible(true);
+            progressBar.setIndeterminate(true);
+            progressSize.setText("");
+            progressPercent.setText("");
+            updateViews();
+        }
+
+        /**
+         * Updates progress bar and captions to new values (in bytes).
+         */
+        public void updateProgress(long progress, long total) {
+            long percent = progress * 100 / total;
+            setProgressVisible(true);
+            progressBar.setIndeterminate(false);
+            progressBar.setProgress((int) percent);
+            progressBar.setMax(100);
+            progressSize.setText(readableFileSize(progress) + " / " + readableFileSize(total));
+            progressPercent.setText(Long.toString(percent) + " %");
+        }
+
+        /**
+         * Converts a number of bytes to a human readable file size (eg 3.5 GiB).
+         *
+         * Based on http://stackoverflow.com/a/5599842
+         */
+        public String readableFileSize(long bytes) {
+            final String[] units = getResources().getStringArray(R.array.file_size_units);
+            if (bytes <= 0) return "0 " + units[0];
+            int digitGroups = (int) (Math.log10(bytes) / Math.log10(1024));
+            return new DecimalFormat("#,##0.#")
+                    .format(bytes / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
+        }
+
+        /**
+         * Shows or hides progress bar and related views.
+         */
+        private void setProgressVisible(boolean visible) {
+            int state = (visible) ? View.VISIBLE : View.GONE;
+            progressBar.setVisibility(state);
+            progressSize.setVisibility(state);
+            progressPercent.setVisibility(state);
+            cancelButton.setVisibility(state);
+        }
+
+        /**
+         * Removes progress bar and related views, invokes {@link #updateViews()}.
+         */
+        public void removeProgress() {
+            setProgressVisible(false);
+            updateViews();
+        }
+
+        /**
+         * Cancels download and hides progress bar.
+         */
+        @Override
+        public void onClick(View view) {
+            AppDetails activity = (AppDetails) getActivity();
+            if (activity == null || activity.downloadHandler == null)
+                return;
+
+            activity.downloadHandler.cancel();
+            activity.cleanUpFinishedDownload();
+            setProgressVisible(false);
+            updateViews();
+        }
+
+        public void updateViews() {
             updateViews(getView());
         }
 
         public void updateViews(View view) {
             TextView statusView = (TextView) view.findViewById(R.id.status);
-            Button btMain = (Button) view.findViewById(R.id.btn_main);
             btMain.setVisibility(View.VISIBLE);
 
+            AppDetails activity = (AppDetails) getActivity();
+            if (activity.downloadHandler != null) {
+                btMain.setText(R.string.downloading);
+                btMain.setEnabled(false);
+            }
             /*
             Check count > 0 due to incompatible apps resulting in an empty list.
             If App isn't installed
              */
-            if (!getApp().isInstalled() && getApp().suggestedVercode > 0 && ((AppDetails)getActivity()).adapter.getCount() > 0) {
+            else if (!getApp().isInstalled() && getApp().suggestedVercode > 0 &&
+                    ((AppDetails)getActivity()).adapter.getCount() > 0) {
                 installed = false;
                 statusView.setText(getString(R.string.details_notinstalled));
                 NfcHelper.disableAndroidBeam(getActivity());
                 // Set Install button and hide second button
                 btMain.setText(R.string.menu_install);
                 btMain.setOnClickListener(mOnClickListener);
+                btMain.setEnabled(true);
             }
             // If App is installed
             else if (getApp().isInstalled()) {
@@ -1541,6 +1554,7 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
                     }
                 }
                 btMain.setOnClickListener(mOnClickListener);
+                btMain.setEnabled(true);
             }
             TextView currentVersion = (TextView) view.findViewById(R.id.current_version);
             if (!getApks().isEmpty()) {
@@ -1574,6 +1588,8 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
 
                 // If not installed, install
                 else if (getApp().suggestedVercode > 0) {
+                    btMain.setEnabled(false);
+                    btMain.setText(R.string.system_install_installing);
                     final Apk apkToInstall = ApkProvider.Helper.find(getActivity(), getApp().id, getApp().suggestedVercode);
                     ((AppDetails)getActivity()).install(apkToInstall);
                 }
diff --git a/F-Droid/src/org/fdroid/fdroid/FDroid.java b/F-Droid/src/org/fdroid/fdroid/FDroid.java
index 4fce5be34..768deb6b5 100644
--- a/F-Droid/src/org/fdroid/fdroid/FDroid.java
+++ b/F-Droid/src/org/fdroid/fdroid/FDroid.java
@@ -19,7 +19,6 @@
 
 package org.fdroid.fdroid;
 
-import android.app.AlertDialog;
 import android.app.NotificationManager;
 import android.app.SearchManager;
 import android.bluetooth.BluetoothAdapter;
@@ -30,13 +29,12 @@ import android.content.pm.PackageInfo;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
 import android.support.v4.view.ViewPager;
 import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AlertDialog;
 import android.text.TextUtils;
 import android.util.Log;
-import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -56,7 +54,6 @@ public class FDroid extends ActionBarActivity {
 
     private static final String TAG = "FDroid";
 
-    public static final int REQUEST_MANAGEREPOS = 0;
     public static final int REQUEST_PREFS = 1;
     public static final int REQUEST_ENABLE_BLUETOOTH = 2;
     public static final int REQUEST_SWAP = 3;
@@ -246,12 +243,11 @@ public class FDroid extends ActionBarActivity {
         switch (item.getItemId()) {
 
         case R.id.action_update_repo:
-            updateRepos();
+            UpdateService.updateNow(this);
             return true;
 
         case R.id.action_manage_repos:
-            Intent i = new Intent(this, ManageReposActivity.class);
-            startActivityForResult(i, REQUEST_MANAGEREPOS);
+            startActivity(new Intent(this, ManageReposActivity.class));
             return true;
 
         case R.id.action_settings:
@@ -279,16 +275,7 @@ public class FDroid extends ActionBarActivity {
             return true;
 
         case R.id.action_about:
-            View view;
-            if (Build.VERSION.SDK_INT >= 11) {
-                LayoutInflater li = LayoutInflater.from(this);
-                view = li.inflate(R.layout.about, null);
-            } else {
-                view = View.inflate(
-                        new ContextThemeWrapper(this, R.style.AboutDialogLight),
-                        R.layout.about, null);
-            }
-
+            View view = LayoutInflater.from(this).inflate(R.layout.about, null);
             // Fill in the version...
             try {
                 PackageInfo pi = getPackageManager()
@@ -299,17 +286,8 @@ public class FDroid extends ActionBarActivity {
             } catch (Exception ignored) {
             }
 
-            AlertDialog.Builder builder;
-            if (Build.VERSION.SDK_INT >= 11) {
-                builder = new AlertDialog.Builder(this).setView(view);
-            } else {
-                builder = new AlertDialog.Builder(
-                        new ContextThemeWrapper(
-                                this, R.style.AboutDialogLight)
-                        ).setView(view);
-            }
+            AlertDialog.Builder builder = new AlertDialog.Builder(this).setView(view);
             final AlertDialog alrt = builder.create();
-            alrt.setIcon(R.drawable.ic_launcher);
             alrt.setTitle(getString(R.string.about_title));
             alrt.setButton(AlertDialog.BUTTON_NEUTRAL,
                     getString(R.string.about_website),
@@ -338,32 +316,6 @@ public class FDroid extends ActionBarActivity {
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 
         switch (requestCode) {
-        case REQUEST_MANAGEREPOS:
-            if (data != null && data.hasExtra(ManageReposActivity.REQUEST_UPDATE)) {
-                AlertDialog.Builder ask_alrt = new AlertDialog.Builder(this);
-                ask_alrt.setTitle(getString(R.string.repo_update_title));
-                ask_alrt.setIcon(android.R.drawable.ic_menu_rotate);
-                ask_alrt.setMessage(getString(R.string.repo_alrt));
-                ask_alrt.setPositiveButton(getString(R.string.yes),
-                        new DialogInterface.OnClickListener() {
-                            @Override
-                            public void onClick(DialogInterface dialog,
-                                    int whichButton) {
-                                updateRepos();
-                            }
-                        });
-                ask_alrt.setNegativeButton(getString(R.string.no),
-                        new DialogInterface.OnClickListener() {
-                            @Override
-                            public void onClick(DialogInterface dialog,
-                                    int whichButton) {
-                                // do nothing
-                            }
-                        });
-                AlertDialog alert = ask_alrt.create();
-                alert.show();
-            }
-            break;
         case REQUEST_PREFS:
             // The automatic update settings may have changed, so reschedule (or
             // unschedule) the service accordingly. It's cheap, so no need to
@@ -398,13 +350,6 @@ public class FDroid extends ActionBarActivity {
         });
     }
 
-    // Force a repo update now. A progress dialog is shown and the UpdateService
-    // is told to do the update, which will result in the database changing. The
-    // UpdateReceiver class should get told when this is finished.
-    public void updateRepos() {
-        UpdateService.updateNow(this);
-    }
-
     private TabManager getTabManager() {
         if (tabManager == null) {
             tabManager = new TabManager(this, viewPager);
diff --git a/F-Droid/src/org/fdroid/fdroid/FDroidApp.java b/F-Droid/src/org/fdroid/fdroid/FDroidApp.java
index ab1df5825..85cdbe56d 100644
--- a/F-Droid/src/org/fdroid/fdroid/FDroidApp.java
+++ b/F-Droid/src/org/fdroid/fdroid/FDroidApp.java
@@ -74,7 +74,8 @@ public class FDroidApp extends Application {
     }
 
     public enum Theme {
-        dark, light, lightWithDarkActionBar
+        dark,
+        light,
     }
 
     private static Theme curTheme = Theme.light;
@@ -100,7 +101,7 @@ public class FDroidApp extends Application {
             case light:
                 return R.style.AppThemeLight;
             default:
-                return R.style.AppThemeDark;
+                return R.style.AppThemeLight;
         }
     }
 
diff --git a/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java b/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java
index f3cff0b8b..936d2e963 100644
--- a/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java
+++ b/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java
@@ -3,7 +3,6 @@ package org.fdroid.fdroid;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.text.TextUtils;
@@ -22,6 +21,8 @@ import org.xml.sax.XMLReader;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
 import java.security.CodeSigner;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
@@ -52,16 +53,14 @@ public class RepoUpdater {
 
     /**
      * Updates an app repo as read out of the database into a {@link Repo} instance.
-     *
-     * @param context
-     * @param repo    a {@link Repo} read out of the local database
+     * @param repo A {@link Repo} read out of the local database
      */
     public RepoUpdater(@NonNull Context context, @NonNull Repo repo) {
         this.context = context;
         this.repo = repo;
     }
 
-    public void setProgressListener(ProgressListener progressListener) {
+    public void setProgressListener(@Nullable ProgressListener progressListener) {
         this.progressListener = progressListener;
     }
 
@@ -71,29 +70,23 @@ public class RepoUpdater {
 
     public List<Apk> getApks() { return apks; }
 
-    protected String getIndexAddress() {
+    protected URL getIndexAddress() throws MalformedURLException {
+        String urlString = repo.address + "/index.jar";
         try {
             String versionName = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
-            return repo.address + "/index.jar?client_version=" + versionName;
+            urlString += "?client_version=" + versionName;
         } catch (PackageManager.NameNotFoundException e) {
             e.printStackTrace();
         }
-        return repo.address + "/index.jar";
+        return new URL(urlString);
     }
 
     Downloader downloadIndex() throws UpdateException {
         Downloader downloader = null;
         try {
-            downloader = DownloaderFactory.create(
+            downloader = DownloaderFactory.create(context,
                 getIndexAddress(), File.createTempFile("index-", "-downloaded", context.getCacheDir()));
             downloader.setCacheTag(repo.lastetag);
-
-            if (progressListener != null) { // interactive session, show progress
-                Bundle data = new Bundle(1);
-                data.putString(PROGRESS_DATA_REPO_ADDRESS, repo.address);
-                downloader.setProgressListener(progressListener, data);
-            }
-
             downloader.downloadUninterrupted();
 
             if (downloader.isCached()) {
diff --git a/F-Droid/src/org/fdroid/fdroid/UpdateService.java b/F-Droid/src/org/fdroid/fdroid/UpdateService.java
index 0c6f83201..3e75ff7db 100644
--- a/F-Droid/src/org/fdroid/fdroid/UpdateService.java
+++ b/F-Droid/src/org/fdroid/fdroid/UpdateService.java
@@ -22,11 +22,12 @@ import android.app.AlarmManager;
 import android.app.IntentService;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
 import android.content.ContentProviderOperation;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.OperationApplicationException;
 import android.content.SharedPreferences;
 import android.database.Cursor;
@@ -34,15 +35,12 @@ import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
 import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
 import android.os.RemoteException;
-import android.os.ResultReceiver;
 import android.os.SystemClock;
 import android.preference.PreferenceManager;
-import android.support.annotation.NonNull;
 import android.support.v4.app.NotificationCompat;
 import android.support.v4.app.TaskStackBuilder;
+import android.support.v4.content.LocalBroadcastManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.widget.Toast;
@@ -64,30 +62,28 @@ public class UpdateService extends IntentService implements ProgressListener {
 
     private static final String TAG = "UpdateService";
 
-    public static final String RESULT_MESSAGE     = "msg";
-    public static final String RESULT_EVENT       = "event";
-    public static final String RESULT_REPO_ERRORS = "repoErrors";
+    public static final String LOCAL_ACTION_STATUS = "status";
+
+    public static final String EXTRA_MESSAGE = "msg";
+    public static final String EXTRA_REPO_ERRORS = "repoErrors";
+    public static final String EXTRA_STATUS_CODE = "status";
+    public static final String EXTRA_ADDRESS = "address";
+    public static final String EXTRA_MANUAL_UPDATE = "manualUpdate";
 
     public static final int STATUS_COMPLETE_WITH_CHANGES = 0;
-    public static final int STATUS_COMPLETE_AND_SAME     = 1;
-    public static final int STATUS_ERROR_GLOBAL          = 2;
-    public static final int STATUS_ERROR_LOCAL           = 3;
-    public static final int STATUS_ERROR_LOCAL_SMALL     = 4;
-    public static final int STATUS_INFO                  = 5;
+    public static final int STATUS_COMPLETE_AND_SAME = 1;
+    public static final int STATUS_ERROR_GLOBAL = 2;
+    public static final int STATUS_ERROR_LOCAL = 3;
+    public static final int STATUS_ERROR_LOCAL_SMALL = 4;
+    public static final int STATUS_INFO = 5;
 
-    // I don't like that I've had to dupliacte the statuses above with strings here, however
-    // one method of communication/notification is using ResultReceiver (int status codes)
-    // while the other uses progress events (string event types).
-    public static final String EVENT_COMPLETE_WITH_CHANGES = "repoUpdateComplete (changed)";
-    public static final String EVENT_COMPLETE_AND_SAME     = "repoUpdateComplete (not changed)";
-    public static final String EVENT_FINISHED              = "repoUpdateFinished";
-    public static final String EVENT_ERROR                 = "repoUpdateError";
-    public static final String EVENT_INFO                  = "repoUpdateInfo";
+    private LocalBroadcastManager localBroadcastManager;
 
-    public static final String EXTRA_RECEIVER = "receiver";
-    public static final String EXTRA_ADDRESS = "address";
+    private static final int NOTIFY_ID_UPDATING = 0;
+    private static final int NOTIFY_ID_UPDATES_AVAILABLE = 1;
 
-    private ResultReceiver receiver = null;
+    private NotificationManager notificationManager;
+    private NotificationCompat.Builder notificationBuilder;
 
     public UpdateService() {
         super("UpdateService");
@@ -107,138 +103,17 @@ public class UpdateService extends IntentService implements ProgressListener {
         AppProvider.DataColumns.IGNORE_THISUPDATE
     };
 
-    // For receiving results from the UpdateService when we've told it to
-    // update in response to a user request.
-    public static class UpdateReceiver extends ResultReceiver {
-
-        private final Context context;
-        private ProgressDialog dialog;
-        private ProgressListener listener;
-        private String lastShownMessage = null;
-
-        public UpdateReceiver(@NonNull Context context, Handler handler) {
-            super(handler);
-            this.context = context;
-        }
-
-        public UpdateReceiver setDialog(ProgressDialog dialog) {
-            this.dialog = dialog;
-            return this;
-        }
-
-        public UpdateReceiver setListener(ProgressListener listener) {
-            this.listener = listener;
-            return this;
-        }
-
-        private void forwardEvent(String type) {
-            if (listener != null) {
-                listener.onProgress(new Event(type));
-            }
-        }
-
-        private void ensureDialog() {
-            if (dialog == null) {
-                String title = context.getString(R.string.process_wait_title);
-                String message = lastShownMessage == null ? context.getString(R.string.process_update_msg) : lastShownMessage;
-                dialog = ProgressDialog.show(context, title, message, true, true);
-                dialog.setIcon(android.R.drawable.ic_dialog_info);
-                dialog.setCanceledOnTouchOutside(false);
-            }
-        }
-
-        public UpdateReceiver showDialog() {
-            ensureDialog();
-            dialog.show();
-            return this;
-        }
-
-        public void hideDialog() {
-            if (dialog != null) {
-                dialog.hide();
-                dialog.dismiss();
-                dialog = null;
-            }
-        }
-
-        @Override
-        protected void onReceiveResult(int resultCode, Bundle resultData) {
-            final String message = resultData.getString(RESULT_MESSAGE);
-            boolean finished = false;
-            switch (resultCode) {
-            case STATUS_ERROR_GLOBAL:
-                forwardEvent(EVENT_ERROR);
-                Toast.makeText(context, context.getString(R.string.global_error_updating_repos) + " " + message, Toast.LENGTH_LONG).show();
-                finished = true;
-                break;
-            case STATUS_ERROR_LOCAL:
-            case STATUS_ERROR_LOCAL_SMALL:
-                StringBuilder msgB = new StringBuilder();
-                List<CharSequence> repoErrors = resultData.getCharSequenceArrayList(RESULT_REPO_ERRORS);
-                for (CharSequence error : repoErrors) {
-                    if (msgB.length() > 0) msgB.append('\n');
-                    msgB.append(error);
-                }
-                if (resultCode == STATUS_ERROR_LOCAL_SMALL) {
-                    msgB.append('\n').append(context.getString(R.string.all_other_repos_fine));
-                }
-                Toast.makeText(context, msgB.toString(), Toast.LENGTH_LONG).show();
-                finished = true;
-                break;
-            case STATUS_COMPLETE_WITH_CHANGES:
-                forwardEvent(EVENT_COMPLETE_WITH_CHANGES);
-                finished = true;
-                break;
-            case STATUS_COMPLETE_AND_SAME:
-                forwardEvent(EVENT_COMPLETE_AND_SAME);
-                Toast.makeText(context, context.getString(R.string.repos_unchanged), Toast.LENGTH_LONG).show();
-                finished = true;
-                break;
-            case STATUS_INFO:
-                forwardEvent(EVENT_INFO);
-                if (dialog != null) {
-                    lastShownMessage = message;
-                    dialog.setMessage(message);
-                }
-                break;
-            }
-
-            if (finished) {
-                forwardEvent(EVENT_FINISHED);
-                if (dialog != null && dialog.isShowing()) {
-                    try {
-                        dialog.dismiss();
-                    } catch (IllegalArgumentException e) {
-                        // sometimes dialog.isShowing() doesn't work :(
-                        // https://stackoverflow.com/questions/19538282/view-not-attached-to-window-manager-dialog-dismiss
-                        e.printStackTrace();
-                    }
-                }
-            }
-        }
+    public static void updateNow(Context context) {
+        updateRepoNow(null, context);
     }
 
-    public static UpdateReceiver updateNow(Context context) {
-        return updateRepoNow(null, context);
-    }
-
-    public static UpdateReceiver updateRepoNow(String address, Context context) {
-        return updateRepoNow(address, context, true);
-    }
-
-    public static UpdateReceiver updateRepoNow(String address, Context context, boolean showDialog) {
+    public static void updateRepoNow(String address, Context context) {
         Intent intent = new Intent(context, UpdateService.class);
-        UpdateReceiver receiver = new UpdateReceiver(context, new Handler());
-        if (showDialog) {
-            receiver.showDialog();
-        }
-        intent.putExtra(EXTRA_RECEIVER, receiver);
+        intent.putExtra(EXTRA_MANUAL_UPDATE, true);
         if (!TextUtils.isEmpty(address)) {
             intent.putExtra(EXTRA_ADDRESS, address);
         }
         context.startService(intent);
-
-        return receiver;
     }
 
     // Schedule (or cancel schedule for) this service, according to the
@@ -268,36 +143,135 @@ public class UpdateService extends IntentService implements ProgressListener {
 
     }
 
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        localBroadcastManager = LocalBroadcastManager.getInstance(this);
+        localBroadcastManager.registerReceiver(downloadProgressReceiver,
+                new IntentFilter(Downloader.LOCAL_ACTION_PROGRESS));
+        localBroadcastManager.registerReceiver(updateStatusReceiver,
+                new IntentFilter(LOCAL_ACTION_STATUS));
+
+        notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+
+        notificationBuilder = new NotificationCompat.Builder(this)
+                .setSmallIcon(R.drawable.ic_refresh_white)
+                .setOngoing(true)
+                .setCategory(NotificationCompat.CATEGORY_SERVICE)
+                .setContentTitle(getString(R.string.update_notification_title));
+        notificationManager.notify(NOTIFY_ID_UPDATING, notificationBuilder.build());
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        notificationManager.cancel(NOTIFY_ID_UPDATING);
+        localBroadcastManager.unregisterReceiver(downloadProgressReceiver);
+        localBroadcastManager.unregisterReceiver(updateStatusReceiver);
+    }
+
     protected void sendStatus(int statusCode) {
         sendStatus(statusCode, null);
     }
 
     protected void sendStatus(int statusCode, String message) {
-        if (receiver != null) {
-            Bundle resultData = new Bundle();
-            if (!TextUtils.isEmpty(message)) {
-                resultData.putString(RESULT_MESSAGE, message);
-            }
-            receiver.send(statusCode, resultData);
-        }
+        Intent intent = new Intent(LOCAL_ACTION_STATUS);
+        intent.putExtra(EXTRA_STATUS_CODE, statusCode);
+        if (!TextUtils.isEmpty(message))
+            intent.putExtra(EXTRA_MESSAGE, message);
+        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
     }
 
     protected void sendRepoErrorStatus(int statusCode, ArrayList<CharSequence> repoErrors) {
-        if (receiver != null) {
-            Bundle resultData = new Bundle();
-            resultData.putCharSequenceArrayList(RESULT_REPO_ERRORS, repoErrors);
-            receiver.send(statusCode, resultData);
-        }
+        Intent intent = new Intent(LOCAL_ACTION_STATUS);
+        intent.putExtra(EXTRA_STATUS_CODE, statusCode);
+        intent.putExtra(EXTRA_REPO_ERRORS, repoErrors.toArray(new CharSequence[repoErrors.size()]));
+        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
     }
 
-    /**
-     * We might be doing a scheduled run, or we might have been launched by the
-     * app in response to a user's request. If we have a receiver, it's the
-     * latter...
-     */
-    private boolean isScheduledRun() {
-        return receiver == null;
-    }
+    private final BroadcastReceiver downloadProgressReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (TextUtils.isEmpty(action))
+                return;
+
+            if (!action.equals(Downloader.LOCAL_ACTION_PROGRESS))
+                return;
+
+            String repoAddress = intent.getStringExtra(Downloader.EXTRA_ADDRESS);
+            int downloadedSize = intent.getIntExtra(Downloader.EXTRA_BYTES_READ, -1);
+            int totalSize = intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, -1);
+            int percent = (int) ((double) downloadedSize / totalSize * 100);
+            sendStatus(STATUS_INFO,
+                    getString(R.string.status_download, repoAddress,
+                            Utils.getFriendlySize(downloadedSize),
+                            Utils.getFriendlySize(totalSize), percent));
+        }
+    };
+
+        // For receiving results from the UpdateService when we've told it to
+    // update in response to a user request.
+    private final BroadcastReceiver updateStatusReceiver = new BroadcastReceiver() {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (TextUtils.isEmpty(action))
+                return;
+
+            if (!action.equals(LOCAL_ACTION_STATUS))
+                return;
+
+            final String message = intent.getStringExtra(EXTRA_MESSAGE);
+            int resultCode = intent.getIntExtra(EXTRA_STATUS_CODE, -1);
+
+            String text;
+            switch (resultCode) {
+                case STATUS_INFO:
+                    notificationBuilder.setContentText(message)
+                            .setProgress(0, 0, true)
+                            .setCategory(NotificationCompat.CATEGORY_SERVICE);
+                    notificationManager.notify(NOTIFY_ID_UPDATING, notificationBuilder.build());
+                    break;
+                case STATUS_ERROR_GLOBAL:
+                    text = context.getString(R.string.global_error_updating_repos) + " " + message;
+                    notificationBuilder.setContentText(text)
+                            .setCategory(NotificationCompat.CATEGORY_ERROR)
+                            .setSmallIcon(android.R.drawable.ic_dialog_alert);
+                    notificationManager.notify(NOTIFY_ID_UPDATING, notificationBuilder.build());
+                    Toast.makeText(context, text, Toast.LENGTH_LONG).show();
+                    break;
+                case STATUS_ERROR_LOCAL:
+                case STATUS_ERROR_LOCAL_SMALL:
+                    StringBuilder msgBuilder = new StringBuilder();
+                    CharSequence[] repoErrors = intent.getCharSequenceArrayExtra(EXTRA_REPO_ERRORS);
+                    for (CharSequence error : repoErrors) {
+                        if (msgBuilder.length() > 0) msgBuilder.append('\n');
+                        msgBuilder.append(error);
+                    }
+                    if (resultCode == STATUS_ERROR_LOCAL_SMALL) {
+                        msgBuilder.append('\n').append(context.getString(R.string.all_other_repos_fine));
+                    }
+                    text = msgBuilder.toString();
+                    notificationBuilder.setContentText(text)
+                            .setCategory(NotificationCompat.CATEGORY_ERROR)
+                            .setSmallIcon(android.R.drawable.ic_dialog_info);
+                    notificationManager.notify(NOTIFY_ID_UPDATING, notificationBuilder.build());
+                    Toast.makeText(context, text, Toast.LENGTH_LONG).show();
+                    break;
+                case STATUS_COMPLETE_WITH_CHANGES:
+                    break;
+                case STATUS_COMPLETE_AND_SAME:
+                    text = context.getString(R.string.repos_unchanged);
+                    notificationBuilder.setContentText(text)
+                            .setCategory(NotificationCompat.CATEGORY_SERVICE);
+                    notificationManager.notify(NOTIFY_ID_UPDATING, notificationBuilder.build());
+                    break;
+            }
+        }
+    };
 
     /**
      * Check whether it is time to run the scheduled update.
@@ -341,15 +315,14 @@ public class UpdateService extends IntentService implements ProgressListener {
     @Override
     protected void onHandleIntent(Intent intent) {
 
-        receiver = intent.getParcelableExtra(EXTRA_RECEIVER);
         String address = intent.getStringExtra(EXTRA_ADDRESS);
+        boolean manualUpdate = intent.getBooleanExtra(EXTRA_MANUAL_UPDATE, false);
 
-        long startTime = System.currentTimeMillis();
         try {
             SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
 
             // See if it's time to actually do anything yet...
-            if (!isScheduledRun()) {
+            if (manualUpdate) {
                 Log.d(TAG, "Unscheduled (manually requested) update");
             } else if (!verifyIsTimeForScheduledRun()) {
                 return;
@@ -469,11 +442,6 @@ public class UpdateService extends IntentService implements ProgressListener {
                     "Exception during update processing:\n"
                             + Log.getStackTraceString(e));
             sendStatus(STATUS_ERROR_GLOBAL, e.getMessage());
-        } finally {
-            Log.d(TAG, "Update took "
-                    + ((System.currentTimeMillis() - startTime) / 1000)
-                    + " seconds.");
-            receiver = null;
         }
     }
 
@@ -566,8 +534,7 @@ public class UpdateService extends IntentService implements ProgressListener {
                         .setContentText(contentText)
                         .setStyle(createNotificationBigStyle(hasUpdates));
 
-        NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-        nm.notify(1, builder.build());
+        notificationManager.notify(NOTIFY_ID_UPDATES_AVAILABLE, builder.build());
     }
 
     private List<String> getKnownAppIds(List<App> apps) {
@@ -792,9 +759,6 @@ public class UpdateService extends IntentService implements ProgressListener {
         String totalSize = Utils.getFriendlySize(event.total);
         int percent = (int) ((double) event.progress / event.total * 100);
         switch (event.type) {
-            case Downloader.EVENT_PROGRESS:
-                message = getString(R.string.status_download, repoAddress, downloadedSize, totalSize, percent);
-                break;
             case RepoUpdater.PROGRESS_TYPE_PROCESS_XML:
                 message = getString(R.string.status_processing_xml_percent, repoAddress, downloadedSize, totalSize, percent);
                 break;
diff --git a/F-Droid/src/org/fdroid/fdroid/Utils.java b/F-Droid/src/org/fdroid/fdroid/Utils.java
index 9bfc4909c..a555f3a04 100644
--- a/F-Droid/src/org/fdroid/fdroid/Utils.java
+++ b/F-Droid/src/org/fdroid/fdroid/Utils.java
@@ -436,7 +436,8 @@ public final class Utils {
             return new CommaSeparatedList(sb.toString());
         }
 
-        public static CommaSeparatedList make(String list) {
+        @Nullable
+        public static CommaSeparatedList make(@Nullable String list) {
             if (TextUtils.isEmpty(list))
                 return null;
             return new CommaSeparatedList(list);
diff --git a/F-Droid/src/org/fdroid/fdroid/data/App.java b/F-Droid/src/org/fdroid/fdroid/data/App.java
index 985dd749f..2ed33f98b 100644
--- a/F-Droid/src/org/fdroid/fdroid/data/App.java
+++ b/F-Droid/src/org/fdroid/fdroid/data/App.java
@@ -114,7 +114,8 @@ public class App extends ValueObject implements Comparable<App> {
         checkCursorPosition(cursor);
 
         for (int i = 0; i < cursor.getColumnCount(); i++) {
-            switch (cursor.getColumnName(i)) {
+            String n = cursor.getColumnName(i);
+            switch (n) {
             case AppProvider.DataColumns.IS_COMPATIBLE:
                 compatible = cursor.getInt(i) == 1;
                 break;
@@ -208,6 +209,10 @@ public class App extends ValueObject implements Comparable<App> {
             case AppProvider.DataColumns.InstalledApp.VERSION_NAME:
                 installedVersionName = cursor.getString(i);
                 break;
+            case "_id":
+                break;
+            default:
+                Log.e(TAG, "Unknown column name " + n);
             }
         }
     }
diff --git a/F-Droid/src/org/fdroid/fdroid/data/AppProvider.java b/F-Droid/src/org/fdroid/fdroid/data/AppProvider.java
index e40922072..e9c742969 100644
--- a/F-Droid/src/org/fdroid/fdroid/data/AppProvider.java
+++ b/F-Droid/src/org/fdroid/fdroid/data/AppProvider.java
@@ -5,6 +5,7 @@ import android.content.ContentValues;
 import android.content.Context;
 import android.content.UriMatcher;
 import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
 import android.util.Log;
 
@@ -98,8 +99,9 @@ public class AppProvider extends FDroidProvider {
                     cursor.moveToFirst();
                     while (!cursor.isAfterLast()) {
                         final String categoriesString = cursor.getString(0);
-                        if (categoriesString != null) {
-                            for (final String s : Utils.CommaSeparatedList.make(categoriesString)) {
+                        Utils.CommaSeparatedList categoriesList = Utils.CommaSeparatedList.make(categoriesString);
+                        if (categoriesList != null) {
+                            for (final String s : categoriesList) {
                                 categorySet.add(s);
                             }
                         }
@@ -152,6 +154,22 @@ public class AppProvider extends FDroidProvider {
 
     }
 
+    /**
+     * Class that only exists to call private methods in the {@link AppProvider} without having
+     * to go via a Context/ContentResolver. The reason is that if the {@link DBHelper} class
+     * was to try and use its getContext().getContentResolver() in order to access the app
+     * provider, then the AppProvider will end up creating a new instance of a writeable
+     * SQLiteDatabase. This causes problems because the {@link DBHelper} still has its reference
+     * open and locks certain tables.
+     */
+    static final class UpgradeHelper {
+
+        public static void updateIconUrls(Context context, SQLiteDatabase db) {
+            AppProvider.updateIconUrls(context, db);
+        }
+
+    }
+
     public interface DataColumns {
 
         String _ID = "rowid as _id"; // Required for CursorLoaders
@@ -806,7 +824,7 @@ public class AppProvider extends FDroidProvider {
         updateCompatibleFlags();
         updateSuggestedFromLatest();
         updateSuggestedFromUpstream();
-        updateIconUrls();
+        updateIconUrls(getContext(), write());
     }
 
     /**
@@ -939,11 +957,13 @@ public class AppProvider extends FDroidProvider {
     }
 
     /**
-     * Updates URLs to icons
+     * Made static so that the {@link org.fdroid.fdroid.data.AppProvider.UpgradeHelper} can access
+     * it without instantiating an {@link AppProvider}. This is also the reason it needs to accept
+     * the context and database as arguments.
      */
-    public void updateIconUrls() {
-        final String iconsDir = Utils.getIconsDir(getContext(), 1.0);
-        final String iconsDirLarge = Utils.getIconsDir(getContext(), 1.5);
+    private static void updateIconUrls(Context context, SQLiteDatabase db) {
+        final String iconsDir = Utils.getIconsDir(context, 1.0);
+        final String iconsDirLarge = Utils.getIconsDir(context, 1.5);
         String repoVersion = Integer.toString(Repo.VERSION_DENSITY_SPECIFIC_ICONS);
         Log.d(TAG, "Updating icon paths for apps belonging to repos with version >= "
                 + repoVersion);
@@ -953,7 +973,7 @@ public class AppProvider extends FDroidProvider {
         final String[] params = {
             repoVersion, iconsDir, Utils.FALLBACK_ICONS_DIR,
             repoVersion, iconsDirLarge, Utils.FALLBACK_ICONS_DIR };
-        write().execSQL(query, params);
+        db.execSQL(query, params);
     }
 
     /**
@@ -961,7 +981,7 @@ public class AppProvider extends FDroidProvider {
      *  1) The repo version that introduced density specific icons
      *  2) The dir to density specific icons for the current device.
      */
-    private String getIconUpdateQuery() {
+    private static String getIconUpdateQuery() {
 
         final String apk = DBHelper.TABLE_APK;
         final String app = DBHelper.TABLE_APP;
diff --git a/F-Droid/src/org/fdroid/fdroid/data/DBHelper.java b/F-Droid/src/org/fdroid/fdroid/data/DBHelper.java
index 86b8f363f..ea3a153b0 100644
--- a/F-Droid/src/org/fdroid/fdroid/data/DBHelper.java
+++ b/F-Droid/src/org/fdroid/fdroid/data/DBHelper.java
@@ -101,7 +101,7 @@ public class DBHelper extends SQLiteOpenHelper {
             + InstalledAppProvider.DataColumns.APPLICATION_LABEL + " TEXT NOT NULL "
             + " );";
 
-    private static final int DB_VERSION = 49;
+    private static final int DB_VERSION = 50;
 
     private final Context context;
 
@@ -284,6 +284,7 @@ public class DBHelper extends SQLiteOpenHelper {
         addIsSwapToRepo(db, oldVersion);
         addChangelogToApp(db, oldVersion);
         addIconUrlLargeToApp(db, oldVersion);
+        updateIconUrlLarge(db, oldVersion);
     }
 
     /**
@@ -432,6 +433,24 @@ public class DBHelper extends SQLiteOpenHelper {
         }
     }
 
+    private void updateIconUrlLarge(SQLiteDatabase db, int oldVersion) {
+        if (oldVersion < 50) {
+            Log.i(TAG, "Recalculating app icon URLs so that the newly added large icons will get updated.");
+            AppProvider.UpgradeHelper.updateIconUrls(context, db);
+            clearRepoEtags(db);
+        }
+    }
+
+    /**
+     * By clearing the etags stored in the repo table, it means that next time the user updates
+     * their repos (either manually or on a scheduled task), they will update regardless of whether
+     * they have changed since last update or not.
+     */
+    private void clearRepoEtags(SQLiteDatabase db) {
+        Log.i(TAG, "Clearing repo etags, so next update will not be skipped with \"Repos up to date\".");
+        db.execSQL("update " + TABLE_REPO + " set lastetag = NULL");
+    }
+
     private void resetTransient(SQLiteDatabase db, int oldVersion) {
         // Before version 42, only transient info was stored in here. As of some time
         // just before 42 (F-Droid 0.60ish) it now has "ignore this version" info which
@@ -443,7 +462,7 @@ public class DBHelper extends SQLiteOpenHelper {
                     .putBoolean("triedEmptyUpdate", false).commit();
             db.execSQL("drop table " + TABLE_APP);
             db.execSQL("drop table " + TABLE_APK);
-            db.execSQL("update " + TABLE_REPO + " set lastetag = NULL");
+            clearRepoEtags(db);
             createAppApk(db);
         }
     }
diff --git a/F-Droid/src/org/fdroid/fdroid/installer/AppSecurityPermissions.java b/F-Droid/src/org/fdroid/fdroid/installer/AppSecurityPermissions.java
index 3c2c0c5aa..f3c18ab5b 100644
--- a/F-Droid/src/org/fdroid/fdroid/installer/AppSecurityPermissions.java
+++ b/F-Droid/src/org/fdroid/fdroid/installer/AppSecurityPermissions.java
@@ -18,7 +18,6 @@
 package org.fdroid.fdroid.installer;
 
 import android.annotation.TargetApi;
-import android.app.AlertDialog;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -29,6 +28,7 @@ import android.content.pm.PermissionInfo;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Parcel;
+import android.support.v7.app.AlertDialog;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
 import android.util.AttributeSet;
diff --git a/F-Droid/src/org/fdroid/fdroid/installer/InstallIntoSystemDialogActivity.java b/F-Droid/src/org/fdroid/fdroid/installer/InstallIntoSystemDialogActivity.java
index cb4717b2b..ded1cabb3 100644
--- a/F-Droid/src/org/fdroid/fdroid/installer/InstallIntoSystemDialogActivity.java
+++ b/F-Droid/src/org/fdroid/fdroid/installer/InstallIntoSystemDialogActivity.java
@@ -20,7 +20,6 @@
 package org.fdroid.fdroid.installer;
 
 import android.app.Activity;
-import android.app.AlertDialog;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -32,6 +31,7 @@ import android.os.AsyncTask;
 import android.os.Bundle;
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.NotificationCompat;
+import android.support.v7.app.AlertDialog;
 import android.text.Html;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
diff --git a/F-Droid/src/org/fdroid/fdroid/installer/SystemInstaller.java b/F-Droid/src/org/fdroid/fdroid/installer/SystemInstaller.java
index 8794409ec..0a1cd08e2 100644
--- a/F-Droid/src/org/fdroid/fdroid/installer/SystemInstaller.java
+++ b/F-Droid/src/org/fdroid/fdroid/installer/SystemInstaller.java
@@ -21,7 +21,6 @@
 package org.fdroid.fdroid.installer;
 
 import android.app.Activity;
-import android.app.AlertDialog;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -30,6 +29,7 @@ import android.content.pm.IPackageInstallObserver;
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.RemoteException;
+import android.support.v7.app.AlertDialog;
 import android.util.Log;
 
 import org.fdroid.fdroid.R;
diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/SwapService.java b/F-Droid/src/org/fdroid/fdroid/localrepo/SwapService.java
index aac4008dc..ea917a218 100644
--- a/F-Droid/src/org/fdroid/fdroid/localrepo/SwapService.java
+++ b/F-Droid/src/org/fdroid/fdroid/localrepo/SwapService.java
@@ -152,13 +152,13 @@ public class SwapService extends Service {
         return appsToSwap;
     }
 
-    @Nullable
-    public UpdateService.UpdateReceiver refreshSwap() {
-        return this.peer != null ? connectTo(peer, false) : null;
+    public void refreshSwap() {
+        if (peer != null) {
+            connectTo(peer, false);
+        }
     }
 
-    @NonNull
-    public UpdateService.UpdateReceiver connectTo(@NonNull Peer peer, boolean requestSwapBack) {
+    public void connectTo(@NonNull Peer peer, boolean requestSwapBack) {
         if (peer != this.peer) {
             Log.e(TAG, "Oops, got a different peer to swap with than initially planned.");
         }
@@ -172,7 +172,7 @@ public class SwapService extends Service {
             askServerToSwapWithUs(peerRepo);
         }
 
-        return UpdateService.updateRepoNow(peer.getRepoAddress(), this, false);
+        UpdateService.updateRepoNow(peer.getRepoAddress(), this);
     }
 
     private void askServerToSwapWithUs(final Repo repo) {
diff --git a/F-Droid/src/org/fdroid/fdroid/net/ApkDownloader.java b/F-Droid/src/org/fdroid/fdroid/net/ApkDownloader.java
index 185f55bb1..98e76f0ba 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/ApkDownloader.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/ApkDownloader.java
@@ -52,7 +52,6 @@ public class ApkDownloader implements AsyncDownloadWrapper.Listener {
 
     public static final int ERROR_HASH_MISMATCH = 101;
     public static final int ERROR_DOWNLOAD_FAILED = 102;
-    public static final int ERROR_UNKNOWN = 103;
 
     private static final String EVENT_SOURCE_ID = "sourceId";
     private static long downloadIdCounter = 0;
@@ -63,14 +62,13 @@ public class ApkDownloader implements AsyncDownloadWrapper.Listener {
     public static final String EVENT_DATA_ERROR_TYPE = "apkDownloadErrorType";
 
     @NonNull private final Apk curApk;
+    @NonNull private final Context context;
     @NonNull private final String repoAddress;
     @NonNull private final SanitizedFile localFile;
     @NonNull private final SanitizedFile potentiallyCachedFile;
 
     private ProgressListener listener;
     private AsyncDownloadWrapper dlWrapper = null;
-    private int progress  = 0;
-    private int totalSize = 0;
     private boolean isComplete = false;
 
     private final long id = ++downloadIdCounter;
@@ -84,6 +82,7 @@ public class ApkDownloader implements AsyncDownloadWrapper.Listener {
     }
 
     public ApkDownloader(@NonNull final Context context, @NonNull final Apk apk, @NonNull final String repoAddress) {
+        this.context = context;
         curApk = apk;
         this.repoAddress = repoAddress;
         localFile = new SanitizedFile(Utils.getApkDownloadDir(context), apk.apkName);
@@ -191,7 +190,7 @@ public class ApkDownloader implements AsyncDownloadWrapper.Listener {
         Log.d(TAG, "Downloading apk from " + remoteAddress + " to " + localFile);
 
         try {
-            Downloader downloader = DownloaderFactory.create(remoteAddress, localFile);
+            Downloader downloader = DownloaderFactory.create(context, remoteAddress, localFile);
             dlWrapper = new AsyncDownloadWrapper(downloader, this);
             dlWrapper.download();
             return true;
@@ -214,14 +213,6 @@ public class ApkDownloader implements AsyncDownloadWrapper.Listener {
     }
 
     private void sendProgressEvent(Event event) {
-        switch (event.type) {
-        case Downloader.EVENT_PROGRESS:
-            // Keep a copy of these ourselves, so people can interrogate us for the
-            // info (in addition to receiving events with the info).
-            totalSize = event.total;
-            progress  = event.progress;
-            break;
-        }
 
         event.getData().putLong(EVENT_SOURCE_ID, id);
 
@@ -230,18 +221,6 @@ public class ApkDownloader implements AsyncDownloadWrapper.Listener {
         }
     }
 
-    @Override
-    public void onReceiveTotalDownloadSize(int size) {
-        // Do nothing...
-        // Rather, we will obtain the total download size from the progress events
-        // when they start coming through.
-    }
-
-    @Override
-    public void onReceiveCacheTag(String cacheTag) {
-        // Do nothing...
-    }
-
     @Override
     public void onErrorDownloading(String localisedExceptionDetails) {
         Log.e(TAG, "Download failed: " + localisedExceptionDetails);
@@ -292,7 +271,7 @@ public class ApkDownloader implements AsyncDownloadWrapper.Listener {
 
     public Apk getApk() { return curApk; }
 
-    public int getProgress() { return progress; }
+    public int getBytesRead() { return dlWrapper != null ? dlWrapper.getBytesRead() : 0; }
 
-    public int getTotalSize() { return totalSize; }
+    public int getTotalBytes() { return dlWrapper != null ? dlWrapper.getTotalBytes() : 0; }
 }
diff --git a/F-Droid/src/org/fdroid/fdroid/net/AsyncDownloadWrapper.java b/F-Droid/src/org/fdroid/fdroid/net/AsyncDownloadWrapper.java
index d113f93f3..3588343b8 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/AsyncDownloadWrapper.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/AsyncDownloadWrapper.java
@@ -21,7 +21,6 @@ public class AsyncDownloadWrapper extends Handler {
 
     private static final String TAG = "AsyncDownloadWrapper";
 
-    private static final int MSG_PROGRESS           = 1;
     private static final int MSG_DOWNLOAD_COMPLETE  = 2;
     private static final int MSG_DOWNLOAD_CANCELLED = 3;
     private static final int MSG_ERROR              = 4;
@@ -43,16 +42,6 @@ public class AsyncDownloadWrapper extends Handler {
         this.listener   = listener;
     }
 
-    public void fetchTotalDownloadSize() {
-        int size = downloader.totalDownloadSize();
-        listener.onReceiveTotalDownloadSize(size);
-    }
-
-    public void fetchCacheTag() {
-        String cacheTag = downloader.getCacheTag();
-        listener.onReceiveCacheTag(cacheTag);
-    }
-
     public void download() {
         downloadThread = new DownloadThread();
         downloadThread.start();
@@ -64,22 +53,6 @@ public class AsyncDownloadWrapper extends Handler {
         }
     }
 
-    public static class NotDownloadingException extends Exception {
-        public NotDownloadingException(String message) {
-            super(message);
-        }
-    }
-
-    public void cancelDownload() throws NotDownloadingException {
-        if (downloadThread == null) {
-            throw new RuntimeException("Can't cancel download, it hasn't started yet.");
-        } else if (!downloadThread.isAlive()) {
-            throw new RuntimeException("Can't cancel download, it is already finished.");
-        }
-
-        downloadThread.interrupt();
-    }
-
     /**
      * Receives "messages" from the download thread, and passes them onto the
      * relevant {@link org.fdroid.fdroid.net.AsyncDownloadWrapper.Listener}
@@ -87,11 +60,6 @@ public class AsyncDownloadWrapper extends Handler {
      */
     public void handleMessage(Message message) {
         switch (message.arg1) {
-        case MSG_PROGRESS:
-            Bundle data = message.getData();
-            ProgressListener.Event event = data.getParcelable(MSG_DATA);
-            listener.onProgress(event);
-            break;
         case MSG_DOWNLOAD_COMPLETE:
             listener.onDownloadComplete();
             break;
@@ -104,19 +72,24 @@ public class AsyncDownloadWrapper extends Handler {
         }
     }
 
+    public int getBytesRead() {
+        return downloader.getBytesRead();
+    }
+
+    public int getTotalBytes() {
+        return downloader.getTotalBytes();
+    }
+
     public interface Listener extends ProgressListener {
-        void onReceiveTotalDownloadSize(int size);
-        void onReceiveCacheTag(String cacheTag);
         void onErrorDownloading(String localisedExceptionDetails);
         void onDownloadComplete();
         void onDownloadCancelled();
     }
 
-    private class DownloadThread extends Thread implements ProgressListener {
+    private class DownloadThread extends Thread {
 
         public void run() {
             try {
-                downloader.setProgressListener(this);
                 downloader.download();
                 sendMessage(MSG_DOWNLOAD_COMPLETE);
             } catch (InterruptedException e) {
@@ -137,16 +110,5 @@ public class AsyncDownloadWrapper extends Handler {
             message.arg1 = messageType;
             AsyncDownloadWrapper.this.sendMessage(message);
         }
-
-        @Override
-        public void onProgress(Event event) {
-            Message message = new Message();
-            Bundle  data    = new Bundle();
-            data.putParcelable(MSG_DATA, event);
-            message.setData(data);
-            message.arg1 = MSG_PROGRESS;
-            AsyncDownloadWrapper.this.sendMessage(message);
-        }
     }
-
 }
diff --git a/F-Droid/src/org/fdroid/fdroid/net/BluetoothDownloader.java b/F-Droid/src/org/fdroid/fdroid/net/BluetoothDownloader.java
index a57039dfb..b9d6e2e40 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/BluetoothDownloader.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/BluetoothDownloader.java
@@ -10,11 +10,9 @@ import org.fdroid.fdroid.net.bluetooth.httpish.Request;
 import org.fdroid.fdroid.net.bluetooth.httpish.Response;
 
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.MalformedURLException;
+import java.net.URL;
 
 public class BluetoothDownloader extends Downloader {
 
@@ -24,16 +22,10 @@ public class BluetoothDownloader extends Downloader {
     private FileDetails fileDetails;
     private final String sourcePath;
 
-    public BluetoothDownloader(String macAddress, String sourcePath, Context ctx) throws IOException {
-        super(ctx);
+    public BluetoothDownloader(Context context, String macAddress, URL sourceUrl, File destFile) throws IOException {
+        super(context, sourceUrl, destFile);
         this.connection = new BluetoothClient(macAddress).openConnection();
-        this.sourcePath = sourcePath;
-    }
-
-    public BluetoothDownloader(String macAddress, String sourcePath, File destFile) throws IOException {
-        super(destFile);
-        this.connection = new BluetoothClient(macAddress).openConnection();
-        this.sourcePath = sourcePath;
+        this.sourcePath = sourceUrl.getPath();
     }
 
     @Override
diff --git a/F-Droid/src/org/fdroid/fdroid/net/Downloader.java b/F-Droid/src/org/fdroid/fdroid/net/Downloader.java
index 5cf34d630..18cb48946 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/Downloader.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/Downloader.java
@@ -1,11 +1,10 @@
 package org.fdroid.fdroid.net;
 
 import android.content.Context;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
+import android.content.Intent;
+import android.support.v4.content.LocalBroadcastManager;
 import android.util.Log;
 
-import org.fdroid.fdroid.ProgressListener;
 import org.fdroid.fdroid.Utils;
 
 import java.io.File;
@@ -20,49 +19,31 @@ import java.net.URL;
 public abstract class Downloader {
 
     private static final String TAG = "Downloader";
-    private OutputStream outputStream;
 
-    private ProgressListener progressListener = null;
-    private Bundle eventData = null;
+    public static final String LOCAL_ACTION_PROGRESS = "Downloader.PROGRESS";
+
+    public static final String EXTRA_ADDRESS = "extraAddress";
+    public static final String EXTRA_BYTES_READ = "extraBytesRead";
+    public static final String EXTRA_TOTAL_BYTES = "extraTotalBytes";
+
+    private final OutputStream outputStream;
+
+    private final LocalBroadcastManager localBroadcastManager;
     private final File outputFile;
 
-    protected URL sourceUrl;
+    protected final URL sourceUrl;
     protected String cacheTag = null;
-
-    public static final String EVENT_PROGRESS = "downloadProgress";
+    protected int bytesRead = 0;
+    protected int totalBytes = 0;
 
     public abstract InputStream getInputStream() throws IOException;
 
-    // The context is required for opening the file to write to.
-    Downloader(String destFile, @NonNull Context ctx)
-            throws FileNotFoundException, MalformedURLException {
-        this(new File(ctx.getFilesDir() + File.separator + destFile));
-    }
-
-    // The context is required for opening the file to write to.
-    Downloader(@NonNull Context ctx) throws IOException {
-        this(File.createTempFile("dl-", "", ctx.getCacheDir()));
-    }
-
-    Downloader(File destFile)
+    Downloader(Context context, URL url, File destFile)
             throws FileNotFoundException, MalformedURLException {
+        this.sourceUrl = url;
         outputFile = destFile;
         outputStream = new FileOutputStream(outputFile);
-    }
-
-    Downloader(OutputStream output)
-            throws MalformedURLException {
-        outputStream = output;
-        outputFile   = null;
-    }
-
-    public void setProgressListener(ProgressListener listener) {
-        setProgressListener(listener, null);
-    }
-
-    public void setProgressListener(ProgressListener listener, Bundle eventData) {
-        this.progressListener = listener;
-        this.eventData = eventData;
+        localBroadcastManager = LocalBroadcastManager.getInstance(context);
     }
 
     /**
@@ -157,11 +138,16 @@ public abstract class Downloader {
         }
     }
 
+    /**
+     * This copies the downloaded data from the InputStream to the OutputStream,
+     * keeping track of the number of bytes that have flowed through for the
+     * progress counter.
+     */
     protected void copyInputToOutputStream(InputStream input) throws IOException, InterruptedException {
 
         byte[] buffer = new byte[Utils.BUFFER_SIZE];
         int bytesRead = 0;
-        int totalBytes = totalDownloadSize();
+        this.totalBytes = totalDownloadSize();
 
         // Getting the total download size could potentially take time, depending on how
         // it is implemented, so we may as well check this before we proceed.
@@ -186,13 +172,19 @@ public abstract class Downloader {
     }
 
     protected void sendProgress(int bytesRead, int totalBytes) {
-        sendProgress(new ProgressListener.Event(EVENT_PROGRESS, bytesRead, totalBytes, eventData));
+        this.bytesRead = bytesRead;
+        Intent intent = new Intent(LOCAL_ACTION_PROGRESS);
+        intent.putExtra(EXTRA_ADDRESS, sourceUrl.toString());
+        intent.putExtra(EXTRA_BYTES_READ, bytesRead);
+        intent.putExtra(EXTRA_TOTAL_BYTES, totalBytes);
+        localBroadcastManager.sendBroadcast(intent);
     }
 
-    protected void sendProgress(ProgressListener.Event event) {
-        if (progressListener != null) {
-            progressListener.onProgress(event);
-        }
+    public int getBytesRead() {
+        return bytesRead;
     }
 
+    public int getTotalBytes() {
+        return totalBytes;
+    }
 }
diff --git a/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java b/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java
index 6c99584e4..5b476dd1c 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java
@@ -1,42 +1,57 @@
 package org.fdroid.fdroid.net;
 
 import android.content.Context;
-import android.net.Uri;
 
 import java.io.File;
 import java.io.IOException;
+import java.net.URL;
 
 public class DownloaderFactory {
 
-    public static Downloader create(String url, Context context) throws IOException {
-        Uri uri = Uri.parse(url);
-        if (isBluetoothAddress(uri)) {
-            String macAddress = uri.getHost().replace("-", ":");
-            return new BluetoothDownloader(macAddress, uri.getPath(), context);
+    /**
+     * Downloads to a temporary file, which *you must delete yourself when
+     * you are done.  It is stored in {@link Context#getCacheDir()} and starts
+     * with the prefix {@code dl-}.
+     */
+    public static Downloader create(Context context, String urlString)
+            throws IOException {
+        return create(context, new URL(urlString));
+    }
+
+    /**
+     * Downloads to a temporary file, which *you must delete yourself when
+     * you are done.  It is stored in {@link Context#getCacheDir()} and starts
+     * with the prefix {@code dl-}.
+     */
+    public static Downloader create(Context context, URL url)
+            throws IOException {
+        File destFile = File.createTempFile("dl-", "", context.getCacheDir());
+        destFile.deleteOnExit(); // this probably does nothing, but maybe...
+        return create(context, url, destFile);
+    }
+
+    public static Downloader create(Context context, String urlString, File destFile)
+            throws IOException {
+        return create(context, new URL(urlString), destFile);
+    }
+
+    public static Downloader create(Context context, URL url, File destFile)
+            throws IOException {
+        if (isBluetoothAddress(url)) {
+            String macAddress = url.getHost().replace("-", ":");
+            return new BluetoothDownloader(context, macAddress, url, destFile);
         } else if (isOnionAddress(url)) {
-            return new TorHttpDownloader(url, context);
+            return new TorHttpDownloader(context, url, destFile);
         } else {
-            return new HttpDownloader(url, context);
+            return new HttpDownloader(context, url, destFile);
         }
     }
 
-    public static Downloader create(String url, File destFile) throws IOException {
-        Uri uri = Uri.parse(url);
-        if (isBluetoothAddress(uri)) {
-            String macAddress = uri.getHost().replace("-", ":");
-            return new BluetoothDownloader(macAddress, uri.getPath(), destFile);
-        } else if (isOnionAddress(url)) {
-            return new TorHttpDownloader(url, destFile);
-        } else {
-            return new HttpDownloader(url, destFile);
-        }
+    private static boolean isBluetoothAddress(URL url) {
+        return "bluetooth".equalsIgnoreCase(url.getProtocol());
     }
 
-    private static boolean isBluetoothAddress(Uri uri) {
-        return "bluetooth".equalsIgnoreCase(uri.getScheme());
-    }
-
-    private static boolean isOnionAddress(String url) {
-        return url.matches("^[a-zA-Z0-9]+://[^/]+\\.onion/.*");
+    private static boolean isOnionAddress(URL url) {
+        return url.getHost().endsWith(".onion");
     }
 }
diff --git a/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java b/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java
index c9f76249e..c091fd607 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java
@@ -29,21 +29,9 @@ public class HttpDownloader extends Downloader {
     private int statusCode = -1;
     private boolean onlyStream = false;
 
-    // The context is required for opening the file to write to.
-    HttpDownloader(String source, File destFile)
+    HttpDownloader(Context context, URL url, File destFile)
             throws FileNotFoundException, MalformedURLException {
-        super(destFile);
-        sourceUrl = new URL(source);
-    }
-
-    /**
-     * Downloads to a temporary file, which *you must delete yourself when
-     * you are done*.
-     * @see org.fdroid.fdroid.net.Downloader#getFile()
-     */
-    public HttpDownloader(String source, Context ctx) throws IOException {
-        super(ctx);
-        sourceUrl = new URL(source);
+        super(context, url, destFile);
     }
 
     /**
diff --git a/F-Droid/src/org/fdroid/fdroid/net/IconDownloader.java b/F-Droid/src/org/fdroid/fdroid/net/IconDownloader.java
index 0ba464755..b90876df8 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/IconDownloader.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/IconDownloader.java
@@ -22,7 +22,7 @@ public class IconDownloader extends BaseImageDownloader {
         switch (Scheme.ofUri(imageUri)) {
         case HTTP:
         case HTTPS:
-            Downloader downloader = DownloaderFactory.create(imageUri, context);
+            Downloader downloader = DownloaderFactory.create(context, imageUri);
             return downloader.getInputStream();
         default:
             return super.getStream(imageUri, extra);
diff --git a/F-Droid/src/org/fdroid/fdroid/net/TorHttpDownloader.java b/F-Droid/src/org/fdroid/fdroid/net/TorHttpDownloader.java
index 70848ac66..c0eaf4d45 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/TorHttpDownloader.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/TorHttpDownloader.java
@@ -10,16 +10,13 @@ import java.net.InetSocketAddress;
 import java.net.MalformedURLException;
 import java.net.Proxy;
 import java.net.SocketAddress;
+import java.net.URL;
 
 public class TorHttpDownloader extends HttpDownloader {
 
-    TorHttpDownloader(String url, Context ctx) throws IOException {
-        super(url, ctx);
-    }
-
-    TorHttpDownloader(String url, File destFile)
+    TorHttpDownloader(Context context, URL url, File destFile)
             throws FileNotFoundException, MalformedURLException {
-        super(url, destFile);
+        super(context, url, destFile);
     }
 
     @Override
diff --git a/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java b/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java
index 5bea61a1d..dae0644c8 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java
@@ -19,7 +19,6 @@
 
 package org.fdroid.fdroid.views;
 
-import android.app.AlertDialog;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -42,6 +41,7 @@ import android.support.v4.app.NavUtils;
 import android.support.v4.content.CursorLoader;
 import android.support.v4.content.Loader;
 import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AlertDialog;
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
@@ -64,14 +64,12 @@ import org.apache.http.impl.client.DefaultHttpClient;
 import org.fdroid.fdroid.FDroid;
 import org.fdroid.fdroid.FDroidApp;
 import org.fdroid.fdroid.Preferences;
-import org.fdroid.fdroid.ProgressListener;
 import org.fdroid.fdroid.R;
 import org.fdroid.fdroid.UpdateService;
 import org.fdroid.fdroid.compat.ClipboardCompat;
 import org.fdroid.fdroid.data.NewRepoConfig;
 import org.fdroid.fdroid.data.Repo;
 import org.fdroid.fdroid.data.RepoProvider;
-import org.fdroid.fdroid.views.fragments.RepoDetailsFragment;
 
 import java.io.IOException;
 import java.net.MalformedURLException;
@@ -82,13 +80,6 @@ import java.util.Date;
 import java.util.Locale;
 
 public class ManageReposActivity extends ActionBarActivity {
-
-    /**
-     * If we have a new repo added, or the address of a repo has changed, then
-     * we when we're finished, we'll set this boolean to true in the intent that
-     * we finish with, to signify that we want the main list of apps updated.
-     */
-    public static final String REQUEST_UPDATE = "update";
     private static final String TAG = "ManageReposActivity";
 
     private static final String DEFAULT_NEW_REPO_TEXT = "https://";
@@ -99,10 +90,6 @@ public class ManageReposActivity extends ActionBarActivity {
         IS_SWAP
     }
 
-    private UpdateService.UpdateReceiver updateHandler = null;
-
-    private static boolean changed = false;
-
     private RepoListFragment listFragment;
 
     /**
@@ -147,21 +134,11 @@ public class ManageReposActivity extends ActionBarActivity {
     @Override
     protected void onResume() {
         super.onResume();
-        if (updateHandler != null) {
-            updateHandler.showDialog();
-        }
+
         /* let's see if someone is trying to send us a new repo */
         addRepoFromIntent(getIntent());
     }
 
-    @Override
-    protected void onPause() {
-        super.onPause();
-        if (updateHandler != null) {
-            updateHandler.hideDialog();
-        }
-    }
-
     @Override
     protected void onNewIntent(Intent intent) {
         setIntent(intent);
@@ -170,22 +147,10 @@ public class ManageReposActivity extends ActionBarActivity {
     @Override
     public void finish() {
         Intent ret = new Intent();
-        markChangedIfRequired(ret);
         setResult(RESULT_OK, ret);
         super.finish();
     }
 
-    private boolean hasChanged() {
-        return changed;
-    }
-
-    private void markChangedIfRequired(Intent intent) {
-        if (hasChanged()) {
-            Log.i(TAG, "Repo details have changed, prompting for update.");
-            intent.putExtra(REQUEST_UPDATE, true);
-        }
-    }
-
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         getMenuInflater().inflate(R.menu.manage_repos, menu);
@@ -197,7 +162,6 @@ public class ManageReposActivity extends ActionBarActivity {
         switch (item.getItemId()) {
         case android.R.id.home:
             Intent destIntent = new Intent(this, FDroid.class);
-            markChangedIfRequired(destIntent);
             setResult(RESULT_OK, destIntent);
             NavUtils.navigateUpTo(this, destIntent);
             return true;
@@ -205,32 +169,12 @@ public class ManageReposActivity extends ActionBarActivity {
             showAddRepo();
             return true;
         case R.id.action_update_repo:
-            updateRepos();
+            UpdateService.updateNow(this);
             return true;
         }
         return super.onOptionsItemSelected(item);
     }
 
-    private void updateRepos() {
-        updateHandler = UpdateService.updateNow(this).setListener(
-                new ProgressListener() {
-                    @Override
-                    public void onProgress(Event event) {
-                        switch (event.type) {
-                        case UpdateService.EVENT_COMPLETE_AND_SAME:
-                        case UpdateService.EVENT_COMPLETE_WITH_CHANGES:
-                            // No need to prompt to update any more, we just
-                            // did it!
-                            changed = false;
-                            break;
-                        case UpdateService.EVENT_FINISHED:
-                            updateHandler = null;
-                            break;
-                        }
-                    }
-                });
-    }
-
     private void showAddRepo() {
         /*
          * If there is text in the clipboard, and it looks like a URL, use that.
@@ -291,9 +235,7 @@ public class ManageReposActivity extends ActionBarActivity {
             final EditText uriEditText = (EditText) view.findViewById(R.id.edit_uri);
             final EditText fingerprintEditText = (EditText) view.findViewById(R.id.edit_fingerprint);
 
-            addRepoDialog.setIcon(android.R.drawable.ic_menu_add);
             addRepoDialog.setTitle(getString(R.string.repo_add_title));
-
             addRepoDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
                 getString(R.string.cancel),
                 new DialogInterface.OnClickListener() {
@@ -651,11 +593,11 @@ public class ManageReposActivity extends ActionBarActivity {
 
         /**
          * If started by an intent that expects a result (e.g. QR codes) then we
-         * will set a result and finish. Otherwise, we'll refresh the list of repos
+         * will set a result and finish. Otherwise, we'll updateViews the list of repos
          * to reflect the newly created repo.
          */
         private void finishedAddingRepo() {
-            changed = true;
+            UpdateService.updateNow(ManageReposActivity.this);
             if (addRepoDialog.isShowing()) {
                 addRepoDialog.dismiss();
             }
@@ -728,7 +670,7 @@ public class ManageReposActivity extends ActionBarActivity {
         /**
          * NOTE: If somebody toggles a repo off then on again, it will have
          * removed all apps from the index when it was toggled off, so when it
-         * is toggled on again, then it will require a refresh. Previously, I
+         * is toggled on again, then it will require a updateViews. Previously, I
          * toyed with the idea of remembering whether they had toggled on or
          * off, and then only actually performing the function when the activity
          * stopped, but I think that will be problematic. What about when they
@@ -748,7 +690,7 @@ public class ManageReposActivity extends ActionBarActivity {
                 RepoProvider.Helper.update(getActivity(), repo, values);
 
                 if (isEnabled) {
-                    changed = true;
+                    UpdateService.updateNow(getActivity());
                 } else {
                     FDroidApp app = (FDroidApp) getActivity().getApplication();
                     RepoProvider.Helper.purgeApps(getActivity(), repo, app);
@@ -833,7 +775,7 @@ public class ManageReposActivity extends ActionBarActivity {
 
         public void editRepo(Repo repo) {
             Intent intent = new Intent(getActivity(), RepoDetailsActivity.class);
-            intent.putExtra(RepoDetailsFragment.ARG_REPO_ID, repo.getId());
+            intent.putExtra(RepoDetailsActivity.ARG_REPO_ID, repo.getId());
             startActivityForResult(intent, SHOW_REPO_DETAILS);
         }
 
diff --git a/F-Droid/src/org/fdroid/fdroid/views/RepoDetailsActivity.java b/F-Droid/src/org/fdroid/fdroid/views/RepoDetailsActivity.java
index 51e965828..c49325c45 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/RepoDetailsActivity.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/RepoDetailsActivity.java
@@ -1,33 +1,91 @@
 package org.fdroid.fdroid.views;
 
 import android.annotation.TargetApi;
+import android.content.BroadcastReceiver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.Uri;
 import android.nfc.NdefMessage;
 import android.nfc.NfcAdapter;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcelable;
+import android.support.v7.app.AlertDialog;
 import android.support.v4.app.NavUtils;
+import android.support.v4.content.LocalBroadcastManager;
 import android.support.v7.app.ActionBarActivity;
+import android.text.TextUtils;
 import android.util.Log;
+import android.view.Menu;
 import android.view.MenuItem;
-import android.widget.LinearLayout;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
 import android.widget.Toast;
 
 import org.fdroid.fdroid.FDroidApp;
 import org.fdroid.fdroid.NfcHelper;
+import org.fdroid.fdroid.NfcNotEnabledActivity;
+import org.fdroid.fdroid.QrGenAsyncTask;
+import org.fdroid.fdroid.R;
+import org.fdroid.fdroid.UpdateService;
 import org.fdroid.fdroid.Utils;
 import org.fdroid.fdroid.data.Repo;
 import org.fdroid.fdroid.data.RepoProvider;
-import org.fdroid.fdroid.views.fragments.RepoDetailsFragment;
+
+import java.util.Locale;
 
 public class RepoDetailsActivity extends ActionBarActivity {
     private static final String TAG = "RepoDetailsActivity";
 
-    private Repo repo;
+    public static final String MIME_TYPE = "application/vnd.org.fdroid.fdroid.repo";
+    public static final String ARG_REPO_ID = "repo_id";
 
-    static final String MIME_TYPE = "application/vnd.org.fdroid.fdroid.repo";
+    /**
+     * If the repo has been updated at least once, then we will show
+     * all of this info, otherwise they will be hidden.
+     */
+    private static final int[] SHOW_IF_EXISTS = {
+            R.id.label_repo_name,
+            R.id.text_repo_name,
+            R.id.label_description,
+            R.id.text_description,
+            R.id.label_num_apps,
+            R.id.text_num_apps,
+            R.id.label_last_update,
+            R.id.text_last_update,
+            R.id.label_repo_fingerprint,
+            R.id.text_repo_fingerprint,
+            R.id.text_repo_fingerprint_description
+    };
+    /**
+     * If the repo has <em>not</em> been updated yet, then we only show
+     * these, otherwise they are hidden.
+     */
+    private static final int[] HIDE_IF_EXISTS = {
+            R.id.text_not_yet_updated,
+            R.id.btn_update
+    };
+    private Repo repo;
+    private long repoId;
+    private View repoView;
+
+    /**
+     * Help function to make switching between two view states easier.
+     * Perhaps there is a better way to do this. I recall that using Adobe
+     * Flex, there was a thing called "ViewStates" for exactly this. Wonder if
+     * that exists in  Android?
+     */
+    private static void setMultipleViewVisibility(View parent,
+                                                  int[] viewIds,
+                                                  int visibility) {
+        for (int viewId : viewIds) {
+            parent.findViewById(viewId).setVisibility(visibility);
+        }
+    }
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -35,27 +93,11 @@ public class RepoDetailsActivity extends ActionBarActivity {
         ((FDroidApp) getApplication()).applyTheme(this);
         super.onCreate(savedInstanceState);
 
-        long repoId = getIntent().getLongExtra(RepoDetailsFragment.ARG_REPO_ID, 0);
-
-        if (savedInstanceState == null) {
-
-            // Need to set a dummy view (which will get overridden by the fragment manager
-            // below) so that we can call setContentView(). This is a work around for
-            // a (bug?) thing in 3.0, 3.1 which requires setContentView to be invoked before
-            // the actionbar is played with:
-            // http://blog.perpetumdesign.com/2011/08/strange-case-of-dr-action-and-mr-bar.html
-            if (Build.VERSION.SDK_INT >= 11 && Build.VERSION.SDK_INT <= 13) {
-                setContentView(new LinearLayout(this));
-            }
-
-            RepoDetailsFragment fragment = new RepoDetailsFragment();
-            fragment.setArguments(getIntent().getExtras());
-            getSupportFragmentManager()
-                    .beginTransaction()
-                    .add(android.R.id.content, fragment)
-                    .commit();
-        }
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+        setContentView(R.layout.repodetails);
+        repoView = findViewById(R.id.repoView);
 
+        repoId = getIntent().getLongExtra(ARG_REPO_ID, 0);
         final String[] projection = {
                 RepoProvider.DataColumns.NAME,
                 RepoProvider.DataColumns.ADDRESS,
@@ -63,8 +105,23 @@ public class RepoDetailsActivity extends ActionBarActivity {
         };
         repo = RepoProvider.Helper.findById(this, repoId, projection);
 
-        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-        setTitle(repo.getName());
+        setTitle(repo.name);
+
+        TextView inputUrl = (TextView) findViewById(R.id.input_repo_url);
+        inputUrl.setText(repo.address);
+
+        Button update = (Button) repoView.findViewById(R.id.btn_update);
+        update.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                performUpdate();
+            }
+        });
+
+        Uri uri = Uri.parse(repo.address);
+        uri = uri.buildUpon().appendQueryParameter("fingerprint", repo.fingerprint).build();
+        String qrUriString = uri.toString().toUpperCase(Locale.ENGLISH);
+        new QrGenAsyncTask(this, R.id.qr_code).execute(uri.toString());
     }
 
     @TargetApi(14)
@@ -82,6 +139,19 @@ public class RepoDetailsActivity extends ActionBarActivity {
     @Override
     public void onResume() {
         super.onResume();
+
+        /*
+         * After, for example, a repo update, the details will have changed in the
+         * database. However, or local reference to the Repo object will not
+         * have been updated. The safest way to deal with this is to reload the
+         * repo object directly from the database.
+         */
+        repo = RepoProvider.Helper.findById(this, repoId);
+        updateRepoView();
+
+        LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver,
+                new IntentFilter(UpdateService.LOCAL_ACTION_STATUS));
+
         // FDroid.java and AppDetails set different NFC actions, so reset here
         setNfc();
         processIntent(getIntent());
@@ -105,21 +175,189 @@ public class RepoDetailsActivity extends ActionBarActivity {
             Log.i(TAG, "Got this URL: " + url);
             Toast.makeText(this, "Got this URL: " + url, Toast.LENGTH_LONG).show();
             Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
-            String packageName = getPackageName();
-            intent.setClassName(packageName, packageName + ".ManageRepo");
+            intent.setClass(this, ManageReposActivity.class);
             startActivity(intent);
             finish();
         }
     }
 
+    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            int statusCode = intent.getIntExtra(UpdateService.EXTRA_STATUS_CODE, -1);
+            if (statusCode == UpdateService.STATUS_COMPLETE_WITH_CHANGES)
+                updateRepoView();
+        }
+    };
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.repo_details_activity, menu);
+        return true;
+    }
+
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
-        case android.R.id.home:
-            NavUtils.navigateUpFromSameTask(this);
-            return true;
+            case android.R.id.home:
+                NavUtils.navigateUpFromSameTask(this);
+                return true;
+            case R.id.menu_update:
+                performUpdate();
+                return true;
+            case R.id.menu_delete:
+                promptForDelete();
+                return true;
+            case R.id.menu_enable_nfc:
+                Intent intent = new Intent(this, NfcNotEnabledActivity.class);
+                startActivity(intent);
+                return true;
         }
+
         return super.onOptionsItemSelected(item);
     }
 
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        if (Build.VERSION.SDK_INT >= 14) {
+            prepareNfcMenuItems(menu);
+        }
+        return true;
+    }
+
+    @TargetApi(16)
+    private void prepareNfcMenuItems(Menu menu) {
+        NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+        if (nfcAdapter == null) {
+            return;
+        }
+
+        boolean needsEnableNfcMenuItem;
+        if (Build.VERSION.SDK_INT < 16) {
+            needsEnableNfcMenuItem = !nfcAdapter.isEnabled();
+        } else {
+            needsEnableNfcMenuItem = !nfcAdapter.isNdefPushEnabled();
+        }
+
+        MenuItem menuItem = menu.findItem(R.id.menu_enable_nfc);
+        menuItem.setVisible(needsEnableNfcMenuItem);
+    }
+
+    private void setupDescription(View parent, Repo repo) {
+
+        TextView descriptionLabel = (TextView) parent.findViewById(R.id.label_description);
+        TextView description = (TextView) parent.findViewById(R.id.text_description);
+
+        if (TextUtils.isEmpty(repo.description)) {
+            descriptionLabel.setVisibility(View.GONE);
+            description.setVisibility(View.GONE);
+            description.setText("");
+        } else {
+            descriptionLabel.setVisibility(View.VISIBLE);
+            description.setVisibility(View.VISIBLE);
+            description.setText(repo.description.replaceAll("\n", " "));
+        }
+    }
+
+    private void setupRepoFingerprint(View parent, Repo repo) {
+        TextView repoFingerprintView = (TextView) parent.findViewById(R.id.text_repo_fingerprint);
+        TextView repoFingerprintDescView = (TextView) parent.findViewById(R.id.text_repo_fingerprint_description);
+
+        String repoFingerprint;
+        int repoFingerprintColor;
+
+        // TODO show the current state of the signature check, not just whether there is a key or not
+        if (TextUtils.isEmpty(repo.fingerprint) && TextUtils.isEmpty(repo.pubkey)) {
+            repoFingerprint = getResources().getString(R.string.unsigned);
+            repoFingerprintColor = getResources().getColor(R.color.unsigned);
+            repoFingerprintDescView.setVisibility(View.VISIBLE);
+            repoFingerprintDescView.setText(getResources().getString(R.string.unsigned_description));
+        } else {
+            // this is based on repo.fingerprint always existing, which it should
+            repoFingerprint = Utils.formatFingerprint(this, repo.fingerprint);
+            repoFingerprintColor = getResources().getColor(R.color.signed);
+            repoFingerprintDescView.setVisibility(View.GONE);
+        }
+
+        repoFingerprintView.setText(repoFingerprint);
+        repoFingerprintView.setTextColor(repoFingerprintColor);
+    }
+
+    private void updateRepoView() {
+
+        if (repo.hasBeenUpdated()) {
+            updateViewForExistingRepo(repoView);
+        } else {
+            updateViewForNewRepo(repoView);
+        }
+
+    }
+
+    private void updateViewForNewRepo(View repoView) {
+        setMultipleViewVisibility(repoView, HIDE_IF_EXISTS, View.VISIBLE);
+        setMultipleViewVisibility(repoView, SHOW_IF_EXISTS, View.GONE);
+    }
+
+    private void updateViewForExistingRepo(View repoView) {
+        setMultipleViewVisibility(repoView, SHOW_IF_EXISTS, View.VISIBLE);
+        setMultipleViewVisibility(repoView, HIDE_IF_EXISTS, View.GONE);
+
+        TextView name = (TextView) repoView.findViewById(R.id.text_repo_name);
+        TextView numApps = (TextView) repoView.findViewById(R.id.text_num_apps);
+        TextView lastUpdated = (TextView) repoView.findViewById(R.id.text_last_update);
+
+        name.setText(repo.name);
+
+        int appCount = RepoProvider.Helper.countAppsForRepo(this, repoId);
+        numApps.setText(Integer.toString(appCount));
+
+        setupDescription(repoView, repo);
+        setupRepoFingerprint(repoView, repo);
+
+        // Repos that existed before this feature was supported will have an
+        // "Unknown" last update until next time they update...
+        String lastUpdate = repo.lastUpdated != null
+                ? repo.lastUpdated.toString() : getString(R.string.unknown);
+        lastUpdated.setText(lastUpdate);
+    }
+
+    /**
+     * When an update is performed, notify the listener so that the repo
+     * list can be updated. We will perform the update ourselves though.
+     */
+    private void performUpdate() {
+        // Ensure repo is enabled before updating...
+        ContentValues values = new ContentValues(1);
+        values.put(RepoProvider.DataColumns.IN_USE, 1);
+        RepoProvider.Helper.update(this, repo, values);
+
+        UpdateService.updateRepoNow(repo.address, this);
+    }
+
+    private void promptForDelete() {
+        new AlertDialog.Builder(this)
+                .setTitle(R.string.repo_confirm_delete_title)
+                .setMessage(R.string.repo_confirm_delete_body)
+                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        RepoProvider.Helper.remove(getApplicationContext(), repoId);
+                        finish();
+                    }
+                }).setNegativeButton(android.R.string.cancel,
+                new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        // Do nothing...
+                    }
+                }
+        ).show();
+    }
+
 }
diff --git a/F-Droid/src/org/fdroid/fdroid/views/fragments/CanUpdateAppsFragment.java b/F-Droid/src/org/fdroid/fdroid/views/fragments/CanUpdateAppsFragment.java
index fa155c5eb..47c8c207e 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/fragments/CanUpdateAppsFragment.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/fragments/CanUpdateAppsFragment.java
@@ -138,7 +138,7 @@ public class CanUpdateAppsFragment extends AppListFragment {
         mUpdateAllButton.setId(UPDATE_ALL_BUTTON_ID);
         mUpdateAllButton.setText(R.string.update_all);
         mUpdateAllButton.setCompoundDrawablesWithIntrinsicBounds(
-                getResources().getDrawable(R.drawable.ic_menu_refresh), null, null, null);
+                getResources().getDrawable(R.drawable.ic_refresh_white), null, null, null);
         mUpdateAllButton.setVisibility(View.GONE);
 
         linearLayout.addView(mUpdateAllButton, new FrameLayout.LayoutParams(
diff --git a/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java b/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java
index d5d359a03..f0c84ba1b 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java
@@ -1,6 +1,5 @@
 package org.fdroid.fdroid.views.fragments;
 
-import android.app.AlertDialog;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.SharedPreferences;
@@ -11,6 +10,7 @@ import android.preference.EditTextPreference;
 import android.preference.ListPreference;
 import android.preference.Preference;
 import android.support.v4.preference.PreferenceFragment;
+import android.support.v7.app.AlertDialog;
 import android.text.Html;
 import android.text.TextUtils;
 
diff --git a/F-Droid/src/org/fdroid/fdroid/views/fragments/RepoDetailsFragment.java b/F-Droid/src/org/fdroid/fdroid/views/fragments/RepoDetailsFragment.java
deleted file mode 100644
index 04876d8d2..000000000
--- a/F-Droid/src/org/fdroid/fdroid/views/fragments/RepoDetailsFragment.java
+++ /dev/null
@@ -1,393 +0,0 @@
-package org.fdroid.fdroid.views.fragments;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.ContentValues;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.nfc.NfcAdapter;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.view.MenuItemCompat;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import org.fdroid.fdroid.NfcNotEnabledActivity;
-import org.fdroid.fdroid.ProgressListener;
-import org.fdroid.fdroid.R;
-import org.fdroid.fdroid.UpdateService;
-import org.fdroid.fdroid.Utils;
-import org.fdroid.fdroid.data.Repo;
-import org.fdroid.fdroid.data.RepoProvider;
-
-public class RepoDetailsFragment extends Fragment {
-
-    public static final String ARG_REPO_ID = "repo_id";
-
-    /**
-     * If the repo has been updated at least once, then we will show
-     * all of this info, otherwise they will be hidden.
-     */
-    private static final int[] SHOW_IF_EXISTS = {
-        R.id.label_repo_name,
-        R.id.text_repo_name,
-        R.id.label_description,
-        R.id.text_description,
-        R.id.label_num_apps,
-        R.id.text_num_apps,
-        R.id.label_last_update,
-        R.id.text_last_update,
-        R.id.label_repo_fingerprint,
-        R.id.text_repo_fingerprint,
-        R.id.text_repo_fingerprint_description
-    };
-
-    /**
-     * If the repo has <em>not</em> been updated yet, then we only show
-     * these, otherwise they are hidden.
-     */
-    private static final int[] HIDE_IF_EXISTS = {
-        R.id.text_not_yet_updated,
-        R.id.btn_update
-    };
-
-    private static final int DELETE = 0;
-    private static final int UPDATE = 1;
-    private static final int ENABLE_NFC = 2;
-
-    private static final String TAG = "RepoDetailsFragment";
-
-    private MenuItem enableNfc = null;
-    private UpdateService.UpdateReceiver updateHandler = null;
-
-    // TODO: Currently initialised in onCreateView. Not sure if that is the
-    // best way to go about this...
-    private Repo repo;
-
-    @Override
-    public void onAttach(Activity activity) {
-        super.onAttach(activity);
-        if (updateHandler != null) {
-            updateHandler.showDialog();
-        }
-    }
-
-    @Override
-    public void onDetach() {
-        super.onDetach();
-        if (updateHandler != null) {
-            updateHandler.hideDialog();
-        }
-    }
-
-    private long getRepoId() {
-        return getArguments().getLong(RepoDetailsFragment.ARG_REPO_ID, 0);
-    }
-
-    /**
-     * After, for example, a repo update, the details will have changed in the
-     * database. However, or local reference to the Repo object will not
-     * have been updated. The safest way to deal with this is to reload the
-     * repo object directly from the database.
-     */
-    private Repo loadRepoDetails() {
-        return RepoProvider.Helper.findById(getActivity(), getRepoId());
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-
-        repo = loadRepoDetails();
-
-        if (repo == null) {
-            Log.e(TAG, "Error showing details for repo '" + getRepoId() + "'");
-            return new LinearLayout(container.getContext());
-        }
-
-        ViewGroup repoView = (ViewGroup)inflater.inflate(R.layout.repodetails, null);
-        updateView(repoView);
-
-        // Setup listeners here, rather than in updateView(...),
-        // because otherwise we will end up adding multiple listeners with
-        // subsequent calls to updateView().
-        EditText inputUrl = (EditText)repoView.findViewById(R.id.input_repo_url);
-        inputUrl.addTextChangedListener(new UrlWatcher());
-
-        Button update = (Button)repoView.findViewById(R.id.btn_update);
-        update.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                performUpdate();
-            }
-        });
-
-        return repoView;
-    }
-
-    /**
-     * Populates relevant views with properties from the current repository.
-     * Decides which views to show and hide depending on the state of the
-     * repository.
-     */
-    private void updateView(ViewGroup repoView) {
-
-        EditText inputUrl = (EditText)repoView.findViewById(R.id.input_repo_url);
-        inputUrl.setText(repo.address);
-
-        if (repo.hasBeenUpdated()) {
-            updateViewForExistingRepo(repoView);
-        } else {
-            updateViewForNewRepo(repoView);
-        }
-
-    }
-
-    /**
-     * Help function to make switching between two view states easier.
-     * Perhaps there is a better way to do this. I recall that using Adobe
-     * Flex, there was a thing called "ViewStates" for exactly this. Wonder if
-     * that exists in  Android?
-     */
-    private static void setMultipleViewVisibility(ViewGroup parent,
-                                                  int[] viewIds,
-                                                  int visibility) {
-        for (int viewId : viewIds) {
-            parent.findViewById(viewId).setVisibility(visibility);
-        }
-    }
-
-    private void updateViewForNewRepo(ViewGroup repoView) {
-        setMultipleViewVisibility(repoView, HIDE_IF_EXISTS, View.VISIBLE);
-        setMultipleViewVisibility(repoView, SHOW_IF_EXISTS, View.GONE);
-    }
-
-    private void updateViewForExistingRepo(ViewGroup repoView) {
-        setMultipleViewVisibility(repoView, SHOW_IF_EXISTS, View.VISIBLE);
-        setMultipleViewVisibility(repoView, HIDE_IF_EXISTS, View.GONE);
-
-        TextView name          = (TextView)repoView.findViewById(R.id.text_repo_name);
-        TextView numApps       = (TextView)repoView.findViewById(R.id.text_num_apps);
-        TextView lastUpdated   = (TextView)repoView.findViewById(R.id.text_last_update);
-
-        name.setText(repo.getName());
-
-        int appCount = RepoProvider.Helper.countAppsForRepo(getActivity(), repo.getId());
-        numApps.setText(Integer.toString(appCount));
-
-        setupDescription(repoView, repo);
-        setupRepoFingerprint(repoView, repo);
-
-        // Repos that existed before this feature was supported will have an
-        // "Unknown" last update until next time they update...
-        String lastUpdate = repo.lastUpdated != null
-                ? repo.lastUpdated.toString() : getString(R.string.unknown);
-        lastUpdated.setText(lastUpdate);
-    }
-
-    private void setupDescription(ViewGroup parent, Repo repo) {
-
-        TextView descriptionLabel = (TextView)parent.findViewById(R.id.label_description);
-        TextView description      = (TextView)parent.findViewById(R.id.text_description);
-
-        if (repo.description == null || repo.description.length() == 0) {
-            descriptionLabel.setVisibility(View.GONE);
-            description.setVisibility(View.GONE);
-        } else {
-            descriptionLabel.setVisibility(View.VISIBLE);
-            description.setVisibility(View.VISIBLE);
-        }
-
-        String descriptionText = repo.description == null
-                ? "" : repo.description.replaceAll("\n", " ");
-        description.setText(descriptionText);
-
-    }
-
-    /**
-     * When an update is performed, notify the listener so that the repo
-     * list can be updated. We will perform the update ourselves though.
-     */
-    private void performUpdate() {
-        // Ensure repo is enabled before updating...
-        ContentValues values = new ContentValues(1);
-        values.put(RepoProvider.DataColumns.IN_USE, 1);
-        RepoProvider.Helper.update(getActivity(), repo, values);
-
-        updateHandler = UpdateService.updateRepoNow(repo.address, getActivity()).setListener(new ProgressListener() {
-            @Override
-            public void onProgress(Event event) {
-                switch (event.type) {
-                case UpdateService.EVENT_COMPLETE_WITH_CHANGES:
-                    repo = loadRepoDetails();
-                    updateView((ViewGroup)getView());
-                    break;
-                case UpdateService.EVENT_FINISHED:
-                    updateHandler = null;
-                    break;
-                }
-            }
-        });
-    }
-
-    /**
-     * When the URL is changed, notify the repoChangeListener.
-     */
-    class UrlWatcher implements TextWatcher {
-
-        @Override
-        public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
-
-        @Override
-        public void afterTextChanged(Editable s) {}
-
-        @Override
-        // TODO: This is called each character change, resulting in a DB query.
-        // Doesn't exactly cause performance problems,
-        // but seems silly not to go for more of a "focus out" event then
-        // this "text changed" event.
-        public void onTextChanged(CharSequence s, int start, int before, int count) {
-            if (!repo.address.equals(s.toString())) {
-                ContentValues values = new ContentValues(1);
-                values.put(RepoProvider.DataColumns.ADDRESS, s.toString());
-                RepoProvider.Helper.update(getActivity(), repo, values);
-            }
-        }
-    }
-
-    @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        super.onCreateOptionsMenu(menu, inflater);
-        menu.clear();
-
-        MenuItem update = menu.add(Menu.NONE, UPDATE, 0, R.string.repo_update);
-        update.setIcon(R.drawable.ic_refresh_white);
-        MenuItemCompat.setShowAsAction(update,
-            MenuItemCompat.SHOW_AS_ACTION_ALWAYS |
-            MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT);
-
-        MenuItem delete = menu.add(Menu.NONE, DELETE, 0, R.string.delete);
-        delete.setIcon(R.drawable.ic_delete_white);
-        MenuItemCompat.setShowAsAction(delete,
-            MenuItemCompat.SHOW_AS_ACTION_IF_ROOM |
-            MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT);
-    }
-
-    @Override
-    public void onPrepareOptionsMenu(Menu menu) {
-        if (Build.VERSION.SDK_INT >= 14)
-            prepareNfcMenuItems(menu);
-    }
-
-    @TargetApi(16)
-    private void prepareNfcMenuItems(Menu menu) {
-        boolean needsEnableNfcMenuItem;
-        NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity());
-        if (nfcAdapter == null) {
-            return;
-        }
-        if (Build.VERSION.SDK_INT < 16)
-            needsEnableNfcMenuItem = !nfcAdapter.isEnabled();
-        else
-            needsEnableNfcMenuItem = !nfcAdapter.isNdefPushEnabled();
-        if (needsEnableNfcMenuItem) {
-            if (enableNfc != null)
-                return; // already created
-            enableNfc = menu.add(Menu.NONE, ENABLE_NFC, 0, R.string.enable_nfc_send);
-            enableNfc.setIcon(R.drawable.ic_nfc_white);
-            MenuItemCompat.setShowAsAction(enableNfc,
-                MenuItemCompat.SHOW_AS_ACTION_IF_ROOM |
-                MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT);
-        } else if (enableNfc != null) {
-            // remove the existing MenuItem since NFC is now enabled
-            menu.removeItem(enableNfc.getItemId());
-            enableNfc = null;
-        }
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-
-        switch (item.getItemId()) {
-        case DELETE:
-            promptForDelete();
-            return true;
-        case UPDATE:
-            performUpdate();
-            return true;
-        case ENABLE_NFC:
-            Intent intent = new Intent(getActivity(), NfcNotEnabledActivity.class);
-            startActivity(intent);
-            return true;
-        }
-
-        return false;
-    }
-
-    private void promptForDelete() {
-        new AlertDialog.Builder(getActivity())
-            .setTitle(R.string.repo_confirm_delete_title)
-            .setIcon(android.R.drawable.ic_menu_delete)
-            .setMessage(R.string.repo_confirm_delete_body)
-            .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-                @Override
-                public void onClick(DialogInterface dialog, int which) {
-                    Repo repo = RepoDetailsFragment.this.repo;
-                    RepoProvider.Helper.remove(getActivity(), repo.getId());
-                    getActivity().finish();
-                }
-            }).setNegativeButton(android.R.string.cancel,
-                new DialogInterface.OnClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int which) {
-                        // Do nothing...
-                    }
-                }
-        ).show();
-    }
-
-    private void setupRepoFingerprint(ViewGroup parent, Repo repo) {
-        TextView repoFingerprintView     = (TextView)parent.findViewById(R.id.text_repo_fingerprint);
-        TextView repoFingerprintDescView = (TextView)parent.findViewById(R.id.text_repo_fingerprint_description);
-
-        String repoFingerprint;
-        int repoFingerprintColor;
-
-        // TODO show the current state of the signature check, not just whether there is a key or not
-        if (TextUtils.isEmpty(repo.fingerprint) && TextUtils.isEmpty(repo.pubkey)) {
-            repoFingerprint = getResources().getString(R.string.unsigned);
-            repoFingerprintColor = getResources().getColor(R.color.unsigned);
-            repoFingerprintDescView.setVisibility(View.VISIBLE);
-            repoFingerprintDescView.setText(getResources().getString(R.string.unsigned_description));
-        } else {
-            // this is based on repo.fingerprint always existing, which it should
-            repoFingerprint = Utils.formatFingerprint(getActivity(), repo.fingerprint);
-            repoFingerprintColor = getResources().getColor(R.color.signed);
-            repoFingerprintDescView.setVisibility(View.GONE);
-        }
-
-        repoFingerprintView.setText(repoFingerprint);
-        repoFingerprintView.setTextColor(repoFingerprintColor);
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setHasOptionsMenu(true);
-        setRetainInstance(true);
-    }
-
-}
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/SwapAppsView.java b/F-Droid/src/org/fdroid/fdroid/views/swap/SwapAppsView.java
index 6f91d4e78..45d25ea16 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/swap/SwapAppsView.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/SwapAppsView.java
@@ -1,7 +1,9 @@
 package org.fdroid.fdroid.views.swap;
 
 import android.annotation.TargetApi;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
 import android.database.Cursor;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -14,6 +16,7 @@ import android.support.annotation.Nullable;
 import android.support.v4.app.LoaderManager;
 import android.support.v4.content.CursorLoader;
 import android.support.v4.content.Loader;
+import android.support.v4.content.LocalBroadcastManager;
 import android.support.v4.view.MenuItemCompat;
 import android.support.v4.widget.CursorAdapter;
 import android.support.v7.widget.SearchView;
@@ -35,7 +38,6 @@ import android.widget.TextView;
 import com.nostra13.universalimageloader.core.DisplayImageOptions;
 import com.nostra13.universalimageloader.core.ImageLoader;
 
-import org.fdroid.fdroid.ProgressListener;
 import org.fdroid.fdroid.R;
 import org.fdroid.fdroid.UpdateService;
 import org.fdroid.fdroid.Utils;
@@ -115,6 +117,8 @@ public class SwapAppsView extends ListView implements
         schedulePollForUpdates();
     }
 
+    private BroadcastReceiver pollForUpdatesReceiver;
+
     private void pollForUpdates() {
         if (adapter.getCount() > 1 ||
                 (adapter.getCount() == 1 && !new App((Cursor)adapter.getItem(0)).id.equals("org.fdroid.fdroid"))) {
@@ -123,13 +127,14 @@ public class SwapAppsView extends ListView implements
         }
 
         Log.d(TAG, "Polling swap repo to see if it has any updates.");
-        UpdateService.UpdateReceiver receiver = getState().refreshSwap();
-        if (receiver != null) {
-            receiver.setListener(new ProgressListener() {
+        getState().refreshSwap();
+        if (pollForUpdatesReceiver != null) {
+            pollForUpdatesReceiver = new BroadcastReceiver() {
                 @Override
-                public void onProgress(Event event) {
-                    switch (event.type) {
-                        case UpdateService.EVENT_COMPLETE_WITH_CHANGES:
+                public void onReceive(Context context, Intent intent) {
+                    int statusCode = intent.getIntExtra(UpdateService.EXTRA_STATUS_CODE, -1);
+                    switch (statusCode) {
+                        case UpdateService.STATUS_COMPLETE_WITH_CHANGES:
                             Log.d(TAG, "Swap repo has updates, notifying the list adapter.");
                             getActivity().runOnUiThread(new Runnable() {
                                 @Override
@@ -137,19 +142,22 @@ public class SwapAppsView extends ListView implements
                                     adapter.notifyDataSetChanged();
                                 }
                             });
+                            LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(pollForUpdatesReceiver);
                             break;
 
-                        case UpdateService.EVENT_ERROR:
+                        case UpdateService.STATUS_ERROR_GLOBAL:
                             // TODO: Well, if we can't get the index, we probably can't swapp apps.
                             // Tell the user somethign helpful?
                             break;
 
-                        case UpdateService.EVENT_COMPLETE_AND_SAME:
+                        case UpdateService.STATUS_COMPLETE_AND_SAME:
                             schedulePollForUpdates();
                             break;
                     }
                 }
-            });
+            };
+            // TODO: Unregister this properly, not just when successful (see swithc statement above)
+            LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(pollForUpdatesReceiver);
         }
     }
 
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/SwapConnecting.java b/F-Droid/src/org/fdroid/fdroid/views/swap/SwapConnecting.java
index 89820fc52..8eb458491 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/swap/SwapConnecting.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/SwapConnecting.java
@@ -1,18 +1,23 @@
 package org.fdroid.fdroid.views.swap;
 
 import android.annotation.TargetApi;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.Build;
 import android.support.annotation.ColorRes;
 import android.support.annotation.NonNull;
+import android.support.v4.content.LocalBroadcastManager;
 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.LinearLayout;
 import android.widget.TextView;
 
-import org.fdroid.fdroid.ProgressListener;
 import org.fdroid.fdroid.R;
 import org.fdroid.fdroid.UpdateService;
 import org.fdroid.fdroid.localrepo.SwapService;
@@ -55,8 +60,7 @@ public class SwapConnecting extends LinearLayout implements SwapWorkflowActivity
         if (peer == null) {
             Log.e(TAG, "Cannot find the peer to connect to.");
 
-            // TODO: Don't go to the selected apps, rather show a Toast message and then
-            // go to the intro screen.
+            // TODO: Don't go to the selected apps, rather show a Toast message and then go to the intro screen.
             getActivity().showSelectApps();
             return;
         }
@@ -64,42 +68,68 @@ public class SwapConnecting extends LinearLayout implements SwapWorkflowActivity
         String heading = getContext().getString(R.string.status_connecting_to_repo, peer.getName());
         ((TextView) findViewById(R.id.heading)).setText(heading);
 
-        UpdateService.UpdateReceiver receiver = getManager().connectTo(peer, peer.shouldPromptForSwapBack());
-
-        receiver.hideDialog();
-        receiver.setListener(new ProgressListener() {
+        findViewById(R.id.back).setOnClickListener(new OnClickListener() {
             @Override
-            public void onProgress(Event event) {
-                ((TextView) findViewById(R.id.progress)).setText(event.data.getString(UpdateService.EXTRA_ADDRESS));
-                boolean finished = false;
-                boolean error = false;
-                switch (event.type) {
-                    case UpdateService.EVENT_ERROR:
-                        finished = true;
-                        error = true;
-                        break;
-                    case UpdateService.EVENT_COMPLETE_WITH_CHANGES:
-                        finished = true;
-                        break;
-                    case UpdateService.EVENT_COMPLETE_AND_SAME:
-                        finished = true;
-                        break;
-                    case UpdateService.EVENT_INFO:
-                        break;
-                }
-
-                if (finished) {
-                    if (error) {
-                        // TODO: Feedback to user about error, suggest fixes.
-                    } else {
-                        getActivity().showSwapConnected();
-                    }
-                }
-
+            public void onClick(View v) {
+                getActivity().showIntro();
             }
         });
+
+        // TODO: Unregister correctly, not just when being notified of completion or errors.
+        LocalBroadcastManager.getInstance(getActivity()).registerReceiver(repoUpdateReceiver, new IntentFilter(UpdateService.LOCAL_ACTION_STATUS));
+        getManager().connectTo(peer, peer.shouldPromptForSwapBack());
+
     }
 
+    private BroadcastReceiver repoUpdateReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+
+            int statusCode = intent.getIntExtra(UpdateService.EXTRA_STATUS_CODE, -1);
+
+            TextView progressText = ((TextView) findViewById(R.id.progress));
+            TextView errorText    = ((TextView) findViewById(R.id.error));
+            Button   backButton   = ((Button) findViewById(R.id.back));
+
+            if (intent.hasExtra(UpdateService.EXTRA_MESSAGE)) {
+                progressText.setText(intent.getStringExtra(UpdateService.EXTRA_MESSAGE));
+            }
+
+            boolean finished = false;
+            boolean error = false;
+
+            progressText.setVisibility(View.VISIBLE);
+            errorText.setVisibility(View.GONE);
+            backButton.setVisibility(View.GONE);
+
+            switch (statusCode) {
+                case UpdateService.STATUS_ERROR_GLOBAL:
+                    finished = true;
+                    error = true;
+                    break;
+                case UpdateService.STATUS_COMPLETE_WITH_CHANGES:
+                    finished = true;
+                    break;
+                case UpdateService.STATUS_COMPLETE_AND_SAME:
+                    finished = true;
+                    break;
+                case UpdateService.STATUS_INFO:
+                    break;
+            }
+
+            if (finished) {
+                LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(repoUpdateReceiver);
+                if (error) {
+                    progressText.setVisibility(View.GONE);
+                    errorText.setVisibility(View.VISIBLE);
+                    backButton.setVisibility(View.VISIBLE);
+                } else {
+                    getActivity().showSwapConnected();
+                }
+            }
+        }
+    };
+
     @Override
     public boolean buildMenu(Menu menu, @NonNull MenuInflater inflater) {
         return true;
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 263c62a32..1f142dce9 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java
@@ -294,7 +294,7 @@ public class SwapWorkflowActivity extends AppCompatActivity {
         inflateInnerView(R.layout.swap_initial_loading);
     }
 
-    private void showIntro() {
+    public void showIntro() {
         // If we were previously swapping with a specific client, forget that we were doing that,
         // as we are starting over now.
         getService().swapWith(null);
diff --git a/F-Droid/test/.classpath b/F-Droid/test/.classpath
deleted file mode 100644
index d585386c3..000000000
--- a/F-Droid/test/.classpath
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="src" path="gen"/>
-	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
-	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
-	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/fdroid"/>
-	<classpathentry kind="output" path="bin/classes"/>
-</classpath>
diff --git a/F-Droid/test/.project b/F-Droid/test/.project
deleted file mode 100644
index 0c2b67ff9..000000000
--- a/F-Droid/test/.project
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>fdroid-test</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-</projectDescription>
diff --git a/F-Droid/test/ant.properties b/F-Droid/test/ant.properties
deleted file mode 100644
index 99458bdd5..000000000
--- a/F-Droid/test/ant.properties
+++ /dev/null
@@ -1,19 +0,0 @@
-# This file is used to override default values used by the Ant build system.
-#
-# This file must be checked into Version Control Systems, as it is
-# integral to the build system of your project.
-
-# This file is only used by the Ant script.
-
-# You can use this to override default values such as
-#  'source.dir' for the location of your java source folder and
-#  'out.dir' for the location of your output folder.
-
-# You can also use it define how the release builds are signed by declaring
-# the following properties:
-#  'key.store' for the location of your keystore and
-#  'key.alias' for the name of the key to use.
-# The password will be asked during the build when you use the 'release' target.
-
-tested.project.dir=..
-test.runner=com.zutubi.android.junitreport.JUnitReportTestRunner
diff --git a/F-Droid/test/custom_rules.xml b/F-Droid/test/custom_rules.xml
deleted file mode 100644
index ee90d3fac..000000000
--- a/F-Droid/test/custom_rules.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project name="test_custom_rules" default="fetch-test-report">
-
-  <target name="fetch-test-report">
-    <xpath
-        expression="/manifest/@package"
-        input="${tested.project.dir}/AndroidManifest.xml"
-        output="tested.package" />
-    <echo>Downloading XML test report (/data/data/${tested.package}/files/junit-report.xml)…</echo>
-    <mkdir dir="junitreports"/>
-    <exec executable="${adb}" failonerror="true">
-      <arg line="${adb.device.arg}"/>
-      <arg value="pull" />
-      <arg value="/data/data/${tested.package}/files/junit-report.xml" />
-      <arg value="junit-report.xml" />
-    </exec>
-  </target>
-
-</project>
diff --git a/F-Droid/test/libs/android-junit-report-1.5.8.README b/F-Droid/test/libs/android-junit-report-1.5.8.README
deleted file mode 100644
index a89c98533..000000000
--- a/F-Droid/test/libs/android-junit-report-1.5.8.README
+++ /dev/null
@@ -1,5 +0,0 @@
-
-Needed for Jenkins to get JUnit reports.
-
-* https://github.com/jsankey/android-junit-report
-* https://github.com/downloads/jsankey/android-junit-report/android-junit-report-1.5.8.jar
diff --git a/F-Droid/test/libs/android-junit-report-1.5.8.jar b/F-Droid/test/libs/android-junit-report-1.5.8.jar
deleted file mode 100644
index 09e6a2d4f..000000000
Binary files a/F-Droid/test/libs/android-junit-report-1.5.8.jar and /dev/null differ
diff --git a/F-Droid/test/libs/commons-io-2.2.jar b/F-Droid/test/libs/commons-io-2.2.jar
deleted file mode 100644
index 84ca56585..000000000
Binary files a/F-Droid/test/libs/commons-io-2.2.jar and /dev/null differ
diff --git a/F-Droid/test/libs/commons-io-2.2.jar.README b/F-Droid/test/libs/commons-io-2.2.jar.README
deleted file mode 100644
index a330a9794..000000000
--- a/F-Droid/test/libs/commons-io-2.2.jar.README
+++ /dev/null
@@ -1,5 +0,0 @@
-
-Downloaded from:
-https://archive.apache.org/dist/commons/io/binaries/commons-io-2.2-bin.zip
-https://archive.apache.org/dist/commons/io/binaries/commons-io-2.2-bin.zip.asc
-https://archive.apache.org/dist/commons/io/binaries/commons-io-2.2-bin.zip.sha1
diff --git a/F-Droid/test/proguard-project.txt b/F-Droid/test/proguard-project.txt
deleted file mode 100644
index f2fe1559a..000000000
--- a/F-Droid/test/proguard-project.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-# To enable ProGuard in your project, edit project.properties
-# to define the proguard.config property as described in that file.
-#
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in ${sdk.dir}/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the ProGuard
-# include property in project.properties.
-#
-# For more details, see
-#   http://developer.android.com/guide/developing/tools/proguard.html
-
-# Add any project specific keep options here:
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-#   public *;
-#}
diff --git a/F-Droid/test/project.properties b/F-Droid/test/project.properties
deleted file mode 100644
index e611232d2..000000000
--- a/F-Droid/test/project.properties
+++ /dev/null
@@ -1,21 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-21
-
-# With a target SDK of android-21 (5.0/Lollipop) and a Java 1.7 compiler, you
-# can use Java 1.7 features like the <> diamond operator, multi-catch, strings
-# in switches, etc.
-java.encoding=UTF-8
-java.source=1.7
-java.target=1.7
diff --git a/F-Droid/tools/download-material-icon.sh b/F-Droid/tools/download-material-icon.sh
index fc5ce8639..f41b539fd 100755
--- a/F-Droid/tools/download-material-icon.sh
+++ b/F-Droid/tools/download-material-icon.sh
@@ -15,11 +15,10 @@ function usage {
 }
 
 function download {
-	REMOTE_DRAWABLE_DIR=$1
-	LOCAL_DRAWABLE_DIR=$2
-	FILE="ic_${ICON}_48dp.png"
-	URL="$BASE_URL/$CATEGORY/$REMOTE_DRAWABLE_DIR/$FILE"
-	DIR="$RES_DIR/$LOCAL_DRAWABLE_DIR"
+	DRAWABLE_DIR=$1
+	FILE="ic_${ICON}_24dp.png"
+	URL="$BASE_URL/$CATEGORY/$DRAWABLE_DIR/$FILE"
+	DIR="$RES_DIR/$DRAWABLE_DIR"
 
 	if [ ! -d $DIR ]
 	then
@@ -60,10 +59,9 @@ then
 	exit
 fi
 
-download drawable-mdpi drawable
 for SCREEN in $SCREENS
 do
-	download "drawable-$SCREEN" "drawable-$SCREEN"
+	download "drawable-$SCREEN"
 done
 
 echo ""
diff --git a/HACKING.md b/HACKING.md
index d9aa2ddf3..ee26f8798 100644
--- a/HACKING.md
+++ b/HACKING.md
@@ -29,10 +29,6 @@ You have three options:
 
  * Build with gradle
  * Build with gradle from source
- * Build with ant from source
-
-Any will work, but if you use gradle, remember that you can use --daemon to
-not have to watch gradle load every time.
 
 Debugging
 ---------
diff --git a/README.md b/README.md
index 1605cd67d..b55254314 100644
--- a/README.md
+++ b/README.md
@@ -7,18 +7,18 @@ for Android.
 Building from source with Gradle
 --------------------------------
 
-The only required tools are the [Android SDK](http://developer.android.com/sdk/index.html) and Gradle.
+The only required tools are the [Android
+SDK](http://developer.android.com/sdk/index.html) and Gradle.
 
 You should use a relatively new version of Gradle, such as 2.4, or use the
 gradle wrapper.
 
 Once you have checked out the version you wish to build, run:
 
-```
-git submodule update --init
-cd F-Droid
-gradle clean assembleRelease
-```
+        cd F-Droid
+        gradle assembleRelease
+
+The resulting apk will be in `build/outputs/apk/`.
 
 Android Studio
 --------------
@@ -26,6 +26,13 @@ Android Studio
 From Android Studio: File -> Import Project -> Select the cloned top folder
 
 
+Building tips
+-------------
+
+* Use `gradle --daemon` if you are going to build F-Droid multiple times.
+* If you get a message like `Could not find com.android.support:support-...`,
+  make sure that you have the latest Android support maven repository
+
 Direct download
 ---------------
 
@@ -65,8 +72,42 @@ command line:
 This will build and install F-Droid and the test apk, then execute the entire
 test suite on the device or emulator.
 
-See the [Android Gradle user guide](http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Testing) for more details, including how to use Android Studio to run tests (which provides
-more useful feedback than the command line).
+See the [Android Gradle user
+guide](http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Testing)
+for more details, including how to use Android Studio to run tests (which
+provides more useful feedback than the command line).
+
+
+Versioning
+----------
+
+Each stable version follows the `X.Y` pattern. Hotfix releases - i.e. when a
+stable has an important bug that needs immediate fixing - will follow the
+`X.Y.Z` pattern.
+
+Before each stable release, a number of alpha releases will be released. They
+will follow the pattern `X.Y-alphaN`, where `N` is the current alpha number.
+These will usually include changes and new features that have not been tested
+enough for a stable release, so use at your own risk. Testers and reporters
+are very welcome.
+
+The version codes use a number of digits per each of these keys: `XYYZNN`.
+So for example, 1.3.1 would be `103150` and 0.95-alpha13 would be `95013`
+(leading zeros are omitted).
+
+Note that we use a trailing `50` for actual stable releases, so alphas are
+limited to `-alpha49`.
+
+This is an example of a release process for release **0.95**:
+
+* We are currently at stable **0.94**
+* **0.95-alpha1** is released
+* **0.95-alpha2** is released
+* **0.95-alpha3** is released
+* Testing process (1-2 weeks) during which no new features are merged in
+* **0.95** is released
+* A bug is reported on the stable release and fixed
+* **0.95.1** is released
 
 
 License
diff --git a/build.gradle b/build.gradle
index 20ee7f325..519664ef3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,6 +9,6 @@ buildscript {
         jcenter()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:1.2.2'
+        classpath 'com.android.tools.build:gradle:1.3.0'
     }
 }
diff --git a/extern/Support b/extern/Support
deleted file mode 160000
index e4ecf4ad3..000000000
--- a/extern/Support
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit e4ecf4ad3dd5a1c229e30db56a224eacc72a7fd2
diff --git a/jenkins-build b/jenkins-build
index ab5ffe4f8..f4fbdaf57 100755
--- a/jenkins-build
+++ b/jenkins-build
@@ -1,7 +1,7 @@
 #!/bin/sh
 #
-# Jenkins uses this script to set up the ant build.  Jenkins will then call
-# ant itself once this script has completed.
+# Jenkins uses this script to set up the gradle build. Jenkins will then call
+# gradle itself once this script has completed.
 
 set -e
 set -x
@@ -15,7 +15,6 @@ sed -i \
     -e "s,android:versionName=\"\([^\"][^\"]*\)\",android:versionName=\"\1.$versionNameDate\"," \
     F-Droid/AndroidManifest.xml
 
-
 if [ -z $ANDROID_HOME ]; then
     if [ -e ~/.android/bashrc ]; then
         . ~/.android/bashrc
@@ -26,4 +25,3 @@ if [ -z $ANDROID_HOME ]; then
 fi
 
 cd F-Droid
-./ant-prepare.sh