diff --git a/app/build/generated/source/proto/debug/kotlin/com/stevesoltys/seedvault/proto/SnapshotKt.kt b/app/build/generated/source/proto/debug/kotlin/com/stevesoltys/seedvault/proto/SnapshotKt.kt index f5141adc..1a4bc94c 100644 --- a/app/build/generated/source/proto/debug/kotlin/com/stevesoltys/seedvault/proto/SnapshotKt.kt +++ b/app/build/generated/source/proto/debug/kotlin/com/stevesoltys/seedvault/proto/SnapshotKt.kt @@ -537,6 +537,23 @@ public object SnapshotKt { public fun hasApk(): kotlin.Boolean { return _builder.hasApk() } + + /** + * uint64 size = 8; + */ + public var size: kotlin.Long + @JvmName("getSize") + get() = _builder.getSize() + @JvmName("setSize") + set(value) { + _builder.setSize(value) + } + /** + * uint64 size = 8; + */ + public fun clearSize() { + _builder.clearSize() + } } } @kotlin.jvm.JvmName("-initializeapk") diff --git a/app/src/main/java/com/stevesoltys/seedvault/repo/SnapshotCreator.kt b/app/src/main/java/com/stevesoltys/seedvault/repo/SnapshotCreator.kt index eda04826..37d615a8 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/repo/SnapshotCreator.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/repo/SnapshotCreator.kt @@ -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) diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreSetAdapter.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreSetAdapter.kt index cdf15cc0..bf89c92e 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreSetAdapter.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/restore/RestoreSetAdapter.kt @@ -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(R.id.titleView) - private val subtitleView = v.requireViewById(R.id.subtitleView) - private val sizeView = v.requireViewById(R.id.sizeView) + private val appView = v.requireViewById(R.id.appView) + private val apkView = v.requireViewById(R.id.apkView) + private val timeView = v.requireViewById(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) } } diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestorableBackup.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestorableBackup.kt index cbadca53..2dc7de45 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestorableBackup.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/RestorableBackup.kt @@ -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() } - ?: backupMetadata.size + 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 diff --git a/app/src/main/proto/snapshot.proto b/app/src/main/proto/snapshot.proto index 142a3ea7..80ac4b4f 100644 --- a/app/src/main/proto/snapshot.proto +++ b/app/src/main/proto/snapshot.proto @@ -25,6 +25,7 @@ message Snapshot { bool launchableSystemApp = 5; repeated bytes chunkIds = 6; Apk apk = 7; + uint64 size = 8; } enum BackupType { diff --git a/app/src/main/res/layout/list_item_restore_set.xml b/app/src/main/res/layout/list_item_restore_set.xml index df9c65c0..8ea4b827 100644 --- a/app/src/main/res/layout/list_item_restore_set.xml +++ b/app/src/main/res/layout/list_item_restore_set.xml @@ -1,5 +1,4 @@ - - @@ -25,37 +24,56 @@ + tools:text="@string/restore_restore_set_apps" /> + app:layout_constraintTop_toBottomOf="@+id/appView" + tools:text="@string/restore_restore_set_apks" /> + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5bf7309e..610858d2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -205,8 +205,10 @@ Restore from backup Choose a backup to restore - Last backup %1$s ยท First %2$s. - Size: %1$s + Has user data for %1$d apps (%2$s) + Has user data for %1$d apps + Contains %1$d apps (%2$s) + Contains %1$d apps Don\'t restore Skip restoring apps No backups found diff --git a/app/src/test/java/com/stevesoltys/seedvault/repo/SnapshotCreatorTest.kt b/app/src/test/java/com/stevesoltys/seedvault/repo/SnapshotCreatorTest.kt index d4e4005a..52f1f876 100644 --- a/app/src/test/java/com/stevesoltys/seedvault/repo/SnapshotCreatorTest.kt +++ b/app/src/test/java/com/stevesoltys/seedvault/repo/SnapshotCreatorTest.kt @@ -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)