From 7d567c3499492cbb330cd4e379d52fd04d454d2c Mon Sep 17 00:00:00 2001 From: Jing Jin <8752427+jinjingforever@users.noreply.github.com> Date: Mon, 19 May 2025 14:46:00 -0700 Subject: [PATCH] - Fix a bug where deleting a local model when there are >1 local models shows the wrong download status. - Don't go back to model selection screen automatically when there is an error during model initialization, so that users have a chance to change model parameters (e.g. accelerator) to retry the initialization. - Show error dialog properly in prompt lab screen. --- Android/src/app/build.gradle.kts | 2 +- .../aiedge/gallery/ui/common/ErrorDialog.kt | 67 +++++++++++++++++++ .../gallery/ui/common/chat/ChatPanel.kt | 47 ++----------- .../ui/llmsingleturn/LlmSingleTurnScreen.kt | 14 ++++ .../gallery/ui/modelmanager/ModelList.kt | 2 +- .../ui/modelmanager/ModelManagerViewModel.kt | 3 + 6 files changed, 92 insertions(+), 43 deletions(-) create mode 100644 Android/src/app/src/main/java/com/google/aiedge/gallery/ui/common/ErrorDialog.kt diff --git a/Android/src/app/build.gradle.kts b/Android/src/app/build.gradle.kts index 09b16e5..97e49fe 100644 --- a/Android/src/app/build.gradle.kts +++ b/Android/src/app/build.gradle.kts @@ -30,7 +30,7 @@ android { minSdk = 26 targetSdk = 35 versionCode = 1 - versionName = "0.9.3" + versionName = "0.9.4" // Needed for HuggingFace auth workflows. manifestPlaceholders["appAuthRedirectScheme"] = "com.google.aiedge.gallery.oauth" diff --git a/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/common/ErrorDialog.kt b/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/common/ErrorDialog.kt new file mode 100644 index 0000000..b9bebd1 --- /dev/null +++ b/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/common/ErrorDialog.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.aiedge.gallery.ui.common + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog + +@Composable +fun ErrorDialog(error: String, onDismiss: () -> Unit) { + Dialog( + onDismissRequest = onDismiss + ) { + Card(modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(16.dp)) { + Column( + modifier = Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + // Title + Text( + "Error", + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(bottom = 8.dp) + ) + + // Error + Text( + error, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.error, + ) + + Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { + Button( + onClick = onDismiss + ) { + Text("Close") + } + } + } + } + } +} diff --git a/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/common/chat/ChatPanel.kt b/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/common/chat/ChatPanel.kt index 626ef09..055b75d 100644 --- a/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/common/chat/ChatPanel.kt +++ b/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/common/chat/ChatPanel.kt @@ -43,14 +43,11 @@ import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Timer import androidx.compose.material.icons.rounded.Close import androidx.compose.material.icons.rounded.ContentCopy import androidx.compose.material.icons.rounded.Refresh -import androidx.compose.material3.Button -import androidx.compose.material3.Card import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -97,11 +94,11 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.Dialog import com.google.aiedge.gallery.R import com.google.aiedge.gallery.data.Model import com.google.aiedge.gallery.data.Task import com.google.aiedge.gallery.data.TaskType +import com.google.aiedge.gallery.ui.common.ErrorDialog import com.google.aiedge.gallery.ui.modelmanager.ModelInitializationStatusType import com.google.aiedge.gallery.ui.modelmanager.ModelManagerViewModel import com.google.aiedge.gallery.ui.preview.PreviewChatModel @@ -581,44 +578,12 @@ fun ChatPanel( // Error dialog. if (showErrorDialog) { - Dialog( - onDismissRequest = { - showErrorDialog = false - navigateUp() - }, - ) { - Card(modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(16.dp)) { - Column( - modifier = Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(16.dp) - ) { - // Title - Text( - "Error", - style = MaterialTheme.typography.titleLarge, - modifier = Modifier.padding(bottom = 8.dp) - ) - - // Error - Text( - modelInitializationStatus?.error ?: "", - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.error, - ) - - Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) { - Button(onClick = { - showErrorDialog = false - navigateUp() - }) { - Text("Close") - } - } - } - } - } + ErrorDialog(error = modelInitializationStatus?.error ?: "", onDismiss = { + showErrorDialog = false + }) } -// Benchmark config dialog. + // Benchmark config dialog. if (showBenchmarkConfigsDialog) { BenchmarkConfigDialog(onDismissed = { showBenchmarkConfigsDialog = false }, messageToBenchmark = benchmarkMessage.value, @@ -627,7 +592,7 @@ fun ChatPanel( }) } -// Sheet to show when a message is long-pressed. + // Sheet to show when a message is long-pressed. if (showMessageLongPressedSheet) { val message = longPressedMessage.value if (message != null && message is ChatMessageText) { diff --git a/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/llmsingleturn/LlmSingleTurnScreen.kt b/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/llmsingleturn/LlmSingleTurnScreen.kt index 0748a8f..99bf312 100644 --- a/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/llmsingleturn/LlmSingleTurnScreen.kt +++ b/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/llmsingleturn/LlmSingleTurnScreen.kt @@ -43,8 +43,10 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.lifecycle.viewmodel.compose.viewModel import com.google.aiedge.gallery.data.ModelDownloadStatusType import com.google.aiedge.gallery.ui.ViewModelProvider +import com.google.aiedge.gallery.ui.common.ErrorDialog import com.google.aiedge.gallery.ui.common.ModelPageAppBar import com.google.aiedge.gallery.ui.common.chat.ModelDownloadStatusInfoPanel +import com.google.aiedge.gallery.ui.modelmanager.ModelInitializationStatusType import com.google.aiedge.gallery.ui.modelmanager.ModelManagerViewModel import com.google.aiedge.gallery.ui.preview.PreviewLlmSingleTurnViewModel import com.google.aiedge.gallery.ui.preview.PreviewModelManagerViewModel @@ -78,6 +80,7 @@ fun LlmSingleTurnScreen( val scope = rememberCoroutineScope() val context = LocalContext.current var navigatingUp by remember { mutableStateOf(false) } + var showErrorDialog by remember { mutableStateOf(false) } val handleNavigateUp = { navigatingUp = true @@ -110,6 +113,11 @@ fun LlmSingleTurnScreen( } } + val modelInitializationStatus = modelManagerUiState.modelInitializationStatus[selectedModel.name] + LaunchedEffect(modelInitializationStatus) { + showErrorDialog = modelInitializationStatus?.status == ModelInitializationStatusType.ERROR + } + Scaffold(modifier = modifier, topBar = { ModelPageAppBar( task = task, @@ -186,6 +194,12 @@ fun LlmSingleTurnScreen( } }) } + + if (showErrorDialog) { + ErrorDialog(error = modelInitializationStatus?.error ?: "", onDismiss = { + showErrorDialog = false + }) + } } } } diff --git a/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/modelmanager/ModelList.kt b/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/modelmanager/ModelList.kt index 1e5e00d..d7657c7 100644 --- a/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/modelmanager/ModelList.kt +++ b/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/modelmanager/ModelList.kt @@ -160,7 +160,7 @@ fun ModelList( } // List of imported models within a task. - items(items = importedModels) { model -> + items(items = importedModels, key = { it.name }) { model -> Box { ModelItem( model = model, diff --git a/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/modelmanager/ModelManagerViewModel.kt b/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/modelmanager/ModelManagerViewModel.kt index 50d5a57..5414c6b 100644 --- a/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/modelmanager/ModelManagerViewModel.kt +++ b/Android/src/app/src/main/java/com/google/aiedge/gallery/ui/modelmanager/ModelManagerViewModel.kt @@ -683,6 +683,9 @@ open class ModelManagerViewModel( Log.d(TAG, "Allowlist: $modelAllowlist") // Convert models in the allowlist. + TASK_LLM_CHAT.models.clear() + TASK_LLM_PROMPT_LAB.models.clear() + TASK_LLM_ASK_IMAGE.models.clear() for (allowedModel in modelAllowlist.models) { if (allowedModel.disabled == true) { continue