@@ -21,6 +21,7 @@ import kotlinx.coroutines.Job
2121import kotlinx.coroutines.flow.MutableStateFlow
2222import kotlinx.coroutines.flow.StateFlow
2323import kotlinx.coroutines.flow.asStateFlow
24+ import kotlinx.coroutines.flow.update
2425import kotlinx.coroutines.launch
2526import kotlinx.coroutines.withContext
2627import java.io.File
@@ -60,14 +61,16 @@ class LoraViewModel(application: Application) : AndroidViewModel(application) {
6061 val (registered, loaded) = withContext(Dispatchers .IO ) {
6162 RunAnywhere .allRegisteredLoraAdapters() to RunAnywhere .getLoadedLoraAdapters()
6263 }
63- _uiState .value = _uiState .value.copy(
64- registeredAdapters = registered,
65- loadedAdapters = loaded,
66- error = null ,
67- )
64+ _uiState .update {
65+ it.copy(
66+ registeredAdapters = registered,
67+ loadedAdapters = loaded,
68+ error = null ,
69+ )
70+ }
6871 } catch (e: Exception ) {
6972 Timber .e(e, " Failed to refresh LoRA state" )
70- _uiState .value = _uiState .value. copy(error = e.message)
73+ _uiState .update { it. copy(error = e.message) }
7174 }
7275 }
7376 }
@@ -79,14 +82,16 @@ class LoraViewModel(application: Application) : AndroidViewModel(application) {
7982 val (compatible, loaded) = withContext(Dispatchers .IO ) {
8083 RunAnywhere .loraAdaptersForModel(modelId) to RunAnywhere .getLoadedLoraAdapters()
8184 }
82- _uiState .value = _uiState .value.copy(
83- compatibleAdapters = compatible,
84- loadedAdapters = loaded,
85- error = null ,
86- )
85+ _uiState .update {
86+ it.copy(
87+ compatibleAdapters = compatible,
88+ loadedAdapters = loaded,
89+ error = null ,
90+ )
91+ }
8792 } catch (e: Exception ) {
8893 Timber .e(e, " Failed to refresh for model $modelId " )
89- _uiState .value = _uiState .value. copy(error = e.message)
94+ _uiState .update { it. copy(error = e.message) }
9095 }
9196 }
9297 }
@@ -98,11 +103,11 @@ class LoraViewModel(application: Application) : AndroidViewModel(application) {
98103 val config = LoRAAdapterConfig (path = path, scale = scale)
99104 withContext(Dispatchers .IO ) { RunAnywhere .loadLoraAdapter(config) }
100105 val loaded = withContext(Dispatchers .IO ) { RunAnywhere .getLoadedLoraAdapters() }
101- _uiState .value = _uiState .value. copy(loadedAdapters = loaded, error = null )
106+ _uiState .update { it. copy(loadedAdapters = loaded, error = null ) }
102107 Timber .i(" Loaded LoRA adapter: $path (scale=$scale )" )
103108 } catch (e: Exception ) {
104109 Timber .e(e, " Failed to load LoRA adapter" )
105- _uiState .value = _uiState .value. copy(error = " Failed to load adapter: ${e.message} " )
110+ _uiState .update { it. copy(error = " Failed to load adapter: ${e.message} " ) }
106111 }
107112 }
108113 }
@@ -113,11 +118,11 @@ class LoraViewModel(application: Application) : AndroidViewModel(application) {
113118 try {
114119 withContext(Dispatchers .IO ) { RunAnywhere .removeLoraAdapter(path) }
115120 val loaded = withContext(Dispatchers .IO ) { RunAnywhere .getLoadedLoraAdapters() }
116- _uiState .value = _uiState .value. copy(loadedAdapters = loaded, error = null )
121+ _uiState .update { it. copy(loadedAdapters = loaded, error = null ) }
117122 Timber .i(" Unloaded LoRA adapter: $path " )
118123 } catch (e: Exception ) {
119124 Timber .e(e, " Failed to unload LoRA adapter" )
120- _uiState .value = _uiState .value. copy(error = " Failed to unload adapter: ${e.message} " )
125+ _uiState .update { it. copy(error = " Failed to unload adapter: ${e.message} " ) }
121126 }
122127 }
123128 }
@@ -127,11 +132,11 @@ class LoraViewModel(application: Application) : AndroidViewModel(application) {
127132 viewModelScope.launch {
128133 try {
129134 withContext(Dispatchers .IO ) { RunAnywhere .clearLoraAdapters() }
130- _uiState .value = _uiState .value. copy(loadedAdapters = emptyList(), error = null )
135+ _uiState .update { it. copy(loadedAdapters = emptyList(), error = null ) }
131136 Timber .i(" Cleared all LoRA adapters" )
132137 } catch (e: Exception ) {
133138 Timber .e(e, " Failed to clear LoRA adapters" )
134- _uiState .value = _uiState .value. copy(error = e.message)
139+ _uiState .update { it. copy(error = e.message) }
135140 }
136141 }
137142 }
@@ -167,11 +172,13 @@ class LoraViewModel(application: Application) : AndroidViewModel(application) {
167172 fun downloadAdapter (entry : LoraAdapterCatalogEntry ) {
168173 if (_uiState .value.downloadingAdapterId != null ) return
169174
170- _uiState .value = _uiState .value.copy(
171- downloadingAdapterId = entry.id,
172- downloadProgress = 0f ,
173- error = null ,
174- )
175+ _uiState .update {
176+ it.copy(
177+ downloadingAdapterId = entry.id,
178+ downloadProgress = 0f ,
179+ error = null ,
180+ )
181+ }
175182
176183 downloadJob = viewModelScope.launch {
177184 val destFile = File (loraDir, entry.filename)
@@ -196,11 +203,12 @@ class LoraViewModel(application: Application) : AndroidViewModel(application) {
196203 downloaded + = bytesRead
197204 if (totalSize > 0 ) {
198205 val progress = (downloaded.toFloat() / totalSize).coerceIn(0f , 1f )
199- _uiState .value = _uiState .value. copy(downloadProgress = progress)
206+ _uiState .update { it. copy(downloadProgress = progress) }
200207 }
201208 }
202209 }
203210 }
211+ destFile.delete()
204212 if (! tmpFile.renameTo(destFile)) {
205213 tmpFile.delete()
206214 throw Exception (" Failed to move downloaded file to final location" )
@@ -209,17 +217,21 @@ class LoraViewModel(application: Application) : AndroidViewModel(application) {
209217 }
210218
211219 Timber .i(" Downloaded LoRA adapter: ${entry.name} -> ${destFile.absolutePath} " )
212- _uiState .value = _uiState .value.copy(
213- downloadingAdapterId = null ,
214- downloadProgress = 0f ,
215- )
220+ _uiState .update {
221+ it.copy(
222+ downloadingAdapterId = null ,
223+ downloadProgress = 0f ,
224+ )
225+ }
216226 } catch (e: Exception ) {
217227 Timber .e(e, " Failed to download LoRA adapter: ${entry.name} " )
218- _uiState .value = _uiState .value.copy(
219- downloadingAdapterId = null ,
220- downloadProgress = 0f ,
221- error = " Download failed: ${e.message} " ,
222- )
228+ _uiState .update {
229+ it.copy(
230+ downloadingAdapterId = null ,
231+ downloadProgress = 0f ,
232+ error = " Download failed: ${e.message} " ,
233+ )
234+ }
223235 } finally {
224236 if (! downloadComplete && tmpFile.exists()) {
225237 tmpFile.delete()
@@ -232,10 +244,12 @@ class LoraViewModel(application: Application) : AndroidViewModel(application) {
232244 fun cancelDownload () {
233245 downloadJob?.cancel()
234246 downloadJob = null
235- _uiState .value = _uiState .value.copy(
236- downloadingAdapterId = null ,
237- downloadProgress = 0f ,
238- )
247+ _uiState .update {
248+ it.copy(
249+ downloadingAdapterId = null ,
250+ downloadProgress = 0f ,
251+ )
252+ }
239253 }
240254
241255 /* * Delete a downloaded adapter file. Always attempts unload first (ignores if not loaded). */
@@ -255,15 +269,15 @@ class LoraViewModel(application: Application) : AndroidViewModel(application) {
255269 }
256270 }
257271 val loaded = withContext(Dispatchers .IO ) { RunAnywhere .getLoadedLoraAdapters() }
258- _uiState .value = _uiState .value. copy(loadedAdapters = loaded)
272+ _uiState .update { it. copy(loadedAdapters = loaded) }
259273 } catch (e: Exception ) {
260274 Timber .e(e, " Failed to delete adapter: ${entry.filename} " )
261- _uiState .value = _uiState .value. copy(error = " Delete failed: ${e.message} " )
275+ _uiState .update { it. copy(error = " Delete failed: ${e.message} " ) }
262276 }
263277 }
264278 }
265279
266280 fun clearError () {
267- _uiState .value = _uiState .value. copy(error = null )
281+ _uiState .update { it. copy(error = null ) }
268282 }
269283}
0 commit comments