Show list of packages that we could restore data for
This commit is contained in:
parent
96a4642f4f
commit
2f352fe828
6 changed files with 119 additions and 44 deletions
|
@ -72,5 +72,5 @@ class NotificationBackupObserver(context: Context, private val userInitiated: Bo
|
|||
fun getAppName(pm: PackageManager, packageId: String): CharSequence {
|
||||
if (packageId == MAGIC_PACKAGE_MANAGER) return packageId
|
||||
val appInfo = pm.getApplicationInfo(packageId, 0)
|
||||
return pm.getApplicationLabel(appInfo)
|
||||
return pm.getApplicationLabel(appInfo) ?: packageId
|
||||
}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package com.stevesoltys.seedvault.restore
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView.Adapter
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER
|
||||
import com.stevesoltys.seedvault.R
|
||||
import com.stevesoltys.seedvault.getAppName
|
||||
import com.stevesoltys.seedvault.restore.RestoreProgressAdapter.PackageViewHolder
|
||||
import java.util.*
|
||||
|
||||
internal class RestoreProgressAdapter : Adapter<PackageViewHolder>() {
|
||||
|
||||
private val items = LinkedList<String>().apply { add(MAGIC_PACKAGE_MANAGER) }
|
||||
private var isComplete = false
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PackageViewHolder {
|
||||
val v = LayoutInflater.from(parent.context).inflate(R.layout.list_item_app_status, parent, false)
|
||||
return PackageViewHolder(v)
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun onBindViewHolder(holder: PackageViewHolder, position: Int) {
|
||||
holder.bind(items[position], position == 0)
|
||||
}
|
||||
|
||||
fun add(packageName: String) {
|
||||
items.addFirst(packageName)
|
||||
notifyItemInserted(0)
|
||||
notifyItemRangeChanged(1, items.size - 1)
|
||||
}
|
||||
|
||||
fun setComplete() {
|
||||
isComplete = true
|
||||
notifyItemChanged(0)
|
||||
}
|
||||
|
||||
inner class PackageViewHolder(v: View) : ViewHolder(v) {
|
||||
|
||||
private val context = v.context
|
||||
private val pm = context.packageManager
|
||||
private val appIcon: ImageView = v.findViewById(R.id.appIcon)
|
||||
private val appName: TextView = v.findViewById(R.id.appName)
|
||||
private val appStatus: ImageView = v.findViewById(R.id.appStatus)
|
||||
private val progressBar: ProgressBar = v.findViewById(R.id.progressBar)
|
||||
|
||||
init {
|
||||
appStatus.setImageResource(R.drawable.ic_check_green)
|
||||
}
|
||||
|
||||
fun bind(item: String, isLast: Boolean) {
|
||||
if (item == MAGIC_PACKAGE_MANAGER) {
|
||||
appIcon.setImageDrawable(pm.getApplicationIcon("android"))
|
||||
appName.text = context.getString(R.string.restore_magic_package)
|
||||
} else {
|
||||
appIcon.setImageDrawable(pm.getApplicationIcon(item))
|
||||
appName.text = getAppName(pm, item)
|
||||
}
|
||||
if (isLast && !isComplete) {
|
||||
appStatus.visibility = INVISIBLE
|
||||
progressBar.visibility = VISIBLE
|
||||
} else {
|
||||
appStatus.visibility = VISIBLE
|
||||
progressBar.visibility = INVISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -4,16 +4,15 @@ import android.app.Activity.RESULT_OK
|
|||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||
import androidx.core.content.ContextCompat.getColor
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL
|
||||
import com.stevesoltys.seedvault.R
|
||||
import com.stevesoltys.seedvault.getAppName
|
||||
import com.stevesoltys.seedvault.isDebugBuild
|
||||
import kotlinx.android.synthetic.main.fragment_restore_progress.*
|
||||
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
||||
|
||||
|
@ -21,11 +20,27 @@ class RestoreProgressFragment : Fragment() {
|
|||
|
||||
private val viewModel: RestoreViewModel by sharedViewModel()
|
||||
|
||||
private val layoutManager = LinearLayoutManager(context)
|
||||
private val adapter = RestoreProgressAdapter()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_restore_progress, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
appList.apply {
|
||||
layoutManager = this@RestoreProgressFragment.layoutManager
|
||||
adapter = this@RestoreProgressFragment.adapter
|
||||
addItemDecoration(DividerItemDecoration(context, VERTICAL))
|
||||
}
|
||||
|
||||
button.setOnClickListener {
|
||||
requireActivity().setResult(RESULT_OK)
|
||||
requireActivity().finishAfterTransition()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
|
@ -37,27 +52,23 @@ class RestoreProgressFragment : Fragment() {
|
|||
})
|
||||
|
||||
viewModel.restoreProgress.observe(this, Observer { currentPackage ->
|
||||
val appName = getAppName(requireActivity().packageManager, currentPackage)
|
||||
val displayName = if (isDebugBuild()) "$appName (${currentPackage})" else appName
|
||||
currentPackageView.text = getString(R.string.restore_current_package, displayName)
|
||||
// TODO maybe check against metadata and add packages that weren't called as failed in the end
|
||||
val position = layoutManager.findFirstVisibleItemPosition()
|
||||
adapter.add(currentPackage)
|
||||
if (position == 0) layoutManager.scrollToPosition(0)
|
||||
})
|
||||
|
||||
viewModel.restoreBackupResult.observe(this, Observer { finished ->
|
||||
progressBar.visibility = INVISIBLE
|
||||
button.visibility = VISIBLE
|
||||
adapter.setComplete()
|
||||
button.isEnabled = true
|
||||
if (finished.hasError()) {
|
||||
currentPackageView.text = finished.errorMsg
|
||||
currentPackageView.setTextColor(getColor(requireContext(), R.color.red))
|
||||
backupNameView.text = finished.errorMsg
|
||||
backupNameView.setTextColor(getColor(requireContext(), R.color.red))
|
||||
} else {
|
||||
currentPackageView.text = getString(R.string.restore_finished_success)
|
||||
backupNameView.text = getString(R.string.restore_finished_success)
|
||||
}
|
||||
activity?.window?.clearFlags(FLAG_KEEP_SCREEN_ON)
|
||||
})
|
||||
|
||||
button.setOnClickListener {
|
||||
requireActivity().setResult(RESULT_OK)
|
||||
requireActivity().finishAfterTransition()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,30 +41,16 @@
|
|||
app:layout_constraintTop_toBottomOf="@+id/titleView"
|
||||
tools:text="Pixel 2 XL" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/currentPackageView"
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/appList"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/backupNameView"
|
||||
tools:text="@string/restore_current_package" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_margin="16dp"
|
||||
android:indeterminate="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/backupNameView"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/currentPackageView" />
|
||||
tools:listitem="@layout/list_item_app_status" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
|
@ -74,11 +60,10 @@
|
|||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/restore_finished_button"
|
||||
android:visibility="invisible"
|
||||
android:enabled="false"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/progressBar"
|
||||
app:layout_constraintVertical_bias="1.0"
|
||||
tools:visibility="visible" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/appList"
|
||||
app:layout_constraintVertical_bias="1.0" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -92,7 +92,8 @@
|
|||
<string name="restore_next">Next</string>
|
||||
<string name="restore_restoring">Restoring Backup</string>
|
||||
<string name="restore_current_package">Restoring %s…</string>
|
||||
<string name="restore_finished_success">Restore complete.</string>
|
||||
<string name="restore_magic_package">System Package Manager</string>
|
||||
<string name="restore_finished_success">Restore complete</string>
|
||||
<string name="restore_finished_error">An error occurred while restoring the backup.</string>
|
||||
<string name="restore_finished_button">Finish</string>
|
||||
<string name="storage_internal_warning_title">Warning</string>
|
||||
|
|
|
@ -143,7 +143,7 @@ internal class ApkRestoreTest : RestoreTest() {
|
|||
@Test
|
||||
fun `test successful run`(@TempDir tmpDir: Path) = runBlocking {
|
||||
val installResult = MutableInstallResult(1).apply {
|
||||
put(packageName, ApkRestoreResult(progress = 1, total = 1, status = SUCCEEDED))
|
||||
put(packageName, ApkRestoreResult(packageName, progress = 1, total = 1, status = SUCCEEDED))
|
||||
}
|
||||
|
||||
every { strictContext.cacheDir } returns File(tmpDir.toString())
|
||||
|
|
Loading…
Reference in a new issue