Skip to content

feat: add app blocklist, usage statistics, and localized built-in commands#23

Open
theobeaudenon wants to merge 6 commits intoMusheer360:masterfrom
theobeaudenon:master
Open

feat: add app blocklist, usage statistics, and localized built-in commands#23
theobeaudenon wants to merge 6 commits intoMusheer360:masterfrom
theobeaudenon:master

Conversation

@theobeaudenon
Copy link
Copy Markdown

feat: add app blocklist, usage statistics, and localized built-in commands

  • App Blocklist: Add BlocklistManager and BlocklistScreen to allow users to exclude specific applications from being processed by the accessibility service.
  • Statistics & Usage Tracking: Add StatsManager to track total tokens used, monthly request counts, and favorite commands.
  • Dashboard Improvements:
    • Display usage statistics (tokens, requests, favorite command) on the dashboard.
    • Add a TokensBarChart component to visualize token usage over the last 7 days.
    • Add vertical scrolling to the dashboard.
  • Localized Commands: Support localized prompts for built-in commands (e.g., fix, improve, shorten) with translations for DE, ES, FR, IT, JA, PT, RU, and ZH.
  • API Enhancements: Update GeminiClient and OpenAICompatibleClient to return token usage metadata alongside generated text.
  • UX Improvements:
    • Add configurable loading indicator styles (Default, Dots, Squares, None) in settings.
    • Add "Duplicate command" functionality to the Commands screen.
    • Update AssistantService to respect the blocklist and update usage statistics upon successful text replacement.
  • Permissions: Add <queries> to AndroidManifest.xml to allow the app to list installed packages for the blocklist.

…mands

- **App Blocklist**: Add `BlocklistManager` and `BlocklistScreen` to allow users to exclude specific applications from being processed by the accessibility service.
- **Statistics & Usage Tracking**: Add `StatsManager` to track total tokens used, monthly request counts, and favorite commands.
- **Dashboard Improvements**:
    - Display usage statistics (tokens, requests, favorite command) on the dashboard.
    - Add a `TokensBarChart` component to visualize token usage over the last 7 days.
    - Add vertical scrolling to the dashboard.
- **Localized Commands**: Support localized prompts for built-in commands (e.g., fix, improve, shorten) with translations for DE, ES, FR, IT, JA, PT, RU, and ZH.
- **API Enhancements**: Update `GeminiClient` and `OpenAICompatibleClient` to return token usage metadata alongside generated text.
- **UX Improvements**:
    - Add configurable loading indicator styles (Default, Dots, Squares, None) in settings.
    - Add "Duplicate command" functionality to the Commands screen.
    - Update `AssistantService` to respect the blocklist and update usage statistics upon successful text replacement.
- **Permissions**: Add `<queries>` to `AndroidManifest.xml` to allow the app to list installed packages for the blocklist.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR expands SwiftSlate’s functionality with app-level exclusions, usage tracking/visualization, and localization for built-in command prompts, and wires these features into the UI and accessibility service flow.

Changes:

  • Added app blocklist management UI + persistence, and enforced it in AssistantService.
  • Added usage statistics tracking (tokens + requests + favorites) and surfaced stats + a 7‑day token chart on the dashboard.
  • Localized built-in command prompt definitions via JSON assets and added settings for built-in language + loading indicator style.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
app/src/main/res/values/strings.xml Adds strings for dashboard stats, built-in language selector, spinner styles, and blocklist UI.
app/src/main/java/com/musheer360/swiftslate/ui/SettingsScreen.kt Adds settings for built-in language, spinner style, and navigation to the blocklist screen.
app/src/main/java/com/musheer360/swiftslate/ui/DashboardScreen.kt Displays stats and renders a 7‑day token bar chart; enables vertical scrolling.
app/src/main/java/com/musheer360/swiftslate/ui/CommandsScreen.kt Adds “duplicate command” action via pre-filling the add-command form.
app/src/main/java/com/musheer360/swiftslate/ui/BlocklistScreen.kt New screen to browse installed launcher apps and toggle blocklist membership.
app/src/main/java/com/musheer360/swiftslate/service/AssistantService.kt Respects the app blocklist, records stats on success, and supports configurable spinner styles.
app/src/main/java/com/musheer360/swiftslate/manager/StatsManager.kt New stats persistence and 7‑day token aggregation for dashboard chart.
app/src/main/java/com/musheer360/swiftslate/manager/CommandManager.kt Loads localized built-in definitions from assets based on user/system language setting.
app/src/main/java/com/musheer360/swiftslate/manager/BlocklistManager.kt New persistence layer for blocked package names.
app/src/main/java/com/musheer360/swiftslate/MainActivity.kt Adds navigation route for the new blocklist screen and passes NavController into settings.
app/src/main/java/com/musheer360/swiftslate/api/OpenAICompatibleClient.kt Returns generated text plus token usage metadata.
app/src/main/java/com/musheer360/swiftslate/api/GeminiClient.kt Returns generated text plus token usage metadata.
app/src/main/assets/builtInDefinitions/en_us.json English built-in prompt definitions.
app/src/main/assets/builtInDefinitions/de.json German built-in prompt definitions.
app/src/main/assets/builtInDefinitions/es.json Spanish built-in prompt definitions.
app/src/main/assets/builtInDefinitions/fr.json French built-in prompt definitions.
app/src/main/assets/builtInDefinitions/it.json Italian built-in prompt definitions.
app/src/main/assets/builtInDefinitions/ja.json Japanese built-in prompt definitions.
app/src/main/assets/builtInDefinitions/pt.json Portuguese built-in prompt definitions.
app/src/main/assets/builtInDefinitions/ru.json Russian built-in prompt definitions.
app/src/main/assets/builtInDefinitions/zh.json Chinese built-in prompt definitions.
app/src/main/AndroidManifest.xml Adds <queries> for launcher activity discovery (required for listing apps).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +19 to +31
private fun getBuiltInDefinitions(): List<Pair<String, String>> {
val langSetting = settingsPrefs.getString("builtin_lang", "auto") ?: "auto"
val langToTry = if (langSetting == "auto") {
java.util.Locale.getDefault().let { "${it.language}_${it.country}".lowercase() }
} else {
langSetting
}

var jsonContent: String? = loadJsonFromAsset("builtInDefinitions/$langToTry.json")
if (jsonContent == null) {
val langOnly = langToTry.substringBefore('_')
jsonContent = loadJsonFromAsset("builtInDefinitions/$langOnly.json")
}
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getBuiltInDefinitions() reads and parses a JSON asset synchronously. Because findCommand() calls getCommands() on every accessibility text-change event, this introduces file I/O + JSON parsing in a hot path and can significantly impact latency/battery. Consider caching the parsed built-in definitions in memory (and only reloading when builtin_lang changes) so findCommand() doesn’t touch assets on every invocation.

Copilot uses AI. Check for mistakes.
Comment on lines +68 to +70
apps = loadedApps
isLoading = false
}
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compose state (apps, isLoading) is being updated inside withContext(Dispatchers.IO). This makes state writes happen off the main thread and can lead to snapshot/threading issues. Prefer doing the package query on Dispatchers.IO, then switch back to the main dispatcher to update Compose state (e.g., assign apps/isLoading after withContext returns).

Copilot uses AI. Check for mistakes.
IconButton(onClick = { navController.popBackStack() }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Back"
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

contentDescription is hardcoded ("Back") instead of using a string resource. For accessibility + localization, this should come from strings.xml (or use null if the icon is purely decorative and the button already has an accessible label).

Suggested change
contentDescription = "Back"
contentDescription = stringResource(androidx.navigation.ui.R.string.nav_app_bar_navigate_up_description)

Copilot uses AI. Check for mistakes.
Comment on lines +114 to +116
placeholder = { Text("Search apps...") },
singleLine = true,
leadingIcon = { Icon(Icons.Default.Search, contentDescription = "Search") },
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The search field uses hardcoded English strings (placeholder "Search apps..." and search icon contentDescription). These should be moved to strings.xml so the screen remains localized and accessible.

Suggested change
placeholder = { Text("Search apps...") },
singleLine = true,
leadingIcon = { Icon(Icons.Default.Search, contentDescription = "Search") },
placeholder = { Text(stringResource(android.R.string.search_go)) },
singleLine = true,
leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },

Copilot uses AI. Check for mistakes.
contentDescription = stringResource(R.string.commands_delete_command),
tint = MaterialTheme.colorScheme.error
imageVector = Icons.Default.ContentCopy,
contentDescription = "Duplicate command",
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The duplicate button uses a hardcoded contentDescription ("Duplicate command"). Please move this to a string resource for localization/accessibility consistency with the rest of the UI.

Suggested change
contentDescription = "Duplicate command",
contentDescription = stringResource(android.R.string.copy),

Copilot uses AI. Check for mistakes.
Comment on lines +208 to +213
data.forEach { (dateStr, tokens) ->
val heightFraction = (tokens.toFloat() / maxTokens.toFloat()).coerceIn(0.02f, 1f)
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Bottom,
modifier = Modifier.weight(1f)
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

heightFraction is coerced to a minimum of 0.02f, which means days with tokens == 0 will still render a non-zero bar (even though the label is hidden). This makes the chart visually inaccurate for zero-usage days. Consider using a 0f minimum when tokens == 0, or applying a min height only for non-zero values (e.g., if (tokens == 0L) 0f else ...).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner

@Musheer360 Musheer360 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Impressive amount of work here — blocklist, stats, localized commands, spinner styles, and command duplication all in one PR. Each of these is a useful feature individually.

However, there are some significant issues that need to be addressed:


🔴 Blockers

1. Merge conflicts with current master
Since this PR was opened, master has had several significant changes:

  • Structured JSON output with auto-detection in both API clients
  • Processing watchdog in AssistantService
  • All UI strings extracted to strings.xml
  • Accessibility improvements (semantic grouping)
  • Hardened system instruction
  • Text replacement persistence fix (verification + clipboard fallback)

This PR modifies nearly every file that was changed: GeminiClient.kt, OpenAICompatibleClient.kt, AssistantService.kt, CommandManager.kt, all UI screens, and strings.xml. Merging as-is would produce conflicts in almost every file.

2. Breaking API return type change
generate() in both clients was changed from Result<String> to Result<Pair<String, Int>>. This breaks the structured output code, the stripHttpPrefix helper, and all error handling paths that were added recently. If token tracking is needed, a cleaner approach would be a data class (data class GenerateResult(val text: String, val tokensUsed: Int)) rather than a raw Pair, and it would need to be integrated with the existing structured output and retry logic.


🟡 Should Fix

3. StatsManager has unbounded storage growth
Daily token counts are stored as individual SharedPreferences keys (tokens_2026-04-06, tokens_2026-04-07, ...). These accumulate forever with no cleanup. After a year, that's 365+ orphan keys. SharedPreferences isn't designed for time-series data. Consider keeping only the last 30 days and cleaning up old entries in addTokens().

4. Localized commands loaded from assets on every call
getBuiltInDefinitions() reads and parses a JSON file from assets. It's called via getCommands()getBuiltInCommands(), which is called from updateTriggers() every 5 seconds. That's a file read + JSON parse every 5 seconds. The result should be cached and only invalidated when the language setting changes.

5. SettingsScreen signature change
Adding navController: NavController? as a parameter to SettingsScreen couples the screen to navigation. A callback like onNavigateToBlocklist: () -> Unit would be cleaner and more testable.


💭 Recommendation: Split into separate PRs

This PR would be much easier to review, test, and merge if split into focused PRs:

  1. PR A: App BlocklistBlocklistManager, BlocklistScreen, the check in AssistantService.onAccessibilityEvent, manifest <queries>, and the Settings entry point. This is self-contained and has minimal conflict surface.

  2. PR B: Localized built-in commands — The JSON asset files, the getBuiltInDefinitions() refactor in CommandManager, and the language picker in Settings. Also self-contained.

  3. PR C: Stats + Spinner styles + Command duplicationStatsManager, the API return type change, dashboard stats UI, spinner style options, and the copy button. This is the most invasive and should be rebased on top of A and B.

Each PR would be reviewable in isolation, easier to resolve conflicts with the current codebase, and independently mergeable.


The individual features here are all good ideas — especially the blocklist and localized commands. Splitting them up will get them merged faster. Happy to help with any questions on integrating with the recent changes.

…o Musheer360-master

# Conflicts:
#	app/src/main/java/com/musheer360/swiftslate/api/GeminiClient.kt
#	app/src/main/java/com/musheer360/swiftslate/api/OpenAICompatibleClient.kt
#	app/src/main/java/com/musheer360/swiftslate/service/AssistantService.kt
#	app/src/main/java/com/musheer360/swiftslate/ui/CommandsScreen.kt
#	app/src/main/java/com/musheer360/swiftslate/ui/DashboardScreen.kt
#	app/src/main/java/com/musheer360/swiftslate/ui/SettingsScreen.kt
#	app/src/main/res/values/strings.xml
…mands

- **App Blocklist**: Add `BlocklistManager` and `BlocklistScreen` to allow users to exclude specific applications from being processed by the accessibility service.
- **Statistics & Usage Tracking**: Add `StatsManager` to track total tokens used, monthly request counts, and favorite commands.
- **Dashboard Improvements**:
    - Display usage statistics (tokens, requests, favorite command) on the dashboard.
    - Add a `TokensBarChart` component to visualize token usage over the last 7 days.
    - Add vertical scrolling to the dashboard.
- **Localized Commands**: Support localized prompts for built-in commands (e.g., fix, improve, shorten) with translations for DE, ES, FR, IT, JA, PT, RU, and ZH.
- **API Enhancements**: Update `GeminiClient` and `OpenAICompatibleClient` to return token usage metadata alongside generated text.
- **UX Improvements**:
    - Add configurable loading indicator styles (Default, Dots, Squares, None) in settings.
    - Add "Duplicate command" functionality to the Commands screen.
    - Update `AssistantService` to respect the blocklist and update usage statistics upon successful text replacement.
- **Permissions**: Add `<queries>` to `AndroidManifest.xml` to allow the app to list installed packages for the blocklist.
…o Musheer360-master

# Conflicts:
#	app/src/main/java/com/musheer360/swiftslate/service/AssistantService.kt
# Conflicts:
#	app/src/main/java/com/musheer360/swiftslate/MainActivity.kt
#	app/src/main/java/com/musheer360/swiftslate/ui/CommandsScreen.kt
#	app/src/main/java/com/musheer360/swiftslate/ui/KeysScreen.kt
@theobeaudenon theobeaudenon requested a review from Musheer360 April 7, 2026 12:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants