1- /*
2- * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3- */
4-
51package at.bitfire.icsdroid.model
62
73import android.content.Context
84import android.net.Uri
95import android.util.Log
10- import androidx.compose.runtime.getValue
11- import androidx.compose.runtime.mutableStateOf
12- import androidx.compose.runtime.setValue
136import at.bitfire.ical4android.Css3Color
147import at.bitfire.ical4android.Event
158import at.bitfire.ical4android.ICalendar
9+ import at.bitfire.icsdroid.AppHttpClient
1610import at.bitfire.icsdroid.CalendarFetcher
1711import at.bitfire.icsdroid.Constants
18- import at.bitfire.icsdroid.AppHttpClient
1912import at.bitfire.icsdroid.HttpUtils.toURI
2013import at.bitfire.icsdroid.HttpUtils.toUri
2114import at.bitfire.icsdroid.ui.ResourceInfo
2215import dagger.hilt.android.qualifiers.ApplicationContext
2316import io.ktor.http.ContentType
2417import io.ktor.http.charset
25- import kotlinx.coroutines.CoroutineScope
26- import kotlinx.coroutines.Dispatchers
27- import kotlinx.coroutines.launch
2818import net.fortuna.ical4j.model.property.Color
2919import java.io.InputStream
3020import java.io.InputStreamReader
@@ -36,86 +26,67 @@ class ValidationUseCase @Inject constructor(
3626 @param:ApplicationContext private val context : Context ,
3727 private val appHttpClient : AppHttpClient ,
3828) {
39-
40- data class UiState (
41- val isVerifyingUrl : Boolean = false ,
42- val result : ResourceInfo ? = null
43- )
44-
45- var uiState by mutableStateOf(UiState ())
46- private set
47-
48- fun resetResult () {
49- uiState = uiState.copy(result = null )
50- }
51-
52- fun validate (
29+ suspend fun validate (
5330 originalUri : Uri ,
5431 username : String? ,
5532 password : String?
56- ) = CoroutineScope (Dispatchers .IO ).launch {
57- try {
58- Log .i(Constants .TAG , " Validating Webcal feed $originalUri (authentication: $username )" )
59-
60- uiState = uiState.copy(isVerifyingUrl = true )
61-
62- val info = ResourceInfo (originalUri)
63- val downloader = object : CalendarFetcher (context, originalUri, appHttpClient) {
64- override suspend fun onSuccess (
65- data : InputStream ,
66- contentType : ContentType ? ,
67- eTag : String? ,
68- lastModified : Long? ,
69- displayName : String?
70- ) {
71- InputStreamReader (data, contentType?.charset() ? : Charsets .UTF_8 ).use { reader ->
72- val properties = mutableMapOf<String , String >()
73- val events = Event .eventsFromReader(reader, properties)
74-
75- info.calendarName = properties[ICalendar .CALENDAR_NAME ] ? : displayName
76- info.calendarColor =
33+ ): ResourceInfo {
34+ Log .i(Constants .TAG , " Validating Webcal feed $originalUri (authentication: $username )" )
35+
36+ val info = ResourceInfo (originalUri)
37+ val downloader = object : CalendarFetcher (context, originalUri, appHttpClient) {
38+ override suspend fun onSuccess (
39+ data : InputStream ,
40+ contentType : ContentType ? ,
41+ eTag : String? ,
42+ lastModified : Long? ,
43+ displayName : String?
44+ ) {
45+ InputStreamReader (data, contentType?.charset() ? : Charsets .UTF_8 ).use { reader ->
46+ val properties = mutableMapOf<String , String >()
47+ val events = Event .Companion .eventsFromReader(reader, properties)
48+
49+ info.calendarName = properties[ICalendar .Companion .CALENDAR_NAME ] ? : displayName
50+ info.calendarColor =
7751 // try COLOR first
78- properties[Color .PROPERTY_NAME ]?.let { colorValue ->
79- Css3Color .colorFromString(colorValue)
80- } ? :
52+ properties[Color .PROPERTY_NAME ]?.let { colorValue ->
53+ Css3Color . Companion .colorFromString(colorValue)
54+ } ? :
8155 // try X-APPLE-CALENDAR-COLOR second
8256 try {
83- properties[ICalendar .CALENDAR_COLOR ]?.let { colorValue ->
84- Css3Color .colorFromString(colorValue)
57+ properties[ICalendar .Companion . CALENDAR_COLOR ]?.let { colorValue ->
58+ Css3Color .Companion . colorFromString(colorValue)
8559 }
8660 } catch (e: IllegalArgumentException ) {
8761 Log .w(Constants .TAG , " Couldn't parse calendar COLOR" , e)
8862 null
8963 }
90- info.eventsFound = events.size
91- }
92-
93- uiState = uiState.copy(result = info)
64+ info.eventsFound = events.size
9465 }
66+ }
9567
96- override suspend fun onNewPermanentUrl (target : Uri ) {
97- Log .i(Constants .TAG , " Got permanent redirect when validating, saving new URL: $target " )
98- val location = uri.toURI().resolve(target.toURI())
99- info.uri = location.toUri()
100- }
68+ override suspend fun onNewPermanentUrl (target : Uri ) {
69+ Log .i(Constants .TAG , " Got permanent redirect when validating, saving new URL: $target " )
70+ val location = uri.toURI().resolve(target.toURI())
71+ info.uri = location.toUri()
72+ }
10173
102- override suspend fun onError (error : Exception ) {
103- Log .e(Constants .TAG , " Couldn't validate calendar" , error)
104- info.exception = error
105- uiState = uiState.copy(result = info)
106- }
74+ override suspend fun onError (error : Exception ) {
75+ Log .e(Constants .TAG , " Couldn't validate calendar" , error)
76+ info.exception = error
10777 }
78+ }
10879
109- downloader.username = username
110- downloader.password = password
80+ downloader.username = username
81+ downloader.password = password
11182
112- // directly ask for confirmation of custom certificates
113- downloader.inForeground = true
83+ // directly ask for confirmation of custom certificates
84+ downloader.inForeground = true
11485
115- downloader.fetch()
116- } finally {
117- uiState = uiState.copy(isVerifyingUrl = false )
118- }
119- }
86+ // downloader.fetch blocks the current thread until the request is completed
87+ downloader.fetch()
12088
121- }
89+ // at this point, info's contents has already been updated correctly
90+ return info
91+ }
92+ }
0 commit comments