1 parent
15ab99c740
commit
2b89bbd959
6 files changed
+76
-150
No files matched your search
@@ -1,36 +1,23 @@
|
||||
package io.github.transui.android;
|
||||
|
||||
final class AppConfig {
|
||||
final String remoteEndpoint;
|
||||
final String remoteModel;
|
||||
final String remoteKey;
|
||||
final String localEndpoint;
|
||||
final String localModel;
|
||||
final String localKey;
|
||||
final String endpoint;
|
||||
final String model;
|
||||
final String apiKey;
|
||||
final String prompt;
|
||||
final boolean remoteFirst;
|
||||
|
||||
AppConfig(
|
||||
String remoteEndpoint,
|
||||
String remoteModel,
|
||||
String remoteKey,
|
||||
String localEndpoint,
|
||||
String localModel,
|
||||
String localKey,
|
||||
String prompt,
|
||||
boolean remoteFirst) {
|
||||
this.remoteEndpoint = remoteEndpoint;
|
||||
this.remoteModel = remoteModel;
|
||||
this.remoteKey = remoteKey;
|
||||
this.localEndpoint = localEndpoint;
|
||||
this.localModel = localModel;
|
||||
this.localKey = localKey;
|
||||
String endpoint,
|
||||
String model,
|
||||
String apiKey,
|
||||
String prompt) {
|
||||
this.endpoint = endpoint;
|
||||
this.model = model;
|
||||
this.apiKey = apiKey;
|
||||
this.prompt = prompt;
|
||||
this.remoteFirst = remoteFirst;
|
||||
}
|
||||
|
||||
boolean hasUsableEndpoint() {
|
||||
if (remoteFirst && !remoteEndpoint.isEmpty()) return true;
|
||||
return !localEndpoint.isEmpty();
|
||||
return !endpoint.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -12,34 +12,38 @@ final class ConfigRepository {
|
||||
|
||||
ConfigRepository(Context context) {
|
||||
preferences = context.getApplicationContext().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
|
||||
removeObsoleteLocalSettings();
|
||||
}
|
||||
|
||||
AppConfig load() {
|
||||
String remoteKey = decryptOrEmpty(preferences.getString("remote_key", ""));
|
||||
String localKey = decryptOrEmpty(preferences.getString("local_key", ""));
|
||||
return new AppConfig(
|
||||
preferences.getString("remote_endpoint", ""),
|
||||
preferences.getString("remote_model", ""),
|
||||
remoteKey,
|
||||
preferences.getString("local_endpoint", "http://localhost:8080"),
|
||||
preferences.getString("local_model", ""),
|
||||
localKey,
|
||||
preferences.getString("prompt", ""),
|
||||
preferences.getBoolean("remote_first", true));
|
||||
decryptOrEmpty(preferences.getString("remote_key", "")),
|
||||
preferences.getString("prompt", ""));
|
||||
}
|
||||
|
||||
void save(AppConfig config) throws GeneralSecurityException, IOException {
|
||||
String remoteKey = SecretStore.encrypt(config.remoteKey);
|
||||
String localKey = SecretStore.encrypt(config.localKey);
|
||||
preferences.edit()
|
||||
.putString("remote_endpoint", config.remoteEndpoint)
|
||||
.putString("remote_model", config.remoteModel)
|
||||
.putString("remote_key", remoteKey)
|
||||
.putString("local_endpoint", config.localEndpoint)
|
||||
.putString("local_model", config.localModel)
|
||||
.putString("local_key", localKey)
|
||||
.putString("remote_endpoint", config.endpoint)
|
||||
.putString("remote_model", config.model)
|
||||
.putString("remote_key", SecretStore.encrypt(config.apiKey))
|
||||
.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();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
@@ -20,14 +19,12 @@ import java.security.GeneralSecurityException;
|
||||
public final class MainActivity extends Activity {
|
||||
private static final int MICROPHONE_REQUEST = 1;
|
||||
|
||||
private EditText remoteEndpoint;
|
||||
private EditText remoteModel;
|
||||
private EditText remoteKey;
|
||||
private EditText localEndpoint;
|
||||
private EditText localModel;
|
||||
private EditText localKey;
|
||||
private static final String ACTION_LANGUAGE_SETTINGS = "android.settings.LANGUAGE_SETTINGS";
|
||||
|
||||
private EditText endpoint;
|
||||
private EditText model;
|
||||
private EditText apiKey;
|
||||
private EditText prompt;
|
||||
private Switch remoteFirst;
|
||||
private TextView providerStatus;
|
||||
private Button microphone;
|
||||
private ConfigRepository repository;
|
||||
@@ -42,9 +39,7 @@ public final class MainActivity extends Activity {
|
||||
|
||||
findViewById(R.id.save).setOnClickListener(view -> saveConfig());
|
||||
microphone.setOnClickListener(view -> requestMicrophone());
|
||||
findViewById(R.id.choose_provider).setOnClickListener(view -> {
|
||||
if (saveConfig()) openVoiceInputSettings();
|
||||
});
|
||||
findViewById(R.id.language_settings).setOnClickListener(view -> openLanguageSettings());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,39 +55,27 @@ public final class MainActivity extends Activity {
|
||||
}
|
||||
|
||||
private void bindViews() {
|
||||
remoteEndpoint = findViewById(R.id.remote_endpoint);
|
||||
remoteModel = findViewById(R.id.remote_model);
|
||||
remoteKey = findViewById(R.id.remote_key);
|
||||
localEndpoint = findViewById(R.id.local_endpoint);
|
||||
localModel = findViewById(R.id.local_model);
|
||||
localKey = findViewById(R.id.local_key);
|
||||
endpoint = findViewById(R.id.endpoint);
|
||||
model = findViewById(R.id.model);
|
||||
apiKey = findViewById(R.id.api_key);
|
||||
prompt = findViewById(R.id.prompt);
|
||||
remoteFirst = findViewById(R.id.remote_first);
|
||||
providerStatus = findViewById(R.id.provider_status);
|
||||
microphone = findViewById(R.id.microphone);
|
||||
}
|
||||
|
||||
private void showConfig(AppConfig config) {
|
||||
remoteEndpoint.setText(config.remoteEndpoint);
|
||||
remoteModel.setText(config.remoteModel);
|
||||
remoteKey.setText(config.remoteKey);
|
||||
localEndpoint.setText(config.localEndpoint);
|
||||
localModel.setText(config.localModel);
|
||||
localKey.setText(config.localKey);
|
||||
endpoint.setText(config.endpoint);
|
||||
model.setText(config.model);
|
||||
apiKey.setText(config.apiKey);
|
||||
prompt.setText(config.prompt);
|
||||
remoteFirst.setChecked(config.remoteFirst);
|
||||
}
|
||||
|
||||
private boolean saveConfig() {
|
||||
AppConfig config = new AppConfig(
|
||||
value(remoteEndpoint).trim(),
|
||||
value(remoteModel).trim(),
|
||||
value(remoteKey).trim(),
|
||||
value(localEndpoint).trim(),
|
||||
value(localModel).trim(),
|
||||
value(localKey).trim(),
|
||||
value(prompt),
|
||||
remoteFirst.isChecked());
|
||||
value(endpoint).trim(),
|
||||
value(model).trim(),
|
||||
value(apiKey).trim(),
|
||||
value(prompt));
|
||||
try {
|
||||
repository.save(config);
|
||||
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 {
|
||||
startActivity(new Intent(Settings.ACTION_VOICE_INPUT_SETTINGS));
|
||||
} catch (ActivityNotFoundException error) {
|
||||
Toast.makeText(this, R.string.voice_settings_unavailable, Toast.LENGTH_LONG).show();
|
||||
startActivity(new Intent(ACTION_LANGUAGE_SETTINGS));
|
||||
return;
|
||||
} 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;
|
||||
|
||||
Result transcribe(AppConfig config, byte[] wav) {
|
||||
Result remoteResult = null;
|
||||
if (config.remoteFirst && !config.remoteEndpoint.isEmpty()) {
|
||||
remoteResult = request(
|
||||
config.remoteEndpoint, config.remoteModel, config.remoteKey, config.prompt, wav);
|
||||
if (remoteResult.text != null) return remoteResult;
|
||||
if (config.endpoint.isEmpty()) {
|
||||
return Result.error("No transcription endpoint is configured", SpeechRecognizer.ERROR_CLIENT);
|
||||
}
|
||||
|
||||
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);
|
||||
return request(config.endpoint, config.model, config.apiKey, config.prompt, wav);
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
|
||||
@@ -48,51 +48,26 @@
|
||||
|
||||
<TextView
|
||||
style="@style/SectionLabel"
|
||||
android:text="@string/remote" />
|
||||
android:text="@string/server" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/remote_endpoint"
|
||||
android:id="@+id/endpoint"
|
||||
style="@style/Field"
|
||||
android:hint="@string/remote_endpoint"
|
||||
android:hint="@string/endpoint"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textUri" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/remote_model"
|
||||
android:id="@+id/model"
|
||||
style="@style/Field"
|
||||
android:hint="@string/remote_model"
|
||||
android:hint="@string/model"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/remote_key"
|
||||
android:id="@+id/api_key"
|
||||
style="@style/Field"
|
||||
android:hint="@string/remote_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:hint="@string/api_key"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textPassword" />
|
||||
|
||||
@@ -109,15 +84,6 @@
|
||||
android:maxLines="3"
|
||||
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
|
||||
android:id="@+id/save"
|
||||
android:layout_width="match_parent"
|
||||
@@ -133,10 +99,10 @@
|
||||
android:text="@string/allow_microphone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/choose_provider"
|
||||
android:id="@+id/language_settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/choose_provider" />
|
||||
android:text="@string/open_language_settings" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@@ -5,22 +5,17 @@
|
||||
<string name="title">Transui 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="remote">REMOTE</string>
|
||||
<string name="local">LOCAL</string>
|
||||
<string name="server">SERVER</string>
|
||||
<string name="request">REQUEST</string>
|
||||
<string name="remote_endpoint">Remote endpoint</string>
|
||||
<string name="remote_model">Remote model</string>
|
||||
<string name="remote_api_key">Remote 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="endpoint">Transcription endpoint</string>
|
||||
<string name="model">Model</string>
|
||||
<string name="api_key">API key</string>
|
||||
<string name="prompt">Prompt</string>
|
||||
<string name="remote_first">Remote first, local fallback</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="microphone_allowed">Microphone allowed</string>
|
||||
<string name="settings_saved">Settings saved</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>
|
||||
Reference in new issue
Block a user