Skip to content

Commit e248746

Browse files
feat: show number of backup's generation and total generations count
1 parent 616a37c commit e248746

File tree

6 files changed

+99
-29
lines changed

6 files changed

+99
-29
lines changed

app/src/main/java/org/androidlabs/applistbackup/BackupService.kt

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@ import kotlinx.coroutines.flow.MutableStateFlow
3434
import kotlinx.coroutines.launch
3535
import org.androidlabs.applistbackup.data.BackupAppDetails
3636
import org.androidlabs.applistbackup.data.BackupAppInfo
37+
import org.androidlabs.applistbackup.data.BackupFile
3738
import org.androidlabs.applistbackup.data.BackupFormat
3839
import org.androidlabs.applistbackup.data.BackupFormatResult
3940
import org.androidlabs.applistbackup.data.BackupRawFile
41+
import org.androidlabs.applistbackup.data.FileInfo
4042
import org.androidlabs.applistbackup.settings.Settings
4143
import org.androidlabs.applistbackup.utils.Utils.clearPrefixSlash
4244
import org.androidlabs.applistbackup.utils.Utils.isTV
@@ -50,12 +52,6 @@ import java.util.Date
5052
import java.util.Locale
5153
import java.util.regex.Pattern
5254

53-
data class BackupFile(
54-
val uri: Uri,
55-
val date: Date,
56-
val title: String
57-
)
58-
5955
class BackupService : Service() {
6056
private val tag: String = "BackupService"
6157

@@ -178,22 +174,52 @@ class BackupService : Service() {
178174
fun getBackupFiles(context: Context): List<BackupFile> {
179175
val dateFormat = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault())
180176

181-
return getRawBackupFiles(context)
177+
val files = getRawBackupFiles(context)
178+
179+
val infos = files
182180
.map { file ->
183181
val name = file.name
184182
val dateString = name.removePrefix(FILE_NAME_PREFIX).substringBeforeLast('.')
185-
val date = if (dateString.isNotEmpty()) {
186-
dateFormat.parse(dateString) ?: Date()
187-
} else {
188-
val timestamp = getFileDate(context, file.uri)
189-
Date(timestamp)
183+
val date = try {
184+
if (dateString.isNotEmpty()) {
185+
dateFormat.parse(dateString) ?: Date()
186+
} else {
187+
val timestamp = getFileDate(context, file.uri)
188+
Date(timestamp)
189+
}
190+
} catch (e: Exception) {
191+
Log.e("BackupService", "Error parsing date: ${e.message}")
192+
Date()
190193
}
191-
val title = getTitleFromUri(context, file.uri) ?: name
192-
BackupFile(file.uri, date, title)
194+
195+
getFileInfoFromUri(context, file.uri) ?: FileInfo(
196+
file.uri,
197+
name,
198+
name.substringAfterLast("."),
199+
date
200+
)
193201
}
202+
203+
val filesByGeneration = infos
204+
.groupBy { dateFormat.format(it.date) }
205+
.toSortedMap(reverseOrder())
206+
207+
val generationsCount = filesByGeneration.size
208+
209+
return filesByGeneration.values.flatMapIndexed { generationNumber, filesInGeneration ->
210+
filesInGeneration.map { info ->
211+
BackupFile(
212+
info.uri,
213+
info.date,
214+
info.name,
215+
generationNumber,
216+
generationsCount
217+
)
218+
}
219+
}.sortedByDescending { it.date }
194220
}
195221

196-
fun getTitleFromUri(context: Context, uri: Uri): String? {
222+
fun getFileInfoFromUri(context: Context, uri: Uri): FileInfo? {
197223
val pattern =
198224
Pattern.compile("$FILE_NAME_PREFIX-(\\d{4}-\\d{2}-\\d{2}-\\d{2}-\\d{2}-\\d{2})\\.(\\w+)")
199225
val matcher = pattern.matcher(uri.toString())
@@ -203,10 +229,11 @@ class BackupService : Service() {
203229
val isFind = matcher.find()
204230
if (!isFind) {
205231
val timestamp = getFileDate(context, uri)
206-
val date = titleFormatter.format(Date(timestamp))
232+
val date = Date(timestamp)
233+
val formattedDate = titleFormatter.format(date)
207234
val extension = uri.toString().split(".").last()
208235
val format = extension.let { BackupFormat.fromExtension(it) }
209-
return "$date (${format.value})"
236+
return FileInfo(uri, "$formattedDate (${format.value})", format.value, date)
210237
} else {
211238
val dateString = matcher.group(1)
212239
val extension = matcher.group(2)
@@ -217,9 +244,14 @@ class BackupService : Service() {
217244
if (date != null) {
218245
if (extension != null) {
219246
val format = extension.let { BackupFormat.fromExtension(it) }
220-
return "${titleFormatter.format(date)} (${format.value})"
247+
return FileInfo(
248+
uri,
249+
"${titleFormatter.format(date)} (${format.value})",
250+
format.value,
251+
date
252+
)
221253
} else {
222-
return titleFormatter.format(date)
254+
return FileInfo(uri, titleFormatter.format(date), "", date)
223255
}
224256
} else {
225257
return null

app/src/main/java/org/androidlabs/applistbackup/backupnow/BackupViewModel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
1212
import kotlinx.coroutines.flow.StateFlow
1313
import kotlinx.coroutines.flow.asStateFlow
1414
import kotlinx.coroutines.launch
15-
import org.androidlabs.applistbackup.BackupFile
1615
import org.androidlabs.applistbackup.BackupService
16+
import org.androidlabs.applistbackup.data.BackupFile
1717
import org.androidlabs.applistbackup.settings.Settings
1818

1919
class BackupViewModel(application: Application) : AndroidViewModel(application) {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.androidlabs.applistbackup.data
2+
3+
import android.net.Uri
4+
import java.util.Date
5+
6+
data class BackupFile(
7+
val uri: Uri,
8+
val date: Date,
9+
val title: String,
10+
val generationNumber: Int,
11+
val generationsCount: Int,
12+
) {
13+
fun titleWithGeneration(): String {
14+
return "$title (${generationNumber + 1} of $generationsCount)"
15+
}
16+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.androidlabs.applistbackup.data
2+
3+
import android.net.Uri
4+
import java.util.Date
5+
6+
data class FileInfo(
7+
val uri: Uri,
8+
val name: String,
9+
val format: String,
10+
val date: Date
11+
)

app/src/main/java/org/androidlabs/applistbackup/reader/BackupReaderFragment.kt

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.androidlabs.applistbackup.reader
22

3-
import android.annotation.SuppressLint
43
import android.os.Bundle
54
import android.view.LayoutInflater
65
import android.view.View
@@ -11,6 +10,7 @@ import androidx.compose.foundation.layout.Row
1110
import androidx.compose.foundation.layout.Spacer
1211
import androidx.compose.foundation.layout.fillMaxSize
1312
import androidx.compose.foundation.layout.height
13+
import androidx.compose.foundation.layout.heightIn
1414
import androidx.compose.foundation.layout.padding
1515
import androidx.compose.material.icons.Icons
1616
import androidx.compose.material.icons.filled.ArrowDropDown
@@ -29,6 +29,7 @@ import androidx.compose.runtime.setValue
2929
import androidx.compose.ui.Alignment
3030
import androidx.compose.ui.Modifier
3131
import androidx.compose.ui.platform.ComposeView
32+
import androidx.compose.ui.platform.LocalConfiguration
3233
import androidx.compose.ui.platform.LocalContext
3334
import androidx.compose.ui.res.stringResource
3435
import androidx.compose.ui.unit.dp
@@ -90,7 +91,7 @@ class BackupReaderFragment(
9091
): View {
9192
return ComposeView(requireContext()).apply {
9293
setContent {
93-
DisplayHtmlContent(
94+
DisplayContent(
9495
viewModel = viewModel,
9596
runBackup = ::runBackup
9697
)
@@ -122,15 +123,16 @@ class BackupReaderFragment(
122123
}
123124
}
124125

125-
@SuppressLint("SetJavaScriptEnabled")
126126
@Composable
127-
private fun DisplayHtmlContent(
127+
private fun DisplayContent(
128128
viewModel: BackupViewModel,
129129
runBackup: () -> Unit,
130130
) {
131131
var expanded by remember { mutableStateOf(false) }
132132

133133
val context = LocalContext.current
134+
val configuration = LocalConfiguration.current
135+
val screenHeight = configuration.screenHeightDp.dp
134136

135137
val uri by viewModel.uri.collectAsState()
136138
val backups by viewModel.backupFiles.collectAsState(initial = emptyList())
@@ -140,14 +142,21 @@ private fun DisplayHtmlContent(
140142
val extension = uri.toString().substringAfterLast('.', "").lowercase()
141143
val format = BackupFormat.fromExtension(extension)
142144

145+
val uriBackup = remember(uri, backups) {
146+
backups.find { backup -> backup.uri == uri }
147+
}
148+
149+
val title = uriBackup?.titleWithGeneration()
150+
?: (BackupService.getFileInfoFromUri(context, uri!!)?.name
151+
?: stringResource(R.string.backup))
152+
143153
Column(modifier = Modifier.fillMaxSize()) {
144154
Row(
145155
verticalAlignment = Alignment.CenterVertically,
146156
modifier = Modifier.padding(start = 16.dp, end = 4.dp)
147157
) {
148158
Text(
149-
text = BackupService.getTitleFromUri(context, uri!!)
150-
?: stringResource(R.string.backup),
159+
text = title,
151160
modifier = Modifier
152161
.weight(1f)
153162
)
@@ -162,11 +171,13 @@ private fun DisplayHtmlContent(
162171

163172
DropdownMenu(
164173
expanded = expanded,
165-
onDismissRequest = { expanded = false }
174+
onDismissRequest = { expanded = false },
175+
modifier = Modifier
176+
.heightIn(max = screenHeight * 0.75f)
166177
) {
167178
backups.forEach { backup ->
168179
DropdownMenuItem(text = {
169-
Text(backup.title)
180+
Text(backup.titleWithGeneration())
170181
}, onClick = {
171182
expanded = false
172183
viewModel.setUri(context, backup.uri)

app/src/main/java/org/androidlabs/applistbackup/reader/BackupViewModel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ import kotlinx.coroutines.flow.StateFlow
1919
import kotlinx.coroutines.flow.asStateFlow
2020
import kotlinx.coroutines.launch
2121
import kotlinx.coroutines.withContext
22-
import org.androidlabs.applistbackup.BackupFile
2322
import org.androidlabs.applistbackup.BackupService
23+
import org.androidlabs.applistbackup.data.BackupFile
2424
import org.androidlabs.applistbackup.settings.Settings
2525
import org.androidlabs.applistbackup.utils.Utils.isTV
2626

0 commit comments

Comments
 (0)