FPII-122: Fix FairphoneUpdater crash

Merge upstream FairphoneUpdater code, which fixes crashes
due to missing crashlytics integration.

Change-Id: Ic12da7a777b176c146b090b36ccc03759275f3b7
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 269f306..137abe6 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2,8 +2,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
           package="com.fairphone.updater"
-    android:versionCode="15"
-    android:versionName="15 (FP 1.8.2)" >
+    android:versionCode="26"
+    android:versionName="26 (FP 1.8.3)" >
 
     <uses-sdk
         android:minSdkVersion="17"
@@ -14,6 +14,8 @@
     <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.RECOVERY"
+                     tools:ignore="ProtectedPermissions"/>
     <uses-permission android:name="android.permission.REBOOT"
                      tools:ignore="ProtectedPermissions"/>
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
@@ -73,6 +75,11 @@
 
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <data android:mimeType="application/zip"/>
+            </intent-filter>
         </activity>
         <activity
             android:name=".BetaEnabler"
diff --git a/res/anim/enter.xml b/res/anim/enter.xml
new file mode 100644
index 0000000..8c9e912
--- /dev/null
+++ b/res/anim/enter.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set>
+    <translate xmlns:android="http://schemas.android.com/apk/res/android"
+               android:fromXDelta="100%"
+               android:toXDelta="0"
+               android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+               android:duration="250"/>
+</set>
\ No newline at end of file
diff --git a/res/anim/exit.xml b/res/anim/exit.xml
new file mode 100644
index 0000000..3f6bf1c
--- /dev/null
+++ b/res/anim/exit.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set>
+    <translate xmlns:android="http://schemas.android.com/apk/res/android"
+               android:fromXDelta="0"
+               android:toXDelta="-100%"
+               android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+               android:duration="250"/>
+</set>
\ No newline at end of file
diff --git a/res/anim/pop_enter.xml b/res/anim/pop_enter.xml
new file mode 100644
index 0000000..ad10da6
--- /dev/null
+++ b/res/anim/pop_enter.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set>
+    <translate xmlns:android="http://schemas.android.com/apk/res/android"
+               android:fromXDelta="-100%"
+               android:toXDelta="0"
+               android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+               android:duration="250"/>
+</set>
\ No newline at end of file
diff --git a/res/anim/pop_exit.xml b/res/anim/pop_exit.xml
new file mode 100644
index 0000000..6b3976a
--- /dev/null
+++ b/res/anim/pop_exit.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set>
+    <translate xmlns:android="http://schemas.android.com/apk/res/android"
+               android:fromXDelta="0"
+               android:toXDelta="100%"
+               android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+               android:duration="250"/>
+</set>
\ No newline at end of file
diff --git a/res/drawable-xxhdpi/checkbox_button_blue_check.png b/res/drawable-xxhdpi/checkbox_button_blue_check.png
index e2ddd20..55f0712 100644
--- a/res/drawable-xxhdpi/checkbox_button_blue_check.png
+++ b/res/drawable-xxhdpi/checkbox_button_blue_check.png
Binary files differ
diff --git a/res/drawable-xxhdpi/checkbox_button_blue_uncheck.png b/res/drawable-xxhdpi/checkbox_button_blue_uncheck.png
index 0a44630..84575bd 100644
--- a/res/drawable-xxhdpi/checkbox_button_blue_uncheck.png
+++ b/res/drawable-xxhdpi/checkbox_button_blue_uncheck.png
Binary files differ
diff --git a/res/drawable-xxhdpi/checkbox_button_green_check.png b/res/drawable-xxhdpi/checkbox_button_green_check.png
index e2ebdc3..2e22883 100644
--- a/res/drawable-xxhdpi/checkbox_button_green_check.png
+++ b/res/drawable-xxhdpi/checkbox_button_green_check.png
Binary files differ
diff --git a/res/drawable-xxhdpi/checkbox_button_green_uncheck.png b/res/drawable-xxhdpi/checkbox_button_green_uncheck.png
index 974d3ad..87fead4 100644
--- a/res/drawable-xxhdpi/checkbox_button_green_uncheck.png
+++ b/res/drawable-xxhdpi/checkbox_button_green_uncheck.png
Binary files differ
diff --git a/res/drawable-xxhdpi/checkbox_button_grey_check.png b/res/drawable-xxhdpi/checkbox_button_grey_check.png
index 1b13a9f..654a2e2 100644
--- a/res/drawable-xxhdpi/checkbox_button_grey_check.png
+++ b/res/drawable-xxhdpi/checkbox_button_grey_check.png
Binary files differ
diff --git a/res/drawable-xxhdpi/checkbox_button_grey_uncheck.png b/res/drawable-xxhdpi/checkbox_button_grey_uncheck.png
index 872c002..55d0205 100644
--- a/res/drawable-xxhdpi/checkbox_button_grey_uncheck.png
+++ b/res/drawable-xxhdpi/checkbox_button_grey_uncheck.png
Binary files differ
diff --git a/res/drawable/button_big_blue_text_background.xml b/res/drawable/button_big_blue_text_background.xml
new file mode 100644
index 0000000..8b9a6cd
--- /dev/null
+++ b/res/drawable/button_big_blue_text_background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:drawable="@color/blue_light" android:state_enabled="false"/>
+    <item android:drawable="@color/blue_dark" android:state_pressed="true"/>
+    <item android:drawable="@color/blue"/>
+
+</selector>
\ No newline at end of file
diff --git a/res/drawable/button_big_green_text_background.xml b/res/drawable/button_big_green_text_background.xml
new file mode 100644
index 0000000..79ce0f0
--- /dev/null
+++ b/res/drawable/button_big_green_text_background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:drawable="@color/green_light" android:state_enabled="false"/>
+    <item android:drawable="@color/green_dark" android:state_pressed="true"/>
+    <item android:drawable="@color/green"/>
+
+</selector>
\ No newline at end of file
diff --git a/res/drawable/button_blue_dark_background.xml b/res/drawable/button_blue_dark_background.xml
index a80b27d..c250f38 100644
--- a/res/drawable/button_blue_dark_background.xml
+++ b/res/drawable/button_blue_dark_background.xml
@@ -2,7 +2,7 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
 
     <item android:drawable="@color/blue_light" android:state_enabled="false"/>
-    <item android:drawable="@color/white" android:state_pressed="true"/>
+    <item android:drawable="@color/blue" android:state_pressed="true"/>
     <item android:drawable="@color/blue_dark"/>
 
 </selector>
\ No newline at end of file
diff --git a/res/drawable/button_blue_dark_white_background.xml b/res/drawable/button_blue_dark_white_background.xml
new file mode 100644
index 0000000..a80b27d
--- /dev/null
+++ b/res/drawable/button_blue_dark_white_background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:drawable="@color/blue_light" android:state_enabled="false"/>
+    <item android:drawable="@color/white" android:state_pressed="true"/>
+    <item android:drawable="@color/blue_dark"/>
+
+</selector>
\ No newline at end of file
diff --git a/res/drawable/button_blue_text_background.xml b/res/drawable/button_blue_text_background.xml
new file mode 100644
index 0000000..0340992
--- /dev/null
+++ b/res/drawable/button_blue_text_background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:drawable="@color/transparent" android:state_enabled="false"/>
+    <item android:drawable="@color/blue_light_transparent" android:state_pressed="true"/>
+    <item android:drawable="@color/transparent"/>
+
+</selector>
\ No newline at end of file
diff --git a/res/drawable/button_green_dark_background.xml b/res/drawable/button_green_dark_background.xml
index e381ea1..f2eab77 100644
--- a/res/drawable/button_green_dark_background.xml
+++ b/res/drawable/button_green_dark_background.xml
@@ -2,7 +2,7 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
 
     <item android:drawable="@color/grey_light" android:state_enabled="false"/>
-    <item android:drawable="@color/white" android:state_pressed="true"/>
+    <item android:drawable="@color/green" android:state_pressed="true"/>
     <item android:drawable="@color/green_dark"/>
 
 </selector>
\ No newline at end of file
diff --git a/res/drawable/button_green_dark_white_background.xml b/res/drawable/button_green_dark_white_background.xml
new file mode 100644
index 0000000..e381ea1
--- /dev/null
+++ b/res/drawable/button_green_dark_white_background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:drawable="@color/grey_light" android:state_enabled="false"/>
+    <item android:drawable="@color/white" android:state_pressed="true"/>
+    <item android:drawable="@color/green_dark"/>
+
+</selector>
\ No newline at end of file
diff --git a/res/drawable/button_green_text_background.xml b/res/drawable/button_green_text_background.xml
new file mode 100644
index 0000000..636a218
--- /dev/null
+++ b/res/drawable/button_green_text_background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:drawable="@color/transparent" android:state_enabled="false"/>
+    <item android:drawable="@color/green_light_transparent" android:state_pressed="true"/>
+    <item android:drawable="@color/transparent"/>
+
+</selector>
\ No newline at end of file
diff --git a/res/drawable/button_pink_text_background.xml b/res/drawable/button_pink_text_background.xml
new file mode 100644
index 0000000..44daf7d
--- /dev/null
+++ b/res/drawable/button_pink_text_background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:drawable="@color/transparent" android:state_enabled="false"/>
+    <item android:drawable="@color/pink_light_transparent" android:state_pressed="true"/>
+    <item android:drawable="@color/transparent"/>
+
+</selector>
\ No newline at end of file
diff --git a/res/drawable/checkbox_button_pink.xml b/res/drawable/checkbox_button_pink.xml
index 37dadd8..ae740ab 100644
--- a/res/drawable/checkbox_button_pink.xml
+++ b/res/drawable/checkbox_button_pink.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <item android:drawable="@drawable/checkbox_button_blue_check" android:state_checked="true"/>
-    <item android:drawable="@drawable/checkbox_button_blue_uncheck"/>
+    <item android:drawable="@drawable/checkbox_button_grey_check" android:state_checked="true"/>
+    <item android:drawable="@drawable/checkbox_button_grey_uncheck"/>
 
 </selector>
\ No newline at end of file
diff --git a/res/layout/activity_updater.xml b/res/layout/activity_updater.xml
index 17f6a34..0e59608 100644
--- a/res/layout/activity_updater.xml
+++ b/res/layout/activity_updater.xml
@@ -2,13 +2,14 @@
               android:layout_width="match_parent"
               android:layout_height="match_parent"
               android:background="@color/background"
+              android:animateLayoutChanges="true"
               android:orientation="vertical">
 
-    <include layout="@layout/header" />
+    <include layout="@layout/header"/>
 
     <FrameLayout
         android:id="@+id/fragment_holder"
         android:layout_width="match_parent"
-        android:layout_height="match_parent" />
+        android:layout_height="match_parent"/>
 
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/fragment_app_store_options_list.xml b/res/layout/fragment_app_store_options_list.xml
index 0a9b7c2..c59d8a3 100644
--- a/res/layout/fragment_app_store_options_list.xml
+++ b/res/layout/fragment_app_store_options_list.xml
@@ -1,23 +1,16 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:id="@+id/older_versions_group"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:gravity="center_horizontal"
-              android:orientation="vertical" >
-
-    <ScrollView
-        android:id="@+id/version_list_scroll"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_marginTop="@dimen/main_padding"
-        android:layout_weight="1"
-        android:orientation="vertical" >
-
-        <LinearLayout
-            android:id="@+id/version_list_container"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+            android:id="@+id/older_versions_group"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:clickable="true"
-            android:orientation="vertical" />
-    </ScrollView>
-</LinearLayout>
\ No newline at end of file
+            android:layout_height="match_parent"
+            android:layout_marginTop="@dimen/main_padding"
+            android:layout_weight="1"
+            android:gravity="center_horizontal"
+            android:orientation="vertical">
+
+    <LinearLayout
+        android:id="@+id/version_list_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:clickable="true"
+        android:orientation="vertical"/>
+</ScrollView>
diff --git a/res/layout/fragment_app_store_options_list_button.xml b/res/layout/fragment_app_store_options_list_button.xml
index a4c553b..62ca2f1 100644
--- a/res/layout/fragment_app_store_options_list_button.xml
+++ b/res/layout/fragment_app_store_options_list_button.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Button xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/android_button"
-    style="@style/ButtonTextPink"
+    style="@style/ButtonTextBackgroundPink"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginTop="@dimen/main_button_margin_top" />
+    android:layout_height="wrap_content" />
diff --git a/res/layout/fragment_download_android.xml b/res/layout/fragment_download_android.xml
index 0758906..ce7e049 100644
--- a/res/layout/fragment_download_android.xml
+++ b/res/layout/fragment_download_android.xml
@@ -23,7 +23,7 @@
 
     <Button
         android:id="@+id/cancel_button"
-        style="@style/ButtonSmallGreenDark"
+        style="@style/ButtonSmallGreenDarkWhite"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/main_padding_small"
diff --git a/res/layout/fragment_download_android_confirmation_popup.xml b/res/layout/fragment_download_android_confirmation_popup.xml
index 2a8a63e..31d421e 100644
--- a/res/layout/fragment_download_android_confirmation_popup.xml
+++ b/res/layout/fragment_download_android_confirmation_popup.xml
@@ -45,7 +45,7 @@
         android:visibility="gone" />
 
     <TextView
-        style="@style/TextRegular14GreyLight"
+        style="@style/TextRegular14GreenDark"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginBottom="@dimen/alert_padding"
@@ -60,12 +60,12 @@
 
         <Button
             android:id="@+id/confirmation_no_button"
-            style="@style/ButtonGreenDarkGrey"
+            style="@style/ButtonGreenDark"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:paddingBottom="25dp"
             android:paddingTop="5dp"
-            android:text="@android:string/no" />
+            android:text="@android:string/cancel" />
 
         <TextView
             style="@style/TextRegular16White"
@@ -90,7 +90,7 @@
             android:layout_height="wrap_content"
             android:paddingBottom="25dp"
             android:paddingTop="5dp"
-            android:text="@android:string/yes" />
+            android:text="@android:string/ok" />
 
         <TextView
             style="@style/TextRegular16White"
diff --git a/res/layout/fragment_download_android_downloading.xml b/res/layout/fragment_download_android_downloading.xml
index e312bf9..064cf0c 100644
--- a/res/layout/fragment_download_android_downloading.xml
+++ b/res/layout/fragment_download_android_downloading.xml
@@ -5,7 +5,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_weight="1"
-        android:background="@color/pink"
+        android:background="@color/green"
         android:gravity="center_horizontal"
         android:orientation="vertical"
         android:visibility="gone" >
@@ -25,7 +25,7 @@
             android:text="@string/os_update_has_started_message" />
 
         <TextView
-            style="@style/TextRegular14PinkLight"
+            style="@style/TextRegular14GreenLight"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginTop="@dimen/main_small_text_margin_top"
diff --git a/res/layout/fragment_download_android_install.xml b/res/layout/fragment_download_android_install.xml
index 504848e..22f567e 100644
--- a/res/layout/fragment_download_android_install.xml
+++ b/res/layout/fragment_download_android_install.xml
@@ -10,6 +10,7 @@
         android:visibility="gone" >
 
         <TextView
+            android:id="@+id/download_complete_label"
             style="@style/TextBold16White"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
diff --git a/res/layout/fragment_download_app_store.xml b/res/layout/fragment_download_app_store.xml
index 5285e28..e05ec26 100644
--- a/res/layout/fragment_download_app_store.xml
+++ b/res/layout/fragment_download_app_store.xml
@@ -19,7 +19,7 @@
 
     <include layout="@layout/fragment_download_app_store_downloading" />
 
-    <include layout="@layout/fragment_download_fairphone_install" />
+    <include layout="@layout/fragment_download_app_store_install" />
 
     <Button
         android:id="@+id/cancel_button"
diff --git a/res/layout/fragment_download_app_store_downloading.xml b/res/layout/fragment_download_app_store_downloading.xml
index 1e6fe3e..5cecf58 100644
--- a/res/layout/fragment_download_app_store_downloading.xml
+++ b/res/layout/fragment_download_app_store_downloading.xml
@@ -17,7 +17,7 @@
             android:text="@string/download_started" />
 
         <TextView
-            style="@style/TextRegular14PinkLight"
+            style="@style/TextRegular14White"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginTop="@dimen/main_small_text_margin_top"
@@ -25,7 +25,7 @@
             android:text="@string/os_update_has_started_message" />
 
         <TextView
-            style="@style/TextRegular14PinkLight"
+            style="@style/TextRegular14White"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginTop="@dimen/main_small_text_margin_top"
diff --git a/res/layout/fragment_download_app_store_install.xml b/res/layout/fragment_download_app_store_install.xml
new file mode 100644
index 0000000..d7d91d8
--- /dev/null
+++ b/res/layout/fragment_download_app_store_install.xml
@@ -0,0 +1,36 @@
+<merge xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <LinearLayout
+        android:id="@+id/version_install_group"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:gravity="center_horizontal"
+        android:orientation="vertical"
+        android:visibility="gone" >
+
+        <TextView
+            android:id="@+id/download_complete_label"
+            style="@style/TextBold16White"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/download_complete" />
+
+        <TextView
+            style="@style/TextRegular14White"
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            android:layout_marginTop="@dimen/main_small_text_margin_top"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:text="@string/restart_your_phone_message" />
+
+        <Button
+            android:id="@+id/restart_button"
+            style="@style/ButtonWhitePink"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/restart" />
+    </LinearLayout>
+
+</merge>
\ No newline at end of file
diff --git a/res/layout/fragment_download_fairphone.xml b/res/layout/fragment_download_fairphone.xml
index 10c2487..5a6d82e 100644
--- a/res/layout/fragment_download_fairphone.xml
+++ b/res/layout/fragment_download_fairphone.xml
@@ -23,7 +23,7 @@
 
     <Button
         android:id="@+id/cancel_button"
-        style="@style/ButtonSmallBlueDark"
+        style="@style/ButtonSmallBlueDarkWhite"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/main_padding_small"
diff --git a/res/layout/fragment_download_fairphone_confirmation_popup.xml b/res/layout/fragment_download_fairphone_confirmation_popup.xml
index 1ed17cf..132cca1 100644
--- a/res/layout/fragment_download_fairphone_confirmation_popup.xml
+++ b/res/layout/fragment_download_fairphone_confirmation_popup.xml
@@ -10,20 +10,20 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginBottom="@dimen/alert_title_margin_bottom"
-        android:text="@string/warning" />
+        android:text="@string/warning"/>
 
     <TextView
         style="@style/TextRegular14BlueDark"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:gravity="center"
-        android:text="@string/your_are_about_to_install" />
+        android:text="@string/your_are_about_to_install"/>
 
     <TextView
         android:id="@+id/installing_version"
         style="@style/TextBold14BlueDark"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content" />
+        android:layout_height="wrap_content"/>
 
     <TextView
         android:id="@+id/version_type_text"
@@ -31,7 +31,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:gravity="center"
-        android:text="@string/an_older_version_of_os" />
+        android:text="@string/an_older_version_of_os"/>
 
     <TextView
         android:id="@+id/erase_all_data_warning_text"
@@ -42,30 +42,30 @@
         android:layout_marginTop="@dimen/alert_padding"
         android:gravity="center"
         android:text="@string/erase_all_data_warning_message"
-        android:visibility="gone" />
+        android:visibility="gone"/>
 
     <TextView
-        style="@style/TextRegular14GreyLight"
+        style="@style/TextRegular14BlueDark"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginBottom="@dimen/alert_padding"
         android:layout_marginTop="@dimen/alert_padding"
         android:gravity="center_horizontal"
-        android:text="@string/older_version_os_install_confirmation" />
+        android:text="@string/older_version_os_install_confirmation"/>
 
     <FrameLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/alert_padding" >
+        android:layout_marginBottom="@dimen/alert_padding">
 
         <Button
             android:id="@+id/confirmation_no_button"
-            style="@style/ButtonBlueDarkGrey"
+            style="@style/ButtonBlueDark"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:paddingBottom="25dp"
             android:paddingTop="5dp"
-            android:text="@android:string/no" />
+            android:text="@android:string/cancel"/>
 
         <TextView
             style="@style/TextRegular16White"
@@ -75,13 +75,13 @@
             android:clickable="false"
             android:focusable="false"
             android:paddingBottom="5dp"
-            android:text="@string/take_me_back" />
+            android:text="@string/take_me_back"/>
     </FrameLayout>
 
     <FrameLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/alert_padding" >
+        android:layout_marginBottom="@dimen/alert_padding">
 
         <Button
             android:id="@+id/confirmation_yes_button"
@@ -90,7 +90,7 @@
             android:layout_height="wrap_content"
             android:paddingBottom="25dp"
             android:paddingTop="5dp"
-            android:text="@android:string/yes" />
+            android:text="@android:string/ok"/>
 
         <TextView
             style="@style/TextRegular16White"
@@ -100,7 +100,7 @@
             android:clickable="false"
             android:focusable="false"
             android:paddingBottom="5dp"
-            android:text="@string/go_ahead_and_install_it" />
+            android:text="@string/go_ahead_and_install_it"/>
     </FrameLayout>
 
     <CheckBox
@@ -108,6 +108,6 @@
         style="@style/CheckBoxBlue"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="@string/older_version_os_install_confirmation_checkbox" />
+        android:text="@string/older_version_os_install_confirmation_checkbox"/>
 
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/fragment_download_fairphone_install.xml b/res/layout/fragment_download_fairphone_install.xml
index 9452299..beb7252 100644
--- a/res/layout/fragment_download_fairphone_install.xml
+++ b/res/layout/fragment_download_fairphone_install.xml
@@ -7,9 +7,10 @@
         android:layout_weight="1"
         android:gravity="center_horizontal"
         android:orientation="vertical"
-        android:visibility="gone" >
+        android:visibility="visible" >
 
         <TextView
+            android:id="@+id/download_complete_label"
             style="@style/TextBold16White"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
diff --git a/res/layout/fragment_info_android_popup.xml b/res/layout/fragment_info_android_popup.xml
index a241d5d..3cf1e58 100644
--- a/res/layout/fragment_info_android_popup.xml
+++ b/res/layout/fragment_info_android_popup.xml
@@ -34,6 +34,6 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/alert_button_top_margin"
-        android:text="@android:string/yes" />
+        android:text="@android:string/ok" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/fragment_info_fairphone_popup.xml b/res/layout/fragment_info_fairphone_popup.xml
index 6271069..d8e2d1a 100644
--- a/res/layout/fragment_info_fairphone_popup.xml
+++ b/res/layout/fragment_info_fairphone_popup.xml
@@ -34,6 +34,6 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/alert_button_top_margin"
-        android:text="@android:string/yes" />
+        android:text="@android:string/ok" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/fragment_main.xml b/res/layout/fragment_main.xml
index f4256b9..ba962c6 100644
--- a/res/layout/fragment_main.xml
+++ b/res/layout/fragment_main.xml
@@ -3,6 +3,30 @@
               android:layout_height="match_parent"
               android:orientation="vertical">
 
+    <LinearLayout
+        android:id="@+id/dev_mode_url_container"
+        android:layout_width="match_parent"
+        android:layout_height="50dp"
+        android:orientation="horizontal"
+        android:visibility="gone">
+
+        <EditText
+            android:id="@+id/dev_mode_url_edit_text"
+            android:layout_width="0dp"
+            android:layout_weight="5"
+            android:layout_height="wrap_content"
+            android:hint="@string/downloadUrl"
+            android:singleLine="true"/>
+
+        <Button
+            android:id="@+id/dev_mode_url_ok_button"
+            style="@style/ButtonTextBackgroundBlue"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:layout_height="match_parent"
+            android:text="@android:string/ok"/>
+    </LinearLayout>
+
     <RelativeLayout
         android:layout_width="match_parent"
         android:layout_height="0dp"
@@ -76,7 +100,8 @@
         android:background="@color/background_other_os_options"
         android:gravity="center_horizontal"
         android:orientation="vertical"
-        android:padding="@dimen/main_padding" >
+        android:paddingTop="@dimen/main_padding"
+        android:paddingBottom="@dimen/main_padding" >
 
         <TextView
             style="@style/TextRegular14GreyLight"
@@ -86,7 +111,7 @@
 
         <Button
             android:id="@+id/other_os_options_button"
-            style="@style/ButtonTextBlue"
+            style="@style/ButtonTextBackgroundBlue"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="@string/other_os_options" />
diff --git a/res/layout/fragment_other_os_options.xml b/res/layout/fragment_other_os_options.xml
index 54c799e..1c958ea 100644
--- a/res/layout/fragment_other_os_options.xml
+++ b/res/layout/fragment_other_os_options.xml
@@ -1,57 +1,50 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent"
-              android:orientation="vertical">
+    android:id="@+id/other_os_options_group"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_weight="1"
+    android:gravity="center_horizontal"
+    android:orientation="vertical">
 
-    <LinearLayout
-        android:id="@+id/other_os_options_group"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        android:gravity="center_horizontal"
-        android:orientation="vertical"
+    <TextView
+        style="@style/TextLight24GreyDark"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         android:paddingLeft="@dimen/main_padding"
-        android:paddingRight="@dimen/main_padding" >
+        android:paddingRight="@dimen/main_padding"
+        android:layout_marginTop="@dimen/main_small_text_margin_top"
+        android:text="@string/important_notice"/>
 
-        <TextView
-            style="@style/TextLight24GreyDark"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/main_small_text_margin_top"
-            android:text="@string/important_notice" />
+    <TextView
+        style="@style/TextRegular14GreyLight"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/main_button_margin_top"
+        android:paddingLeft="@dimen/main_padding"
+        android:paddingRight="@dimen/main_padding"
+        android:gravity="center_horizontal"
+        android:maxLines="10"
+        android:text="@string/important_notice_message"/>
 
-        <TextView
-            style="@style/TextRegular14GreyLight"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/main_button_margin_top"
-            android:gravity="center_horizontal"
-            android:maxLines="10"
-            android:text="@string/important_notice_message" />
+    <Button
+        android:id="@+id/older_fairphone_os_button"
+        style="@style/ButtonTextBackgroundBlue"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/main_small_text_margin_top"
+        android:text="@string/fairphone_os"/>
 
-        <Button
-            android:id="@+id/older_fairphone_os_button"
-            style="@style/ButtonTextBlue"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/main_small_text_margin_top"
-            android:text="@string/fairphone_os" />
+    <Button
+        android:id="@+id/android_os_button"
+        style="@style/ButtonTextBackgroundGreen"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/android_os"/>
 
-        <Button
-            android:id="@+id/android_os_button"
-            style="@style/ButtonTextGreen"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/main_button_margin_top"
-            android:text="@string/android_os" />
-        
-        <Button
-            android:id="@+id/app_store_install_button"
-            style="@style/ButtonTextPink"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/main_button_margin_top"
-            android:text="@string/app_store" />
-    </LinearLayout>
-
+    <Button
+        android:id="@+id/app_store_install_button"
+        style="@style/ButtonTextBackgroundPink"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/app_store"/>
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/fragment_other_os_options_android_list.xml b/res/layout/fragment_other_os_options_android_list.xml
index 95f00bd..4ff987e 100644
--- a/res/layout/fragment_other_os_options_android_list.xml
+++ b/res/layout/fragment_other_os_options_android_list.xml
@@ -4,42 +4,43 @@
               android:gravity="center_horizontal"
               android:orientation="vertical">
 
-    <LinearLayout
+    <FrameLayout
         android:id="@+id/other_os_options_android_latest_version_group"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="@color/green"
-        android:gravity="center_horizontal"
-        android:orientation="vertical"
-        android:padding="@dimen/main_padding" >
-
-        <TextView
-            style="@style/TextRegular16BlueDark"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/latest_version" />
+        android:layout_height="wrap_content">
 
         <Button
             android:id="@+id/other_os_options_android_latest_version_button"
-            style="@style/ButtonTextWhite"
+            style="@style/ButtonBigBackgroundGreen"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
+            android:layout_height="@dimen/main_button_update_height"/>
+
+        <TextView
+            style="@style/TextRegular16White"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="top|center_horizontal"
+            android:gravity="center"
+            android:padding="@dimen/main_padding"
+            android:text="@string/latest_version"/>
 
         <TextView
             android:id="@+id/other_os_options_android_version_installed_indicator_text"
             style="@style/TextBold16White"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:gravity="center_horizontal"
-            android:text="@string/installed" />
-    </LinearLayout>
+            android:padding="@dimen/main_padding_small"
+            android:layout_gravity="bottom|center_horizontal"
+            android:gravity="center"
+            android:text="@string/installed"/>
+    </FrameLayout>
 
     <LinearLayout
         android:id="@+id/older_versions_group"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:gravity="center_horizontal"
-        android:orientation="vertical" >
+        android:orientation="vertical">
 
         <TextView
             style="@style/TextRegular16GreenDark"
@@ -47,21 +48,21 @@
             android:layout_height="wrap_content"
             android:layout_marginBottom="@dimen/main_padding_small"
             android:layout_marginTop="@dimen/main_padding"
-            android:text="@string/older_versions" />
+            android:text="@string/older_versions"/>
 
         <ScrollView
             android:id="@+id/version_list_scroll"
             android:layout_width="match_parent"
             android:layout_height="0dp"
             android:layout_weight="1"
-            android:orientation="vertical" >
+            android:orientation="vertical">
 
             <LinearLayout
                 android:id="@+id/version_list_container"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:clickable="true"
-                android:orientation="vertical" />
+                android:orientation="vertical"/>
         </ScrollView>
     </LinearLayout>
 
diff --git a/res/layout/fragment_other_os_options_android_list_button.xml b/res/layout/fragment_other_os_options_android_list_button.xml
index f505568..0357937 100644
--- a/res/layout/fragment_other_os_options_android_list_button.xml
+++ b/res/layout/fragment_other_os_options_android_list_button.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Button xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/android_button"
-    style="@style/ButtonTextGreen"
+    style="@style/ButtonTextBackgroundGreen"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginTop="@dimen/main_button_margin_top" />
+    android:layout_height="wrap_content" />
diff --git a/res/layout/fragment_other_os_options_fairphone_list.xml b/res/layout/fragment_other_os_options_fairphone_list.xml
index 8b018be..ef265ab 100644
--- a/res/layout/fragment_other_os_options_fairphone_list.xml
+++ b/res/layout/fragment_other_os_options_fairphone_list.xml
@@ -4,42 +4,44 @@
               android:gravity="center_horizontal"
               android:orientation="vertical">
 
-    <LinearLayout
+    <FrameLayout
         android:id="@+id/other_os_options_fairphone_latest_version_group"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="@color/blue"
-        android:gravity="center_horizontal"
-        android:orientation="vertical"
-        android:padding="@dimen/main_padding" >
-
-        <TextView
-            style="@style/TextRegular16BlueDark"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/latest_version" />
+        android:layout_height="wrap_content">
 
         <Button
             android:id="@+id/other_os_options_fairphone_latest_version_button"
-            style="@style/ButtonTextWhite"
+            style="@style/ButtonBigBackgroundBlue"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
+            android:layout_height="@dimen/main_button_update_height"/>
+
+        <TextView
+            style="@style/TextRegular16White"
+            android:layout_width="match_parent"
+            android:padding="@dimen/main_padding"
+            android:layout_gravity="top|center_horizontal"
+            android:gravity="center"
+            android:layout_height="wrap_content"
+            android:text="@string/latest_version"/>
+
 
         <TextView
             android:id="@+id/other_os_options_fairphone_version_installed_indicator_text"
             style="@style/TextBold16White"
-            android:layout_width="wrap_content"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:gravity="center_horizontal"
-            android:text="@string/installed" />
-    </LinearLayout>
+            android:layout_gravity="bottom|center_horizontal"
+            android:gravity="center"
+            android:padding="@dimen/main_padding_small"
+            android:text="@string/installed"/>
+    </FrameLayout>
 
     <LinearLayout
         android:id="@+id/older_versions_group"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:gravity="center_horizontal"
-        android:orientation="vertical" >
+        android:orientation="vertical">
 
         <TextView
             style="@style/TextRegular16BlueDark"
@@ -47,21 +49,21 @@
             android:layout_height="wrap_content"
             android:layout_marginBottom="@dimen/main_padding_small"
             android:layout_marginTop="@dimen/main_padding"
-            android:text="@string/older_versions" />
+            android:text="@string/older_versions"/>
 
         <ScrollView
             android:id="@+id/version_list_scroll"
             android:layout_width="match_parent"
             android:layout_height="0dp"
             android:layout_weight="1"
-            android:orientation="vertical" >
+            android:orientation="vertical">
 
             <LinearLayout
                 android:id="@+id/version_list_container"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:clickable="true"
-                android:orientation="vertical" />
+                android:orientation="vertical"/>
         </ScrollView>
     </LinearLayout>
 
diff --git a/res/layout/fragment_other_os_options_fairphone_list_button.xml b/res/layout/fragment_other_os_options_fairphone_list_button.xml
index c503229..7869a9b 100644
--- a/res/layout/fragment_other_os_options_fairphone_list_button.xml
+++ b/res/layout/fragment_other_os_options_fairphone_list_button.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Button xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/android_button"
-    style="@style/ButtonTextBlue"
+    style="@style/ButtonTextBackgroundBlue"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginTop="@dimen/main_button_margin_top" />
+    android:layout_height="wrap_content" />
diff --git a/res/layout/header.xml b/res/layout/header.xml
index 72e3d82..4cf2ef1 100644
--- a/res/layout/header.xml
+++ b/res/layout/header.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<merge xmlns:android="http://schemas.android.com/apk/res/android"
-    >
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
 
     <FrameLayout
         android:layout_width="match_parent"
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 179246d..c5a6571 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -61,5 +61,6 @@
     <string name="fairphone_os_info">Este sistema operativo consiste numa adaptação do Android para o Fairphone com funcionalidades como o Peace of Mind, Edge Swipe e Your Apps Widget.\n\nRecomendamos a instalação do Android para o Fairphone no seu dispositivo para que tenha a melhor experiência.</string>
     <string name="android_os_info">Este sistema operativo é a mais pura experência Android sem qualquer modificação para o Fairphone.</string>
     <string name="about">Sobre</string>
+    <string name="wifi_discaimer_message_startup">Necessita de acesso à internet para verificar e instalar atualizações. Por favor ligue-se ao Wi-Fi.</string>
 
 </resources>
\ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index ea4d833..7eac390 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -17,12 +17,15 @@
     <color name="blue">#2AA9E0</color>
     <color name="blue_dark">#123C59</color>
     <color name="blue_light">#AADCF2</color>
+    <color name="blue_light_transparent">#dbeaf0</color>
     <color name="green">#6BC1A3</color>
     <color name="green_dark">#17717A</color>
     <color name="green_light">#a0d7c4</color>
+    <color name="green_light_transparent">#d8e8e2</color>
     <color name="pink">#c1454a</color>
     <color name="pink_dark">#823a3d</color>
     <color name="pink_light">#f286b6</color>
+    <color name="pink_light_transparent">#f0dbe4</color>
     <color name="red">#c3474c</color>
 
     <!-- GOOGLE COLORS (DEPENDENCIES) -->
diff --git a/res/values/config.xml b/res/values/config.xml
index 16aa4dd..660e150 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -13,7 +13,7 @@
     <string name="defaultBuildDate" translatable="false">0</string>
     <string name="defaultAndroidVersionNumber" translatable="false">4.2.2</string>
     <string name="defaultImageType" translatable="false">fairphone</string>
-    <string name="defaultBetaStatus" translatable="false">1</string>
+    <string name="defaultBetaStatus" translatable="false">0</string>
     
     <!-- Device Model without spaces separated by semicolon. Ex: FP1;FP1U;FP2 -->
     <string name="knownFPDevices" translatable="false">FP1;FP1U</string>
@@ -21,8 +21,8 @@
     <string name="oneGBDataPartition" translatable="false">/originalPartition</string>
 	<string name="unifiedDataPartition" translatable="false">/unifiedPartition</string>
 	<string name="FP1Model" translatable="false">FP1</string>
-	<string name="recoveryCachePath" translatable="false">cache</string>
-	<string name="recoverySdCardPath" translatable="false">sdcard</string>
+	<string name="recoveryCachePath" translatable="false">/cache/</string>
+	<string name="recoverySdCardPath" translatable="false">/sdcard</string>
 	<string name="updaterFolder" translatable="false">/updater/</string>
 	<integer name="FP1DataPartitionSizeMb">1100</integer>
 	
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 26a9df7..0939e06 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -3,7 +3,7 @@
     <!-- HEADER -->
     <dimen name="header_big_height">94.6dp</dimen>
     <dimen name="header_small_height">56dp</dimen>
-    <dimen name="header_horizontal_padding_right">33dp</dimen>
+    <dimen name="header_horizontal_padding_right">46dp</dimen>
     <dimen name="header_big_text_size">34sp</dimen>
     <dimen name="header_small_text_size">20sp</dimen>
 
@@ -16,6 +16,8 @@
     <dimen name="main_small_text_margin_top">43dp</dimen>
     <dimen name="main_button_margin_top">8dp</dimen>
 
+    <dimen name="main_button_update_height">150dp</dimen>
+
     <!-- ALERT -->
     <dimen name="alert_padding">15dp</dimen>
     <dimen name="alert_title_margin_bottom">5dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9cb14bb..717c5d7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -55,13 +55,14 @@
     <string name="warning">Warning</string>
     <string name="wifi_disabled">Wi-Fi disabled</string>
     <string name="wifi_discaimer_message">The file you\'re trying to download is too large for mobile connections (~200MB). Please enable Wi-Fi connection and try again.</string>
+    <string name="wifi_discaimer_message_startup">You need internet access to check for updates or install software. Please enable Wi-Fi connection.</string>
     <string name="your_are_about_to_install">You are about to install</string>
     <string name="your_current_os_version">Your current operating system version</string>
     <string name="your_os_is_up_to_date">Your operating system is up-to-date</string>
     <string name="fairphone_os_info">This Android for Fairphone operating system has an extra layer of Fairphone flavor with features like the Peace of Mind widget, Edge Swipe, and the Last Used / Most Used Apps widget.\n\nWe recommend installing Android for Fairphone on your device to have the best Fairphone experience.</string>
     <string name="android_os_info">This Stock Android operating system is the pure Android experience without any additional Fairphone modifications.</string>
     <string name="about">about</string>
-    <string name="appStoreReinstall">Reinstall the AppStore</string>
+    <string name="appStoreReinstall">Reinstall the app store</string>
     <string name="app_store">App Stores</string>
     <string name="gapps_store_install">Install Google Apps</string>
     <string name="gapps_store_hide_reminder">or hide reminder</string>
@@ -70,5 +71,10 @@
     <string name="beta_is_enabled">Beta is enabled</string>
     <string name="beta_activation_failed">Failed to activate beta</string>
     <string name="genericContentDescription" translatable="false">genericContentDescription</string>
+    <string name="failed_mkdirs_cache_message">Failed to create directories on /cache.</string>
+    <string name="copy_to_cache_failed_message">Failed to copy file to cache</string>
+    <string name="file_not_found_message">File not found</string>
+    <string name="reboot_failed">Error rebooting to recovery</string>
+    <string name="command_write_to_cache_failed">Error writing commands to cache</string>
 
 </resources>
\ No newline at end of file
diff --git a/res/values/styles.xml b/res/values/styles.xml
index df7473c..2b5b0e6 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -52,7 +52,7 @@
 
     <style name="CheckBoxBlue" parent="@style/CheckBox">
         <item name="android:button">@drawable/checkbox_button_blue</item>
-        <item name="android:textColor">@color/pink</item>
+        <item name="android:textColor">@color/blue</item>
     </style>
 
     <style name="CheckBoxPink" parent="@style/CheckBox">
@@ -93,6 +93,11 @@
         <item name="android:textColor">@color/white</item>
     </style>
 
+    <style name="ButtonBlueDark" parent="@style/Button">
+        <item name="android:background">@drawable/button_blue_dark_background</item>
+        <item name="android:textColor">@color/white</item>
+    </style>
+
     <style name="ButtonGreen" parent="@style/Button">
         <item name="android:background">@drawable/button_green_background</item>
         <item name="android:textColor">@color/white</item>
@@ -103,6 +108,11 @@
         <item name="android:textColor">@color/white</item>
     </style>
 
+    <style name="ButtonGreenDark" parent="@style/Button">
+        <item name="android:background">@drawable/button_green_dark_background</item>
+        <item name="android:textColor">@color/white</item>
+    </style>
+
     <style name="ButtonGreenDarkGrey" parent="@style/Button">
         <item name="android:background">@drawable/button_green_dark_grey_background</item>
         <item name="android:textColor">@color/white</item>
@@ -148,6 +158,16 @@
         <item name="android:textColor">@color/button_green_dark_text</item>
     </style>
 
+    <style name="ButtonSmallBlueDarkWhite" parent="@style/ButtonSmall">
+        <item name="android:background">@drawable/button_blue_dark_white_background</item>
+        <item name="android:textColor">@color/button_blue_dark_text</item>
+    </style>
+
+    <style name="ButtonSmallGreenDarkWhite" parent="@style/ButtonSmall">
+        <item name="android:background">@drawable/button_green_dark_white_background</item>
+        <item name="android:textColor">@color/button_green_dark_text</item>
+    </style>
+
     <style name="ButtonSmallBlueDarkGrey" parent="@style/ButtonSmall">
         <item name="android:background">@drawable/button_blue_dark_grey_background</item>
         <item name="android:textColor">@color/white</item>
@@ -182,6 +202,40 @@
         <item name="android:textColor">@color/button_text_white_text</item>
     </style>
 
+    <style name="ButtonTextBackground" parent="@android:style/Widget.Button">
+        <item name="android:background">@drawable/button_blue_text_background</item>
+        <item name="android:textColor">@color/blue</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:gravity">center</item>
+        <item name="android:padding">15dp</item>
+    </style>
+
+    <style name="ButtonBigBackgroundBlue" parent="@style/ButtonTextBackground">
+        <item name="android:textColor">@color/white</item>
+        <item name="android:background">@drawable/button_big_blue_text_background</item>
+    </style>
+
+    <style name="ButtonBigBackgroundGreen" parent="@style/ButtonTextBackground">
+        <item name="android:textColor">@color/white</item>
+        <item name="android:background">@drawable/button_big_green_text_background</item>
+    </style>
+
+    <style name="ButtonTextBackgroundBlue" parent="@style/ButtonTextBackground">
+        <item name="android:textColor">@color/blue</item>
+        <item name="android:background">@drawable/button_blue_text_background</item>
+    </style>
+
+    <style name="ButtonTextBackgroundGreen" parent="@style/ButtonTextBackground">
+        <item name="android:textColor">@color/green</item>
+        <item name="android:background">@drawable/button_green_text_background</item>
+    </style>
+
+    <style name="ButtonTextBackgroundPink" parent="@style/ButtonTextBackground">
+        <item name="android:textColor">@color/pink</item>
+        <item name="android:background">@drawable/button_pink_text_background</item>
+    </style>
+
+
     <!-- TEXT REGULAR -->
     <style name="TextRegular">
         <item name="android:fontFamily">sans-serif</item>
@@ -198,11 +252,11 @@
     <style name="TextRegular18" parent="@style/TextRegular">
         <item name="android:textSize">18sp</item>
     </style>
-    
+
     <style name="TextRegular20" parent="@style/TextRegular">
         <item name="android:textSize">20sp</item>
     </style>
-    
+
     <style name="TextRegular14White" parent="@style/TextRegular14">
         <item name="android:textColor">@color/white</item>
     </style>
@@ -254,11 +308,11 @@
     <style name="TextRegular18White" parent="@style/TextRegular18">
         <item name="android:textColor">@color/white</item>
     </style>
-    
+
     <style name="TextRegular20White" parent="@style/TextRegular20">
         <item name="android:textColor">@color/white</item>
     </style>
-    
+
     <!-- TEXT LIGHT -->
     <style name="TextLight">
         <item name="android:fontFamily">sans-serif-light</item>
@@ -267,7 +321,7 @@
     <style name="TextLight20" parent="@style/TextLight">
         <item name="android:textSize">20sp</item>
     </style>
-    
+
     <style name="TextLight24" parent="@style/TextLight">
         <item name="android:textSize">24sp</item>
     </style>
@@ -284,7 +338,7 @@
         <item name="android:textColor">@color/white</item>
     </style>
 
-    
+
     <style name="TextLight24GreyDark" parent="@style/TextLight24">
         <item name="android:textColor">@color/grey_dark</item>
     </style>
@@ -292,7 +346,7 @@
     <style name="TextLight20White" parent="@style/TextLight20">
         <item name="android:textColor">@color/white</item>
     </style>
-    
+
     <!-- TEXT BOLD -->
     <style name="TextBold">
         <item name="android:fontFamily">sans-serif</item>
@@ -326,7 +380,7 @@
     <style name="TextBold20" parent="@style/TextBold">
         <item name="android:textSize">20sp</item>
     </style>
-    
+
     <style name="TextBold16White" parent="@style/TextBold16">
         <item name="android:textColor">@color/white</item>
     </style>
@@ -334,7 +388,7 @@
     <style name="TextBold20White" parent="@style/TextBold20">
         <item name="android:textColor">@color/white</item>
     </style>
-    
+
     <style name="TextBold16BlueDark" parent="@style/TextBold16">
         <item name="android:textColor">@color/blue_dark</item>
     </style>
diff --git a/src/com/fairphone/updater/BetaEnabler.java b/src/com/fairphone/updater/BetaEnabler.java
index 82b9353..5d64c24 100644
--- a/src/com/fairphone/updater/BetaEnabler.java
+++ b/src/com/fairphone/updater/BetaEnabler.java
@@ -42,7 +42,7 @@
         }
     }
     
-    private boolean isBetaEnabled(){
+    private static boolean isBetaEnabled(){
         return Utils.getprop(FAIRPHONE_BETA_PROPERTY, BETA_DISABLED).equals(BETA_ENABLED);
     }
     
diff --git a/src/com/fairphone/updater/BootBroadcastReceiver.java b/src/com/fairphone/updater/BootBroadcastReceiver.java
index 130731c..c777652 100644
--- a/src/com/fairphone/updater/BootBroadcastReceiver.java
+++ b/src/com/fairphone/updater/BootBroadcastReceiver.java
@@ -23,7 +23,9 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 
+import com.fairphone.updater.fragments.MainFragment;
 import com.fairphone.updater.tools.Utils;
 
 public class BootBroadcastReceiver extends BroadcastReceiver
@@ -38,6 +40,17 @@
         //
         // if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction()))
         // {
+        SharedPreferences sharedPreferences = context.getSharedPreferences(FairphoneUpdater.FAIRPHONE_UPDATER_PREFERENCES, Context.MODE_PRIVATE);
+
+        SharedPreferences.Editor editor = sharedPreferences.edit();
+        editor.putString(FairphoneUpdater.PREFERENCE_CURRENT_UPDATER_STATE, FairphoneUpdater.UpdaterState.NORMAL.name());
+        editor.putInt(FairphoneUpdater.PREFERENCE_SELECTED_VERSION_NUMBER, 0);
+        editor.putString(FairphoneUpdater.PREFERENCE_SELECTED_VERSION_TYPE, "");
+        editor.putInt(FairphoneUpdater.PREFERENCE_SELECTED_STORE_NUMBER, -1);
+        editor.remove(UpdaterService.LAST_CONFIG_DOWNLOAD_IN_MS);
+        editor.remove(MainFragment.SHARED_PREFERENCES_ENABLE_GAPPS);
+        editor.commit();
+
         AlarmManager service = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
         Intent i = new Intent(context, UpdaterService.class);
         PendingIntent pending = PendingIntent.getService(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);
diff --git a/src/com/fairphone/updater/FairphoneUpdater.java b/src/com/fairphone/updater/FairphoneUpdater.java
index 511a103..e67037e 100644
--- a/src/com/fairphone/updater/FairphoneUpdater.java
+++ b/src/com/fairphone/updater/FairphoneUpdater.java
@@ -1,9 +1,16 @@
 package com.fairphone.updater;
 
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.Editor;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.Uri;
 import android.os.Bundle;
+import android.provider.Settings;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.FragmentManager;
@@ -35,6 +42,8 @@
 
     private static final String TAG = FairphoneUpdater.class.getSimpleName();
 
+	private static final String CRASHLYTICS_OPT_IN = "crashlytics_opt_in"; // IMPORTANT: keep synced with Settings.Global.CRASHLYTICS_OPT_IN
+
     public static final String FAIRPHONE_UPDATER_NEW_VERSION_RECEIVED = "FairphoneUpdater.NEW.VERSION.RECEIVED";
 
     private static final String PREFERENCE_FIRST_TIME_ANDROID = "FirstTimeAndroid";
@@ -43,15 +52,15 @@
 
     private static final String PREFERENCE_FIRST_TIME_APP_STORE = "FirstTimeAppStore";
 
-    private static final String PREFERENCE_CURRENT_UPDATER_STATE = "CurrentUpdaterState";
+    public static final String PREFERENCE_CURRENT_UPDATER_STATE = "CurrentUpdaterState";
 
     private static final String PREFERENCE_DOWNLOAD_ID = "LatestUpdateDownloadId";
 
     public static final String FAIRPHONE_UPDATER_PREFERENCES = "FairphoneUpdaterPreferences";
 
-    private static final String PREFERENCE_SELECTED_VERSION_NUMBER = "SelectedVersionNumber";
+    public static final String PREFERENCE_SELECTED_VERSION_NUMBER = "SelectedVersionNumber";
 
-    private static final String PREFERENCE_SELECTED_VERSION_TYPE = "SelectedVersionImageType";
+    public static final String PREFERENCE_SELECTED_VERSION_TYPE = "SelectedVersionImageType";
 
     public static final String FAIRPHONE_UPDATER_CONFIG_DOWNLOAD_FAILED = "FairphoneUpdater.Config.File.Download.FAILED";
 
@@ -62,11 +71,13 @@
     public static final String PREFERENCE_OTA_DOWNLOAD_URL = "OtaDownloadUrl";
     
     private static final String TAG_FIRST_FRAGMENT = "FIRST_FRAGMENT";
+    private String mZipPath;
+	private AlertDialog wifiOffDialog;
 
 
-    public static enum UpdaterState
+	public static enum UpdaterState
     {
-        NORMAL, DOWNLOAD, PREINSTALL
+        NORMAL, DOWNLOAD, PREINSTALL, ZIP_INSTALL
     }
 
     private Version mDeviceVersion;
@@ -80,9 +91,6 @@
 
     public static boolean DEV_MODE_ENABLED;
     public static boolean BETA_MODE_ENABLED;
-    private int mIsDevModeCounter;
-    
-    public String mOtaDownloadUrl;
 
     private TextView headerMainFairphoneText;
     private TextView headerMainAndroidText;
@@ -109,16 +117,19 @@
     protected void onCreate(Bundle savedInstanceState)
     {
         super.onCreate(savedInstanceState);
+        mSharedPreferences = getSharedPreferences(FAIRPHONE_UPDATER_PREFERENCES, MODE_PRIVATE);
+
         onNewIntent(getIntent());
         
         setContentView(R.layout.activity_updater);
 
-        Crashlytics.start(this);
+	    if (Settings.Global.getInt(getContentResolver(), CRASHLYTICS_OPT_IN, 0) == 1)
+        {
+            Log.d(TAG, "Crash reports active.");
+            Crashlytics.start(this);
+        }
 
         DEV_MODE_ENABLED = false;
-        mIsDevModeCounter = 10;
-
-        mSharedPreferences = getSharedPreferences(FAIRPHONE_UPDATER_PREFERENCES, MODE_PRIVATE);
 
         // update first times
         mIsFirstTimeAndroid = mSharedPreferences.getBoolean(PREFERENCE_FIRST_TIME_ANDROID, true);
@@ -126,8 +137,13 @@
         mIsFirstTimeFairphone = mSharedPreferences.getBoolean(PREFERENCE_FIRST_TIME_FAIRPHONE, true);
 
         mIsFirstTimeAppStore = false;//mSharedPreferences.getBoolean(PREFERENCE_FIRST_TIME_APP_STORE, true);
-        
-        mOtaDownloadUrl = mSharedPreferences.getString(PREFERENCE_OTA_DOWNLOAD_URL, getResources().getString(R.string.downloadUrl));
+
+        String otaDownloadUrl = getResources().getString(R.string.downloadUrl);
+
+        // reset download URL
+        Editor editor = mSharedPreferences.edit();
+        editor.putString(PREFERENCE_OTA_DOWNLOAD_URL, otaDownloadUrl);
+        editor.commit();
 
         // get system data
         mDeviceVersion = VersionParserHelper.getDeviceVersion(this);
@@ -162,6 +178,8 @@
 
         setupFragments(savedInstanceState);
     }
+
+
     
     void setupBetaStatus()
     {
@@ -186,7 +204,7 @@
 
     void getSelectedStoreFromSharedPreferences()
     {
-        int storeNumber = mSharedPreferences.getInt(PREFERENCE_SELECTED_STORE_NUMBER, 0);
+        int storeNumber = mSharedPreferences.getInt(PREFERENCE_SELECTED_STORE_NUMBER, -1);
         mSelectedStore = UpdaterData.getInstance().getStore(storeNumber);
     }
 
@@ -219,6 +237,18 @@
         editor.commit();
     }
 
+    public void changeOTADownloadURL(String newUrl){
+        String otaDownloadUrl = TextUtils.isEmpty(newUrl) ? getResources().getString(R.string.downloadUrl) : newUrl;
+
+        Editor editor = mSharedPreferences.edit();
+        editor.putString(PREFERENCE_OTA_DOWNLOAD_URL, otaDownloadUrl);
+        editor.commit();
+    }
+
+    public void forceConfigDownload(){
+        Utils.downloadConfigFile(this, true);
+    }
+
     private void initHeaderViews()
     {
         headerMainFairphoneText = (TextView) findViewById(R.id.header_main_fairphone_text);
@@ -280,9 +310,10 @@
         Fragment fragment = getTopFragment();
         if (fragment != null && fragment instanceof DownloadAndRestartFragment && getCurrentUpdaterState() != UpdaterState.NORMAL)
         {
-            ((DownloadAndRestartFragment) fragment).abortUpdateProcess();
+            ((DownloadAndRestartFragment) fragment).abortUpdateProcess("");
         }
-        else if (fragment != null && TAG_FIRST_FRAGMENT.equals(fragment.getTag()))
+
+        if (fragment != null && TAG_FIRST_FRAGMENT.equals(fragment.getTag()))
         {
             clearSelectedItems();
             finish();
@@ -506,6 +537,9 @@
                     firstFragment = new MainFragment();
                 }
                 break;
+            case ZIP_INSTALL:
+                firstFragment = new DownloadAndRestartFragment(true);
+                break;
             default:
                 firstFragment = new MainFragment();
                 break;
@@ -536,7 +570,7 @@
                 // and add the transaction to the back stack so the user can
                 // navigate
                 // back
-                transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
+				transaction.setCustomAnimations(R.anim.enter, R.anim.exit, R.anim.pop_enter, R.anim.pop_exit);
                 transaction.replace(R.id.fragment_holder, newFragment);
                 transaction.addToBackStack(null);
 
@@ -565,27 +599,6 @@
         return topFragment;
     }
 
-    public void onEnableDevMode()
-    {
-        if (!DEV_MODE_ENABLED)
-        {
-            mIsDevModeCounter--;
-
-            Log.d(TAG, "Developer mode in " + mIsDevModeCounter + " Clicks...");
-
-            if (mIsDevModeCounter <= 0)
-            {
-                DEV_MODE_ENABLED = true;
-
-                Toast.makeText(getApplicationContext(), getResources().getString(R.string.dev_mode_message), Toast.LENGTH_LONG).show();
-
-                Log.d(TAG, "Developer mode enabled for this session");
-
-                Utils.downloadConfigFile(this, true);
-            }
-        }
-    }
-
     public boolean isUpdateAvailable()
     {
         boolean update = false;
@@ -618,12 +631,15 @@
         String itemName = "";
         if (version != null)
         {
-            itemName = version.getImageTypeDescription(getResources()) + " " + version.getName() + " " + version.getBuildNumber();
+            if(mCurrentState != UpdaterState.ZIP_INSTALL) {
+                itemName = version.getImageTypeDescription(getResources()) + " ";
+            }
+            itemName += version.getName() + " " + version.getBuildNumber();
         }
         return itemName;
     }
 
-    public String getStoreName(Store store)
+    public static String getStoreName(Store store)
     {
         String itemName = "";
         if (store != null)
@@ -663,7 +679,7 @@
         return mSelectedStore;
     }
 
-    public HeaderType getHeaderTypeFromImageType(String imageType)
+    public static HeaderType getHeaderTypeFromImageType(String imageType)
     {
         HeaderType type = HeaderType.MAIN_FAIRPHONE;
         if (Version.IMAGE_TYPE_AOSP.equalsIgnoreCase(imageType))
@@ -685,13 +701,13 @@
         clearSelectedVersion(versionNumber, versionImageType);
 
         mSelectedVersion = UpdaterData.getInstance().getVersion(versionImageType, versionNumber);
-        clearSelectedStore(0);
+        clearSelectedStore(-1);
     }
 
     public void clearSelectedItems()
     {
         clearSelectedVersion(0, "");
-        clearSelectedStore(0);
+        clearSelectedStore(-1);
     }
 
     private void clearSelectedVersion(int versionNumber, String versionImageType)
@@ -706,7 +722,7 @@
 
     public void setSelectedStore(Store selectedStore)
     {
-        int storeNumber = selectedStore != null ? selectedStore.getNumber() : 0;
+        int storeNumber = selectedStore != null ? selectedStore.getNumber() : -1;
 
         clearSelectedStore(storeNumber);
 
@@ -752,35 +768,70 @@
     {
         super.onNewIntent(intent);
         setIntent(intent);
-        
         mLaunchGapps = false;
 
-        if (checkStartGappsInstall(intent))
-        {
-            mLaunchGapps = true;
-        }
-        else
-        {
-            Intent parentIntent = getParentActivityIntent();
-            if (checkStartGappsInstall(parentIntent))
+        if(intent != null) {
+            String action = intent.getAction();
+            switch (action)
             {
-                mLaunchGapps = true;
+                case GappsInstallerHelper.EXTRA_START_GAPPS_INSTALL:
+                    if (checkStartGappsInstall(intent)) {
+                        mLaunchGapps = true;
+                    } else {
+                        Intent parentIntent = getParentActivityIntent();
+                        if (checkStartGappsInstall(parentIntent)) {
+                            mLaunchGapps = true;
+                        }
+                    }
+                    break;
+                case Intent.ACTION_VIEW:
+                    Uri data = intent.getData();
+                    if(data != null)
+                    {
+
+                        String zipPath = Utils.getPath(this, data);
+                        if(!TextUtils.isEmpty(zipPath)) {
+                            mZipPath = zipPath;
+                            updateStatePreference(UpdaterState.ZIP_INSTALL);
+                        }
+                        else
+                        {
+                            mZipPath = "";
+                        }
+                    }
+                    break;
+                default:
+                    //no action
+                    break;
             }
         }
     }
 
-    private boolean checkStartGappsInstall(Intent intent)
+    private static boolean checkStartGappsInstall(Intent intent)
     {
         return intent != null && GappsInstallerHelper.EXTRA_START_GAPPS_INSTALL.equals(intent.getAction());
     }
 
-    @Override
+	@Override
+	protected void onPause() {
+		super.onPause();
+		if(wifiOffDialog != null) {
+			wifiOffDialog.cancel();
+			wifiOffDialog = null;
+		}
+	}
+
+	@Override
     protected void onResume()
     {
         super.onResume();
 
         // check current state
         mCurrentState = getCurrentUpdaterState();
+        if(mCurrentState == UpdaterState.ZIP_INSTALL)
+        {
+            updateStatePreference(TextUtils.isEmpty(mZipPath) ? UpdaterState.NORMAL : UpdaterState.ZIP_INSTALL);
+        }
 
         startService();
 
@@ -800,11 +851,36 @@
         getSelectedStoreFromSharedPreferences();
         
         changeFragment(getFragmentFromState());
+
+	    // Show wifi disable dialog if in a blank state and no wifi is available
+	    if (    wifiOffDialog == null &&
+			    mCurrentState == UpdaterState.NORMAL &&
+			    !Utils.isWiFiEnabled(this) &&
+			    UpdaterData.getInstance().isAppStoreListEmpty() &&
+			    !UpdaterData.getInstance().isAOSPVersionListNotEmpty() &&
+			    !UpdaterData.getInstance().isFairphoneVersionListNotEmpty() )
+	    {
+		    Resources resources = getResources();
+
+		    AlertDialog.Builder wifiOffDialogBuilder = new AlertDialog.Builder(this);
+
+		    wifiOffDialogBuilder.setTitle(resources.getString(R.string.wifi_disabled));
+
+		    // Setting Dialog Message
+		    wifiOffDialogBuilder.setMessage(resources.getString(R.string.wifi_discaimer_message_startup));
+		    wifiOffDialogBuilder.setPositiveButton(resources.getString(android.R.string.ok), new DialogInterface.OnClickListener() {
+			    public void onClick(DialogInterface dialog, int id) {
+				    // do nothing, since the state is still the same
+			    }
+		    });
+		    wifiOffDialog = wifiOffDialogBuilder.create();
+		    wifiOffDialog.show();
+	    }
     }
 
     public Fragment startGappsInstall()
     {
-        getSelectedStoreFromSharedPreferences();
+        mSelectedStore = Utils.getGappsStore();
 
         VersionDetailFragment fragment = new VersionDetailFragment(false);
 
@@ -872,4 +948,14 @@
     {
         savePreference(UpdaterService.PREFERENCE_LAST_CONFIG_DOWNLOAD_ID, 0L);
     }
+
+    public String getZipFilePath()
+    {
+        return mZipPath;
+    }
+
+    public String getPreferenceOtaDownloadUrl()
+    {
+        return mSharedPreferences.getString(FairphoneUpdater.PREFERENCE_OTA_DOWNLOAD_URL, getResources().getString(R.string.downloadUrl));
+    }
 }
diff --git a/src/com/fairphone/updater/UpdaterService.java b/src/com/fairphone/updater/UpdaterService.java
index 09c63de..89158e3 100644
--- a/src/com/fairphone/updater/UpdaterService.java
+++ b/src/com/fairphone/updater/UpdaterService.java
@@ -51,9 +51,11 @@
 
 import java.util.concurrent.TimeoutException;
 
+import com.fairphone.updater.data.UpdaterData;
 import com.fairphone.updater.data.Version;
 import com.fairphone.updater.data.VersionParserHelper;
 import com.fairphone.updater.gappsinstaller.GappsInstallerHelper;
+import com.fairphone.updater.tools.PrivilegeChecker;
 import com.fairphone.updater.tools.RSAUtils;
 import com.fairphone.updater.tools.Utils;
 import com.stericson.RootTools.execution.CommandCapture;
@@ -82,7 +84,7 @@
 
 	private SharedPreferences mSharedPreferences;
 
-    private final static long DOWNLOAD_GRACE_PERIOD_IN_MS = 4 /* hour */ * Utils.MINUTES_IN_HOUR /* minute */ * Utils.SECONDS_IN_MINUTE /* second */ * 1000 /* millisecond */;
+    private final static long DOWNLOAD_GRACE_PERIOD_IN_MS = 24 /* hour */ * Utils.MINUTES_IN_HOUR /* minute */ * Utils.SECONDS_IN_MINUTE /* second */ * 1000 /* millisecond */;
 
 	@Override
     public int onStartCommand(Intent intent, int flags, int startId)
@@ -124,35 +126,33 @@
 
         getApplicationContext().registerReceiver(mBCastConfigFileDownload, new IntentFilter(ACTION_FAIRPHONE_UPDATER_CONFIG_FILE_DOWNLOAD));
 
-        runInstallationDisclaimer();
+        runInstallationDisclaimer(getApplicationContext());
 
         return Service.START_STICKY;
     }
 
-    private void runInstallationDisclaimer()
+    private static void runInstallationDisclaimer(Context context)
     {
-
-        if (mSharedPreferences.getBoolean(PREFERENCE_REINSTALL_GAPPS, true))
+	    SharedPreferences sharedPreferences = context.getApplicationContext().getSharedPreferences(FairphoneUpdater.FAIRPHONE_UPDATER_PREFERENCES, MODE_PRIVATE);
+	    if (sharedPreferences.getBoolean(PREFERENCE_REINSTALL_GAPPS, true) && !UpdaterData.getInstance().isAppStoreListEmpty())
         {
             if(!GappsInstallerHelper.areGappsInstalled()){
-                showReinstallAlert();
+                showReinstallAlert(context);
             }
 
-            Editor editor = mSharedPreferences.edit();
+            Editor editor = sharedPreferences.edit();
             editor.putBoolean(PREFERENCE_REINSTALL_GAPPS, false);
 
             editor.apply();
         }
     }
 
-    void showReinstallAlert()
+    private static void showReinstallAlert(Context context)
     {
-
         if ( FairphoneUpdater.BETA_MODE_ENABLED )
         {
             return;
         }
-        Context context = getApplicationContext();
 
 	    NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
         
@@ -164,7 +164,7 @@
         PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
 
 	    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context).setSmallIcon(R.drawable.updater_tray_icon)
-			    .setContentTitle(getResources().getString(R.string.app_name))
+			    .setContentTitle(context.getResources().getString(R.string.app_name))
 			    .setContentText(context.getResources().getString(R.string.appStoreReinstall))
 			    .setAutoCancel(true)
 			    .setDefaults(Notification.DEFAULT_SOUND)
@@ -201,18 +201,47 @@
 //    }
 // --Commented out by Inspection STOP (06/02/2015 12:27)
 
-    void clearDataLogs()
+    private static void clearDataLogs()
     {
-        try
-        {
-            Log.d(TAG, "Clearing dump log data...");
-            Shell.runCommand(new CommandCapture(0, "rm /data/log_other_mode/*_log"));
-        } catch (IOException | TimeoutException e)
-        {
-            e.printStackTrace();
+        if (PrivilegeChecker.isPrivilegedApp()) {
+	        clearDataLogsPrivileged();
+        } else {
+	        clearDataLogsUnprivileged();
         }
     }
 
+	private static void clearDataLogsPrivileged()
+	{
+		try
+		{
+			Log.d(TAG, "Clearing dump log data...");
+			Process p = Runtime.getRuntime().exec("rm /data/log_other_mode/*_log");
+            try
+            {
+                p.waitFor();
+            } catch (InterruptedException e)
+            {
+                e.printStackTrace();
+            }
+		} catch (IOException e)
+		{
+			Log.d(TAG, "Clearing dump log data failed: " + e.getLocalizedMessage());
+		}
+	}
+
+	private static void clearDataLogsUnprivileged()
+	{
+		try
+		{
+			Log.d(TAG, "Clearing dump log data...");
+			Shell.runCommand(new CommandCapture(0, "rm /data/log_other_mode/*_log"));
+		} catch (IOException | TimeoutException e)
+		{
+			Log.d(TAG, "Clearing dump log data failed: " + e.getLocalizedMessage());
+		}
+	}
+
+
     @Override
     public IBinder onBind(Intent intent)
     {
@@ -297,7 +326,7 @@
         return downloadLink;
     }
 
-    private void addModelAndOS(Context context, StringBuilder sb)
+    private static void addModelAndOS(Context context, StringBuilder sb)
     {
         // attach the model and the os
         sb.append("?");
@@ -449,15 +478,8 @@
         }
     }
 
-    private void removeBroadcastReceiver()
-    {
-        getApplicationContext().unregisterReceiver(mDownloadBroadCastReceiver);
-        mDownloadBroadCastReceiver = null;
-    }
-
     private static void checkVersionValidation(Context context)
     {
-
         Version latestVersion = VersionParserHelper.getLatestVersion(context.getApplicationContext());
         Version currentVersion = VersionParserHelper.getDeviceVersion(context.getApplicationContext());
 
@@ -467,10 +489,12 @@
             {
                 setNotification(context);
             }
-            // to update the activity
-            Intent updateIntent = new Intent(FairphoneUpdater.FAIRPHONE_UPDATER_NEW_VERSION_RECEIVED);
-            context.sendBroadcast(updateIntent);
         }
+	    runInstallationDisclaimer(context);
+
+        // to update the activity
+        Intent updateIntent = new Intent(FairphoneUpdater.FAIRPHONE_UPDATER_NEW_VERSION_RECEIVED);
+        context.sendBroadcast(updateIntent);
     }
 
     public static boolean readUpdaterData(Context context)
@@ -630,7 +654,6 @@
             {
                 Log.d(TAG, "Configuration download failed. Clearing grace period.");
                 mSharedPreferences.edit().remove(LAST_CONFIG_DOWNLOAD_IN_MS).apply();
-                removeBroadcastReceiver();
             }
         }
     }
diff --git a/src/com/fairphone/updater/data/DownloadableItem.java b/src/com/fairphone/updater/data/DownloadableItem.java
index a2c0e86..1513fca 100644
--- a/src/com/fairphone/updater/data/DownloadableItem.java
+++ b/src/com/fairphone/updater/data/DownloadableItem.java
@@ -46,17 +46,30 @@
 
     DownloadableItem()
     {
-        setNumber(0);
-        setName("");
-        setDownloadLink("");
-        setMd5Sum("");
-        setBuildNumber("");
-        setReleaseDate("");
-        setThumbnailLink("");
+        mNumber = 0;
+        mName = "";
+        mOTADownloadLink = "";
+        mOTAMd5Sum = "";
+        mBuildNumber = "";
+        mReleaseDate = "";
+        mThumbnailImageLink = "";
 
         mReleaseNotesMap = new HashMap<>();
     }
 
+    DownloadableItem(DownloadableItem other)
+    {
+        mNumber = other.mNumber;
+        mName = other.mName;
+        mOTADownloadLink = other.mOTADownloadLink;
+        mOTAMd5Sum = other.mOTAMd5Sum;
+        mBuildNumber = other.mBuildNumber;
+        mReleaseDate = other.mReleaseDate;
+        mThumbnailImageLink = other.mThumbnailImageLink;
+
+        mReleaseNotesMap = other.mReleaseNotesMap;
+    }
+
     public int getNumber()
     {
         return mNumber;
@@ -69,6 +82,7 @@
             this.mNumber = Integer.valueOf(number);
         } catch (NumberFormatException e)
         {
+            Log.w(TAG, "Error decoding version number. Defaulting to 0: " + e.getLocalizedMessage());
             this.mNumber = 0;
         }
     }
@@ -115,7 +129,7 @@
 
         if (item != null)
         {
-            result = this.getNumber() > item.getNumber();
+            result = this.mNumber > item.mNumber;
         }
         else
         {
@@ -175,6 +189,13 @@
         return TextUtils.isEmpty(releaseNotes) ? "" : releaseNotes;
     }
 
+// --Commented out by Inspection START (09/02/2015 19:48):
+//    Map<String, String> getReleaseNotes()
+//    {
+//        return mReleaseNotesMap;
+//    }
+// --Commented out by Inspection STOP (09/02/2015 19:48)
+
 // --Commented out by Inspection START (06/02/2015 12:36):
 //    void resetReleaseNotes()
 //    {
diff --git a/src/com/fairphone/updater/data/Store.java b/src/com/fairphone/updater/data/Store.java
index f055544..0b5813e 100644
--- a/src/com/fairphone/updater/data/Store.java
+++ b/src/com/fairphone/updater/data/Store.java
@@ -8,7 +8,13 @@
     public Store()
     {
         super();
-        setShowDisclaimer(false);
+        mShowDisclaimer = false;
+    }
+
+    public Store(Store other)
+    {
+        super(other);
+        mShowDisclaimer = other.showDisclaimer();
     }
     
     public boolean showDisclaimer()
@@ -16,9 +22,9 @@
         return mShowDisclaimer;
     }
 
-    public void setShowDisclaimer(boolean showDisclaimer)
+    public void setShowDisclaimer()
     {
-        this.mShowDisclaimer = showDisclaimer;
+        this.mShowDisclaimer = true;
     }
 
     @Override
diff --git a/src/com/fairphone/updater/data/UpdaterData.java b/src/com/fairphone/updater/data/UpdaterData.java
index 1a06167..3daacb2 100644
--- a/src/com/fairphone/updater/data/UpdaterData.java
+++ b/src/com/fairphone/updater/data/UpdaterData.java
@@ -1,5 +1,7 @@
 package com.fairphone.updater.data;
 
+import android.util.Log;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -10,6 +12,8 @@
 public class UpdaterData
 {
 
+    private static final String TAG = UpdaterData.class.getSimpleName();
+
     private static UpdaterData mInstance;
 
     private int mLatestAOSPVersionNumber;
@@ -54,7 +58,7 @@
         mLatestAOSPVersionNumber = getLatestVersionFromTag(latestVersion);
     }
 
-    private int getLatestVersionFromTag(String latestVersion)
+    private static int getLatestVersionFromTag(String latestVersion)
     {
         int latestVersionNumber;
         try
@@ -62,6 +66,7 @@
             latestVersionNumber = Integer.valueOf(latestVersion);
         } catch (NumberFormatException e)
         {
+            Log.w(TAG, "Error decoding latest version number. Defaulting to 0: " + e.getLocalizedMessage());
             latestVersionNumber = 0;
         }
         return latestVersionNumber;
@@ -102,7 +107,7 @@
         return version;
     }
 
-    List<Version> mapToOrderedVersionList(Collection<Version> a)
+    private static List<Version> mapToOrderedVersionList(Collection<Version> a)
     {
         List<Version> retval = new ArrayList<>();
         for (Version version : a)
@@ -113,7 +118,7 @@
         return retval;
     }
     
-    List<Store> mapToOrderedStoreList(Collection<Store> a)
+    private static List<Store> mapToOrderedStoreList(Collection<Store> a)
     {
         List<Store> retval = new ArrayList<>();
         for (Store store : a)
diff --git a/src/com/fairphone/updater/data/Version.java b/src/com/fairphone/updater/data/Version.java
index 72f895c..29ba80a 100644
--- a/src/com/fairphone/updater/data/Version.java
+++ b/src/com/fairphone/updater/data/Version.java
@@ -43,7 +43,9 @@
 
     private boolean mErasePartitionsWarning;
 
-    private List<Integer> mDependencies;
+    private final List<Integer> mDependencies;
+
+    public static final int ZIP_INSTALL_VERSION = 999;
 
     public Version()
     {
@@ -51,13 +53,23 @@
         mDependencies = new ArrayList<>();
         mAndroidVersion = "";
         mImageType = IMAGE_TYPE_FAIRPHONE;
-        setEraseAllPartitionWarning(false);
+        mErasePartitionsWarning = false;
         mBetaStatus = "";
     }
 
-    public void setEraseAllPartitionWarning(boolean erasePartitionsWarning)
+    public Version(Version other)
     {
-        mErasePartitionsWarning = erasePartitionsWarning;
+        super(other);
+        mDependencies = other.mDependencies;
+        mAndroidVersion = other.mAndroidVersion;
+        mImageType = other.mImageType;
+        mErasePartitionsWarning = other.hasEraseAllPartitionWarning();
+        mBetaStatus = other.mBetaStatus;
+    }
+
+    public void setEraseAllPartitionWarning()
+    {
+        mErasePartitionsWarning = true;
     }
 
     public boolean hasEraseAllPartitionWarning()
@@ -135,11 +147,11 @@
         int retVal;
         if (another != null)
         {
-            if (this.getNumber() < another.getNumber() && this.getImageType().equalsIgnoreCase(another.getImageType()))
+            if (this.getNumber() < another.getNumber() && this.mImageType.equalsIgnoreCase(another.mImageType))
             {
                 retVal = 1;
             }
-            else if (this.getNumber() == another.getNumber() && this.getImageType().equalsIgnoreCase(another.getImageType()))
+            else if (this.getNumber() == another.getNumber() && this.mImageType.equalsIgnoreCase(another.mImageType))
             {
                 retVal = 0;
             }
@@ -171,16 +183,16 @@
                     mDependencies.add(Integer.valueOf(dependency));
                 } catch (NumberFormatException e)
                 {
-                    Log.e(TAG, "Invalid dependency");
+                    Log.e(TAG, "Invalid dependency: " + e.getLocalizedMessage());
                 }
             }
         }
     }
 
-// --Commented out by Inspection START (06/02/2015 12:36):
-//    public ArrayList<Integer> getVersionDependencies()
+// --Commented out by Inspection START (09/02/2015 19:47):
+//    List<Integer> getVersionDependencies()
 //    {
 //        return mDependencies;
 //    }
-// --Commented out by Inspection STOP (06/02/2015 12:36)
+// --Commented out by Inspection STOP (09/02/2015 19:47)
 }
diff --git a/src/com/fairphone/updater/data/VersionParserHelper.java b/src/com/fairphone/updater/data/VersionParserHelper.java
index 73fdf1c..1392271 100644
--- a/src/com/fairphone/updater/data/VersionParserHelper.java
+++ b/src/com/fairphone/updater/data/VersionParserHelper.java
@@ -21,6 +21,7 @@
 import android.os.Build;
 import android.os.Environment;
 import android.util.Log;
+import android.util.Pair;
 
 import com.fairphone.updater.R;
 import com.fairphone.updater.tools.Utils;
@@ -64,7 +65,9 @@
             version.setNumber(Integer.valueOf(getSystemData(context, CURRENT_VERSION_NUMBER, knownFPDevice)));
         } catch (NumberFormatException e)
         {
-            version.setNumber(context.getResources().getInteger(R.integer.defaultVersionNumber));
+            int defaultVersionNumber = context.getResources().getInteger(R.integer.defaultVersionNumber);
+            Log.w(TAG, "Error parsing current version number. Defaulting to " + defaultVersionNumber + ": " + e.getLocalizedMessage());
+            version.setNumber(defaultVersionNumber);
         }
         version.setName(getSystemData(context, CURRENT_VERSION_NAME, knownFPDevice));
         version.setBuildNumber(getSystemData(context, CURRENT_VERSION_BUILD_NUMBER, knownFPDevice));
@@ -161,6 +164,7 @@
 
         Version version = null;
         Store store = null;
+        Pair<Version, Store> result;
 
         UpdaterData update = UpdaterData.getInstance();
         update.resetUpdaterData();
@@ -188,60 +192,16 @@
                 case XmlPullParser.START_TAG:
                     tagName = xpp.getName();
                     currentTag = getCurrentXmlElement(tagName, currentTag);
-                    switch (currentTag)
-                    {
-                        case AOSP:
-                            if (tagName.equalsIgnoreCase(XML_TAGS.AOSP.name()))
-                            {
-                                update.setLatestAOSPVersionNumber(xpp.getAttributeValue(0));
-                            }
-                            version = readVersion(version, xpp, tagName);
-                            version.setImageType(Version.IMAGE_TYPE_AOSP);
-                            break;
-                        case FAIRPHONE:
-                            if (tagName.equalsIgnoreCase(XML_TAGS.FAIRPHONE.name()))
-                            {
-                                update.setLatestFairphoneVersionNumber(xpp.getAttributeValue(0));
-                            }
-                            version = readVersion(version, xpp, tagName);
-                            version.setImageType(Version.IMAGE_TYPE_FAIRPHONE);
-                            break;
-                        case STORES:
-                            store = readStore(store, xpp, tagName);
-                            break;
-                        default:
-                            break;
-                    }
+                    result = readStartTag(xpp, currentTag, tagName, update, version, store);
+                    version = result.first;
+                    store = result.second;
                     break;
                 case XmlPullParser.END_TAG:
                     tagName = xpp.getName();
                     currentTag = getCurrentXmlElement(tagName, currentTag);
-                    switch (currentTag)
-                    {
-                        case AOSP:
-                            if (tagName.equalsIgnoreCase(XML_TAGS.VERSION.name()))
-                            {
-                                update.addAOSPVersion(version);
-                                version = null;
-                            }
-                            break;
-                        case FAIRPHONE:
-                            if (tagName.equalsIgnoreCase(XML_TAGS.VERSION.name()))
-                            {
-                                update.addFairphoneVersion(version);
-                                version = null;
-                            }
-                            break;
-                        case STORES:
-                            if (tagName.equalsIgnoreCase(XML_TAGS.STORE.name()))
-                            {
-                                update.addAppStore(store);
-                                store = null;
-                            }
-                            break;
-                        default:
-                            break;
-                    }
+                    result = readEndTag(currentTag, tagName, update, version, store);
+                    version = result.first;
+                    store = result.second;
                     break;
                 default:
                     break;
@@ -255,55 +215,171 @@
         return update.getLatestVersion(getSystemData(context, CURRENT_VERSION_IMAGE_TYPE, true));
     }
 
-    private static Version readVersion(Version version, XmlPullParser xpp, String tagName) throws XmlPullParserException, IOException
+    private static Pair<Version, Store> readStartTag(XmlPullParser xpp, XML_LEVEL_TAGS currentTag, String tagName, UpdaterData update, Version version, Store store) throws XmlPullParserException, IOException
     {
-        if (version == null)
+        Version updateVersion = null;
+        Store updateStore = null;
+        switch (currentTag)
         {
-            version = new Version();
+            case AOSP:
+                if (tagName.equalsIgnoreCase(XML_TAGS.AOSP.name()))
+                {
+                    update.setLatestAOSPVersionNumber(xpp.getAttributeValue(0));
+                }
+                updateVersion = readVersion(version, xpp, tagName);
+                updateVersion.setImageType(Version.IMAGE_TYPE_AOSP);
+                break;
+            case FAIRPHONE:
+                if (tagName.equalsIgnoreCase(XML_TAGS.FAIRPHONE.name()))
+                {
+                    update.setLatestFairphoneVersionNumber(xpp.getAttributeValue(0));
+                }
+                updateVersion = readVersion(version, xpp, tagName);
+                updateVersion.setImageType(Version.IMAGE_TYPE_FAIRPHONE);
+                break;
+            case STORES:
+                updateStore = readStore(store, xpp, tagName);
+                break;
+            case NONE:
+            default:
+                if(version != null)
+                {
+                    updateVersion = new Version(version);
+                }
+
+                if(store != null)
+                {
+                    updateStore = new Store(store);
+                }
+                break;
         }
 
-        readDownloadableItem(version, xpp, tagName);
+        return new Pair<>(updateVersion, updateStore);
+    }
+
+    private static Pair<Version, Store> readEndTag(XML_LEVEL_TAGS currentTag, String tagName, UpdaterData update, Version version, Store store) {
+        Version updateVersion = null;
+        Store updateStore = null;
+        switch (currentTag)
+        {
+            case AOSP:
+                if (tagName.equalsIgnoreCase(XML_TAGS.VERSION.name()))
+                {
+                    update.addAOSPVersion(version);
+                    updateVersion = null;
+                }
+                else
+                {
+                    if(version != null)
+                    {
+                        updateVersion = new Version(version);
+                    }
+                }
+                break;
+            case FAIRPHONE:
+                if (tagName.equalsIgnoreCase(XML_TAGS.VERSION.name()))
+                {
+                    update.addFairphoneVersion(version);
+                    updateVersion = null;
+                }
+                else
+                {
+                    if(version != null)
+                    {
+                        updateVersion = new Version(version);
+                    }
+                }
+                break;
+            case STORES:
+                if (tagName.equalsIgnoreCase(XML_TAGS.STORE.name()))
+                {
+                    update.addAppStore(store);
+                    updateStore = null;
+                }
+                else
+                {
+                    if(store != null)
+                    {
+                        updateStore = new Store(store);
+                    }
+                }
+                break;
+            case NONE:
+            default:
+                if(version != null)
+                {
+                    updateVersion = new Version(version);
+                }
+
+                if(store != null)
+                {
+                    updateStore = new Store(store);
+                }
+                break;
+        }
+
+        return new Pair<>(updateVersion, updateStore);
+    }
+
+    private static Version readVersion(Version version, XmlPullParser xpp, String tagName) throws XmlPullParserException, IOException
+    {
+        Version updateVersion;
+        if (version == null)
+        {
+            updateVersion = new Version();
+        }
+        else
+        {
+            updateVersion = new Version(version);
+        }
+
+        readDownloadableItem(updateVersion, xpp, tagName);
 
         if (tagName.equalsIgnoreCase(XML_TAGS.VERSION.name()))
         {
-            version.setNumber(xpp.getAttributeValue(0));
+            updateVersion.setNumber(xpp.getAttributeValue(0));
         }
         else if (tagName.equalsIgnoreCase(XML_TAGS.ANDROID_VERSION.name()))
         {
-            version.setAndroidVersion(xpp.nextText());
+            updateVersion.setAndroidVersion(xpp.nextText());
         }
         else if (tagName.equalsIgnoreCase(XML_TAGS.DEPENDENCIES.name()))
         {
-            version.setVersionDependencies(xpp.nextText());
+            updateVersion.setVersionDependencies(xpp.nextText());
         }
         else if (tagName.equalsIgnoreCase(XML_TAGS.ERASE_DATA_WARNING.name()))
         {
-            version.setEraseAllPartitionWarning(true);
+            updateVersion.setEraseAllPartitionWarning();
         }
 
-        return version;
+        return updateVersion;
     }
 
     private static Store readStore(Store store, XmlPullParser xpp, String tagName) throws XmlPullParserException, IOException
     {
 
+        Store updateStore;
         if (store == null)
         {
-            store = new Store();
+            updateStore = new Store();
+        }
+        else
+        {
+            updateStore = new Store(store);
         }
 
-        readDownloadableItem(store, xpp, tagName);
+        readDownloadableItem(updateStore, xpp, tagName);
 
         if (tagName.equalsIgnoreCase(XML_TAGS.STORE.name()))
         {
-            store.setNumber(xpp.getAttributeValue(0));
+            updateStore.setNumber(xpp.getAttributeValue(0));
         }
         else if (tagName.equalsIgnoreCase(XML_TAGS.SHOW_DISCLAIMER.name()))
         {
-            store.setShowDisclaimer(true);
+            updateStore.setShowDisclaimer();
         }
 
-        return store;
+        return updateStore;
     }
 
     private static void readDownloadableItem(DownloadableItem item, XmlPullParser xpp, String tagName) throws XmlPullParserException, IOException
diff --git a/src/com/fairphone/updater/fragments/ConfirmationPopupDialog.java b/src/com/fairphone/updater/fragments/ConfirmationPopupDialog.java
index d58289d..6fba623 100644
--- a/src/com/fairphone/updater/fragments/ConfirmationPopupDialog.java
+++ b/src/com/fairphone/updater/fragments/ConfirmationPopupDialog.java
@@ -65,6 +65,7 @@
                 break;
             case UPDATE_FAIRPHONE:
             case FAIRPHONE:
+            case APP_STORE:
             default:
                 view = inflater.inflate(R.layout.fragment_download_fairphone_confirmation_popup, container);
                 break;
diff --git a/src/com/fairphone/updater/fragments/DownloadAndRestartFragment.java b/src/com/fairphone/updater/fragments/DownloadAndRestartFragment.java
index 0dd7bcf..285a1ce 100644
--- a/src/com/fairphone/updater/fragments/DownloadAndRestartFragment.java
+++ b/src/com/fairphone/updater/fragments/DownloadAndRestartFragment.java
@@ -21,6 +21,7 @@
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Environment;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -38,10 +39,11 @@
 import com.fairphone.updater.UpdaterService;
 import com.fairphone.updater.data.DownloadableItem;
 import com.fairphone.updater.data.Store;
+import com.fairphone.updater.data.UpdaterData;
 import com.fairphone.updater.data.Version;
 import com.fairphone.updater.data.VersionParserHelper;
+import com.fairphone.updater.tools.PrivilegeChecker;
 import com.fairphone.updater.tools.Utils;
-import com.stericson.RootTools.RootTools;
 import com.stericson.RootTools.exceptions.RootDeniedException;
 import com.stericson.RootTools.execution.CommandCapture;
 import com.stericson.RootTools.execution.Shell;
@@ -54,6 +56,7 @@
 
     private static final String TAG = DownloadAndRestartFragment.class.getSimpleName();
     private static final int GET_LATEST_DOWNLOAD_ID_RETRIES = 12;
+    private boolean mIsZipInstall;
 
     private TextView mDownloadVersionName;
     private LinearLayout mVersionDownloadingGroup;
@@ -73,6 +76,7 @@
     private BroadcastReceiver mNetworkStateReceiver;
 
     private long mLatestUpdateDownloadId;
+    private TextView mDownloadCompleteLabel;
 
     public DownloadAndRestartFragment(boolean isVersion)
     {
@@ -85,10 +89,11 @@
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
     {
         // Inflate the layout for this fragment
+        mIsZipInstall = UpdaterState.ZIP_INSTALL == mainActivity.getCurrentUpdaterState();
         View view;
         if (mIsVersion)
         {
-            mSelectedVersion = mainActivity.getSelectedVersion();
+            mSelectedVersion = getSelectedVersion();
             view = inflateViewByImageType(inflater, container);
             mSelectedStore = null;
         }
@@ -104,6 +109,30 @@
         return view;
     }
 
+    private Version getSelectedVersion() {
+        Version version;
+        if(mIsZipInstall) {
+            Resources resources = mainActivity.getResources();
+
+            //Get the zip file name
+            String[] zipPath = getDownloadPath(null).split("/");
+            String zipName = "";
+            if (zipPath != null && zipPath.length > 0) {
+                zipName = zipPath[zipPath.length - 1];
+            }
+
+            version = new Version();
+            version.setName(resources.getString(R.string.install) + " " + zipName);
+            version.setDownloadLink(mainActivity.getZipFilePath());
+            version.setNumber(Version.ZIP_INSTALL_VERSION);
+        }
+        else
+        {
+            version = mainActivity.getSelectedVersion();
+        }
+        return version;
+    }
+
     private View inflateViewByImageType(LayoutInflater inflater, ViewGroup container)
     {
         View view = inflater.inflate(R.layout.fragment_download_fairphone, container, false);
@@ -121,7 +150,7 @@
         return view;
     }
 
-    private View inflateStoreView(LayoutInflater inflater, ViewGroup container)
+    private static View inflateStoreView(LayoutInflater inflater, ViewGroup container)
     {
 
 	    return inflater.inflate(R.layout.fragment_download_app_store, container, false);
@@ -133,17 +162,17 @@
         switch (state)
         {
             case DOWNLOAD:
+	            mVersionInstallGroup.setVisibility(View.GONE);
+	            mVersionDownloadingGroup.setVisibility(View.VISIBLE);
                 setupDownloadState();
-
-                mVersionInstallGroup.setVisibility(View.GONE);
-                mVersionDownloadingGroup.setVisibility(View.VISIBLE);
                 break;
 
             case PREINSTALL:
-                setupPreInstallState();
+            case ZIP_INSTALL:
+	            mVersionDownloadingGroup.setVisibility(View.GONE);
+	            mVersionInstallGroup.setVisibility(View.VISIBLE);
 
-                mVersionDownloadingGroup.setVisibility(View.GONE);
-                mVersionInstallGroup.setVisibility(View.VISIBLE);
+	            setupPreInstallState();
 
                 mRestartButton.setOnClickListener(new OnClickListener()
                 {
@@ -163,7 +192,7 @@
                 });
 
                 break;
-
+            case NORMAL:
             default:
                 Log.w(TAG, "Wrong State: " + state + "\nOnly DOWNLOAD and PREINSTALL are supported");
                 mainActivity.onBackPressed();
@@ -177,7 +206,11 @@
             @Override
             public void onClick(View v)
             {
-                abortUpdateProcess();
+                abortUpdateProcess("");
+                if(Utils.hasUnifiedPartition(mainActivity.getResources()))
+                {
+                    Utils.clearCache();
+                }
                 mainActivity.onBackPressed();
             }
         });
@@ -279,8 +312,7 @@
                             if ((bytes_total + Utils.BUFFER_SIZE_10_MBYTES) > Utils.getAvailablePartitionSizeInBytes(Environment.getExternalStorageDirectory()))
                             {
                                 downloading = false;
-                                Toast.makeText(mainActivity, getResources().getString(R.string.no_space_available_sd_card_message), Toast.LENGTH_LONG).show();
-                                abortUpdateProcess();
+                                abortUpdateProcess(getResources().getString(R.string.no_space_available_sd_card_message));
                             }
                             else
                             {
@@ -335,6 +367,7 @@
     private void setupLayout(View view)
     {
         mDownloadVersionName = (TextView) view.findViewById(R.id.download_version_name_text);
+        mDownloadCompleteLabel = (TextView) view.findViewById(R.id.download_complete_label);
 
         // download in progress group
         mVersionDownloadingGroup = (LinearLayout) view.findViewById(R.id.version_downloading_group);
@@ -355,7 +388,7 @@
         setupInstallationReceivers();
         registerDownloadBroadCastReceiver();
 
-        registerNetworkStatusBoradcastReceiver();
+        registerNetworkStatusBroadcastReceiver();
 
         updateHeader();
 
@@ -363,34 +396,42 @@
         if (item != null)
         {
             mDownloadVersionName.setText(mainActivity.getItemName(item, mIsVersion));
+            if(mIsZipInstall)
+            {
+                mDownloadCompleteLabel.setVisibility(View.GONE);
+            }
+            else
+            {
+                mDownloadCompleteLabel.setVisibility(View.VISIBLE);
+            }
         }
 
         toggleDownloadProgressAndRestart();
     }
 
-    private void registerNetworkStatusBoradcastReceiver()
+    private void registerNetworkStatusBroadcastReceiver()
     {
+		unregisterNetworkStatusBroadcastReceiver();
         // Setup monitoring for future connectivity status changes
-        if (mNetworkStateReceiver != null)
+		mNetworkStateReceiver = new BroadcastReceiver()
         {
-            mNetworkStateReceiver = new BroadcastReceiver()
+            @Override
+            public void onReceive(Context context, Intent intent)
             {
-                @Override
-                public void onReceive(Context context, Intent intent)
+                if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false))
                 {
-                    if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false))
-                    {
-                        abortUpdateProcess();
-                    }
+                    Log.w(TAG, "Aborted due to connection failure.");
+                    abortUpdateProcess("");
+	                mainActivity.onBackPressed();
                 }
-            };
-        }
+            }
+        };
 
         IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
         mainActivity.registerReceiver(mNetworkStateReceiver, filter);
     }
 
-    private void unregisterNetworkStatusBoradcastReceiver()
+    private void unregisterNetworkStatusBroadcastReceiver()
     {
         if (mNetworkStateReceiver != null)
         {
@@ -407,7 +448,7 @@
 
         unregisterBroadCastReceiver();
 
-        unregisterNetworkStatusBoradcastReceiver();
+        unregisterNetworkStatusBroadcastReceiver();
     }
 
     private void setupInstallationReceivers()
@@ -472,16 +513,17 @@
                     case DownloadManager.STATUS_FAILED:
                         Resources resources = getResources();
                         DownloadableItem item = mIsVersion ? mSelectedVersion : mSelectedStore;
+                        String error;
                         if (item != null)
                         {
                             String downloadTitle = Utils.getDownloadTitleFromDownloadableItem(getResources(), item, mIsVersion);
-                            Toast.makeText(mainActivity, resources.getString(R.string.error_downloading) + " " + downloadTitle, Toast.LENGTH_LONG).show();
+                            error = resources.getString(R.string.error_downloading) + " " + downloadTitle;
                         }
                         else
                         {
-                            Toast.makeText(mainActivity, resources.getString(R.string.error_downloading), Toast.LENGTH_LONG).show();
+                            error = resources.getString(R.string.error_downloading);
                         }
-                        abortUpdateProcess();
+                        abortUpdateProcess(error);
                         break;
                     default:
                         break;
@@ -489,7 +531,7 @@
             }
             else
             {
-                abortUpdateProcess();
+                abortUpdateProcess("");
             }
 
             if (cursor != null)
@@ -513,7 +555,7 @@
 
             if (file.exists())
             {
-                if (Utils.checkMD5(item.getMd5Sum(), file))
+                if (Utils.checkMD5(item.getMd5Sum(), file) || mIsZipInstall)
                 {
                     copyUpdateToCache(file);
                 }
@@ -544,10 +586,10 @@
 
 	        final boolean notDeleted = !updateDir.delete();
 	        if(notDeleted) {
-		        Log.d(TAG, "Unable to delete "+updateDir.getAbsolutePath());
+		        Log.d(TAG, "Unable to delete " + updateDir.getAbsolutePath());
 	        }
 
-	        abortUpdateProcess();
+	        abortUpdateProcess("");
 
             return;
         }
@@ -560,7 +602,7 @@
             // invalid download Id
             if (mLatestUpdateDownloadId == 0)
             {
-                abortUpdateProcess();
+                abortUpdateProcess("");
                 return;
             }
         }
@@ -573,57 +615,48 @@
         final Resources resources = getResources();
         DownloadableItem item = mIsVersion ? mSelectedVersion : mSelectedStore;
 
-        File f = new File("/" + resources.getString(R.string.recoveryCachePath) + "/" + Utils.getFilenameFromDownloadableItem(item, mIsVersion));
-        if (!f.exists())
+        String otaPackagePath = Utils.getOtaPackagePath(resources, item, mIsVersion, mIsZipInstall);
+
+        boolean fileNotExists = !Utils.fileExists(otaPackagePath);
+
+        if (fileNotExists)
         {
-            abortUpdateProcess();
+            abortUpdateProcess(resources.getString(R.string.file_not_found_message) + ": " + otaPackagePath);
         }
-        else if (item != null && RootTools.isAccessGiven())
+        else if (item != null)
         {
             // set the command for the recovery
             try
             {
+                Utils.writeCacheCommand(mainActivity, otaPackagePath);
 
-                Shell.runRootCommand(new CommandCapture(0, "rm -f /cache/recovery/command"));
+                new Thread(new Runnable() {
+                    @SuppressLint("CommitPrefEdits")
+                    @Override
+                    public void run() {
+                        Editor editor = mSharedPreferences.edit();
+                        editor.remove(UpdaterService.PREFERENCE_REINSTALL_GAPPS);
+                        editor.commit();
 
-                Shell.runRootCommand(new CommandCapture(0, "rm -f /cache/recovery/extendedcommand"));
+                        if (Utils.hasUnifiedPartition(resources))
+                        {
+                            removeLastUpdateDownload();
+                        }
 
-                Shell.runRootCommand(new CommandCapture(0, "echo '--wipe_cache' >> /cache/recovery/command"));
+                        // remove the gapps stuff
+//                        String model = Utils.getModelAndOS(getActivity());
+//                        if( model.contains("FP1") ) {
+                            try {
 
-                if (Utils.hasUnifiedPartition(resources))
-                {
-                    Shell.runRootCommand(new CommandCapture(0, "echo '--update_package=/" + resources.getString(R.string.recoveryCachePath) + "/"
-                            + Utils.getFilenameFromDownloadableItem(item, mIsVersion) + "' >> /cache/recovery/command"));
-                }
-                else
-                {
-                    Shell.runRootCommand(new CommandCapture(0, "echo '--update_package=/" + resources.getString(R.string.recoverySdCardPath)
-                            + resources.getString(R.string.updaterFolder) + Utils.getFilenameFromDownloadableItem(item, mIsVersion) + "' >> /cache/recovery/command"));
-                }
-            } catch (IOException | NotFoundException | TimeoutException | RootDeniedException e)
-            {
-                e.printStackTrace();
-            }
+                                Utils.clearGappsData();
+                            } catch (RootDeniedException | InterruptedException | IOException e) {
+                                e.printStackTrace();
+                            }
+//                        }
 
-            new Thread(new Runnable() {
-                @SuppressLint("CommitPrefEdits")
-                @Override
-                public void run() {
-                    Editor editor = mSharedPreferences.edit();
-                    editor.remove(UpdaterService.PREFERENCE_REINSTALL_GAPPS);
-                    editor.commit();
+                        // remove the update files from data
+                        removeUpdateFilesFromData();
 
-                    if (Utils.hasUnifiedPartition(resources))
-                    {
-                        removeLastUpdateDownload();
-                    }
-
-                    // remove the update files from data
-                    removeUpdateFilesFromData();
-
-                    // reboot the device into recovery
-                    try
-                    {
                         mainActivity.updateStatePreference(UpdaterState.NORMAL);
                         mainActivity.clearSelectedItems();
                         clearConfigFile();
@@ -631,17 +664,26 @@
                         editor.remove(UpdaterService.LAST_CONFIG_DOWNLOAD_IN_MS);
                         editor.remove(MainFragment.SHARED_PREFERENCES_ENABLE_GAPPS);
                         editor.commit();
-                        Shell.runRootCommand(new CommandCapture(0, "reboot recovery"));
-                    } catch (IOException | TimeoutException | RootDeniedException e)
-                    {
-                        e.printStackTrace();
+
+                        // reboot the device into recovery
+                        if(!Utils.rebootToRecovery(mainActivity)) {
+                            String error = resources.getString(R.string.reboot_failed);
+                            Log.w(TAG, error);
+                            abortUpdateProcess(error);
+                        }
                     }
-                }
-            }).start();
+                }).start();
+            } catch (IOException | NotFoundException | TimeoutException | RootDeniedException e)
+            {
+                String error = resources.getString(R.string.command_write_to_cache_failed);
+                Log.e(TAG, error + ": " + e.getLocalizedMessage());
+                abortUpdateProcess(error);
+            }
         }
         else
         {
-            abortUpdateProcess();
+            Log.e(TAG, "Null item");
+            abortUpdateProcess("");
         }
     }
 
@@ -668,18 +710,15 @@
             }
             else
             {
-                Toast.makeText(mainActivity, getResources().getString(R.string.no_space_available_cache_message), Toast.LENGTH_LONG).show();
-                abortUpdateProcess();
+                abortUpdateProcess(getResources().getString(R.string.no_space_available_cache_message));
             }
         }
         else
         {
             if (Utils.hasUnifiedPartition(getResources()))
             {
-                Log.d(TAG, "No space on cache. Defaulting to Sdcard");
-                Toast.makeText(mainActivity, getResources().getString(R.string.no_space_available_cache_message), Toast.LENGTH_LONG).show();
-
-                abortUpdateProcess();
+                Log.w(TAG, "No space on cache. Defaulting to Sdcard");
+                abortUpdateProcess(getResources().getString(R.string.no_space_available_cache_message));
             }
         }
     }
@@ -687,8 +726,41 @@
     // ************************************************************************************
     // Update Removal
     // ************************************************************************************
-    private void removeUpdateFilesFromData()
-    {
+    private void removeUpdateFilesFromData() {
+		if (PrivilegeChecker.isPrivilegedApp()) {
+			removeUpdateFilesFromDataPrivileged();
+		} else {
+			removeUpdateFilesFromDataUnprivileged();
+		}
+    }
+
+	private void removeUpdateFilesFromDataPrivileged() {
+		Resources resource = getResources();
+
+		try {
+			Process p = Runtime.getRuntime().exec(resource.getString(R.string.removePlayStoreCommand));
+            p.waitFor();
+            p = Runtime.getRuntime().exec(resource.getString(R.string.removeGooglePlusCommand));
+            p.waitFor();
+            p = Runtime.getRuntime().exec(resource.getString(R.string.removeSoundSearchCommand));
+            p.waitFor();
+            p = Runtime.getRuntime().exec(resource.getString(R.string.removeGmailCommand));
+            p.waitFor();
+            p = Runtime.getRuntime().exec(resource.getString(R.string.removePlayServicesCommand));
+            p.waitFor();
+            p = Runtime.getRuntime().exec(resource.getString(R.string.removeQuicksearchCommand));
+            p.waitFor();
+            p = Runtime.getRuntime().exec(resource.getString(R.string.removeTalkbackCommand));
+            p.waitFor();
+            p = Runtime.getRuntime().exec(resource.getString(R.string.removeText2SpeechCommand));
+            p.waitFor();
+		} catch (IOException | InterruptedException e) {
+			Log.d(TAG, "Failed to remove files from data:" +e);
+		}
+	}
+
+	private void removeUpdateFilesFromDataUnprivileged()
+	{
         try
         {
             Shell.runRootCommand(new CommandCapture(0, getResources().getString(R.string.removePlayStoreCommand), getResources().getString(
@@ -698,7 +770,7 @@
                     R.string.removeText2SpeechCommand)));
         } catch (IOException | TimeoutException | RootDeniedException e)
         {
-            e.printStackTrace();
+	        Log.d(TAG, "Failed to remove files from data:" +e.getLocalizedMessage());
         }
     }
 
@@ -731,21 +803,27 @@
             String originalFilePath = params[0];
             String destinyFilePath = params[1];
 
-            if (RootTools.isAccessGiven())
+            Utils.clearCache();
+
+            File otaOriginalFile = new File(originalFilePath);
+            File otaDestinyFile = new File(destinyFilePath);
+
+            if (otaOriginalFile.exists())
             {
-                Utils.clearCache();
-
-                File otaFilePath = new File(originalFilePath);
-                File otaFileCache = new File(destinyFilePath);
-
-                if (!otaFileCache.exists())
-                {
-                    RootTools.copyFile(otaFilePath.getPath(), otaFileCache.getPath(), false, false);
-                }
+	            try {
+		            Utils.copy(otaOriginalFile, otaDestinyFile);
+	            } catch (IOException e) {
+                    String error = mainActivity.getResources().getString(R.string.copy_to_cache_failed_message);
+		            Log.e(TAG, error + ": " + originalFilePath + ". " + e.getLocalizedMessage());
+		            abortUpdateProcess(error);
+	            }
             }
             else
             {
-                abortUpdateProcess();
+                Resources resources = mainActivity.getResources();
+                String error = resources.getString(R.string.copy_to_cache_failed_message) + ". " + resources.getString(R.string.file_not_found_message) + ": " + originalFilePath;
+                Log.e(TAG, error);
+                abortUpdateProcess(error);
             }
 
             return 1;
@@ -780,12 +858,30 @@
 
     private String getDownloadPath(DownloadableItem item)
     {
-        Resources resources = mainActivity.getResources();
-        return Environment.getExternalStorageDirectory() + resources.getString(R.string.updaterFolder) + Utils.getFilenameFromDownloadableItem(item, mIsVersion);
+        String path;
+        if(mIsZipInstall)
+        {
+           path = mainActivity.getZipFilePath();
+        }
+        else
+        {
+            Resources resources = mainActivity.getResources();
+            path = Environment.getExternalStorageDirectory() + resources.getString(R.string.updaterFolder) + Utils.getFilenameFromDownloadableItem(item, mIsVersion);
+        }
+        return path;
     }
 
-    public void abortUpdateProcess()
+    public void abortUpdateProcess(final String reason)
     {
+        if(!TextUtils.isEmpty(reason)) {
+            mainActivity.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    Toast.makeText(mainActivity, reason, Toast.LENGTH_LONG).show();
+                }
+            });
+        }
+
         removeLastUpdateDownload();
 
         mainActivity.clearSelectedItems();
diff --git a/src/com/fairphone/updater/fragments/InfoPopupDialog.java b/src/com/fairphone/updater/fragments/InfoPopupDialog.java
index b23d9bb..e00161b 100644
--- a/src/com/fairphone/updater/fragments/InfoPopupDialog.java
+++ b/src/com/fairphone/updater/fragments/InfoPopupDialog.java
@@ -44,6 +44,7 @@
                 break;
             case UPDATE_FAIRPHONE:
             case FAIRPHONE:
+            case APP_STORE:
             default:
                 view = inflater.inflate(R.layout.fragment_info_fairphone_popup, container);
                 break;
diff --git a/src/com/fairphone/updater/fragments/MainFragment.java b/src/com/fairphone/updater/fragments/MainFragment.java
index efec4a1..9ea0b7c 100644
--- a/src/com/fairphone/updater/fragments/MainFragment.java
+++ b/src/com/fairphone/updater/fragments/MainFragment.java
@@ -7,11 +7,13 @@
 import android.content.SharedPreferences.Editor;
 import android.os.Bundle;
 import android.support.v4.app.Fragment;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.Button;
+import android.widget.EditText;
 import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
@@ -26,10 +28,12 @@
 import com.fairphone.updater.data.Version;
 import com.fairphone.updater.fragments.VersionDetailFragment.DetailLayoutType;
 import com.fairphone.updater.gappsinstaller.GappsInstallerHelper;
+import com.fairphone.updater.tools.Utils;
 
 public class MainFragment extends BaseFragment
 {
 
+	private static final String TAG = MainFragment.class.getSimpleName();
     public static final String SHARED_PREFERENCES_ENABLE_GAPPS = "SHARED_PREFERENCES_ENABLE_GAPPS_POPUP";
 
     private LinearLayout mVersionUpToDateGroup;
@@ -46,6 +50,10 @@
 	private RelativeLayout mGappsIcon;
     private Button mGappsButton;
     private Button mGappsDismissButton;
+    private LinearLayout mDevModeUrlContainer;
+    private EditText mDevModeUrlEditText;
+    private Button mDevModeUrlButton;
+	private int mIsDevModeCounter;
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
@@ -54,8 +62,10 @@
         View view = inflater.inflate(R.layout.fragment_main, container, false);
 
         setupLayout(inflater, view);
+	    mDevModeUrlContainer.setVisibility(FairphoneUpdater.DEV_MODE_ENABLED ? View.VISIBLE : View.GONE);
+	    mIsDevModeCounter = FairphoneUpdater.DEV_MODE_ENABLED ? 0 : 10;
 
-        return view;
+	    return view;
     }
 
     private void updateHeader()
@@ -92,20 +102,34 @@
         mGappsButton = (Button) view.findViewById(R.id.install_gapps_button);
         mGappsDismissButton = (Button) view.findViewById(R.id.install_gapps_dismiss_button);
         mGappsIcon = (RelativeLayout) view.findViewById(R.id.gapps_reminder_group);
+
+        // Dev mode
+        mDevModeUrlEditText = (EditText)view.findViewById(R.id.dev_mode_url_edit_text);
+        mDevModeUrlButton = (Button)view.findViewById(R.id.dev_mode_url_ok_button);
+        mDevModeUrlContainer = (LinearLayout)view.findViewById(R.id.dev_mode_url_container);
+
+        mDevModeUrlButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                String url = mDevModeUrlEditText.getText().toString();
+
+                // set the URL for the shared prefs
+                mainActivity.changeOTADownloadURL(url);
+
+                // download new config
+                mainActivity.forceConfigDownload();
+            }
+        });
+
     }
 
     private boolean getGappsInstalationButtonState()
     {
         boolean showGappsGroup = mSharedPreferences.getBoolean(SHARED_PREFERENCES_ENABLE_GAPPS, true);
         boolean gappsNotInstalled = !GappsInstallerHelper.areGappsInstalled();
-        boolean hasStoreInfo = getSelectedStoreFromSharedPreferences() != null;
+        boolean hasStoreInfo = Utils.getGappsStore() != null;
         return  showGappsGroup && gappsNotInstalled && hasStoreInfo;
     }
-    
-    Store getSelectedStoreFromSharedPreferences()
-    {
-        return UpdaterData.getInstance().getStore(mSharedPreferences.getInt(FairphoneUpdater.PREFERENCE_SELECTED_STORE_NUMBER, 0));
-    }
 
     private void disableGappsInstalationButton()
     {
@@ -242,7 +266,7 @@
         });
     }
 
-    private VersionDetailFragment.DetailLayoutType getDetailLayoutFromDeviceVersion(Version latestVersion)
+    private static VersionDetailFragment.DetailLayoutType getDetailLayoutFromDeviceVersion(Version latestVersion)
     {
         VersionDetailFragment.DetailLayoutType type = DetailLayoutType.UPDATE_FAIRPHONE;
         if (Version.IMAGE_TYPE_FAIRPHONE.equalsIgnoreCase(latestVersion.getImageType()))
@@ -268,7 +292,7 @@
                 @Override
                 public void onClick(View v)
                 {
-                    mainActivity.onEnableDevMode();
+                    onEnableDevMode();
                 }
             });
         }
@@ -281,7 +305,7 @@
                 @Override
                 public void onClick(View v)
                 {
-                    mainActivity.onEnableDevMode();
+                    onEnableDevMode();
                 }
             });
         }
@@ -323,7 +347,7 @@
                 if (FairphoneUpdater.FAIRPHONE_UPDATER_NEW_VERSION_RECEIVED.equals(action))
                 {
                     mainActivity.updateLatestVersionFromConfig();
-                    if (mainActivity.getCurrentUpdaterState() == UpdaterState.NORMAL)
+                    if (mainActivity.getCurrentUpdaterState() != UpdaterState.DOWNLOAD && mainActivity.getCurrentUpdaterState() != UpdaterState.PREINSTALL)
                     {
                         toogleUpdateAvailableGroup();
                         enableGappsGroup(getGappsInstalationButtonState());
@@ -352,4 +376,26 @@
         mainActivity.unregisterReceiver(newVersionbroadcastReceiver);
     }
 
+	public void onEnableDevMode()
+	{
+		if (!FairphoneUpdater.DEV_MODE_ENABLED)
+		{
+			mIsDevModeCounter--;
+
+			Log.d(TAG, "Developer mode in " + mIsDevModeCounter + " Clicks...");
+
+			if (mIsDevModeCounter <= 0)
+			{
+				FairphoneUpdater.DEV_MODE_ENABLED = true;
+
+				Toast.makeText(mainActivity.getApplicationContext(), getResources().getString(R.string.dev_mode_message), Toast.LENGTH_LONG).show();
+
+				Log.d(TAG, "Developer mode enabled for this session");
+
+				mDevModeUrlContainer.setVisibility(FairphoneUpdater.DEV_MODE_ENABLED ? View.VISIBLE : View.GONE);
+				mainActivity.forceConfigDownload();
+				//Utils.downloadConfigFile(this, true);
+			}
+		}
+	}
 }
diff --git a/src/com/fairphone/updater/fragments/OtherOSOptionsFragment.java b/src/com/fairphone/updater/fragments/OtherOSOptionsFragment.java
index b58b280..9efb8b5 100644
--- a/src/com/fairphone/updater/fragments/OtherOSOptionsFragment.java
+++ b/src/com/fairphone/updater/fragments/OtherOSOptionsFragment.java
@@ -28,7 +28,7 @@
         olderFairphoneOSButton = (Button) view.findViewById(R.id.older_fairphone_os_button);
         androidOSButton = (Button) view.findViewById(R.id.android_os_button);
         mAppStoreButton = (Button) view.findViewById(R.id.app_store_install_button);
-        
+
         mainActivity.updateHeader(HeaderType.OTHER_OS, mainActivity.getResources().getString(R.string.other_os_options), false);
 
         return view;
@@ -87,7 +87,7 @@
         else
         {
             androidOSButton.setVisibility(View.GONE);
-        } 
+        }
     }
 
     private void setupOlderFairphoneVersionsButton()
diff --git a/src/com/fairphone/updater/fragments/VersionDetailFragment.java b/src/com/fairphone/updater/fragments/VersionDetailFragment.java
index 51400be..0632ed6 100644
--- a/src/com/fairphone/updater/fragments/VersionDetailFragment.java
+++ b/src/com/fairphone/updater/fragments/VersionDetailFragment.java
@@ -21,6 +21,7 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import com.fairphone.updater.FairphoneUpdater;
 import com.fairphone.updater.FairphoneUpdater.HeaderType;
 import com.fairphone.updater.FairphoneUpdater.UpdaterState;
 import com.fairphone.updater.R;
@@ -186,7 +187,7 @@
 
         if (mIsVersion && mSelectedVersion != null)
         {
-            mHeaderType = mainActivity.getHeaderTypeFromImageType(mSelectedVersion.getImageType());
+            mHeaderType = FairphoneUpdater.getHeaderTypeFromImageType(mSelectedVersion.getImageType());
         }
         else if (mSelectedStore != null)
         {
@@ -215,7 +216,7 @@
                         (deviceVersion.getImageType().equalsIgnoreCase(Version.IMAGE_TYPE_AOSP) && deviceVersion.isNewerVersionThan(mSelectedVersion));
                 break;
             case APP_STORE:
-                mHeaderText = mainActivity.getStoreName(mSelectedStore);
+                mHeaderText = FairphoneUpdater.getStoreName(mSelectedStore);
                 mVersionDetailsTitle = resources.getString(R.string.install);
                 mIsOSChange = false;
                 mIsOlderVersion = false;
@@ -231,14 +232,6 @@
         }
     }
 
-    private boolean isWiFiEnabled()
-    {
-
-        ConnectivityManager manager = (ConnectivityManager) mainActivity.getSystemService(Context.CONNECTIVITY_SERVICE);
-
-	    return manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnectedOrConnecting();
-    }
-
     private Request createDownloadRequest(String url, String fileName, String downloadTitle)
     {
 
@@ -260,6 +253,7 @@
             request.setTitle(downloadTitle);
         } catch (Exception e)
         {
+            Log.w(TAG, "Error setting the download request: " + e.getLocalizedMessage());
             request = null;
         }
 
@@ -270,7 +264,7 @@
     {
         DownloadableItem item = mIsVersion ? mSelectedVersion : mSelectedStore;
         // use only on WiFi
-        if (isWiFiEnabled())
+        if (Utils.isWiFiEnabled(mainActivity))
         {
             if (item != null)
             {
@@ -282,7 +276,7 @@
                 if (!(download_link.startsWith("http://") || download_link.startsWith("https://")))
                 {
                     // If the download URL is a relative path, make it an absolute
-                    download_link = mainActivity.mOtaDownloadUrl + "/" + download_link;
+                    download_link = mainActivity.getPreferenceOtaDownloadUrl() + "/" + download_link;
                     // Sanitize URL - e.g. turn http://a.b//c/./d/../e to http://a.b/c/e
                     download_link = download_link.replaceAll("([^:])//", "/");
                     download_link = download_link.replaceAll("/([^/]+)/\\.\\./", "/");
@@ -290,7 +284,7 @@
                     try {
                         download_link = new URL(download_link).toExternalForm();
                     } catch (MalformedURLException e) {
-                        Log.w(TAG, "Potentially malformed download link.");
+                        Log.w(TAG, "Potentially malformed download link " + download_link + ": " + e.getLocalizedMessage());
                     }
                 }
                 Request request = createDownloadRequest(download_link + Utils.getModelAndOS(mainActivity), fileName, downloadTitle);
diff --git a/src/com/fairphone/updater/fragments/VersionListFragment.java b/src/com/fairphone/updater/fragments/VersionListFragment.java
index 821d202..938d9c3 100644
--- a/src/com/fairphone/updater/fragments/VersionListFragment.java
+++ b/src/com/fairphone/updater/fragments/VersionListFragment.java
@@ -9,8 +9,6 @@
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.TextView;
 
 import com.fairphone.updater.FairphoneUpdater.HeaderType;
 import com.fairphone.updater.R;
@@ -29,10 +27,10 @@
 
     private ListLayoutType mListLayoutType;
     private List<Version> mVersionList;
-	private LinearLayout mVersionListContainer;
+	private ViewGroup mVersionListContainer;
     private Button mLatestVersionDetailsButton;
-    private TextView mLatestVersionInstalledIndicator;
-    private LinearLayout mOlderVersionsGroup;
+    private View mLatestVersionInstalledIndicator;
+    private View mOlderVersionsGroup;
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
@@ -53,8 +51,8 @@
                 view = inflater.inflate(R.layout.fragment_app_store_options_list, container, false);
                 mainActivity.updateHeader(HeaderType.APP_STORE, resources.getString(R.string.app_store), true);
 
-                mOlderVersionsGroup = (LinearLayout) view.findViewById(R.id.older_versions_group);
-                mVersionListContainer = (LinearLayout) view.findViewById(R.id.version_list_container);
+                mOlderVersionsGroup = view.findViewById(R.id.older_versions_group);
+                mVersionListContainer = (ViewGroup) view.findViewById(R.id.version_list_container);
 
                 setupAppStoreVersions(container);
                 break;
@@ -62,11 +60,11 @@
                 view = inflater.inflate(R.layout.fragment_other_os_options_android_list, container, false);
                 mainActivity.updateHeader(HeaderType.ANDROID, resources.getString(R.string.android_os), true);
 
-                mOlderVersionsGroup = (LinearLayout) view.findViewById(R.id.older_versions_group);
-                mVersionListContainer = (LinearLayout) view.findViewById(R.id.version_list_container);
+                mOlderVersionsGroup = view.findViewById(R.id.older_versions_group);
+                mVersionListContainer = (ViewGroup) view.findViewById(R.id.version_list_container);
 
                 mLatestVersionDetailsButton = (Button) view.findViewById(R.id.other_os_options_android_latest_version_button);
-                mLatestVersionInstalledIndicator = (TextView) view.findViewById(R.id.other_os_options_android_version_installed_indicator_text);
+                mLatestVersionInstalledIndicator = view.findViewById(R.id.other_os_options_android_version_installed_indicator_text);
 
                 setupAndroidLatestVersion();
                 setupAndroidVersions(container);
@@ -76,11 +74,11 @@
                 view = inflater.inflate(R.layout.fragment_other_os_options_fairphone_list, container, false);
                 mainActivity.updateHeader(HeaderType.FAIRPHONE, resources.getString(R.string.fairphone_os), true);
 
-                mOlderVersionsGroup = (LinearLayout) view.findViewById(R.id.older_versions_group);
-                mVersionListContainer = (LinearLayout) view.findViewById(R.id.version_list_container);
+                mOlderVersionsGroup = view.findViewById(R.id.older_versions_group);
+                mVersionListContainer = (ViewGroup) view.findViewById(R.id.version_list_container);
 
                 mLatestVersionDetailsButton = (Button) view.findViewById(R.id.other_os_options_fairphone_latest_version_button);
-                mLatestVersionInstalledIndicator = (TextView) view.findViewById(R.id.other_os_options_fairphone_version_installed_indicator_text);
+                mLatestVersionInstalledIndicator = view.findViewById(R.id.other_os_options_fairphone_version_installed_indicator_text);
 
                 setupFairphoneLatestVersion();
                 setupFairphoneVersions(container);
diff --git a/src/com/fairphone/updater/gappsinstaller/GappsInstallerHelper.java b/src/com/fairphone/updater/gappsinstaller/GappsInstallerHelper.java
index 291de2c..49cba1f 100644
--- a/src/com/fairphone/updater/gappsinstaller/GappsInstallerHelper.java
+++ b/src/com/fairphone/updater/gappsinstaller/GappsInstallerHelper.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 
+import com.fairphone.updater.tools.Utils;
 import com.fairphone.updater.widgets.gapps.GoogleAppsInstallerWidget;
 
 import java.io.File;
@@ -36,9 +37,7 @@
 
     public static boolean areGappsInstalled()
     {
-        File f = new File("/system/app/OneTimeInitializer.apk");
-
-        return f.exists();
+        return Utils.fileExists("/system/app/OneTimeInitializer.apk");
     }
 
     public static void checkGappsAreInstalled(Context context)
diff --git a/src/com/fairphone/updater/tools/PrivilegeChecker.java b/src/com/fairphone/updater/tools/PrivilegeChecker.java
new file mode 100644
index 0000000..10283b0
--- /dev/null
+++ b/src/com/fairphone/updater/tools/PrivilegeChecker.java
@@ -0,0 +1,32 @@
+package com.fairphone.updater.tools;
+
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+
+public final class PrivilegeChecker {
+    private static final String TAG = PrivilegeChecker.class.getSimpleName();
+
+	private static final boolean isPrivilegedApp;
+
+	static {
+		// If we have permissions to write instructions to the recovery, we are a privileged app.
+		File f = new File("/cache/test.txt");
+        boolean success = false;
+        try {
+            success = f.createNewFile() && f.delete();
+        } catch (IOException ignored) {
+            success = false;
+        } finally {
+            isPrivilegedApp = success;
+        }
+		Log.i(TAG, "App is " + (isPrivilegedApp ? "" : "not") + " privileged.");
+	}
+
+	public static boolean isPrivilegedApp(){
+		return isPrivilegedApp;
+	}
+
+}
diff --git a/src/com/fairphone/updater/tools/Utils.java b/src/com/fairphone/updater/tools/Utils.java
index c146d11..55e51a8 100644
--- a/src/com/fairphone/updater/tools/Utils.java
+++ b/src/com/fairphone/updater/tools/Utils.java
@@ -16,21 +16,32 @@
 
 package com.fairphone.updater.tools;
 
+import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningServiceInfo;
+import android.app.DownloadManager;
+import android.content.ContentUris;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.ConnectivityManager;
+import android.net.Uri;
 import android.os.Build;
 import android.os.Environment;
+import android.os.PowerManager;
+import android.provider.DocumentsContract;
+import android.provider.MediaStore;
 import android.text.TextUtils;
 import android.util.Log;
+import android.widget.Toast;
 
 import com.fairphone.updater.BetaEnabler;
 import com.fairphone.updater.R;
 import com.fairphone.updater.UpdaterService;
 import com.fairphone.updater.data.DownloadableItem;
 import com.fairphone.updater.data.Store;
+import com.fairphone.updater.data.UpdaterData;
 import com.fairphone.updater.data.Version;
 import com.fairphone.updater.data.VersionParserHelper;
 import com.stericson.RootTools.RootTools;
@@ -38,12 +49,16 @@
 import com.stericson.RootTools.execution.CommandCapture;
 import com.stericson.RootTools.execution.Shell;
 
+import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.PrintWriter;
 import java.math.BigInteger;
+import java.nio.channels.FileChannel;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.NoSuchElementException;
@@ -55,8 +70,8 @@
     private static final String TAG = Utils.class.getSimpleName();
     private static final int DELAY_100_MILLIS = 100;
     public static final int DELAY_HALF_SECOND = 500;
-    public static final long SECONDS_IN_MINUTE = 60l;
-    public static final long MINUTES_IN_HOUR = 60l;
+    public static final long SECONDS_IN_MINUTE = 60L;
+    public static final long MINUTES_IN_HOUR = 60L;
 
     private static final double BUFFER_1024_BYTES = 1024d;
     // --Commented out by Inspection (06/02/2015 12:27):public static final int BUFFER_SIZE_4_KBYTES = 4096;
@@ -65,6 +80,9 @@
     public static final int BUFFER_SIZE_10_MBYTES = 10240;
     private static final int RADIX_BASE_16 = 16;
     private static final double PERCENT_100 = 100d;
+    private static final char CHAR_SPACE = ' ';
+    private static final char CHAR_ZERO = '0';
+    public static final int GAPPS_STORE_NUMBER = 0;
 
     private static double getPartitionSizeInGBytes(File path)
     {
@@ -137,7 +155,7 @@
         }
         else if (forceDownload)
         {
-            downloadConfigFile(context);
+            downloadConfigFile(context, true);
         }
     }
 
@@ -177,10 +195,6 @@
 //    }
 // --Commented out by Inspection STOP (06/02/2015 12:26)
 
-    private static void downloadConfigFile(Context context) {
-        downloadConfigFile(context, false);
-    }
-
     public static void downloadConfigFile(Context context, boolean forceDownload)
     {
         Intent i = new Intent(UpdaterService.ACTION_FAIRPHONE_UPDATER_CONFIG_FILE_DOWNLOAD);
@@ -200,7 +214,7 @@
             return false;
         }
 
-        if (md5 == null || md5.equals("") )
+        if (md5 == null || md5.isEmpty())
         {
             Log.e(TAG, "MD5 String NULL or UpdateFile NULL");
             return false;
@@ -250,11 +264,11 @@
             BigInteger bigInt = new BigInteger(1, md5sum);
             String output = bigInt.toString(RADIX_BASE_16);
             // Fill to 32 chars
-            output = String.format("%32s", output).replace(' ', '0');
+            output = String.format("%32s", output).replace(CHAR_SPACE, CHAR_ZERO);
             return output;
         } catch (IOException e)
         {
-            Log.e(TAG, "Error digesting MD5", e);
+            Log.e(TAG, "Error digesting MD5: " + e.getLocalizedMessage());
             return null;
 //            throw new RuntimeException("Unable to process file for MD5", e);
         } finally
@@ -287,25 +301,60 @@
         return sb.toString();
     }
 
+	public static void copy(File src, File dst) throws IOException {
+		if (PrivilegeChecker.isPrivilegedApp()) {
+			copyPrivileged(src, dst);
+		} else {
+			copyUnprivileged(src, dst);
+		}
+	}
+
+	private static void copyUnprivileged(File src, File dst) throws IOException {
+		if (RootTools.isAccessGiven()) {
+			RootTools.copyFile(src.getPath(), dst.getPath(), false, false);
+		} else {
+			throw new IOException("No root permissions granted.");
+		}
+	}
+
+	private static void copyPrivileged(File src, File dst) throws IOException {
+		FileInputStream inStream = new FileInputStream(src);
+		FileOutputStream outStream = new FileOutputStream(dst);
+		FileChannel inChannel = inStream.getChannel();
+		FileChannel outChannel = outStream.getChannel();
+		inChannel.transferTo(0, inChannel.size(), outChannel);
+		inStream.close();
+		outStream.close();
+    }
+
     public static void clearCache()
     {
-        File f = Environment.getDownloadCacheDirectory();
-        File[] files = f.listFiles();
-        if (files != null)
-        {
-            Log.d(TAG, "Size: " + files.length);
-	        for (File file : files) {
-		        String filename = file.getName();
+        if(PrivilegeChecker.isPrivilegedApp()) {
+            File f = Environment.getDownloadCacheDirectory();
+            File[] files = f.listFiles();
+            if (files != null) {
+                Log.d(TAG, "Size: " + files.length);
+                for (File file : files) {
+                    String filename = file.getName();
 
-		        if (filename.endsWith(".zip")) {
-			        final boolean delete = file.delete();
-			        if (delete) {
-				        Log.d(TAG, "Deleted file " + filename);
-			        } else {
-				        Log.d(TAG, "Failed to delete file " + filename);
-			        }
-		        }
-	        }
+                    if (filename.endsWith(".zip")) {
+                        final boolean delete = file.delete();
+                        if (delete) {
+                            Log.d(TAG, "Deleted file " + filename);
+                        } else {
+                            Log.d(TAG, "Failed to delete file " + filename);
+                        }
+                    }
+                }
+            }
+        } else {
+            if(RootTools.isAccessGiven()) {
+                try {
+                    Shell.runRootCommand(new CommandCapture(0, "rm -f *.zip"));
+                } catch (IOException | TimeoutException |RootDeniedException e) {
+                    Log.w(TAG, "Failed to clear cache: " + e.getLocalizedMessage());
+                }
+            }
         }
     }
 
@@ -316,7 +365,7 @@
         double roundedSize = Math.ceil(sizeInGB * PERCENT_100) / PERCENT_100;
         Log.d(TAG, "/data size: " + roundedSize + "Gb");
 
-        double fp1DataPartitionSize = (double) resources.getInteger(R.integer.FP1DataPartitionSizeMb) / PERCENT_100;
+        double fp1DataPartitionSize = (double) resources.getInteger(R.integer.FP1DataPartitionSizeMb) / BUFFER_1024_BYTES;
         // Add a little buffer to the 1gb default just in case
         return roundedSize > fp1DataPartitionSize;
     }
@@ -337,12 +386,12 @@
     {
         double fileSize = file.length();
         double cacheSize = Utils.getPartitionSizeInBytes(Environment.getDownloadCacheDirectory());
-        return cacheSize >= fileSize;
+        return fileSize > 0 && cacheSize >= fileSize;
     }
 
     public static String getFilenameFromDownloadableItem(DownloadableItem item, boolean isVersion)
     {
-        StringBuilder filename = null;
+        StringBuilder filename;
 
         if(isVersion)
         {
@@ -413,6 +462,7 @@
             return prop;
         } catch (NoSuchElementException e)
         {
+            Log.w(TAG, "Error reading prop "+name+". Defaulting to " + defaultValue + ": " + e.getLocalizedMessage());
             return defaultValue;
         } catch (Exception e)
         {
@@ -432,8 +482,26 @@
         }
         return defaultValue;
     }
-    
-    public static void setBetaPropToEnable()
+
+	public static void setBetaPropToEnable() {
+		if (PrivilegeChecker.isPrivilegedApp()) {
+			setBetaPropToEnablePrivileged();
+		} else {
+			setBetaPropToEnableUnprivileged();
+		}
+	}
+
+	private static void setBetaPropToEnablePrivileged() {
+	    ProcessBuilder pb = new ProcessBuilder("/system/bin/setprop", BetaEnabler.FAIRPHONE_BETA_PROPERTY, BetaEnabler.BETA_ENABLED);
+	    try {
+		    Process p = pb.start();
+		    p.waitFor();
+	    } catch (IOException | InterruptedException e) {
+		    Log.d(TAG, "Failed to setprop: " + e.getLocalizedMessage());
+	    }
+	}
+
+	private static void setBetaPropToEnableUnprivileged()
     {
         if(RootTools.isAccessGiven()) {
             CommandCapture command = new CommandCapture(0, "setprop "+ BetaEnabler.FAIRPHONE_BETA_PROPERTY+" "+BetaEnabler.BETA_ENABLED);
@@ -444,6 +512,88 @@
             }
         }
     }
+
+    public static String getOtaPackagePath(Resources resources, DownloadableItem item, boolean isVersion, boolean isZipInstall){
+        String path;
+
+        if (Utils.hasUnifiedPartition(resources))
+        {
+            path = resources.getString(R.string.recoveryCachePath) + Utils.getFilenameFromDownloadableItem(item, isVersion);
+        }
+        else
+        {
+            if(isZipInstall && Build.MODEL.equalsIgnoreCase(resources.getString(R.string.FP1Model)))
+            {
+                //TODO: Find a way to not have this hardcoded
+                String zipPath = item.getDownloadLink();
+                path = zipPath.replace("/storage/sdcard0", resources.getString(R.string.recoverySdCardPath));
+            }
+            else
+            {
+                path = resources.getString(R.string.recoverySdCardPath) + resources.getString(R.string.updaterFolder) + Utils.getFilenameFromDownloadableItem(item, isVersion);
+            }
+        }
+
+        return path;
+    }
+
+    public static void writeCacheCommand(Context context, String otaPackagePath) throws IOException, TimeoutException, RootDeniedException, Resources.NotFoundException {
+        if (PrivilegeChecker.isPrivilegedApp()) {
+            File recovery_dir = new File("/cache/recovery/");
+            final boolean mkdirs = recovery_dir.mkdirs();
+            if(! (mkdirs || recovery_dir.exists()) ) {
+                String errorMessage = context.getResources().getString(R.string.failed_mkdirs_cache_message);
+                Toast.makeText(context, errorMessage, Toast.LENGTH_LONG).show();
+                throw new IOException(errorMessage);
+            }
+
+            File command = new File("/cache/recovery/command");
+            File extendedCommand = new File("/cache/recovery/extendedcommand");
+            final boolean deleteFailed = !extendedCommand.delete();
+            if (deleteFailed) {
+                Log.d(TAG, "Couldn't delete "+extendedCommand.getAbsolutePath());
+            }
+
+            String updateCommand = "--update_package=" + otaPackagePath;
+            PrintWriter writer = new PrintWriter(command, "UTF-8");
+            writer.println("--wipe_cache");
+            writer.println(updateCommand);
+            writer.flush();
+            writer.close();
+        }else {
+            if(RootTools.isAccessGiven()) {
+                Shell.runRootCommand(new CommandCapture(0, "rm -f /cache/recovery/command"));
+                Shell.runRootCommand(new CommandCapture(0, "rm -f /cache/recovery/extendedcommand"));
+                Shell.runRootCommand(new CommandCapture(0, "echo '--wipe_cache' >> /cache/recovery/command"));
+                Shell.runRootCommand(new CommandCapture(0, "echo '--update_package=" + otaPackagePath + "' >> /cache/recovery/command"));
+            }else{
+                throw new RootDeniedException("Root Denied");
+            }
+        }
+    }
+
+    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+    public static boolean rebootToRecovery(Context context) {
+        boolean result;
+        if (PrivilegeChecker.isPrivilegedApp()) {
+            ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).reboot("recovery");
+            result = false;
+        } else {
+            if(RootTools.isAccessGiven()) {
+                try {
+                    Shell.runRootCommand(new CommandCapture(0, "reboot recovery"));
+                    result = true;
+                } catch (IOException | TimeoutException | RootDeniedException e) {
+                    Log.e(TAG, "Error rebooting to recovery: " + e.getLocalizedMessage());
+                    result = false;
+                }
+            }else{
+                result = false;
+            }
+
+        }
+        return result;
+    }
     
 // --Commented out by Inspection START (06/02/2015 12:25):
 //    public static void printStack(String moreLogs)
@@ -457,4 +607,135 @@
 //        Log.wtf(TAG, sb.toString());
 //    }
 // --Commented out by Inspection STOP (06/02/2015 12:25)
+
+    public static boolean fileExists(String otaPackagePath) {
+        boolean fileExists;
+        if(PrivilegeChecker.isPrivilegedApp()){
+            File f = new File(otaPackagePath);
+            fileExists = f.exists();
+        }else {
+            fileExists = RootTools.exists(otaPackagePath);
+        }
+        return fileExists;
+    }
+
+    private final static String[] SHELL_COMMANDS_ERASE_DATA = {
+            // remove data
+            "rm -rf /data/data/com.android.providers.media*",
+            "rm -rf /data/data/com.android.keychain*",
+            "rm -rf /data/data/com.android.location.fused*",
+            "rm -rf /data/data/com.android.providers.applications*",
+            "rm -rf /data/data/com.android.providers.media*",
+            "rm -rf /data/data/com.android.vending*",
+            "rm -rf /data/data/com.google.android.apps.genie.geniewidget*",
+            "rm -rf /data/data/com.google.android.apps.plus*",
+            "rm -rf /data/data/com.google.android.ears*",
+            "rm -rf /data/data/com.google.android.gms*",
+            "rm -rf /data/data/com.google.android.googlequicksearchbox*",
+            "rm -rf /data/data/com.google.android.location*",
+            "rm -rf /data/data/com.google.android.marvin.talkback*",
+            // remove cache
+//            "rm -rf /data/dalvik-cache",
+            // remove data/app
+            "rm -rf /data/app/com.android.apps.plus*",
+            "rm -rf /data/app/com.android.vending*",
+            "rm -rf /data/app/com.android.easr*",
+            "rm -rf /data/app/com.android.gms*",
+            "rm -rf /data/app/com.android.tts*"
+    };
+
+    public final static String SHELL_COMMAND_ERASE_DALVIK_CACHE = "rm -rf /data/dalvik-cache";
+    public final static String SHELL_COMMAND_EXIT = "exit";
+
+    public static void clearGappsData() throws RootDeniedException, IOException, InterruptedException {
+
+        if (PrivilegeChecker.isPrivilegedApp()) {
+            Process p = Runtime.getRuntime().exec(SHELL_COMMAND_ERASE_DALVIK_CACHE);
+            DataOutputStream os = new DataOutputStream(p.getOutputStream());
+            for (String tmpCmd : SHELL_COMMANDS_ERASE_DATA) {
+                os.writeBytes(tmpCmd+"\n");
+            }
+            os.writeBytes(SHELL_COMMAND_EXIT+"\n");
+            os.flush();
+            p.waitFor();
+        }else {
+            if(RootTools.isAccessGiven()) {
+                try {
+                    Shell.runRootCommand(new CommandCapture(0, SHELL_COMMAND_ERASE_DALVIK_CACHE));
+                } catch (TimeoutException e) {
+                    e.printStackTrace();
+                }
+                for (String tmpCmd : SHELL_COMMANDS_ERASE_DATA) {
+                    try {
+                        Shell.runRootCommand(new CommandCapture(0, tmpCmd));
+                    } catch (TimeoutException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }else{
+                throw new RootDeniedException("Root Denied");
+            }
+        }
+    }
+
+    public static String getPath(final Context context, final Uri uri)
+    {
+        String filePath = uri.getPath();
+        if ("content".equalsIgnoreCase(uri.getScheme())) {
+
+            //Get the zip file name
+            String[] path = filePath.split("/");
+            String downloadIdStr = "";
+            if (path != null && path.length > 0) {
+                downloadIdStr = path[path.length - 1];
+            }
+            long downloadId = 0;
+            try {
+                downloadId = Long.parseLong(downloadIdStr);
+            } catch (NumberFormatException nfe) {
+                Log.w(TAG, "NumberFormatException: " + nfe.getMessage());
+            }
+
+            DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
+            DownloadManager.Query query = new DownloadManager.Query();
+
+            query.setFilterById(downloadId);
+
+            Cursor cursor = downloadManager != null ? downloadManager.query(query) : null;
+
+            if (cursor != null && cursor.moveToFirst()) {
+                int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
+                int status = cursor.getInt(columnIndex);
+
+                switch (status) {
+                    case DownloadManager.STATUS_SUCCESSFUL: {
+                        filePath = downloadManager.getUriForDownloadedFile(downloadId).getPath();
+                        break;
+                    }
+                    case DownloadManager.STATUS_FAILED:
+                    case DownloadManager.STATUS_PAUSED:
+                    case DownloadManager.STATUS_PENDING:
+                    case DownloadManager.STATUS_RUNNING:
+                    default:
+                        filePath = "";
+                        break;
+                }
+            }
+        }
+
+        return filePath;
+    }
+
+	public static boolean isWiFiEnabled(Context context)
+	{
+
+		ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+		return manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnectedOrConnecting();
+	}
+
+    public static Store getGappsStore()
+    {
+        return UpdaterData.getInstance().getStore(GAPPS_STORE_NUMBER);
+    }
 }
diff --git a/src/com/fairphone/updater/widgets/gapps/GoogleAppsInstallerWidget.java b/src/com/fairphone/updater/widgets/gapps/GoogleAppsInstallerWidget.java
index 2bb2e66..4b86bad 100644
--- a/src/com/fairphone/updater/widgets/gapps/GoogleAppsInstallerWidget.java
+++ b/src/com/fairphone/updater/widgets/gapps/GoogleAppsInstallerWidget.java
@@ -45,7 +45,7 @@
         super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
     }
 
-    private void setupButtonClickIntents(Context context, RemoteViews widget)
+    private static void setupButtonClickIntents(Context context, RemoteViews widget)
     {
         Intent updater = new Intent(context, FairphoneUpdater.class);
         updater.setAction(GappsInstallerHelper.EXTRA_START_GAPPS_INSTALL);
@@ -58,7 +58,7 @@
 
     }
 
-    private void updateUI(Context context, AppWidgetManager appWidgetManager, int appWidgetId)
+    private static void updateUI(Context context, AppWidgetManager appWidgetManager, int appWidgetId)
     {
         // get the widgets
         RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.widget_google_apps_installer);