fix: bugs
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:name=".ComfyApp"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:usesCleartextTraffic="true"
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package moe.uni.comfy.comfyuikmp
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
|
||||
class ComfyApp: Application() {
|
||||
companion object{
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
lateinit var context: Context
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
context = applicationContext
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package moe.uni.comfy.comfyuikmp.util
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.MediaStore
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.asAndroidBitmap
|
||||
import moe.uni.comfy.comfyuikmp.ComfyApp
|
||||
import java.io.File
|
||||
|
||||
|
||||
actual suspend fun saveImageToLocal(image: ImageBitmap, name: String): Result<Boolean> {
|
||||
val bitmap = image.asAndroidBitmap()
|
||||
|
||||
val imagesCollection =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
|
||||
} else {
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
||||
}
|
||||
|
||||
val contentValues = ContentValues().apply {
|
||||
put(MediaStore.Images.Media.DISPLAY_NAME, "$name.png")
|
||||
put(MediaStore.Images.Media.MIME_TYPE, "image/png")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/ComfyApp")
|
||||
put(MediaStore.Images.Media.IS_PENDING, 1)
|
||||
}
|
||||
}
|
||||
|
||||
val resolver = ComfyApp.context.contentResolver
|
||||
val uri = resolver.insert(imagesCollection, contentValues)
|
||||
uri?.let {
|
||||
try {
|
||||
resolver.openOutputStream(uri)?.use { out ->
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
|
||||
} ?: throw kotlinx.io.IOException("OpenOutputStream returned null")
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
contentValues.clear()
|
||||
contentValues.put(MediaStore.Images.Media.IS_PENDING, 0)
|
||||
resolver.update(uri, contentValues, null, null)
|
||||
}
|
||||
return Result.success(true)
|
||||
} catch (e: Exception) {
|
||||
// 出错时删除占位条目,避免相册里出现空文件
|
||||
runCatching { resolver.delete(uri, null, null) }
|
||||
e.printStackTrace()
|
||||
return Result.failure(e)
|
||||
}
|
||||
|
||||
}
|
||||
return Result.failure(kotlinx.io.IOException("Failed to create new MediaStore record."))
|
||||
|
||||
|
||||
}
|
||||
@@ -4,6 +4,11 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
|
||||
|
||||
object NodeType{
|
||||
const val KSampler = "KSampler"
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用 ComfyUI 节点模型,尽量保持结构宽松以兼容不同节点类型与输入形态。
|
||||
*/
|
||||
|
||||
+3
-1
@@ -6,12 +6,14 @@ import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import moe.uni.comfy.comfyuikmp.data.model.ComfyPrompt
|
||||
import moe.uni.comfy.comfyuikmp.data.settings.KeyValueStore
|
||||
import moe.uni.comfy.comfyuikmp.data.model.ImageRef
|
||||
|
||||
@Serializable
|
||||
data class WorkflowItem(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val prompt: ComfyPrompt
|
||||
val prompt: ComfyPrompt,
|
||||
val cover: ImageRef? = null
|
||||
)
|
||||
|
||||
class WorkflowRepository {
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
package moe.uni.comfy.comfyuikmp.ui
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.History
|
||||
import androidx.compose.material.icons.filled.Save
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import moe.uni.comfy.comfyuikmp.data.model.NodeType
|
||||
import moe.uni.comfy.comfyuikmp.di.AppGraph
|
||||
import moe.uni.comfy.comfyuikmp.ui.gallery.GalleryScreen
|
||||
import moe.uni.comfy.comfyuikmp.ui.generate.GenerateScreen
|
||||
@@ -21,6 +26,7 @@ import moe.uni.comfy.comfyuikmp.ui.navigation.rememberNavController
|
||||
import moe.uni.comfy.comfyuikmp.ui.settings.SettingsScreen
|
||||
import moe.uni.comfy.comfyuikmp.ui.view.ViewScreen
|
||||
import moe.uni.comfy.comfyuikmp.util.mainViewModelFactory
|
||||
import moe.uni.comfy.comfyuikmp.util.saveImageToLocal
|
||||
import kotlin.random.Random
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.ExperimentalTime
|
||||
@@ -31,6 +37,7 @@ fun AppRoot(viewModel: AppViewModel = viewModel(factory = mainViewModelFactory))
|
||||
val hasSaved = remember { AppGraph.workflows.list().isNotEmpty() }
|
||||
val startDest = if (hasSaved) Dest.Home else Dest.Settings
|
||||
val navController = rememberNavController(startDest)
|
||||
|
||||
MaterialTheme {
|
||||
val snackbar = remember { SnackbarHostState() }
|
||||
val scope = rememberCoroutineScope()
|
||||
@@ -72,11 +79,29 @@ fun AppRoot(viewModel: AppViewModel = viewModel(factory = mainViewModelFactory))
|
||||
},
|
||||
actions = {
|
||||
if (current == Dest.Generate) {
|
||||
if (uiState.image!=null){
|
||||
Icon(Icons.Filled.Save, contentDescription = null, modifier = Modifier.clickable{
|
||||
uiState.image?.let {
|
||||
scope.launch {
|
||||
val result = saveImageToLocal(it,"${uiState.workflowName}_${Random.nextInt()}.png")
|
||||
|
||||
if (result.isSuccess){
|
||||
snackbar.showSnackbar("保存成功")
|
||||
}else{
|
||||
snackbar.showSnackbar(result.exceptionOrNull()?.message?:"保存失败")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
Spacer(Modifier.size(16.dp))
|
||||
}
|
||||
Icon(
|
||||
Icons.Filled.History,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.clickable { navController.navigate(Dest.Gallery) }
|
||||
)
|
||||
Spacer(Modifier.size(16.dp))
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -90,7 +115,7 @@ fun AppRoot(viewModel: AppViewModel = viewModel(factory = mainViewModelFactory))
|
||||
uiState.prompt.let { p ->
|
||||
scope.launch {
|
||||
if (uiState.useRandom) {
|
||||
p.values.find { it.classType == "KSampler" }?.let {
|
||||
p.values.find { it.classType == NodeType.KSampler }?.let {
|
||||
val inputs = it.inputs.toMutableMap()
|
||||
inputs["seed"] = JsonPrimitive(Random.nextInt(0, Int.MAX_VALUE))
|
||||
it.inputs = inputs
|
||||
@@ -145,7 +170,8 @@ fun AppRoot(viewModel: AppViewModel = viewModel(factory = mainViewModelFactory))
|
||||
viewModel.useRandomSeed(it)
|
||||
|
||||
},
|
||||
onOpenImage = { ref -> viewModel.openImage(ref) }
|
||||
onOpenImage = { ref -> viewModel.openImage(ref) },
|
||||
onSetCover = { }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import moe.uni.comfy.comfyuikmp.data.network.createDefaultHttpClient
|
||||
import moe.uni.comfy.comfyuikmp.data.repository.WorkflowItem
|
||||
import moe.uni.comfy.comfyuikmp.di.AppGraph
|
||||
import moe.uni.comfy.comfyuikmp.util.decodeImage
|
||||
import moe.uni.comfy.comfyuikmp.util.saveImageToLocal
|
||||
|
||||
class AppViewModel : ViewModel() {
|
||||
private val _uiState = MutableStateFlow(AppUiState())
|
||||
@@ -145,5 +146,5 @@ data class AppUiState(
|
||||
val image: ImageBitmap? = null,
|
||||
val progress:ProgressUpdate = ProgressUpdate(0f, ""),
|
||||
val prompt: ComfyPrompt = emptyMap(),
|
||||
val useRandom: Boolean = false
|
||||
val useRandom: Boolean = true
|
||||
)
|
||||
+12
-16
@@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -23,6 +24,12 @@ import coil3.compose.AsyncImage
|
||||
import moe.uni.comfy.comfyuikmp.di.AppGraph
|
||||
import moe.uni.comfy.comfyuikmp.ui.AppViewModel
|
||||
import moe.uni.comfy.comfyuikmp.ui.navigation.Dest
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import moe.uni.comfy.comfyuikmp.util.buildImageUrl
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
@@ -42,23 +49,12 @@ fun GalleryScreen(contentPadding: PaddingValues, viewModel: AppViewModel) {
|
||||
|
||||
LazyColumn(contentPadding = contentPadding, modifier = Modifier.padding(horizontal = 16.dp)) {
|
||||
items(history, key = { it.filename }) { img ->
|
||||
val url = buildString {
|
||||
append(base)
|
||||
append("/view?filename=")
|
||||
append(img.filename)
|
||||
img.subfolder?.let {
|
||||
append("&subfolder=")
|
||||
append(it)
|
||||
}
|
||||
img.type?.let {
|
||||
append("&type=")
|
||||
append(it)
|
||||
}
|
||||
}
|
||||
val url = buildImageUrl(img)
|
||||
|
||||
Card(modifier = Modifier.fillMaxWidth().aspectRatio(3f / 4f).clickable {
|
||||
viewModel.openImage(img)
|
||||
}) {
|
||||
|
||||
Card(modifier = Modifier.fillMaxWidth().aspectRatio(3f / 4f).combinedClickable(
|
||||
onClick = { viewModel.openImage(img) }
|
||||
)) {
|
||||
AsyncImage(
|
||||
model = url,
|
||||
contentDescription = null,
|
||||
|
||||
+27
-2
@@ -26,6 +26,7 @@ import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.rememberBottomSheetScaffoldState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.booleanOrNull
|
||||
@@ -36,6 +37,9 @@ import moe.uni.comfy.comfyuikmp.data.model.updateNodeStringInput
|
||||
import moe.uni.comfy.comfyuikmp.data.model.updateNodePrimitiveInput
|
||||
import moe.uni.comfy.comfyuikmp.data.network.ProgressUpdate
|
||||
import moe.uni.comfy.comfyuikmp.data.model.ImageRef
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import moe.uni.comfy.comfyuikmp.data.model.NodeType
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -49,6 +53,7 @@ fun GenerateScreen(
|
||||
onPromptChange: (ComfyPrompt) -> Unit,
|
||||
onRandomSwitchChange: (Boolean) -> Unit,
|
||||
onOpenImage: (ImageRef) -> Unit = {},
|
||||
onSetCover: (ImageRef) -> Unit = {},
|
||||
) {
|
||||
val state = rememberBottomSheetScaffoldState()
|
||||
Scaffold(modifier = Modifier.padding(contentPadding)) {
|
||||
@@ -141,7 +146,8 @@ fun GenerateScreen(
|
||||
)
|
||||
|
||||
//KSampler
|
||||
if (node.classType == "KSampler" && key.equals("seed", ignoreCase = true)) {
|
||||
if (node.classType == NodeType.KSampler && key.equals("seed", ignoreCase = true)) {
|
||||
inputEnable = !useRandom
|
||||
Row(modifier = Modifier.padding(top = 8.dp)) {
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Switch(useRandom, onCheckedChange = {
|
||||
@@ -189,6 +195,7 @@ fun GenerateScreen(
|
||||
Alignment.Center))
|
||||
}
|
||||
|
||||
var showSetCover by remember { mutableStateOf(false) }
|
||||
if (latestImage != null) {
|
||||
if (latestImageRef != null) {
|
||||
Image(
|
||||
@@ -197,7 +204,10 @@ fun GenerateScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 32.dp)
|
||||
.clickable { onOpenImage(latestImageRef) }
|
||||
.combinedClickable(
|
||||
onClick = { onOpenImage(latestImageRef) },
|
||||
onLongClick = { showSetCover = true }
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Image(
|
||||
@@ -207,6 +217,21 @@ fun GenerateScreen(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (showSetCover && latestImageRef != null) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showSetCover = false },
|
||||
confirmButton = {
|
||||
Button(onClick = {
|
||||
onSetCover(latestImageRef)
|
||||
showSetCover = false
|
||||
}) { Text("设为封面") }
|
||||
},
|
||||
dismissButton = { Button(onClick = { showSetCover = false }) { Text("取消") } },
|
||||
title = { Text("设置封面") },
|
||||
text = { Text("将当前图片设为该工作流的封面?") }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,16 @@ import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -28,6 +32,10 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.Alignment
|
||||
import coil3.compose.AsyncImage
|
||||
import moe.uni.comfy.comfyuikmp.util.buildImageUrl
|
||||
|
||||
|
||||
@Composable
|
||||
@@ -49,27 +57,48 @@ fun HomeScreen(
|
||||
) { Text("新建/导入工作流", modifier = Modifier.padding(16.dp), style = MaterialTheme.typography.titleMedium) }
|
||||
}
|
||||
items(items) { w ->
|
||||
Card(modifier = Modifier
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
.fillMaxWidth()
|
||||
.clickable { onPick(w) }
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
.fillMaxWidth()
|
||||
.clickable { onPick(w) },
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp)
|
||||
) {
|
||||
Text(w.name, modifier = Modifier.padding(16.dp), style = MaterialTheme.typography.titleMedium)
|
||||
Row(modifier = Modifier.padding(horizontal = 8.dp)) {
|
||||
IconButton(onClick = { renaming = w; newName = w.name }) { Icon(Icons.Filled.Edit, contentDescription = null,
|
||||
Modifier.size(24.dp)) }
|
||||
IconButton(onClick = { AppGraph.workflows.delete(w.id); items = repo.list() }) { Icon(Icons.Filled.Delete, contentDescription = null,
|
||||
Modifier.size(24.dp)) }
|
||||
if (w.cover != null) {
|
||||
AsyncImage(
|
||||
model = buildImageUrl(w.cover),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(4f / 3f)
|
||||
)
|
||||
} else {
|
||||
// 占位封面区域
|
||||
androidx.compose.foundation.layout.Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(4f / 3f)
|
||||
)
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.padding(12.dp)) {
|
||||
Text(w.name, style = MaterialTheme.typography.titleMedium)
|
||||
Row(modifier = Modifier.padding(top = 4.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
IconButton(onClick = { renaming = w; newName = w.name }) { Icon(Icons.Filled.Edit, contentDescription = null,
|
||||
Modifier.size(24.dp)) }
|
||||
IconButton(onClick = { AppGraph.workflows.delete(w.id); items = repo.list() }) { Icon(Icons.Filled.Delete, contentDescription = null,
|
||||
Modifier.size(24.dp)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val r = renaming
|
||||
if (r != null) {
|
||||
if (renaming != null) {
|
||||
AlertDialog(onDismissRequest = { renaming = null }, confirmButton = {
|
||||
Button(onClick = {
|
||||
AppGraph.workflows.rename(r.id, newName.ifBlank { r.name })
|
||||
AppGraph.workflows.rename(renaming!!.id, newName.ifBlank { renaming!!.name })
|
||||
items = repo.list()
|
||||
renaming = null
|
||||
}) { Text("确定") }
|
||||
|
||||
@@ -19,24 +19,13 @@ import moe.uni.comfy.comfyuikmp.data.model.ImageRef
|
||||
import moe.uni.comfy.comfyuikmp.di.AppGraph
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import moe.uni.comfy.comfyuikmp.util.buildImageUrl
|
||||
|
||||
@Composable
|
||||
fun ViewScreen(ref: ImageRef, contentPadding: PaddingValues) {
|
||||
val base = AppGraph.settings.getBaseUrl() ?: ""
|
||||
val url = remember(ref, base) {
|
||||
buildString {
|
||||
append(base)
|
||||
append("/view?filename=")
|
||||
append(ref.filename)
|
||||
ref.subfolder?.let {
|
||||
append("&subfolder=")
|
||||
append(it)
|
||||
}
|
||||
ref.type?.let {
|
||||
append("&type=")
|
||||
append(it)
|
||||
}
|
||||
}
|
||||
buildImageUrl(ref)
|
||||
}
|
||||
|
||||
val scaleState = remember { mutableFloatStateOf(1f) }
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package moe.uni.comfy.comfyuikmp.util
|
||||
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import moe.uni.comfy.comfyuikmp.data.model.ImageRef
|
||||
import moe.uni.comfy.comfyuikmp.di.AppGraph
|
||||
|
||||
fun buildImageUrl(ref: ImageRef): String {
|
||||
val base = AppGraph.settings.getBaseUrl() ?: ""
|
||||
return buildString {
|
||||
append(base)
|
||||
append("/view?filename=")
|
||||
append(ref.filename)
|
||||
ref.subfolder?.let {
|
||||
append("&subfolder=")
|
||||
append(it)
|
||||
}
|
||||
ref.type?.let {
|
||||
append("&type=")
|
||||
append(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect suspend fun saveImageToLocal(image: ImageBitmap, name: String): Result<Boolean>
|
||||
|
||||
|
||||
|
||||
@@ -19,11 +19,6 @@ actual fun createPlatformHttpClient(configure: HttpClientConfig<*>.() -> Unit):
|
||||
install(UserAgent) {
|
||||
agent = "ComfyUIKMP-iOS"
|
||||
}
|
||||
defaultRequest {
|
||||
url {
|
||||
protocol = URLProtocol.HTTP
|
||||
}
|
||||
}
|
||||
configure()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package moe.uni.comfy.comfyuikmp.util
|
||||
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import coil3.Bitmap
|
||||
|
||||
actual suspend fun saveImageToLocal(image: ImageBitmap, name: String): Result<Boolean> {
|
||||
return Result.failure(kotlinx.io.IOException("Failed to create new MediaStore record."))
|
||||
}
|
||||
@@ -4,12 +4,47 @@
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>需要访问 ComfyUI 服务以生成图像</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>NSAllowsArbitraryLoadsInWebContent</key>
|
||||
<true/>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
<true/>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<!-- 示例:开发期允许局域网 IP 和自签名证书。可按需添加具体域名/IP。 -->
|
||||
<key>localhost</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>127.0.0.1</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>10.0.0.0/8</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>172.16.0.0/12</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>192.168.0.0/16</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
Reference in New Issue
Block a user