diff --git a/examples/llama.android/app/src/main/java/com/example/llama/MainActivity.kt b/examples/llama.android/app/src/main/java/com/example/llama/MainActivity.kt index 6ae32ef4f5..43044ed68b 100644 --- a/examples/llama.android/app/src/main/java/com/example/llama/MainActivity.kt +++ b/examples/llama.android/app/src/main/java/com/example/llama/MainActivity.kt @@ -99,6 +99,7 @@ fun AppContent( val engineState by mainViewModel.engineState.collectAsState() val showModelImportTooltip by mainViewModel.showModelImportTooltip.collectAsState() val showChatTooltip by mainViewModel.showChatTooltip.collectAsState() + val showManagementTooltip by mainViewModel.showModelManagementTooltip.collectAsState() // Model state val modelScreenUiMode by modelsViewModel.modelScreenUiMode.collectAsState() @@ -223,9 +224,11 @@ fun AppContent( modelsViewModel.resetPreselection() openDrawer() }, + showTooltip = showManagementTooltip && !showChatTooltip && hasModelsInstalled, showManagingToggle = !showChatTooltip && hasModelsInstalled, onToggleManaging = { if (hasModelsInstalled) { + mainViewModel.waiveModelManagementTooltip() modelsViewModel.toggleMode(ModelScreenUiMode.MANAGING) } }, diff --git a/examples/llama.android/app/src/main/java/com/example/llama/data/source/prefs/AppPreferences.kt b/examples/llama.android/app/src/main/java/com/example/llama/data/source/prefs/AppPreferences.kt index 2d9e827a30..b40c941fee 100644 --- a/examples/llama.android/app/src/main/java/com/example/llama/data/source/prefs/AppPreferences.kt +++ b/examples/llama.android/app/src/main/java/com/example/llama/data/source/prefs/AppPreferences.kt @@ -29,6 +29,7 @@ class AppPreferences @Inject constructor ( // Preference keys private val USER_HAS_IMPORTED_FIRST_MODEL = booleanPreferencesKey("user_has_imported_first_model") private val USER_HAS_CHATTED_WITH_MODEL = booleanPreferencesKey("user_has_chatted_with_model") + private val USER_HAS_NAVIGATED_TO_MANAGEMENT = booleanPreferencesKey("user_has_navigated_to_management") } /** @@ -64,4 +65,21 @@ class AppPreferences @Inject constructor ( preferences[USER_HAS_CHATTED_WITH_MODEL] = done } } + + /** + * Gets whether the user has navigated to model management screen. + */ + fun userHasNavigatedToManagement(): Flow = + context.appDataStore.data.map { preferences -> + preferences[USER_HAS_NAVIGATED_TO_MANAGEMENT] == true + } + + /** + * Sets whether the user has navigated to model management screen. + */ + suspend fun setUserHasNavigatedToManagement(done: Boolean) = withContext(Dispatchers.IO) { + context.appDataStore.edit { preferences -> + preferences[USER_HAS_NAVIGATED_TO_MANAGEMENT] = done + } + } } diff --git a/examples/llama.android/app/src/main/java/com/example/llama/ui/scaffold/AppScaffold.kt b/examples/llama.android/app/src/main/java/com/example/llama/ui/scaffold/AppScaffold.kt index 77e2eb1d30..d2f067bca4 100644 --- a/examples/llama.android/app/src/main/java/com/example/llama/ui/scaffold/AppScaffold.kt +++ b/examples/llama.android/app/src/main/java/com/example/llama/ui/scaffold/AppScaffold.kt @@ -71,6 +71,7 @@ fun AppScaffold( is TopBarConfig.ModelsBrowsing -> ModelsBrowsingTopBar( title = topBarconfig.title, + showTooltip = topBarconfig.showTooltip, showManagingToggle = topBarconfig.showManagingToggle, onToggleManaging = topBarconfig.onToggleManaging, onNavigateBack = topBarconfig.navigationIcon.backAction, diff --git a/examples/llama.android/app/src/main/java/com/example/llama/ui/scaffold/bottombar/ModelsManagementBottomBar.kt b/examples/llama.android/app/src/main/java/com/example/llama/ui/scaffold/bottombar/ModelsManagementBottomBar.kt index cf2b972275..e0d1d0f4a7 100644 --- a/examples/llama.android/app/src/main/java/com/example/llama/ui/scaffold/bottombar/ModelsManagementBottomBar.kt +++ b/examples/llama.android/app/src/main/java/com/example/llama/ui/scaffold/bottombar/ModelsManagementBottomBar.kt @@ -207,7 +207,7 @@ fun ModelsManagementBottomBar( onDismissRequest = { importingConfig.toggleMenu(false) } ) { DropdownMenuItem( - text = { Text("Import a local GGUF model") }, + text = { Text("Import a local model") }, leadingIcon = { Icon( imageVector = Icons.Default.FolderOpen, @@ -217,7 +217,7 @@ fun ModelsManagementBottomBar( onClick = importingConfig.importFromLocal ) DropdownMenuItem( - text = { Text("Download from HuggingFace") }, + text = { Text("Download a model from Hugging Face") }, leadingIcon = { Icon( painter = painterResource(id = R.drawable.logo_huggingface), diff --git a/examples/llama.android/app/src/main/java/com/example/llama/ui/scaffold/topbar/ModelsBrowsingTopBar.kt b/examples/llama.android/app/src/main/java/com/example/llama/ui/scaffold/topbar/ModelsBrowsingTopBar.kt index 5b5b4bfeff..df1931e7c0 100644 --- a/examples/llama.android/app/src/main/java/com/example/llama/ui/scaffold/topbar/ModelsBrowsingTopBar.kt +++ b/examples/llama.android/app/src/main/java/com/example/llama/ui/scaffold/topbar/ModelsBrowsingTopBar.kt @@ -12,10 +12,16 @@ import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.PlainTooltip import androidx.compose.material3.Text +import androidx.compose.material3.TooltipAnchorPosition +import androidx.compose.material3.TooltipBox +import androidx.compose.material3.TooltipDefaults import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.rememberTooltipState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @@ -24,11 +30,23 @@ import androidx.compose.ui.unit.dp @Composable fun ModelsBrowsingTopBar( title: String, + showTooltip: Boolean, showManagingToggle: Boolean, onToggleManaging: () -> Unit, onNavigateBack: (() -> Unit)? = null, onMenuOpen: (() -> Unit)? = null, ) { + val tooltipState = rememberTooltipState( + initialIsVisible = showTooltip, + isPersistent = showTooltip + ) + + LaunchedEffect(showTooltip, showManagingToggle) { + if (showTooltip && showManagingToggle) { + tooltipState.show() + } + } + TopAppBar( title = { Text(title) }, navigationIcon = { @@ -54,8 +72,20 @@ fun ModelsBrowsingTopBar( }, actions = { if (showManagingToggle) { - ModelManageActionToggle(onToggleManaging) + TooltipBox( + positionProvider = TooltipDefaults.rememberTooltipPositionProvider( + TooltipAnchorPosition.Below), + state = tooltipState, + tooltip = { + PlainTooltip { + Text("Tap this button to install another model or manage your models!") + } + }, + onDismissRequest = {} + ) { + ModelManageActionToggle(onToggleManaging) } + } }, colors = TopAppBarDefaults.topAppBarColors( containerColor = MaterialTheme.colorScheme.surface, diff --git a/examples/llama.android/app/src/main/java/com/example/llama/ui/scaffold/topbar/TopBarConfig.kt b/examples/llama.android/app/src/main/java/com/example/llama/ui/scaffold/topbar/TopBarConfig.kt index a27ebf4365..1279efc49a 100644 --- a/examples/llama.android/app/src/main/java/com/example/llama/ui/scaffold/topbar/TopBarConfig.kt +++ b/examples/llama.android/app/src/main/java/com/example/llama/ui/scaffold/topbar/TopBarConfig.kt @@ -28,6 +28,7 @@ sealed class TopBarConfig { data class ModelsBrowsing( override val title: String, override val navigationIcon: NavigationIcon, + val showTooltip: Boolean, val showManagingToggle: Boolean, val onToggleManaging: () -> Unit, ) : TopBarConfig() diff --git a/examples/llama.android/app/src/main/java/com/example/llama/ui/screens/ModelLoadingScreen.kt b/examples/llama.android/app/src/main/java/com/example/llama/ui/screens/ModelLoadingScreen.kt index ba14fdee71..451d5ab531 100644 --- a/examples/llama.android/app/src/main/java/com/example/llama/ui/screens/ModelLoadingScreen.kt +++ b/examples/llama.android/app/src/main/java/com/example/llama/ui/screens/ModelLoadingScreen.kt @@ -43,6 +43,7 @@ import androidx.compose.material3.RadioButton import androidx.compose.material3.SegmentedButton import androidx.compose.material3.SegmentedButtonDefaults import androidx.compose.material3.SingleChoiceSegmentedButtonRow +import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -250,6 +251,7 @@ fun ModelLoadingScreen( if (!showedSystemPromptWarning) { onScaffoldEvent(ScaffoldEvent.ShowSnackbar( message = "Model may not support system prompt!\nProceed with caution.", + duration = SnackbarDuration.Long, )) showedSystemPromptWarning = true } diff --git a/examples/llama.android/app/src/main/java/com/example/llama/ui/screens/ModelsManagementAndDeletingScreen.kt b/examples/llama.android/app/src/main/java/com/example/llama/ui/screens/ModelsManagementAndDeletingScreen.kt index 0a95a2ebf4..39fc63496b 100644 --- a/examples/llama.android/app/src/main/java/com/example/llama/ui/screens/ModelsManagementAndDeletingScreen.kt +++ b/examples/llama.android/app/src/main/java/com/example/llama/ui/screens/ModelsManagementAndDeletingScreen.kt @@ -117,7 +117,7 @@ fun ModelsManagementAndDeletingScreen( } else if (filteredModels.isEmpty()) { // Prompt the user to import a model val title = when (activeFiltersCount) { - 0 -> "Import or download,\n have it your way" + 0 -> "Choose a model:\nImport locally or download online" 1 -> "No models match\n the selected filter" else -> "No models match\n the selected filters" } diff --git a/examples/llama.android/app/src/main/java/com/example/llama/viewmodel/MainViewModel.kt b/examples/llama.android/app/src/main/java/com/example/llama/viewmodel/MainViewModel.kt index dd09f0e49a..3e87233935 100644 --- a/examples/llama.android/app/src/main/java/com/example/llama/viewmodel/MainViewModel.kt +++ b/examples/llama.android/app/src/main/java/com/example/llama/viewmodel/MainViewModel.kt @@ -29,6 +29,9 @@ class MainViewModel @Inject constructor ( private val _showChatTooltip = MutableStateFlow(true) val showChatTooltip: StateFlow = _showChatTooltip.asStateFlow() + private val _showModelManagementTooltip = MutableStateFlow(true) + val showModelManagementTooltip: StateFlow = _showModelManagementTooltip.asStateFlow() + /** * Unload the current model and release the resources @@ -47,12 +50,11 @@ class MainViewModel @Inject constructor ( _showChatTooltip.value = !it } } - } - } - - fun waiveModelImportTooltip() { - viewModelScope.launch { - appPreferences.setUserHasImportedFirstModel(true) + launch { + appPreferences.userHasNavigatedToManagement().collect { + _showModelManagementTooltip.value = !it + } + } } } @@ -61,4 +63,15 @@ class MainViewModel @Inject constructor ( appPreferences.setUserHasChattedWithModel(true) } } + fun waiveModelImportTooltip() { + viewModelScope.launch { + appPreferences.setUserHasImportedFirstModel(true) + } + } + + fun waiveModelManagementTooltip() { + viewModelScope.launch { + appPreferences.setUserHasNavigatedToManagement(true) + } + } }