Skip to content

Commit 220a027

Browse files
committed
config backup; log backup duration
1 parent 9083c8c commit 220a027

File tree

7 files changed

+50
-28
lines changed

7 files changed

+50
-28
lines changed

app/src/main/java/de/lolhens/resticui/BackupManager.kt

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ class BackupManager private constructor(context: Context) {
247247

248248
updateNotification(context, folder.id, activeBackup)
249249

250+
val beforeBackup = ZonedDateTime.now()
251+
250252
resticRepo.backup(
251253
listOf(folder.path),
252254
{ progress ->
@@ -267,19 +269,23 @@ class BackupManager private constructor(context: Context) {
267269
if (throwable == null || cancelled) null
268270
else throwable.message
269271

272+
val afterBackup = ZonedDateTime.now()
273+
270274
val historyEntry = BackupHistoryEntry(
271-
timestamp = ZonedDateTime.now(),
272-
duration = HourDuration(Duration.ZERO),
275+
timestamp = afterBackup,
276+
duration = Duration.ofMillis(
277+
afterBackup.toInstant().toEpochMilli() - beforeBackup.toInstant().toEpochMilli()
278+
),
273279
scheduled = scheduled,
274280
cancelled = cancelled,
275281
snapshotId = summary?.snapshot_id,
276282
errorMessage = errorMessage
277283
)
278284

279285
configure { config ->
280-
config.copy(folders = config.folders.map { folder ->
281-
if (folder.id == folder.id) folder.plusHistoryEntry(historyEntry)
282-
else folder
286+
config.copy(folders = config.folders.map { currentFolder ->
287+
if (currentFolder.id == folder.id) currentFolder.plusHistoryEntry(historyEntry)
288+
else currentFolder
283289
})
284290
}
285291

@@ -291,7 +297,7 @@ class BackupManager private constructor(context: Context) {
291297
if (removeOld && throwable == null && (folder.keepLast != null || folder.keepWithin != null)) {
292298
resticRepo.forget(
293299
folder.keepLast,
294-
folder.keepWithin?.duration,
300+
folder.keepWithin,
295301
prune = true
296302
).handle { _, _ ->
297303
callback()

app/src/main/java/de/lolhens/resticui/Serializers.kt

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
66
import kotlinx.serialization.descriptors.SerialDescriptor
77
import kotlinx.serialization.encoding.Decoder
88
import kotlinx.serialization.encoding.Encoder
9+
import kotlinx.serialization.json.JsonPrimitive
10+
import kotlinx.serialization.json.long
911
import java.io.File
1012
import java.net.URI
1113
import java.time.Duration
@@ -22,17 +24,21 @@ object ZonedDateTimeSerializer : KSerializer<ZonedDateTime> {
2224
ZonedDateTime.parse(decoder.decodeString())
2325
}
2426

25-
data class HourDuration(val duration: Duration)
26-
27-
object HourDurationSerializer : KSerializer<HourDuration> {
27+
object DurationSerializer : KSerializer<Duration> {
2828
override val descriptor: SerialDescriptor =
29-
PrimitiveSerialDescriptor((HourDuration::class).simpleName!!, PrimitiveKind.LONG)
30-
31-
override fun serialize(encoder: Encoder, value: HourDuration) =
32-
encoder.encodeLong(value.duration.toHours())
33-
34-
override fun deserialize(decoder: Decoder): HourDuration =
35-
HourDuration(Duration.ofHours(decoder.decodeLong()))
29+
PrimitiveSerialDescriptor("Custom${(Duration::class).simpleName!!}", PrimitiveKind.STRING)
30+
31+
override fun serialize(encoder: Encoder, value: Duration) =
32+
encoder.encodeString("${value.toMillis()}ms")
33+
34+
override fun deserialize(decoder: Decoder): Duration {
35+
val value = decoder.decodeSerializableValue(JsonPrimitive.serializer())
36+
if (value.isString && value.content.endsWith("ms")) {
37+
return Duration.ofMillis(value.content.dropLast(2).toLong())
38+
} else {
39+
return Duration.ofHours(value.long)
40+
}
41+
}
3642
}
3743

3844
object FileSerializer : KSerializer<File> {

app/src/main/java/de/lolhens/resticui/config/BackupHistoryEntry.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
package de.lolhens.resticui.config
22

3-
import de.lolhens.resticui.HourDuration
4-
import de.lolhens.resticui.HourDurationSerializer
3+
import de.lolhens.resticui.DurationSerializer
54
import de.lolhens.resticui.ZonedDateTimeSerializer
65
import de.lolhens.resticui.restic.ResticSnapshotId
76
import kotlinx.serialization.Serializable
7+
import java.time.Duration
88
import java.time.ZonedDateTime
99

1010
@Serializable
1111
data class BackupHistoryEntry(
1212
val timestamp: @Serializable(with = ZonedDateTimeSerializer::class) ZonedDateTime,
13-
val duration: @Serializable(with = HourDurationSerializer::class) HourDuration,
13+
val duration: @Serializable(with = DurationSerializer::class) Duration,
1414
val scheduled: Boolean,
1515
val cancelled: Boolean,
1616
val snapshotId: ResticSnapshotId?,

app/src/main/java/de/lolhens/resticui/config/ConfigManager.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import androidx.security.crypto.MasterKey
66
import java.io.ByteArrayOutputStream
77
import java.io.File
88
import java.nio.charset.StandardCharsets
9+
import java.time.Instant
910

1011
class ConfigManager(
1112
context: Context,
@@ -54,13 +55,22 @@ class ConfigManager(
5455
return Config.fromJsonString(json)
5556
}
5657

58+
fun backupConfigFile(file: File) {
59+
if (file.exists()) {
60+
val now = Instant.now().toEpochMilli()
61+
val configFileBackup = context.dataDir.resolve("${configFile.name}.$now.backup")
62+
file.copyTo(configFileBackup)
63+
}
64+
}
65+
5766
Secret.loadKey(context)
5867

5968
if (configFile.exists()) {
6069
try {
6170
return readConfigFile(configFile)
6271
} catch (e: Exception) {
6372
e.printStackTrace()
73+
backupConfigFile(configFile)
6474
}
6575
}
6676

@@ -71,6 +81,7 @@ class ConfigManager(
7181
return readConfigFile(configFile)
7282
} catch (e: Exception) {
7383
e.printStackTrace()
84+
backupConfigFile(configFile)
7485
}
7586
}
7687

@@ -80,6 +91,7 @@ class ConfigManager(
8091
return readEncryptedConfig(context, encryptedConfigFile)
8192
} catch (e: Exception) {
8293
e.printStackTrace()
94+
backupConfigFile(encryptedConfigFile)
8395
}
8496
}
8597

app/src/main/java/de/lolhens/resticui/config/FolderConfig.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package de.lolhens.resticui.config
22

3+
import de.lolhens.resticui.DurationSerializer
34
import de.lolhens.resticui.FileSerializer
4-
import de.lolhens.resticui.HourDuration
5-
import de.lolhens.resticui.HourDurationSerializer
65
import de.lolhens.resticui.ui.folder.FolderEditFragment
76
import kotlinx.serialization.Serializable
87
import java.io.File
8+
import java.time.Duration
99
import java.time.ZonedDateTime
1010

1111
@Serializable
@@ -16,7 +16,7 @@ data class FolderConfig(
1616
val path: @Serializable(with = FileSerializer::class) File,
1717
val schedule: String,
1818
val keepLast: Int? = null,
19-
val keepWithin: @Serializable(with = HourDurationSerializer::class) HourDuration? = null,
19+
val keepWithin: @Serializable(with = DurationSerializer::class) Duration? = null,
2020
val history: List<BackupHistoryEntry> = emptyList()
2121
) {
2222
fun repo(config: Config): RepoConfig? = config.repos.find { it.base.id == repoId }
@@ -41,8 +41,7 @@ data class FolderConfig(
4141
fun shouldBackup(dateTime: ZonedDateTime): Boolean {
4242
val scheduleMinutes = FolderEditFragment.schedules.find { it.first == schedule }?.second
4343
if (scheduleMinutes == null || scheduleMinutes < 0) return false
44-
val lastBackup = lastBackup(filterScheduled = true)?.timestamp
45-
if (lastBackup == null) return true
44+
val lastBackup = lastBackup(filterScheduled = true)?.timestamp ?: return true
4645
var quantized = lastBackup.withMinute(0).withSecond(0).withNano(0)
4746
if (scheduleMinutes >= 24 * 60)
4847
quantized = quantized.withHour(0)

app/src/main/java/de/lolhens/resticui/ui/folder/FolderEditFragment.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import android.view.*
55
import android.widget.ArrayAdapter
66
import androidx.fragment.app.Fragment
77
import de.lolhens.resticui.BackupManager
8-
import de.lolhens.resticui.HourDuration
98
import de.lolhens.resticui.R
109
import de.lolhens.resticui.config.FolderConfig
1110
import de.lolhens.resticui.config.FolderConfigId
@@ -110,7 +109,7 @@ class FolderEditFragment : Fragment() {
110109
binding.editFolder.setText(folder.path.path)
111110
binding.spinnerSchedule.setSelection(schedules.indexOfFirst { it.first == folder.schedule })
112111
val scheduleIndex = retainProfiles.indexOfFirst {
113-
it.toLong() == folder.keepWithin?.duration?.toHours()
112+
it.toLong() == folder.keepWithin?.toHours()
114113
}
115114
binding.spinnerRetainWithin.setSelection(if (scheduleIndex == -1) 0 else scheduleIndex)
116115
}
@@ -134,7 +133,7 @@ class FolderEditFragment : Fragment() {
134133
val schedule = binding.spinnerSchedule.selectedItem?.toString()
135134
val keepWithin =
136135
if (retainProfiles[binding.spinnerRetainWithin.selectedItemPosition] < 0) null
137-
else HourDuration(Duration.ofHours(retainProfiles[binding.spinnerRetainWithin.selectedItemPosition].toLong()))
136+
else Duration.ofHours(retainProfiles[binding.spinnerRetainWithin.selectedItemPosition].toLong())
138137

139138
if (
140139
repo != null &&

app/src/main/java/de/lolhens/resticui/ui/folder/FolderFragment.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class FolderFragment : Fragment() {
7373
if (folder.keepLast == null) "" else "in last ${folder.keepLast}",
7474
if (folder.keepWithin == null) "" else "within ${
7575
Formatters.durationDaysHours(
76-
folder.keepWithin.duration
76+
folder.keepWithin
7777
)
7878
}"
7979
).filter { it.isNotEmpty() }.joinToString(" and ")

0 commit comments

Comments
 (0)