Improve RestoreSet display
when user is asked to choose a backup to restore
This commit is contained in:
parent
84dc13d267
commit
7696b88a5a
8 changed files with 102 additions and 39 deletions
|
@ -537,6 +537,23 @@ public object SnapshotKt {
|
|||
public fun hasApk(): kotlin.Boolean {
|
||||
return _builder.hasApk()
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>uint64 size = 8;</code>
|
||||
*/
|
||||
public var size: kotlin.Long
|
||||
@JvmName("getSize")
|
||||
get() = _builder.getSize()
|
||||
@JvmName("setSize")
|
||||
set(value) {
|
||||
_builder.setSize(value)
|
||||
}
|
||||
/**
|
||||
* <code>uint64 size = 8;</code>
|
||||
*/
|
||||
public fun clearSize() {
|
||||
_builder.clearSize()
|
||||
}
|
||||
}
|
||||
}
|
||||
@kotlin.jvm.JvmName("-initializeapk")
|
||||
|
|
|
@ -97,6 +97,7 @@ internal class SnapshotCreator(
|
|||
system = isSystemApp
|
||||
launchableSystemApp = isSystemApp && launchableSystemApps.contains(packageName)
|
||||
addAllChunkIds(chunkIds)
|
||||
size = backupData.size
|
||||
}
|
||||
blobsMap.putAll(backupData.blobMap)
|
||||
metadataManager.onPackageBackedUp(packageInfo, backupType, backupData.size)
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
package com.stevesoltys.seedvault.restore
|
||||
|
||||
import android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE
|
||||
import android.text.format.DateUtils.HOUR_IN_MILLIS
|
||||
import android.text.format.DateUtils.MINUTE_IN_MILLIS
|
||||
import android.text.format.DateUtils.getRelativeTimeSpanString
|
||||
import android.text.format.Formatter
|
||||
import android.text.format.Formatter.formatShortFileSize
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
|
@ -41,32 +41,40 @@ internal class RestoreSetAdapter(
|
|||
inner class RestoreSetViewHolder(private val v: View) : ViewHolder(v) {
|
||||
|
||||
private val titleView = v.requireViewById<TextView>(R.id.titleView)
|
||||
private val subtitleView = v.requireViewById<TextView>(R.id.subtitleView)
|
||||
private val sizeView = v.requireViewById<TextView>(R.id.sizeView)
|
||||
private val appView = v.requireViewById<TextView>(R.id.appView)
|
||||
private val apkView = v.requireViewById<TextView>(R.id.apkView)
|
||||
private val timeView = v.requireViewById<TextView>(R.id.timeView)
|
||||
|
||||
internal fun bind(item: RestorableBackup) {
|
||||
v.setOnClickListener { listener.onRestorableBackupClicked(item) }
|
||||
titleView.text = item.name
|
||||
|
||||
val lastBackup = getRelativeTime(item.time)
|
||||
val setup = getRelativeTime(item.token)
|
||||
subtitleView.text =
|
||||
v.context.getString(R.string.restore_restore_set_times, lastBackup, setup)
|
||||
val size = item.size
|
||||
if (size == null) {
|
||||
sizeView.visibility = GONE
|
||||
} else {
|
||||
sizeView.text = v.context.getString(
|
||||
R.string.restore_restore_set_size,
|
||||
Formatter.formatShortFileSize(v.context, size),
|
||||
appView.text = if (item.sizeAppData > 0) {
|
||||
v.context.getString(
|
||||
R.string.restore_restore_set_apps,
|
||||
item.numAppData,
|
||||
formatShortFileSize(v.context, item.sizeAppData),
|
||||
)
|
||||
sizeView.visibility = VISIBLE
|
||||
} else {
|
||||
v.context.getString(R.string.restore_restore_set_apps_no_size, item.numAppData)
|
||||
}
|
||||
appView.visibility = if (item.numAppData > 0) VISIBLE else GONE
|
||||
apkView.text = if (item.sizeApks > 0) {
|
||||
v.context.getString(
|
||||
R.string.restore_restore_set_apks,
|
||||
item.numApks,
|
||||
formatShortFileSize(v.context, item.sizeApks),
|
||||
)
|
||||
} else {
|
||||
v.context.getString(R.string.restore_restore_set_apks_no_size, item.numApks)
|
||||
}
|
||||
apkView.visibility = if (item.numApks > 0) VISIBLE else GONE
|
||||
timeView.text = getRelativeTime(item.time)
|
||||
}
|
||||
|
||||
private fun getRelativeTime(time: Long): CharSequence {
|
||||
val now = System.currentTimeMillis()
|
||||
return getRelativeTimeSpanString(time, now, HOUR_IN_MILLIS, FORMAT_ABBREV_RELATIVE)
|
||||
return getRelativeTimeSpanString(time, now, MINUTE_IN_MILLIS, FORMAT_ABBREV_RELATIVE)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ package com.stevesoltys.seedvault.transport.restore
|
|||
|
||||
import com.stevesoltys.seedvault.metadata.BackupMetadata
|
||||
import com.stevesoltys.seedvault.metadata.PackageMetadataMap
|
||||
import com.stevesoltys.seedvault.metadata.PackageState.APK_AND_DATA
|
||||
import com.stevesoltys.seedvault.proto.Snapshot
|
||||
|
||||
sealed class RestorableBackupResult {
|
||||
|
@ -20,7 +21,6 @@ data class RestorableBackup(
|
|||
val snapshot: Snapshot? = null,
|
||||
) {
|
||||
|
||||
// FIXME creating this mapping is expensive, a single call can take several seconds to complete
|
||||
constructor(repoId: String, snapshot: Snapshot) : this(
|
||||
backupMetadata = BackupMetadata.fromSnapshot(snapshot),
|
||||
repoId = repoId,
|
||||
|
@ -40,18 +40,33 @@ data class RestorableBackup(
|
|||
get() = backupMetadata.salt
|
||||
|
||||
val time: Long
|
||||
get() = backupMetadata.time
|
||||
get() = snapshot?.token ?: backupMetadata.time
|
||||
|
||||
val size: Long
|
||||
get() = snapshot?.blobsMap?.values?.sumOf { it.uncompressedLength.toLong() }
|
||||
val size: Long = snapshot?.blobsMap?.values?.sumOf { it.uncompressedLength.toLong() }
|
||||
?: backupMetadata.size
|
||||
|
||||
val deviceName: String
|
||||
get() = backupMetadata.deviceName
|
||||
|
||||
val user: String?
|
||||
get() = snapshot?.user?.takeIf { it.isNotBlank() }
|
||||
|
||||
val d2dBackup: Boolean
|
||||
get() = backupMetadata.d2dBackup
|
||||
|
||||
val numAppData: Int = snapshot?.appsMap?.values?.count { it.chunkIdsCount > 0 }
|
||||
?: packageMetadataMap.values.count { packageMetadata ->
|
||||
packageMetadata.backupType != null && packageMetadata.state == APK_AND_DATA
|
||||
}
|
||||
|
||||
val sizeAppData: Long = snapshot?.appsMap?.values?.sumOf { it.size }
|
||||
?: packageMetadataMap.values.sumOf { it.size ?: 0L }
|
||||
|
||||
val numApks: Int = snapshot?.appsMap?.values?.count { it.apk.splitsCount > 0 }
|
||||
?: packageMetadataMap.values.count { it.hasApk() }
|
||||
|
||||
val sizeApks: Long = size - sizeAppData
|
||||
|
||||
val packageMetadataMap: PackageMetadataMap
|
||||
get() = backupMetadata.packageMetadataMap
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ message Snapshot {
|
|||
bool launchableSystemApp = 5;
|
||||
repeated bytes chunkIds = 6;
|
||||
Apk apk = 7;
|
||||
uint64 size = 8;
|
||||
}
|
||||
|
||||
enum BackupType {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
SPDX-FileCopyrightText: 2020 The Calyx Institute
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
@ -25,37 +24,56 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/titleView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="18sp"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/appView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Pixel 2 XL backup" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitleView"
|
||||
android:id="@+id/appView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textColor="?android:attr/textColorTertiary"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@+id/titleView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/titleView"
|
||||
tools:text="@string/restore_restore_set_times" />
|
||||
tools:text="@string/restore_restore_set_apps" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sizeView"
|
||||
android:id="@+id/apkView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/timeView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@+id/titleView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/subtitleView"
|
||||
tools:text="Size: 5 GB" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/appView"
|
||||
tools:text="@string/restore_restore_set_apks" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timeView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:gravity="end"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@+id/titleView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/apkView"
|
||||
tools:text="5 days ago" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -205,8 +205,10 @@
|
|||
<!-- Restore -->
|
||||
<string name="restore_title">Restore from backup</string>
|
||||
<string name="restore_choose_restore_set">Choose a backup to restore</string>
|
||||
<string name="restore_restore_set_times">Last backup %1$s · First %2$s.</string>
|
||||
<string name="restore_restore_set_size">Size: <xliff:g example="1 GB" id="size">%1$s</xliff:g></string>
|
||||
<string name="restore_restore_set_apps">Has user data for <xliff:g example="42" id="apps">%1$d</xliff:g> apps (<xliff:g example="1 GB" id="size">%2$s</xliff:g>)</string>
|
||||
<string name="restore_restore_set_apps_no_size">Has user data for <xliff:g example="42" id="apps">%1$d</xliff:g> apps</string>
|
||||
<string name="restore_restore_set_apks">Contains <xliff:g example="42" id="apps">%1$d</xliff:g> apps (<xliff:g example="1 GB" id="size">%2$s</xliff:g>)</string>
|
||||
<string name="restore_restore_set_apks_no_size">Contains <xliff:g example="42" id="apps">%1$d</xliff:g> apps</string>
|
||||
<string name="restore_skip">Don\'t restore</string>
|
||||
<string name="restore_skip_apps">Skip restoring apps</string>
|
||||
<string name="restore_invalid_location_title">No backups found</string>
|
||||
|
|
|
@ -77,6 +77,7 @@ internal class SnapshotCreatorTest : TransportTest() {
|
|||
assertEquals(name, s.appsMap[packageName]?.name)
|
||||
assertEquals(token, s.appsMap[packageName]?.time)
|
||||
assertEquals(Snapshot.BackupType.FULL, s.appsMap[packageName]?.type)
|
||||
assertEquals(size, s.appsMap[packageName]?.size)
|
||||
assertEquals(isSystem, s.appsMap[packageName]?.system)
|
||||
assertEquals(isSystem, s.appsMap[packageName]?.launchableSystemApp)
|
||||
assertEquals(apkBackupData.chunkIds.forProto(), s.appsMap[packageName]?.chunkIdsList)
|
||||
|
|
Loading…
Reference in a new issue