From 4ee420c965a6b0f1b6728fb93a7113ebb10009fd Mon Sep 17 00:00:00 2001
From: Davide Depau <davide@depau.eu>
Date: Fri, 17 Aug 2018 02:43:58 +0200
Subject: [PATCH] Show DMG partition table in image picker screen

---
 app/build.gradle                              |  1 +
 .../java/eu/depau/etchdroid/StateKeeper.kt    |  2 +-
 .../PartitionTableRecyclerViewAdapter.kt      | 76 +++++++++++++++++++
 .../depau/etchdroid/enums/FilesystemType.kt   | 35 ++++++++-
 .../etchdroid/enums/PartitionTableType.kt     | 22 +++++-
 .../fragments/ImageLocationFragment.kt        | 54 +++++++++----
 .../main/res/layout/fragment_confirminfo.xml  | 16 ++--
 .../res/layout/fragment_select_location.xml   | 73 +++++++++---------
 .../main/res/layout/part_data_keyvalue.xml    | 26 +++++++
 app/src/main/res/layout/partition_row.xml     | 31 ++++++++
 app/src/main/res/layout/usb_device_row.xml    |  4 +-
 app/src/main/res/values/dimens.xml            |  3 +-
 app/src/main/res/values/strings.xml           | 44 ++++++++++-
 app/src/main/res/values/styles.xml            |  4 +-
 14 files changed, 325 insertions(+), 66 deletions(-)
 create mode 100644 app/src/main/java/eu/depau/etchdroid/adapters/PartitionTableRecyclerViewAdapter.kt
 create mode 100644 app/src/main/res/layout/part_data_keyvalue.xml
 create mode 100644 app/src/main/res/layout/partition_row.xml

diff --git a/app/build.gradle b/app/build.gradle
index c8245d4..b2ebfeb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -30,6 +30,7 @@ dependencies {
     implementation 'com.android.support.constraint:constraint-layout:1.1.2'
     implementation 'com.android.support:design:28.0.0-rc01'
     implementation 'com.android.support:recyclerview-v7:28.0.0-rc01'
+    implementation 'com.android.support:gridlayout-v7:28.0.0-rc01'
 
     implementation 'com.github.isabsent:filepicker:1.1.01'
 //    implementation 'com.github.mjdev:libaums:0.5.5'
diff --git a/app/src/main/java/eu/depau/etchdroid/StateKeeper.kt b/app/src/main/java/eu/depau/etchdroid/StateKeeper.kt
index 412799a..69d85fd 100644
--- a/app/src/main/java/eu/depau/etchdroid/StateKeeper.kt
+++ b/app/src/main/java/eu/depau/etchdroid/StateKeeper.kt
@@ -13,7 +13,7 @@ object StateKeeper {
     var wizardStep: WizardStep = WizardStep.SELECT_FLASH_METHOD
     var currentFragment: WizardFragment? = null
     var flashMethod: FlashMethod? = null
-    var imageLocation: ImageLocation? = null
+    var imageLocation: ImageLocation? = ImageLocation.LOCAL
     var streamingWrite: Boolean = false
     var imageFile: Uri? = null
     var imageRepr: Image? = null
diff --git a/app/src/main/java/eu/depau/etchdroid/adapters/PartitionTableRecyclerViewAdapter.kt b/app/src/main/java/eu/depau/etchdroid/adapters/PartitionTableRecyclerViewAdapter.kt
new file mode 100644
index 0000000..8b7d9aa
--- /dev/null
+++ b/app/src/main/java/eu/depau/etchdroid/adapters/PartitionTableRecyclerViewAdapter.kt
@@ -0,0 +1,76 @@
+package eu.depau.etchdroid.adapters
+
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import eu.depau.etchdroid.R
+import eu.depau.etchdroid.kotlin_exts.toHRSize
+import eu.depau.etchdroid.utils.Partition
+import kotlinx.android.synthetic.main.part_data_keyvalue.view.*
+import kotlinx.android.synthetic.main.partition_row.view.*
+
+class PartitionTableRecyclerViewAdapter(private val dataset: List<Partition>) : RecyclerView.Adapter<PartitionTableRecyclerViewAdapter.ViewHolder>() {
+
+    class ViewHolder(val layout: LinearLayout) : RecyclerView.ViewHolder(layout)
+
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
+            ViewHolder {
+
+        val layout = LayoutInflater.from(parent.context)
+                .inflate(R.layout.partition_row, parent, false) as LinearLayout
+        return ViewHolder(layout)
+    }
+
+
+    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+        val part = dataset[position]
+        val layout = holder.layout
+        val li = LayoutInflater.from(layout.context)
+        var kv: LinearLayout
+
+        layout.part_number.text = "${part.number}"
+
+        layout.part_data_grid.removeAllViewsInLayout()
+
+        if (part.partLabel != null) {
+            kv = li.inflate(R.layout.part_data_keyvalue, layout.part_data_grid, false) as LinearLayout
+            kv.key.text = layout.context.getString(R.string.part_label)
+            kv.value.text = part.partLabel
+            layout.part_data_grid.addView(kv)
+        }
+        if (part.fsLabel != null) {
+            kv = li.inflate(R.layout.part_data_keyvalue, layout.part_data_grid, false) as LinearLayout
+            kv.key.text = layout.context.getString(R.string.fs_label)
+            kv.value.text = part.fsLabel
+            layout.part_data_grid.addView(kv)
+        }
+        if (part.fsType != null) {
+            kv = li.inflate(R.layout.part_data_keyvalue, layout.part_data_grid, false) as LinearLayout
+            kv.key.text = layout.context.getString(R.string.fs_type)
+            kv.value.text = part.fsType.getString(layout.context)
+            layout.part_data_grid.addView(kv)
+        }
+        if (part.fsLabel != null) {
+            kv = li.inflate(R.layout.part_data_keyvalue, layout.part_data_grid, false) as LinearLayout
+            kv.key.text = layout.context.getString(R.string.fs_label)
+            kv.value.text = part.fsLabel
+            layout.part_data_grid.addView(kv)
+        }
+        if (part.size != null) {
+            kv = li.inflate(R.layout.part_data_keyvalue, layout.part_data_grid, false) as LinearLayout
+            kv.key.text = layout.context.getString(R.string.part_size)
+            kv.value.text = part.size.toHRSize()
+            layout.part_data_grid.addView(kv)
+        }
+    }
+
+
+    override fun getItemCount(): Int = dataset.size
+
+
+    fun get(position: Int): Partition {
+        return dataset[position]
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/depau/etchdroid/enums/FilesystemType.kt b/app/src/main/java/eu/depau/etchdroid/enums/FilesystemType.kt
index b1b066c..43349dd 100644
--- a/app/src/main/java/eu/depau/etchdroid/enums/FilesystemType.kt
+++ b/app/src/main/java/eu/depau/etchdroid/enums/FilesystemType.kt
@@ -1,5 +1,8 @@
 package eu.depau.etchdroid.enums
 
+import android.content.Context
+import eu.depau.etchdroid.R
+
 enum class FilesystemType {
     // Microsoft
     FAT12,
@@ -35,5 +38,35 @@ enum class FilesystemType {
 
     FREE,
     UNFORMATTED,
-    UNKNOWN
+    UNKNOWN;
+
+    fun getString(context: Context): String {
+        return when(this) {
+            FilesystemType.FAT12        -> context.getString(R.string.fs_fat12)
+            FilesystemType.FAT16        -> context.getString(R.string.fs_fat16)
+            FilesystemType.FAT32        -> context.getString(R.string.fs_fat32)
+            FilesystemType.EXFAT        -> context.getString(R.string.fs_exfat)
+            FilesystemType.NTFS         -> context.getString(R.string.fs_ntfs)
+            FilesystemType.REFS         -> context.getString(R.string.fs_refs)
+            FilesystemType.HFS          -> context.getString(R.string.fs_hfs)
+            FilesystemType.HFSPLUS      -> context.getString(R.string.fs_hfsplus)
+            FilesystemType.APFS         -> context.getString(R.string.fs_apfs)
+            FilesystemType.APT_DATA     -> context.getString(R.string.fs_apt_data)
+            FilesystemType.ISO9660      -> context.getString(R.string.fs_iso9660)
+            FilesystemType.EXT2         -> context.getString(R.string.fs_ext2)
+            FilesystemType.EXT3         -> context.getString(R.string.fs_ext3)
+            FilesystemType.EXT4         -> context.getString(R.string.fs_ext4)
+            FilesystemType.BTRFS        -> context.getString(R.string.fs_btrfs)
+            FilesystemType.F2FS         -> context.getString(R.string.fs_f2fs)
+            FilesystemType.LUKS         -> context.getString(R.string.fs_luks)
+            FilesystemType.LINUX_SWAP   -> context.getString(R.string.fs_linux_swap)
+            FilesystemType.LINUX_LVM_PV -> context.getString(R.string.fs_linux_lvm_pv)
+            FilesystemType.UFS          -> context.getString(R.string.fs_ufs)
+            FilesystemType.XFS          -> context.getString(R.string.fs_xfs)
+            FilesystemType.ZFS          -> context.getString(R.string.fs_zfs)
+            FilesystemType.FREE         -> context.getString(R.string.fs_free)
+            FilesystemType.UNFORMATTED  -> context.getString(R.string.fs_unformatted)
+            FilesystemType.UNKNOWN      -> context.getString(R.string.fs_unknown)
+        }
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/eu/depau/etchdroid/enums/PartitionTableType.kt b/app/src/main/java/eu/depau/etchdroid/enums/PartitionTableType.kt
index 20676be..d687bfe 100644
--- a/app/src/main/java/eu/depau/etchdroid/enums/PartitionTableType.kt
+++ b/app/src/main/java/eu/depau/etchdroid/enums/PartitionTableType.kt
@@ -1,5 +1,8 @@
 package eu.depau.etchdroid.enums
 
+import android.content.Context
+import eu.depau.etchdroid.R
+
 enum class PartitionTableType {
     AIX,
     AMIGA,
@@ -10,5 +13,20 @@ enum class PartitionTableType {
     MAC,
     MSDOS,
     PC98,
-    SUN
-}
\ No newline at end of file
+    SUN;
+
+    fun getString(context: Context): String {
+        return when(this) {
+            PartitionTableType.AIX      -> context.getString(R.string.ptt_aix)
+            PartitionTableType.AMIGA    -> context.getString(R.string.ptt_amiga)
+            PartitionTableType.BSD      -> context.getString(R.string.ptt_bsd)
+            PartitionTableType.DVH      -> context.getString(R.string.ptt_dvh)
+            PartitionTableType.GPT      -> context.getString(R.string.ptt_gpt)
+            PartitionTableType.LOOP     -> context.getString(R.string.ptt_loop)
+            PartitionTableType.MAC      -> context.getString(R.string.ptt_mac)
+            PartitionTableType.MSDOS    -> context.getString(R.string.ptt_msdos)
+            PartitionTableType.PC98     -> context.getString(R.string.ptt_pc98)
+            PartitionTableType.SUN      -> context.getString(R.string.ptt_sun)
+        }
+    }
+}
diff --git a/app/src/main/java/eu/depau/etchdroid/fragments/ImageLocationFragment.kt b/app/src/main/java/eu/depau/etchdroid/fragments/ImageLocationFragment.kt
index 72620cf..107e21f 100644
--- a/app/src/main/java/eu/depau/etchdroid/fragments/ImageLocationFragment.kt
+++ b/app/src/main/java/eu/depau/etchdroid/fragments/ImageLocationFragment.kt
@@ -9,6 +9,7 @@ import android.os.Bundle
 import android.os.Environment
 import android.support.v4.app.ActivityCompat
 import android.support.v4.content.ContextCompat
+import android.support.v7.widget.LinearLayoutManager
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
@@ -17,6 +18,7 @@ import com.github.isabsent.filepicker.SimpleFilePickerDialog
 import eu.depau.etchdroid.R
 import eu.depau.etchdroid.StateKeeper
 import eu.depau.etchdroid.activities.WizardActivity
+import eu.depau.etchdroid.adapters.PartitionTableRecyclerViewAdapter
 import eu.depau.etchdroid.kotlin_exts.getFileName
 import eu.depau.etchdroid.kotlin_exts.snackbar
 import eu.depau.etchdroid.enums.FlashMethod
@@ -37,6 +39,7 @@ class ImageLocationFragment : WizardFragment(), SimpleFilePickerDialog.Interacti
     val MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 29
     val TAG = "ImageLocationFragment"
     val PICKER_DIALOG_TAG = "eu.depau.etchdroid.filepicker.DIALOG_TAG"
+    var issuesFound = false
 
     fun isStreamingAvailable(): Boolean {
         if (StateKeeper.imageLocation != ImageLocation.REMOTE)
@@ -46,7 +49,7 @@ class ImageLocationFragment : WizardFragment(), SimpleFilePickerDialog.Interacti
         return true
     }
 
-    fun setStreamingCheckBoxAvailability(context: WizardActivity) {
+/*    fun setStreamingCheckBoxAvailability(context: WizardActivity) {
         val checkBox = streaming_write_checkbox
 
         if (checkBox == null)
@@ -59,28 +62,29 @@ class ImageLocationFragment : WizardFragment(), SimpleFilePickerDialog.Interacti
             checkBox.isEnabled = enabled
             onCheckBoxClicked(checkBox)
         }
-    }
+    }*/
 
-    override fun onCheckBoxClicked(view: View) {
+/*    override fun onCheckBoxClicked(view: View) {
         super.onCheckBoxClicked(view)
 
         if (view.id == R.id.streaming_write_checkbox)
             StateKeeper.streamingWrite = view.isActivated && view.isEnabled
-    }
+    }*/
 
     override fun onRadioButtonClicked(view: View) {
-        StateKeeper.imageLocation = when (view.id) {
+        StateKeeper.imageLocation = ImageLocation.LOCAL
+        /*when (view.id) {
             R.id.download_img_radio -> ImageLocation.REMOTE
             R.id.use_local_img_radio -> ImageLocation.LOCAL
             else -> null
-        }
+        }*/
 
         fab?.show()
 
         pick_file_btn?.isEnabled = StateKeeper.imageLocation == ImageLocation.LOCAL
-        img_url_textview?.isEnabled = StateKeeper.imageLocation == ImageLocation.REMOTE
+//        img_url_textview?.isEnabled = StateKeeper.imageLocation == ImageLocation.REMOTE
 
-        setStreamingCheckBoxAvailability(activity as WizardActivity)
+//        setStreamingCheckBoxAvailability(activity as WizardActivity)
         loadImageChanges(activity as WizardActivity)
     }
 
@@ -127,12 +131,17 @@ class ImageLocationFragment : WizardFragment(), SimpleFilePickerDialog.Interacti
     }
 
     override fun nextStep(view: View?) {
+        if (issuesFound) {
+            view?.snackbar(getString(R.string.issues_found_expl))
+            return
+        }
+
         if (StateKeeper.imageLocation == null) {
             view?.snackbar(getString(R.string.select_image_location))
             return
         }
 
-        if (StateKeeper.imageLocation == ImageLocation.REMOTE) {
+/*        if (StateKeeper.imageLocation == ImageLocation.REMOTE) {
             try {
                 StateKeeper.imageFile = getRemoteImageUri(activity as WizardActivity)
             } catch (e: RuntimeException) {
@@ -140,7 +149,7 @@ class ImageLocationFragment : WizardFragment(), SimpleFilePickerDialog.Interacti
                 view?.snackbar(getString(R.string.provided_url_invalid))
                 return
             }
-        }
+        }*/
 
         if (StateKeeper.imageFile == null) {
             view?.snackbar(getString(R.string.provide_image_file))
@@ -185,7 +194,7 @@ class ImageLocationFragment : WizardFragment(), SimpleFilePickerDialog.Interacti
 
     override fun onFragmentAdded(activity: WizardActivity) {
         super.onFragmentAdded(activity)
-        setStreamingCheckBoxAvailability(activity)
+//        setStreamingCheckBoxAvailability(activity)
     }
 
     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
@@ -196,10 +205,10 @@ class ImageLocationFragment : WizardFragment(), SimpleFilePickerDialog.Interacti
         return inflater.inflate(R.layout.fragment_select_location, container, false)
     }
 
-    fun getRemoteImageUri(context: WizardActivity): Uri {
+/*    fun getRemoteImageUri(context: WizardActivity): Uri {
         val text = img_url_textview.text.toString()
         return Uri.parse(text)
-    }
+    }*/
 
     fun loadImageChanges(context: WizardActivity) {
         val button = pick_file_btn
@@ -214,7 +223,24 @@ class ImageLocationFragment : WizardFragment(), SimpleFilePickerDialog.Interacti
 
         if (StateKeeper.flashMethod == FlashMethod.FLASH_DMG_API) {
             StateKeeper.imageRepr = DMGImage(uri, context)
-            Log.d(TAG, (StateKeeper.imageRepr as DMGImage).partitionTable.toString())
+            val imgRepr = StateKeeper.imageRepr as DMGImage
+
+            if (imgRepr.tableType == null && (imgRepr.partitionTable == null || imgRepr.partitionTable?.size == 0)) {
+                part_table_header.text = getString(R.string.image_is_not_dmg)
+                issuesFound = true
+                return
+            } else {
+                part_table_header.text = if (imgRepr.tableType != null) "Partition table:" else ""
+                part_table_header_side.text = imgRepr.tableType?.getString(context) ?: ""
+                issuesFound = false
+
+                val viewAdapter = PartitionTableRecyclerViewAdapter(imgRepr.partitionTable!!)
+                part_table_recycler.apply {
+                    setHasFixedSize(true)
+                    layoutManager = LinearLayoutManager(activity)
+                    adapter = viewAdapter
+                }
+            }
         }
     }
 
diff --git a/app/src/main/res/layout/fragment_confirminfo.xml b/app/src/main/res/layout/fragment_confirminfo.xml
index 32e3805..286b885 100644
--- a/app/src/main/res/layout/fragment_confirminfo.xml
+++ b/app/src/main/res/layout/fragment_confirminfo.xml
@@ -16,10 +16,10 @@
                     android:clickable="true"
                     android:focusable="true"
                     android:orientation="vertical"
-                    android:paddingBottom="@dimen/row_padding_vertical"
+                    android:paddingBottom="@dimen/row_padding"
                     android:paddingLeft="@dimen/activity_horizontal_margin"
                     android:paddingRight="@dimen/activity_horizontal_margin"
-                    android:paddingTop="@dimen/row_padding_vertical">
+                    android:paddingTop="@dimen/row_padding">
 
         <TextView
             android:id="@+id/confirm_sel_method_title"
@@ -46,10 +46,10 @@
                     android:clickable="true"
                     android:focusable="true"
                     android:orientation="vertical"
-                    android:paddingBottom="@dimen/row_padding_vertical"
+                    android:paddingBottom="@dimen/row_padding"
                     android:paddingLeft="@dimen/activity_horizontal_margin"
                     android:paddingRight="@dimen/activity_horizontal_margin"
-                    android:paddingTop="@dimen/row_padding_vertical">
+                    android:paddingTop="@dimen/row_padding">
 
         <TextView
             android:id="@+id/confirm_sel_image_title"
@@ -85,10 +85,10 @@
                     android:clickable="true"
                     android:focusable="true"
                     android:orientation="vertical"
-                    android:paddingBottom="@dimen/row_padding_vertical"
+                    android:paddingBottom="@dimen/row_padding"
                     android:paddingLeft="@dimen/activity_horizontal_margin"
                     android:paddingRight="@dimen/activity_horizontal_margin"
-                    android:paddingTop="@dimen/row_padding_vertical">
+                    android:paddingTop="@dimen/row_padding">
 
         <TextView
             android:id="@+id/confirm_sel_usbdev_title"
@@ -124,10 +124,10 @@
                     android:clickable="true"
                     android:focusable="true"
                     android:orientation="vertical"
-                    android:paddingBottom="@dimen/row_padding_vertical"
+                    android:paddingBottom="@dimen/row_padding"
                     android:paddingLeft="@dimen/activity_horizontal_margin"
                     android:paddingRight="@dimen/activity_horizontal_margin"
-                    android:paddingTop="@dimen/row_padding_vertical">
+                    android:paddingTop="@dimen/row_padding">
 
         <TextView
             android:id="@+id/confirm_extra_info"
diff --git a/app/src/main/res/layout/fragment_select_location.xml b/app/src/main/res/layout/fragment_select_location.xml
index 5624e26..f22c403 100644
--- a/app/src/main/res/layout/fragment_select_location.xml
+++ b/app/src/main/res/layout/fragment_select_location.xml
@@ -5,52 +5,55 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
-    tools:context=".fragments.FlashMethodFragment"
-    tools:showIn="@layout/activity_main">
+    tools:context=".fragments.FlashMethodFragment">
 
     <RadioGroup
         android:layout_width="match_parent"
         android:layout_height="wrap_content">
 
-        <RadioButton
-            android:id="@+id/use_local_img_radio"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:onClick="onRadioButtonClicked"
-            android:text="@string/use_local_image"/>
-
         <Button
             android:id="@+id/pick_file_btn"
-            android:layout_width="wrap_content"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:enabled="false"
             android:onClick="onButtonClicked"
             android:text="@string/pick_a_file"/>
 
-        <RadioButton
-            android:id="@+id/download_img_radio"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:enabled="false"
-            android:onClick="onRadioButtonClicked"
-            android:text="@string/download_image_from_url"/>
-
-        <EditText
-            android:id="@+id/img_url_textview"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:enabled="false"
-            android:hint="@string/image_url_hint"
-            android:inputType="textUri"/>
-
-        <CheckBox
-            android:id="@+id/streaming_write_checkbox"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:checked="false"
-            android:enabled="false"
-            android:onClick="onCheckBoxClicked"
-            android:text="@string/download_streaming"/>
     </RadioGroup>
 
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:background="?android:attr/selectableItemBackground"
+                  android:clickable="true"
+                  android:focusable="true"
+                  android:orientation="horizontal"
+                  android:paddingBottom="@dimen/row_padding"
+                  android:paddingLeft="@dimen/activity_horizontal_margin"
+                  android:paddingRight="@dimen/activity_horizontal_margin"
+                  android:paddingTop="@dimen/row_padding">
+
+        <TextView
+            android:id="@+id/part_table_header"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:padding="@dimen/grid_padding"
+            android:textColor="@color/name"
+            android:textSize="16sp"
+            android:textStyle="bold"/>
+
+        <TextView
+            android:id="@+id/part_table_header_side"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:padding="@dimen/grid_padding"
+            android:textColor="@color/info"/>
+
+    </LinearLayout>
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/part_table_recycler"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
 </LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/part_data_keyvalue.xml b/app/src/main/res/layout/part_data_keyvalue.xml
new file mode 100644
index 0000000..55edd5d
--- /dev/null
+++ b/app/src/main/res/layout/part_data_keyvalue.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:grid="http://schemas.android.com/apk/res-auto"
+
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+
+    <TextView
+        android:id="@+id/key"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:ellipsize="end"
+        android:textColor="@color/name"
+        android:textSize="16sp"
+        android:textStyle="bold"
+        android:paddingStart="@dimen/grid_padding"
+        android:paddingEnd="@dimen/grid_padding"/>
+
+    <TextView
+        android:id="@+id/value"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingStart="@dimen/grid_padding"
+        android:paddingEnd="@dimen/grid_padding"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/partition_row.xml b/app/src/main/res/layout/partition_row.xml
new file mode 100644
index 0000000..03321d5
--- /dev/null
+++ b/app/src/main/res/layout/partition_row.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:grid="http://schemas.android.com/apk/res-auto"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:background="?android:attr/selectableItemBackground"
+              android:clickable="true"
+              android:focusable="true"
+              android:orientation="horizontal"
+              android:paddingBottom="@dimen/row_padding"
+              android:paddingLeft="@dimen/activity_horizontal_margin"
+              android:paddingRight="@dimen/activity_horizontal_margin"
+              android:paddingTop="@dimen/row_padding">
+
+    <TextView
+        android:id="@+id/part_number"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:padding="@dimen/grid_padding"/>
+
+    <LinearLayout
+        android:id="@+id/part_data_grid"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingStart="@dimen/row_padding"
+        android:paddingEnd="@dimen/row_padding"
+        android:orientation="vertical">
+
+
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/usb_device_row.xml b/app/src/main/res/layout/usb_device_row.xml
index 9bf8ada..d787398 100644
--- a/app/src/main/res/layout/usb_device_row.xml
+++ b/app/src/main/res/layout/usb_device_row.xml
@@ -6,10 +6,10 @@
                 android:clickable="true"
                 android:focusable="true"
                 android:orientation="vertical"
-                android:paddingBottom="@dimen/row_padding_vertical"
+                android:paddingBottom="@dimen/row_padding"
                 android:paddingLeft="@dimen/activity_horizontal_margin"
                 android:paddingRight="@dimen/activity_horizontal_margin"
-                android:paddingTop="@dimen/row_padding_vertical">
+                android:paddingTop="@dimen/row_padding">
 
     <TextView
         android:id="@+id/name"
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index c768b19..3378500 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -2,5 +2,6 @@
     <dimen name="fab_margin">16dp</dimen>
     <dimen name="activity_horizontal_margin">16dp</dimen>
     <dimen name="activity_vertical_margin">16dp</dimen>
-    <dimen name="row_padding_vertical">10dp</dimen>
+    <dimen name="row_padding">10dp</dimen>
+    <dimen name="grid_padding">5dp</dimen>
 </resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index fb82b3b..31aa7c6 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -8,7 +8,7 @@
     <string name="flash_unetbootin">Unetbootin-style flash (MBR only, requires root)</string>
     <string name="flash_woeusb">Write Windows image (using WoeUSB, requires root)</string>
     <string name="download_streaming">Stream directly to USB drive</string>
-    <string name="pick_a_file">Pick a file</string>
+    <string name="pick_a_file">Pick an image file</string>
     <string name="image_url_hint">Image URL</string>
     <string name="provided_url_invalid">Provided URL is invalid</string>
     <string name="select_image_location">Please select image location</string>
@@ -29,4 +29,46 @@
     <string name="notchan_writestatus_desc">Used to display the status of images being written to USB drives</string>
     <string name="notif_initializing">Initializing...</string>
     <string name="notif_writing_img">Writing image</string>
+    <string name="issues_found_expl">Selected image cannot be flashed</string>
+    <string name="image_is_not_dmg">Selected image is not a DMG image (maybe it\'s corrupted)</string>
+    <string name="ptt_aix">IBM AIX</string>
+    <string name="ptt_amiga">AMIGA</string>
+    <string name="ptt_bsd">BSD</string>
+    <string name="ptt_dvh">DVH</string>
+    <string name="ptt_gpt">GPT</string>
+    <string name="ptt_loop">None</string>
+    <string name="ptt_mac">APT</string>
+    <string name="ptt_msdos">MBR</string>
+    <string name="ptt_pc98">PC98</string>
+    <string name="ptt_sun">Sun</string>
+    <string name="fs_fat12">FAT12</string>
+    <string name="fs_fat16">FAT16</string>
+    <string name="fs_fat32">FAT32</string>
+    <string name="fs_exfat">ExFAT</string>
+    <string name="fs_ntfs">NTFS</string>
+    <string name="fs_refs">ReFS</string>
+    <string name="fs_hfs">HFS</string>
+    <string name="fs_hfsplus">HFS+</string>
+    <string name="fs_apfs">APFS</string>
+    <string name="fs_apt_data">APT data</string>
+    <string name="fs_iso9660">ISO 9660</string>
+    <string name="fs_ext2">Linux Ext2</string>
+    <string name="fs_ext3">Linux Ext3</string>
+    <string name="fs_ext4">Linux Ext4</string>
+    <string name="fs_btrfs">Btrfs</string>
+    <string name="fs_f2fs">f2fs</string>
+    <string name="fs_luks">LUKS</string>
+    <string name="fs_linux_swap">Linux swap</string>
+    <string name="fs_linux_lvm_pv">Linux LVM2 PV</string>
+    <string name="fs_ufs">UFS</string>
+    <string name="fs_xfs">XFS</string>
+    <string name="fs_zfs">ZFS</string>
+    <string name="fs_free">Free space</string>
+    <string name="fs_unformatted">Unformatted</string>
+    <string name="fs_unknown">Unknown</string>
+    <string name="part_name">Name:</string>
+    <string name="part_label">Name</string>
+    <string name="fs_label">Label</string>
+    <string name="fs_type">Type</string>
+    <string name="part_size">Size</string>
 </resources>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 177cefc..ebfa3ab 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -1,11 +1,13 @@
 <resources>
 
     <!-- Base application theme. -->
-    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+    <style name="AppTheme" parent="Base.Theme.AppCompat.Light.DarkActionBar">
         <!-- Customize your theme here. -->
         <item name="colorPrimary">@color/colorPrimary</item>
         <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
         <item name="colorAccent">@color/colorAccent</item>
+        <item name="coordinatorLayoutStyle">@style/Widget.Support.CoordinatorLayout</item>
+        <item name="floatingActionButtonStyle">@style/Widget.Design.FloatingActionButton</item>
     </style>
 
     <style name="AppTheme.NoActionBar">