Expose app status information to screen readers

This is for backup, restore and re-install status.
diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressAdapter.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressAdapter.kt
index 40e7ca1..ff08bad 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressAdapter.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressAdapter.kt
@@ -1,5 +1,6 @@
 package com.stevesoltys.seedvault.restore.install
 
+import android.os.Build.VERSION.SDK_INT
 import android.view.LayoutInflater
 import android.view.View
 import android.view.View.GONE
@@ -44,7 +45,8 @@
                 return if (finished) finishedComparator.compare(item1, item2)
                 else item1.compareTo(item2)
             }
-        })
+        }
+    )
 
     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppInstallViewHolder {
         val v = LayoutInflater.from(parent.context)
@@ -79,15 +81,23 @@
                 IN_PROGRESS -> {
                     appStatus.visibility = INVISIBLE
                     progressBar.visibility = VISIBLE
+                    if (SDK_INT >= 30) {
+                        progressBar.stateDescription =
+                            context.getString(R.string.restore_app_status_installing)
+                    }
                 }
                 SUCCEEDED -> {
                     appStatus.setImageResource(R.drawable.ic_check_green)
                     appStatus.visibility = VISIBLE
+                    appStatus.contentDescription =
+                        context.getString(R.string.restore_app_status_installed)
                     progressBar.visibility = INVISIBLE
                 }
                 FAILED -> {
                     appStatus.setImageResource(R.drawable.ic_error_red)
                     appStatus.visibility = VISIBLE
+                    appStatus.contentDescription =
+                        context.getString(R.string.restore_app_status_install_error)
                     progressBar.visibility = INVISIBLE
                     if (finished) {
                         v.background = clickableBackground
@@ -100,6 +110,8 @@
                 }
                 FAILED_SYSTEM_APP -> {
                     appStatus.setImageResource(R.drawable.ic_error_red)
+                    appStatus.contentDescription =
+                        context.getString(R.string.restore_app_status_install_error)
                     appStatus.visibility = VISIBLE
                     progressBar.visibility = INVISIBLE
                 }
diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressFragment.kt
index 0bdfc50..c78d47a 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressFragment.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressFragment.kt
@@ -60,17 +60,17 @@
         button.setText(R.string.restore_next)
         button.setOnClickListener { viewModel.onNextClickedAfterInstallingApps() }
 
-        viewModel.chosenRestorableBackup.observe(viewLifecycleOwner, { restorableBackup ->
+        viewModel.chosenRestorableBackup.observe(viewLifecycleOwner) { restorableBackup ->
             backupNameView.text = restorableBackup.name
-        })
+        }
 
-        viewModel.installResult.observe(viewLifecycleOwner, { result ->
+        viewModel.installResult.observe(viewLifecycleOwner) { result ->
             onInstallResult(result)
-        })
+        }
 
-        viewModel.nextButtonEnabled.observe(viewLifecycleOwner, { enabled ->
+        viewModel.nextButtonEnabled.observe(viewLifecycleOwner) { enabled ->
             button.isEnabled = enabled
-        })
+        }
     }
 
     private fun onInstallResult(installResult: InstallResult) {
diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/AppViewHolder.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/AppViewHolder.kt
index b316520..e17800d 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/ui/AppViewHolder.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/ui/AppViewHolder.kt
@@ -2,6 +2,7 @@
 
 import android.content.Context
 import android.content.pm.PackageManager
+import android.os.Build.VERSION.SDK_INT
 import android.view.View
 import android.view.View.GONE
 import android.view.View.INVISIBLE
@@ -39,21 +40,45 @@
             appInfo.visibility = GONE
             appStatus.visibility = INVISIBLE
             progressBar.visibility = VISIBLE
+            if (SDK_INT >= 30) {
+                progressBar.stateDescription = context.getString(
+                    if (isRestore) R.string.restore_restoring
+                    else R.string.backup_app_in_progress
+                )
+            }
         } else {
             appStatus.visibility = VISIBLE
             progressBar.visibility = INVISIBLE
             appInfo.visibility = GONE
+            val contentDescription: String?
             when (state) {
-                SUCCEEDED -> appStatus.setImageResource(R.drawable.ic_check_green)
-                FAILED -> appStatus.setImageResource(R.drawable.ic_error_red)
+                SUCCEEDED -> {
+                    appStatus.setImageResource(R.drawable.ic_check_green)
+                    contentDescription = context.getString(
+                        if (isRestore) R.string.restore_app_status_restored
+                        else R.string.backup_app_success
+                    )
+                }
+                FAILED -> {
+                    appStatus.setImageResource(R.drawable.ic_error_red)
+                    contentDescription = context.getString(
+                        if (isRestore) R.string.restore_app_status_failed
+                        else R.string.notification_failed_title
+                    )
+                }
                 else -> {
                     appStatus.setImageResource(R.drawable.ic_warning_yellow)
+                    contentDescription = context.getString(
+                        if (isRestore) R.string.restore_app_status_warning
+                        else R.string.backup_app_warning
+                    )
                     appInfo.text =
                         if (isRestore) state.getRestoreText(context)
                         else state.getBackupText(context)
                     appInfo.visibility = VISIBLE
                 }
             }
+            appStatus.contentDescription = contentDescription
         }
     }
 
diff --git a/app/src/main/res/layout/list_item_app_status.xml b/app/src/main/res/layout/list_item_app_status.xml
index 434e508..5d630c7 100644
--- a/app/src/main/res/layout/list_item_app_status.xml
+++ b/app/src/main/res/layout/list_item_app_status.xml
@@ -4,21 +4,22 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="?android:selectableItemBackground"
     android:layout_marginStart="40dp"
     android:layout_marginEnd="40dp"
+    android:background="?android:selectableItemBackground"
     android:paddingTop="8dp"
-    android:paddingBottom="8dp">
+    android:paddingBottom="8dp"
+    android:screenReaderFocusable="true">
 
     <ImageView
         android:id="@+id/appIcon"
         android:layout_width="42dp"
         android:layout_height="42dp"
+        android:importantForAccessibility="no"
         android:scaleType="fitCenter"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"
-        tools:ignore="ContentDescription"
         tools:srcCompat="@tools:sample/avatars" />
 
     <TextView
@@ -72,6 +73,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:clickable="false"
+        android:focusable="false"
         android:visibility="invisible"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d69a44e..43732b5 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -141,6 +141,9 @@
     <!-- This text gets shown for apps that the OS did not try to backup for whatever reason e.g. no backup was run yet -->
     <string name="backup_app_not_yet_backed_up">Waiting to back up…</string>
     <string name="restore_app_not_yet_backed_up">Was not yet backed up</string>
+    <string name="backup_app_in_progress">Backing up</string>
+    <string name="backup_app_success">Backed up</string>
+    <string name="backup_app_warning">Backup warning</string>
     <string name="backup_app_was_stopped">Not backed up as it wasn\'t used recently</string>
     <string name="restore_app_was_stopped">Was not backed up as it hadn\'t been used recently</string>
     <string name="backup_app_no_data">App reported no data for backup</string>
@@ -161,6 +164,12 @@
     <string name="restore_set_error">An error occurred while loading the backups.</string>
     <string name="restore_set_empty_result">No suitable backups found at given location.\n\nThis is most likely due to a wrong recovery code or a storage error.</string>
     <string name="restore_installing_packages">Re-installing apps</string>
+    <string name="restore_app_status_installing">Re-installing</string>
+    <string name="restore_app_status_installed">Re-installed</string>
+    <string name="restore_app_status_install_error">Could not install</string>
+    <string name="restore_app_status_restored">Data restored</string>
+    <string name="restore_app_status_failed">Restore failed</string>
+    <string name="restore_app_status_warning">Restore warning</string>
     <string name="restore_installing_error_title">Some apps not installed</string>
     <string name="restore_installing_error_message">Data can only be restored if an app is installed.\n\nTap failed apps to try to install them manually before proceeding.</string>
     <string name="restore_installing_tap_to_install">Tap to install</string>