Skip to content

Commit f0c6fbd

Browse files
Merge pull request #76 from Android-PowerUser/add-mistral-medium-latest-with-retry-logic
Add mistral medium latest with retry logic
2 parents e22e9f8 + 29a6212 commit f0c6fbd

File tree

97 files changed

+4912
-6997
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+4912
-6997
lines changed

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,19 @@
1919
build/
2020

2121
# Local configuration file (sdk path, etc)
22+
local.properties
2223

2324

2425
# Log Files
2526
*.log
27+
build_log*.txt
28+
build_errors*.txt
29+
build_output*.txt
30+
build_apk_log.txt
31+
compile_error.txt
32+
ho_build_result.txt
33+
inspection_output.txt
34+
signaling-server/node_modules/
2635

2736
# Android Studio Navigation editor files
2837
.navigation/

AGENTS.md

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
1-
AM WICHTIGSTEN: 1. Befolge Befehle genau und tue NICHTS ANDERES als exakt angeordet!!! Nicht mehr und nicht weniger.
1+
MOST IMPORTANT: 1. Follow commands exactly and do NOTHING BUT what is precisely instructed!!! Nothing more and nothing less.
22

3-
AM WICHTIGSTEN: 1. Befolge Befehle genau und tue NICHTS ANDERES als exakt angeordet!!! Nicht mehr und nicht weniger.
3+
2. Write to me in the language in which I give the task.
44

5-
AM WICHTIGSTEN: 1. Befolge Befehle genau und tue NICHTS ANDERES als exakt angeordet!!! Nicht mehr und nicht weniger.
5+
3. Before each build, enter critic mode and evaluate the changes as if you were someone else. Check if they meet the requirements, if anything else is affected, and fix any problems. Repeat this until no more critics find any errors.
66

7-
2. Schreibe mit mir auf Deutsch, übersetzte für dich aber die Anweisung auf Englisch.
7+
4. Do not build with minor changes.
88

9-
3. Vor jedem Build gehst du in ein Kritikermodus und bewertest die Änderungen als wärst du eine andere Person und schaust ob es den Anforderungen gerecht wird, nichts anderes beeinflusst wird und behebst entsprechende Probleme. Wiederhole das solange bis keine Kritikerpersönlichkeiten mehr Fehler finden.
9+
5. Ask questions about things I haven't specified and don't assume anything implicitly.
1010

11-
4. Builde AUSSCHLIESSLICH über die Github CLI in Github Actions und überwache die Ausgabe. Builde niemals lokal. Vergiss nicht das pushen.
11+
6. Ensure that other functions and properties are not affected or broken.
1212

13-
5. Builde bei kleinen Änderungen nicht.
13+
7. Do not make compilation errors. Pay attention to imports.
1414

15-
6. Ignoriere "Target content not found" beim Ersetzen nicht.
15+
8. If you see potential for refactoring to eliminate avoidable complexity or technical debt without changing behavior, do so immediately.
1616

17-
7. Stelle Fragen zu Dingen zu denen ich keine Angaben gemacht habe und nehme nichts implizit an.
17+
9. If debug compilation fails in your environment, resolve the issue before reporting it as complete.
1818

19-
8. Achte darauf, dass andere Funktionen und Eigenschaften nicht beeinflusst werden oder gar kaputt gehen.
20-
21-
9. Mache keine Kompilierfehler. Achte auf imports.
22-
23-
10. Bei der App handelt es sich um Produktionssoftware und ist kein Spielzeug.
19+
10. This app is production software and not a toy.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ Updates in Github are much faster than on the Play Store and have no restriction
2121

2222
This app urgently needs an update. Google's interfaces (API's) have changed, and it should also use shared parameter model (shared backbone), that can actually see the screen content, not just receive a description. However, I am no longer active as a developer, but that doesn't mean it's over, because this app can be developed entirely by AI and therefore its development can be continued by anyone:
2323

24-
To vibe coding for free with Claude Opus 4.6 thinking I use [Google Antigravity](https://antigravity.google/download). You can connect it directly to GitHub. You will probably have to fork the project first so that you can edit it. It has a free quota that can be extended with a one-month free subscription that can be canceled free of charge 29 days in advance. Unfortunately, this is not available for Android. The Linux version could also run on Android with Termux, PRoot, Ubuntu, and a window manager, but if you have a laptop/tablet computer, it's easier. Google Antigravity is much better and faster than Jules, which is also a vibe coding platform for the browser, but Joules is bad. Then it's better to use Termux Ubuntu, etc.. However, I completed the free month subscription with [Jules](https://jules.google.com). Theoretically, you could do the same with another Google account afterwards. Always enter several tasks at once, numbered and written very precisely and in detail.
24+
To vibe coding for free I use [Codex](https://ChatGPT.com/codex). You can connect it directly to GitHub. You will probably have to fork the project first so that you can edit it. It has a free quota that will be weekly refreshed and you can also easy switch the account to refresh the free quota. It is available in the browser for Android. Select the branch with the latest changes. Always enter tasks, if necessary numbered, and written very precisely and in detail.
2525

26-
Google Antigravity can build for you (recommended), but you can also build with Github action workflows. Stay in your fork, on your user account (you won't be able to start it otherwise), on mobile, click the gear icon and then Actions, and on desktop, click Actions directly. Click Workflows, select Android build, and start your chosen branch. After about 5 minutes, your app will be ready! If compilation errors occur, copy the output to Jules. If there are no more than 5 errors, even Gemini 2.5 Pro can usually handle this very reliably. Otherwise, proceed as you would when programming: Jules, then Claude, then Jules again. After that, the app needs to be signed. I use MiXplorer and select the test signature option. The others don't always work as well or as quickly. You can install it now.
26+
You can build the apk with Github actions: Stay in your fork, on your user account (you won't be able to start it otherwise), on mobile, click the gear icon and then Actions, and on desktop, click Actions directly. Click Workflows, select Android build, and start your chosen branch. After about 5 minutes, your app will be ready! If compilation errors occur, copy the output to Jules. If there are no more than 5 errors, even Gemini 2.5 Pro can usually handle this very reliably. Otherwise, proceed as you would when programming: Jules, then Claude, then Jules again. After that, the app needs to be signed. I use MiXplorer and select the test signature option. The others don't always work as well or as quickly. You can install it now.
2727

2828
### needed updates
2929

-23.5 MB
Binary file not shown.

app-release-signed.apk

-66 MB
Binary file not shown.

app/build.gradle.kts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ plugins {
88
id("com.google.gms.google-services")
99
}
1010

11-
// Redirect build output to C: drive (NTFS) to avoid corrupted ExFAT build cache
12-
if (System.getenv("CI") == null) {
13-
layout.buildDirectory = file("C:/GradleBuild/app")
11+
// Optional override for local build directory (e.g. external storage issues on specific setups).
12+
// If not provided, Gradle default build directory is used.
13+
System.getenv("SCREENOPERATOR_BUILD_DIR")?.takeIf { it.isNotBlank() }?.let { customBuildDir ->
14+
layout.buildDirectory = file(customBuildDir)
1415
}
1516

1617
android {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.google.ai.sample
2+
3+
import com.google.ai.sample.util.Command
4+
import java.util.LinkedList
5+
import java.util.concurrent.atomic.AtomicBoolean
6+
7+
internal class AccessibilityCommandQueue {
8+
private val queue = LinkedList<Command>()
9+
private val processing = AtomicBoolean(false)
10+
11+
@Synchronized
12+
fun clearAndUnlock() {
13+
queue.clear()
14+
processing.set(false)
15+
}
16+
17+
@Synchronized
18+
fun enqueue(command: Command): Int {
19+
queue.add(command)
20+
return queue.size
21+
}
22+
23+
@Synchronized
24+
fun peek(): Command? = queue.peek()
25+
26+
@Synchronized
27+
fun poll(): Command? = queue.poll()
28+
29+
@Synchronized
30+
fun size(): Int = queue.size
31+
32+
@Synchronized
33+
fun isEmpty(): Boolean = queue.isEmpty()
34+
fun tryAcquireProcessing(): Boolean = processing.compareAndSet(false, true)
35+
fun releaseProcessing() = processing.set(false)
36+
}
37+
/*
38+
test
39+
*/
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.google.ai.sample
2+
3+
import android.content.Context
4+
import android.provider.Settings
5+
import android.util.Log
6+
7+
internal object AccessibilityServiceStateChecker {
8+
fun isEnabled(context: Context, tag: String): Boolean {
9+
val accessibilityEnabled = try {
10+
Settings.Secure.getInt(
11+
context.contentResolver,
12+
Settings.Secure.ACCESSIBILITY_ENABLED
13+
)
14+
} catch (e: Settings.SettingNotFoundException) {
15+
Log.e(tag, "Error finding accessibility setting: ${e.message}")
16+
return false
17+
}
18+
19+
if (accessibilityEnabled != 1) {
20+
Log.d(tag, "Accessibility is not enabled")
21+
return false
22+
}
23+
24+
val serviceString =
25+
"${context.packageName}/${ScreenOperatorAccessibilityService::class.java.canonicalName}"
26+
val enabledServices = Settings.Secure.getString(
27+
context.contentResolver,
28+
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
29+
) ?: ""
30+
31+
val isEnabled = enabledServices.contains(serviceString)
32+
Log.d(tag, "Service $serviceString is ${if (isEnabled) "enabled" else "not enabled"}")
33+
return isEnabled
34+
}
35+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.google.ai.sample
2+
3+
import android.content.ContentResolver
4+
import android.provider.Settings
5+
6+
internal object AccessibilityServiceStatusResolver {
7+
fun isServiceEnabled(contentResolver: ContentResolver, packageName: String): Boolean {
8+
val service = "$packageName/${ScreenOperatorAccessibilityService::class.java.canonicalName}"
9+
val enabledServices = Settings.Secure.getString(
10+
contentResolver,
11+
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
12+
)
13+
return enabledServices?.contains(service, ignoreCase = true) == true
14+
}
15+
}

app/src/main/kotlin/com/google/ai/sample/GenerativeAiViewModelFactory.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ enum class ModelOption(
3131
) {
3232
PUTER_GLM5("GLM-5 (Puter)", "z-ai/glm-5", ApiProvider.PUTER, supportsScreenshot = false),
3333
MISTRAL_LARGE_3("Mistral Large 3", "mistral-large-latest", ApiProvider.MISTRAL),
34+
MISTRAL_MEDIUM_3_1("Mistral Medium 3.1", "mistral-medium-latest", ApiProvider.MISTRAL),
3435
GPT_5_1_CODEX_MAX("GPT-5.1 Codex Max (Vercel)", "openai/gpt-5.1-codex-max", ApiProvider.VERCEL),
3536
GPT_5_1_CODEX_MINI("GPT-5.1 Codex Mini (Vercel)", "openai/gpt-5.1-codex-mini", ApiProvider.VERCEL),
3637
GPT_5_NANO("GPT-5 Nano (Vercel)", "openai/gpt-5-nano", ApiProvider.VERCEL),
@@ -59,7 +60,7 @@ enum class ModelOption(
5960

6061
val GenerativeViewModelFactory = object : ViewModelProvider.Factory {
6162
override fun <T : ViewModel> create(
62-
viewModelClass: Class<T>,
63+
modelClass: Class<T>,
6364
extras: CreationExtras
6465
): T {
6566
// Get the application context from extras
@@ -86,7 +87,7 @@ val GenerativeViewModelFactory = object : ViewModelProvider.Factory {
8687
throw IllegalStateException("API key for ${currentModel.apiProvider} is not available. Please set an API key.")
8788
}
8889

89-
return with(viewModelClass) {
90+
val createdViewModel = with(modelClass) {
9091
when {
9192
isAssignableFrom(PhotoReasoningViewModel::class.java) -> {
9293
if (currentModel.modelName.contains("live")) {
@@ -128,9 +129,11 @@ val GenerativeViewModelFactory = object : ViewModelProvider.Factory {
128129
}
129130

130131
else ->
131-
throw IllegalArgumentException("Unknown ViewModel class: ${viewModelClass.name}")
132+
throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
132133
}
133-
} as T
134+
}
135+
136+
return modelClass.cast(createdViewModel)
134137
}
135138
}
136139

0 commit comments

Comments
 (0)