@@ -3,9 +3,7 @@ package org.mozilla.tryfox.data
33import kotlinx.datetime.Clock
44import kotlinx.datetime.DateTimeUnit
55import kotlinx.datetime.LocalDate
6- import kotlinx.datetime.LocalDateTime
76import kotlinx.datetime.TimeZone
8- import kotlinx.datetime.format.char
97import kotlinx.datetime.minus
108import kotlinx.datetime.todayIn
119import org.mozilla.tryfox.model.ParsedNightlyApk
@@ -33,22 +31,27 @@ class MozillaArchiveRepositoryImpl(
3331 }
3432 }
3533
36- private suspend fun getNightlyBuilds (appName : String ): NetworkResult <List <ParsedNightlyApk >> {
34+ private suspend fun getNightlyBuilds (appName : String , date : LocalDate ? = null): NetworkResult <List <ParsedNightlyApk >> {
35+ if (date != null ) {
36+ val url = archiveUrlForDate(appName, date)
37+ return fetchAndParseNightlyBuilds(url, appName, date)
38+ }
39+
3740 val today = clock.todayIn(TimeZone .currentSystemDefault())
3841 val currentMonthUrl = archiveUrlForDate(appName, today)
39- val result = fetchAndParseNightlyBuilds(currentMonthUrl, appName)
42+ val result = fetchAndParseNightlyBuilds(currentMonthUrl, appName, null )
4043
4144 if (result is NetworkResult .Error && (result.cause as ? HttpException )?.code() == 404 ) {
4245 val lastMonth = today.minus(1 , DateTimeUnit .MONTH )
4346 val lastMonthUrl = archiveUrlForDate(appName, lastMonth)
44- return fetchAndParseNightlyBuilds(lastMonthUrl, appName)
47+ return fetchAndParseNightlyBuilds(lastMonthUrl, appName, null )
4548 }
4649 return result
4750 }
4851
49- override suspend fun getFenixNightlyBuilds (): NetworkResult <List <ParsedNightlyApk >> = getNightlyBuilds(FENIX )
52+ override suspend fun getFenixNightlyBuilds (date : LocalDate ? ): NetworkResult <List <ParsedNightlyApk >> = getNightlyBuilds(FENIX , date )
5053
51- override suspend fun getFocusNightlyBuilds (): NetworkResult <List <ParsedNightlyApk >> = getNightlyBuilds(FOCUS )
54+ override suspend fun getFocusNightlyBuilds (date : LocalDate ? ): NetworkResult <List <ParsedNightlyApk >> = getNightlyBuilds(FOCUS , date )
5255
5356 override suspend fun getReferenceBrowserNightlyBuilds (): NetworkResult <List <ParsedNightlyApk >> {
5457 return try {
@@ -71,10 +74,10 @@ class MozillaArchiveRepositoryImpl(
7174 }
7275 }
7376
74- private suspend fun fetchAndParseNightlyBuilds (archiveBaseUrl : String , appNameFilter : String ): NetworkResult <List <ParsedNightlyApk >> {
77+ private suspend fun fetchAndParseNightlyBuilds (archiveBaseUrl : String , appNameFilter : String , date : LocalDate ? ): NetworkResult <List <ParsedNightlyApk >> {
7578 return try {
7679 val htmlResult = archiveApiService.getHtmlPage(archiveBaseUrl)
77- val parsedApks = parseNightlyBuildsFromHtml(htmlResult, archiveBaseUrl, appNameFilter)
80+ val parsedApks = parseNightlyBuildsFromHtml(htmlResult, archiveBaseUrl, appNameFilter, date )
7881 NetworkResult .Success (parsedApks)
7982 } catch (e: Exception ) {
8083 NetworkResult .Error (" Failed to fetch or parse $appNameFilter builds: ${e.message} " , e)
@@ -85,24 +88,28 @@ class MozillaArchiveRepositoryImpl(
8588 html : String ,
8689 archiveUrl : String ,
8790 app : String ,
91+ date : LocalDate ? ,
8892 ): List <ParsedNightlyApk > {
89- val htmlPattern = Regex (""" <td>Dir</td>\s*<td><a href="[^"]*">([^<]+/)</a></td>"" " )
93+ val htmlPattern = Regex (" <td>Dir</td>\\ s*<td><a href=\ " [^\ " ]*\ " >([^<]+/)</a></td>" )
9094 val rawBuildStrings = htmlPattern.findAll(html)
9195 .mapNotNull { it.groups[1 ]?.value }
9296 .filter { it != " ../" }
9397 .toList()
9498
95- val buildsByDate = rawBuildStrings.groupBy { it.substringBefore(" -$app " ) }
96- if (buildsByDate.isEmpty()) return emptyList()
97-
98- val lastBuildDateStr = buildsByDate.keys.maxByOrNull(::parseDateString) ? : return emptyList()
99-
100- val lastBuildsForDate = buildsByDate[lastBuildDateStr] ? : return emptyList()
99+ val buildsForDate = if (date != null ) {
100+ val dateString = date.toString()
101+ rawBuildStrings.filter { it.startsWith(dateString) }
102+ } else {
103+ val buildsByDay = rawBuildStrings.groupBy { it.substring(0 , 10 ) }
104+ if (buildsByDay.isEmpty()) return emptyList()
105+ val latestDay = buildsByDay.keys.maxOrNull() ? : return emptyList()
106+ buildsByDay[latestDay] ? : emptyList()
107+ }
101108
102109 val apkPattern =
103- Pattern .compile(""" ^(\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2})-(.*?)-([^-]+)-android-(.*?)/$"" " )
110+ Pattern .compile(" ^(\\ d{4}-\\ d{2}-\\ d{2}-\\ d{2}-\\ d{2}-\\ d{2})-(.*?)-([^-]+)-android-(.*?)/$" )
104111
105- return lastBuildsForDate .mapNotNull { buildString ->
112+ return buildsForDate .mapNotNull { buildString ->
106113 val matcher = apkPattern.matcher(buildString)
107114 if (matcher.matches()) {
108115 val rawDate = matcher.group(1 ) ? : " "
@@ -127,17 +134,4 @@ class MozillaArchiveRepositoryImpl(
127134 }
128135 }
129136 }
130-
131- private fun parseDateString (dateStr : String ): LocalDateTime {
132- return try {
133- val format = LocalDateTime .Format {
134- year(); char(' -' ); monthNumber(); char(' -' ); dayOfMonth()
135- char(' -' ); hour(); char(' -' ); minute(); char(' -' ); second()
136- }
137- LocalDateTime .parse(dateStr, format)
138- } catch (_: Exception ) {
139- // Return a very old date so that it's never chosen as the max
140- LocalDateTime (1 , 1 , 1 , 0 , 0 )
141- }
142- }
143137}
0 commit comments