fix:android
Android / build-android (push) Successful in 2m46s

This commit is contained in:
intelligence committed 2026-08-29 15:16:21 +02:00
1 parent 15ab99c740
commit 2b89bbd959
6 files changed
+76 -150

No files matched your search

@@ -1,36 +1,23 @@
package io.github.transui.android; package io.github.transui.android;
final class AppConfig { final class AppConfig {
final String remoteEndpoint; final String endpoint;
final String remoteModel; final String model;
final String remoteKey; final String apiKey;
final String localEndpoint;
final String localModel;
final String localKey;
final String prompt; final String prompt;
final boolean remoteFirst;
AppConfig( AppConfig(
String remoteEndpoint, String endpoint,
String remoteModel, String model,
String remoteKey, String apiKey,
String localEndpoint, String prompt) {
String localModel, this.endpoint = endpoint;
String localKey, this.model = model;
String prompt, this.apiKey = apiKey;
boolean remoteFirst) {
this.remoteEndpoint = remoteEndpoint;
this.remoteModel = remoteModel;
this.remoteKey = remoteKey;
this.localEndpoint = localEndpoint;
this.localModel = localModel;
this.localKey = localKey;
this.prompt = prompt; this.prompt = prompt;
this.remoteFirst = remoteFirst;
} }
boolean hasUsableEndpoint() { boolean hasUsableEndpoint() {
if (remoteFirst && !remoteEndpoint.isEmpty()) return true; return !endpoint.isEmpty();
return !localEndpoint.isEmpty();
} }
} }
@@ -12,34 +12,38 @@ final class ConfigRepository {
ConfigRepository(Context context) { ConfigRepository(Context context) {
preferences = context.getApplicationContext().getSharedPreferences(PREFS, Context.MODE_PRIVATE); preferences = context.getApplicationContext().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
removeObsoleteLocalSettings();
} }
AppConfig load() { AppConfig load() {
String remoteKey = decryptOrEmpty(preferences.getString("remote_key", ""));
String localKey = decryptOrEmpty(preferences.getString("local_key", ""));
return new AppConfig( return new AppConfig(
preferences.getString("remote_endpoint", ""), preferences.getString("remote_endpoint", ""),
preferences.getString("remote_model", ""), preferences.getString("remote_model", ""),
remoteKey, decryptOrEmpty(preferences.getString("remote_key", "")),
preferences.getString("local_endpoint", "http://localhost:8080"), preferences.getString("prompt", ""));
preferences.getString("local_model", ""),
localKey,
preferences.getString("prompt", ""),
preferences.getBoolean("remote_first", true));
} }
void save(AppConfig config) throws GeneralSecurityException, IOException { void save(AppConfig config) throws GeneralSecurityException, IOException {
String remoteKey = SecretStore.encrypt(config.remoteKey);
String localKey = SecretStore.encrypt(config.localKey);
preferences.edit() preferences.edit()
.putString("remote_endpoint", config.remoteEndpoint) .putString("remote_endpoint", config.endpoint)
.putString("remote_model", config.remoteModel) .putString("remote_model", config.model)
.putString("remote_key", remoteKey) .putString("remote_key", SecretStore.encrypt(config.apiKey))
.putString("local_endpoint", config.localEndpoint)
.putString("local_model", config.localModel)
.putString("local_key", localKey)
.putString("prompt", config.prompt) .putString("prompt", config.prompt)
.putBoolean("remote_first", config.remoteFirst) .apply();
}
private void removeObsoleteLocalSettings() {
if (!preferences.contains("local_endpoint")
&& !preferences.contains("local_model")
&& !preferences.contains("local_key")
&& !preferences.contains("remote_first")) {
return;
}
preferences.edit()
.remove("local_endpoint")
.remove("local_model")
.remove("local_key")
.remove("remote_first")
.apply(); .apply();
} }
@@ -10,7 +10,6 @@ import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.Switch;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
@@ -20,14 +19,12 @@ import java.security.GeneralSecurityException;
public final class MainActivity extends Activity { public final class MainActivity extends Activity {
private static final int MICROPHONE_REQUEST = 1; private static final int MICROPHONE_REQUEST = 1;
private EditText remoteEndpoint; private static final String ACTION_LANGUAGE_SETTINGS = "android.settings.LANGUAGE_SETTINGS";
private EditText remoteModel;
private EditText remoteKey; private EditText endpoint;
private EditText localEndpoint; private EditText model;
private EditText localModel; private EditText apiKey;
private EditText localKey;
private EditText prompt; private EditText prompt;
private Switch remoteFirst;
private TextView providerStatus; private TextView providerStatus;
private Button microphone; private Button microphone;
private ConfigRepository repository; private ConfigRepository repository;
@@ -42,9 +39,7 @@ public final class MainActivity extends Activity {
findViewById(R.id.save).setOnClickListener(view -> saveConfig()); findViewById(R.id.save).setOnClickListener(view -> saveConfig());
microphone.setOnClickListener(view -> requestMicrophone()); microphone.setOnClickListener(view -> requestMicrophone());
findViewById(R.id.choose_provider).setOnClickListener(view -> { findViewById(R.id.language_settings).setOnClickListener(view -> openLanguageSettings());
if (saveConfig()) openVoiceInputSettings();
});
} }
@Override @Override
@@ -60,39 +55,27 @@ public final class MainActivity extends Activity {
} }
private void bindViews() { private void bindViews() {
remoteEndpoint = findViewById(R.id.remote_endpoint); endpoint = findViewById(R.id.endpoint);
remoteModel = findViewById(R.id.remote_model); model = findViewById(R.id.model);
remoteKey = findViewById(R.id.remote_key); apiKey = findViewById(R.id.api_key);
localEndpoint = findViewById(R.id.local_endpoint);
localModel = findViewById(R.id.local_model);
localKey = findViewById(R.id.local_key);
prompt = findViewById(R.id.prompt); prompt = findViewById(R.id.prompt);
remoteFirst = findViewById(R.id.remote_first);
providerStatus = findViewById(R.id.provider_status); providerStatus = findViewById(R.id.provider_status);
microphone = findViewById(R.id.microphone); microphone = findViewById(R.id.microphone);
} }
private void showConfig(AppConfig config) { private void showConfig(AppConfig config) {
remoteEndpoint.setText(config.remoteEndpoint); endpoint.setText(config.endpoint);
remoteModel.setText(config.remoteModel); model.setText(config.model);
remoteKey.setText(config.remoteKey); apiKey.setText(config.apiKey);
localEndpoint.setText(config.localEndpoint);
localModel.setText(config.localModel);
localKey.setText(config.localKey);
prompt.setText(config.prompt); prompt.setText(config.prompt);
remoteFirst.setChecked(config.remoteFirst);
} }
private boolean saveConfig() { private boolean saveConfig() {
AppConfig config = new AppConfig( AppConfig config = new AppConfig(
value(remoteEndpoint).trim(), value(endpoint).trim(),
value(remoteModel).trim(), value(model).trim(),
value(remoteKey).trim(), value(apiKey).trim(),
value(localEndpoint).trim(), value(prompt));
value(localModel).trim(),
value(localKey).trim(),
value(prompt),
remoteFirst.isChecked());
try { try {
repository.save(config); repository.save(config);
Toast.makeText(this, R.string.settings_saved, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.settings_saved, Toast.LENGTH_SHORT).show();
@@ -109,11 +92,17 @@ public final class MainActivity extends Activity {
} }
} }
private void openVoiceInputSettings() { private void openLanguageSettings() {
try { try {
startActivity(new Intent(Settings.ACTION_VOICE_INPUT_SETTINGS)); startActivity(new Intent(ACTION_LANGUAGE_SETTINGS));
} catch (ActivityNotFoundException error) { return;
Toast.makeText(this, R.string.voice_settings_unavailable, Toast.LENGTH_LONG).show(); } catch (ActivityNotFoundException ignored) {
// Older Android builds expose the combined Languages and input page here.
}
try {
startActivity(new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS));
} catch (ActivityNotFoundException ignored) {
Toast.makeText(this, R.string.language_settings_unavailable, Toast.LENGTH_LONG).show();
} }
} }
@@ -20,25 +20,10 @@ final class TranscriptionClient {
private volatile HttpURLConnection activeConnection; private volatile HttpURLConnection activeConnection;
Result transcribe(AppConfig config, byte[] wav) { Result transcribe(AppConfig config, byte[] wav) {
Result remoteResult = null; if (config.endpoint.isEmpty()) {
if (config.remoteFirst && !config.remoteEndpoint.isEmpty()) { return Result.error("No transcription endpoint is configured", SpeechRecognizer.ERROR_CLIENT);
remoteResult = request(
config.remoteEndpoint, config.remoteModel, config.remoteKey, config.prompt, wav);
if (remoteResult.text != null) return remoteResult;
} }
return request(config.endpoint, config.model, config.apiKey, config.prompt, wav);
if (!config.localEndpoint.isEmpty()) {
Result localResult = request(
config.localEndpoint, config.localModel, config.localKey, config.prompt, wav);
if (localResult.text != null || remoteResult == null) return localResult;
return Result.error(
remoteResult.error + "; local: " + localResult.error,
localResult.errorCode);
}
return remoteResult != null
? remoteResult
: Result.error("No transcription endpoint is configured", SpeechRecognizer.ERROR_CLIENT);
} }
void cancel() { void cancel() {
@@ -48,51 +48,26 @@
<TextView <TextView
style="@style/SectionLabel" style="@style/SectionLabel"
android:text="@string/remote" /> android:text="@string/server" />
<EditText <EditText
android:id="@+id/remote_endpoint" android:id="@+id/endpoint"
style="@style/Field" style="@style/Field"
android:hint="@string/remote_endpoint" android:hint="@string/endpoint"
android:importantForAutofill="no" android:importantForAutofill="no"
android:inputType="textUri" /> android:inputType="textUri" />
<EditText <EditText
android:id="@+id/remote_model" android:id="@+id/model"
style="@style/Field" style="@style/Field"
android:hint="@string/remote_model" android:hint="@string/model"
android:importantForAutofill="no" android:importantForAutofill="no"
android:inputType="text" /> android:inputType="text" />
<EditText <EditText
android:id="@+id/remote_key" android:id="@+id/api_key"
style="@style/Field" style="@style/Field"
android:hint="@string/remote_api_key" android:hint="@string/api_key"
android:importantForAutofill="no"
android:inputType="textPassword" />
<TextView
style="@style/SectionLabel"
android:text="@string/local" />
<EditText
android:id="@+id/local_endpoint"
style="@style/Field"
android:hint="@string/local_endpoint"
android:importantForAutofill="no"
android:inputType="textUri" />
<EditText
android:id="@+id/local_model"
style="@style/Field"
android:hint="@string/local_model"
android:importantForAutofill="no"
android:inputType="text" />
<EditText
android:id="@+id/local_key"
style="@style/Field"
android:hint="@string/local_api_key"
android:importantForAutofill="no" android:importantForAutofill="no"
android:inputType="textPassword" /> android:inputType="textPassword" />
@@ -109,15 +84,6 @@
android:maxLines="3" android:maxLines="3"
android:singleLine="false" /> android:singleLine="false" />
<Switch
android:id="@+id/remote_first"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="14dp"
android:checked="true"
android:text="@string/remote_first"
android:textColor="@color/ink" />
<Button <Button
android:id="@+id/save" android:id="@+id/save"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -133,10 +99,10 @@
android:text="@string/allow_microphone" /> android:text="@string/allow_microphone" />
<Button <Button
android:id="@+id/choose_provider" android:id="@+id/language_settings"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:text="@string/choose_provider" /> android:text="@string/open_language_settings" />
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
+6 -11
View File
@@ -5,22 +5,17 @@
<string name="title">Transui voice input</string> <string name="title">Transui voice input</string>
<string name="status_selected">Selected as voice input</string> <string name="status_selected">Selected as voice input</string>
<string name="status_not_selected">Select Transui as voice input</string> <string name="status_not_selected">Select Transui as voice input</string>
<string name="remote">REMOTE</string> <string name="server">SERVER</string>
<string name="local">LOCAL</string>
<string name="request">REQUEST</string> <string name="request">REQUEST</string>
<string name="remote_endpoint">Remote endpoint</string> <string name="endpoint">Transcription endpoint</string>
<string name="remote_model">Remote model</string> <string name="model">Model</string>
<string name="remote_api_key">Remote API key</string> <string name="api_key">API key</string>
<string name="local_endpoint">Local endpoint</string>
<string name="local_model">Local model</string>
<string name="local_api_key">Local API key</string>
<string name="prompt">Prompt</string> <string name="prompt">Prompt</string>
<string name="remote_first">Remote first, local fallback</string>
<string name="save">Save settings</string> <string name="save">Save settings</string>
<string name="choose_provider">Choose voice input</string> <string name="open_language_settings">Open language settings</string>
<string name="allow_microphone">Allow microphone</string> <string name="allow_microphone">Allow microphone</string>
<string name="microphone_allowed">Microphone allowed</string> <string name="microphone_allowed">Microphone allowed</string>
<string name="settings_saved">Settings saved</string> <string name="settings_saved">Settings saved</string>
<string name="settings_save_failed">Could not save API keys</string> <string name="settings_save_failed">Could not save API keys</string>
<string name="voice_settings_unavailable">Voice input settings are unavailable</string> <string name="language_settings_unavailable">Language settings are unavailable</string>
</resources> </resources>