1 parent
066c7d758e
commit
e4cf707d0a
14 files changed
+242
-52
No files matched your search
@@ -0,0 +1,50 @@
|
|||||||
|
package io.github.transui.android;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public final class AmplitudeView extends View {
|
||||||
|
private static final int LEVEL_COUNT = 22;
|
||||||
|
|
||||||
|
private final float[] levels = new float[LEVEL_COUNT];
|
||||||
|
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
|
||||||
|
public AmplitudeView(Context context, AttributeSet attributes) {
|
||||||
|
super(context, attributes);
|
||||||
|
paint.setColor(context.getColor(R.color.signal));
|
||||||
|
paint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAmplitude(float amplitude) {
|
||||||
|
System.arraycopy(levels, 1, levels, 0, levels.length - 1);
|
||||||
|
levels[levels.length - 1] = Math.max(0, Math.min(1, amplitude));
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
Arrays.fill(levels, 0);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
super.onDraw(canvas);
|
||||||
|
float availableWidth = getWidth() - getPaddingLeft() - getPaddingRight();
|
||||||
|
float gap = availableWidth / (LEVEL_COUNT * 2 - 1);
|
||||||
|
float strokeWidth = Math.max(2, gap);
|
||||||
|
float centerY = getHeight() / 2f;
|
||||||
|
float maximumHeight = Math.max(0, getHeight() - getPaddingTop() - getPaddingBottom());
|
||||||
|
paint.setStrokeWidth(strokeWidth);
|
||||||
|
|
||||||
|
for (int index = 0; index < levels.length; index++) {
|
||||||
|
float height = Math.max(strokeWidth, levels[index] * maximumHeight);
|
||||||
|
float x = getPaddingLeft() + strokeWidth / 2 + index * gap * 2;
|
||||||
|
canvas.drawLine(x, centerY - height / 2, x, centerY + height / 2, paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,9 +3,9 @@ package io.github.transui.android;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.widget.Button;
|
import android.widget.ImageButton;
|
||||||
|
|
||||||
public final class PushToTalkButton extends Button {
|
public final class PushToTalkButton extends ImageButton {
|
||||||
private Runnable pressedAction;
|
private Runnable pressedAction;
|
||||||
private Runnable releasedAction;
|
private Runnable releasedAction;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import android.os.Looper;
|
|||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.speech.RecognizerIntent;
|
import android.speech.RecognizerIntent;
|
||||||
import android.speech.SpeechRecognizer;
|
import android.speech.SpeechRecognizer;
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -32,6 +31,7 @@ public final class SpeechRecognitionActivity extends Activity {
|
|||||||
|
|
||||||
private TextView status;
|
private TextView status;
|
||||||
private TextView elapsed;
|
private TextView elapsed;
|
||||||
|
private AmplitudeView amplitude;
|
||||||
private PushToTalkButton record;
|
private PushToTalkButton record;
|
||||||
private AppConfig config;
|
private AppConfig config;
|
||||||
private VoiceRecordingController controller;
|
private VoiceRecordingController controller;
|
||||||
@@ -46,6 +46,7 @@ public final class SpeechRecognitionActivity extends Activity {
|
|||||||
setContentView(R.layout.recognition_panel);
|
setContentView(R.layout.recognition_panel);
|
||||||
status = findViewById(R.id.recognition_status);
|
status = findViewById(R.id.recognition_status);
|
||||||
elapsed = findViewById(R.id.recognition_elapsed);
|
elapsed = findViewById(R.id.recognition_elapsed);
|
||||||
|
amplitude = findViewById(R.id.recognition_amplitude);
|
||||||
record = findViewById(R.id.recognition_record);
|
record = findViewById(R.id.recognition_record);
|
||||||
findViewById(R.id.recognition_cancel).setOnClickListener(view -> cancelAndFinish());
|
findViewById(R.id.recognition_cancel).setOnClickListener(view -> cancelAndFinish());
|
||||||
|
|
||||||
@@ -77,11 +78,14 @@ public final class SpeechRecognitionActivity extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void configureRecordButton() {
|
private void configureRecordButton() {
|
||||||
|
record.setAlpha(1);
|
||||||
if (config.pushToTalk) {
|
if (config.pushToTalk) {
|
||||||
record.setText(R.string.hold_to_talk);
|
record.setImageResource(R.drawable.ic_mic);
|
||||||
|
record.setContentDescription(getString(R.string.hold_to_talk));
|
||||||
record.setPushToTalkActions(this::startRecording, this::stopRecording);
|
record.setPushToTalkActions(this::startRecording, this::stopRecording);
|
||||||
} else {
|
} else {
|
||||||
record.setText(R.string.stop);
|
record.setImageResource(R.drawable.ic_stop);
|
||||||
|
record.setContentDescription(getString(R.string.stop));
|
||||||
record.setOnClickListener(view -> stopRecording());
|
record.setOnClickListener(view -> stopRecording());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,13 +116,17 @@ public final class SpeechRecognitionActivity extends Activity {
|
|||||||
private void stopRecording() {
|
private void stopRecording() {
|
||||||
if (!sessionActive || controller == null) return;
|
if (!sessionActive || controller == null) return;
|
||||||
record.setEnabled(false);
|
record.setEnabled(false);
|
||||||
|
record.setAlpha(0.4f);
|
||||||
|
amplitude.reset();
|
||||||
controller.stop();
|
controller.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showUnavailable(int message) {
|
private void showUnavailable(int message) {
|
||||||
status.setText(message);
|
status.setText(message);
|
||||||
elapsed.setText("");
|
elapsed.setText("");
|
||||||
|
amplitude.reset();
|
||||||
record.setEnabled(false);
|
record.setEnabled(false);
|
||||||
|
record.setAlpha(0.4f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cancelAndFinish() {
|
private void cancelAndFinish() {
|
||||||
@@ -173,18 +181,26 @@ public final class SpeechRecognitionActivity extends Activity {
|
|||||||
public void onRecording() {
|
public void onRecording() {
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
status.setText(R.string.listening);
|
status.setText(R.string.listening);
|
||||||
|
record.setImageResource(R.drawable.ic_stop);
|
||||||
recordingStartedAt = SystemClock.elapsedRealtime();
|
recordingStartedAt = SystemClock.elapsedRealtime();
|
||||||
elapsed.setText(R.string.elapsed_zero);
|
elapsed.setText(R.string.elapsed_zero);
|
||||||
handler.post(updateElapsed);
|
handler.post(updateElapsed);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAmplitude(float level) {
|
||||||
|
runOnUiThread(() -> amplitude.setAmplitude(level));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTranscribing() {
|
public void onTranscribing() {
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
handler.removeCallbacks(updateElapsed);
|
handler.removeCallbacks(updateElapsed);
|
||||||
status.setText(R.string.transcribing);
|
status.setText(R.string.transcribing);
|
||||||
|
amplitude.reset();
|
||||||
record.setEnabled(false);
|
record.setEnabled(false);
|
||||||
|
record.setAlpha(0.4f);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import android.os.Looper;
|
|||||||
import android.speech.SpeechRecognizer;
|
import android.speech.SpeechRecognizer;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.inputmethod.InputConnection;
|
import android.view.inputmethod.InputConnection;
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@@ -17,6 +16,7 @@ public final class TransuiInputMethodService extends InputMethodService {
|
|||||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||||
|
|
||||||
private TextView status;
|
private TextView status;
|
||||||
|
private AmplitudeView amplitude;
|
||||||
private PushToTalkButton record;
|
private PushToTalkButton record;
|
||||||
private AppConfig config;
|
private AppConfig config;
|
||||||
private VoiceRecordingController controller;
|
private VoiceRecordingController controller;
|
||||||
@@ -27,6 +27,7 @@ public final class TransuiInputMethodService extends InputMethodService {
|
|||||||
FrameLayout parent = new FrameLayout(this);
|
FrameLayout parent = new FrameLayout(this);
|
||||||
View view = getLayoutInflater().inflate(R.layout.voice_input, parent, false);
|
View view = getLayoutInflater().inflate(R.layout.voice_input, parent, false);
|
||||||
status = view.findViewById(R.id.voice_status);
|
status = view.findViewById(R.id.voice_status);
|
||||||
|
amplitude = view.findViewById(R.id.voice_amplitude);
|
||||||
record = view.findViewById(R.id.voice_record);
|
record = view.findViewById(R.id.voice_record);
|
||||||
view.findViewById(R.id.voice_keyboard).setOnClickListener(button -> returnToKeyboard());
|
view.findViewById(R.id.voice_keyboard).setOnClickListener(button -> returnToKeyboard());
|
||||||
return view;
|
return view;
|
||||||
@@ -63,13 +64,16 @@ public final class TransuiInputMethodService extends InputMethodService {
|
|||||||
|
|
||||||
private void configureRecordButton() {
|
private void configureRecordButton() {
|
||||||
record.setEnabled(true);
|
record.setEnabled(true);
|
||||||
|
record.setAlpha(1);
|
||||||
record.setOnClickListener(null);
|
record.setOnClickListener(null);
|
||||||
record.setPushToTalkActions(null, null);
|
record.setPushToTalkActions(null, null);
|
||||||
if (config.pushToTalk) {
|
if (config.pushToTalk) {
|
||||||
record.setText(R.string.hold_to_talk);
|
record.setImageResource(R.drawable.ic_mic);
|
||||||
|
record.setContentDescription(getString(R.string.hold_to_talk));
|
||||||
record.setPushToTalkActions(this::startRecording, this::stopRecording);
|
record.setPushToTalkActions(this::startRecording, this::stopRecording);
|
||||||
} else {
|
} else {
|
||||||
record.setText(R.string.stop);
|
record.setImageResource(R.drawable.ic_stop);
|
||||||
|
record.setContentDescription(getString(R.string.stop));
|
||||||
record.setOnClickListener(view -> stopRecording());
|
record.setOnClickListener(view -> stopRecording());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,18 +88,24 @@ public final class TransuiInputMethodService extends InputMethodService {
|
|||||||
private void stopRecording() {
|
private void stopRecording() {
|
||||||
if (!sessionActive || controller == null) return;
|
if (!sessionActive || controller == null) return;
|
||||||
record.setEnabled(false);
|
record.setEnabled(false);
|
||||||
|
record.setAlpha(0.4f);
|
||||||
|
amplitude.reset();
|
||||||
controller.stop();
|
controller.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cancelSession() {
|
private void cancelSession() {
|
||||||
|
handler.removeCallbacksAndMessages(null);
|
||||||
if (controller != null) controller.cancel();
|
if (controller != null) controller.cancel();
|
||||||
controller = null;
|
controller = null;
|
||||||
sessionActive = false;
|
sessionActive = false;
|
||||||
|
if (amplitude != null) amplitude.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showUnavailable(int message) {
|
private void showUnavailable(int message) {
|
||||||
status.setText(message);
|
status.setText(message);
|
||||||
|
amplitude.reset();
|
||||||
record.setEnabled(false);
|
record.setEnabled(false);
|
||||||
|
record.setAlpha(0.4f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void returnToKeyboard() {
|
private void returnToKeyboard() {
|
||||||
@@ -110,14 +120,24 @@ public final class TransuiInputMethodService extends InputMethodService {
|
|||||||
private final class ImeListener implements VoiceRecordingController.Listener {
|
private final class ImeListener implements VoiceRecordingController.Listener {
|
||||||
@Override
|
@Override
|
||||||
public void onRecording() {
|
public void onRecording() {
|
||||||
handler.post(() -> status.setText(R.string.listening));
|
handler.post(() -> {
|
||||||
|
status.setText(R.string.listening);
|
||||||
|
record.setImageResource(R.drawable.ic_stop);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAmplitude(float level) {
|
||||||
|
handler.post(() -> amplitude.setAmplitude(level));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTranscribing() {
|
public void onTranscribing() {
|
||||||
handler.post(() -> {
|
handler.post(() -> {
|
||||||
status.setText(R.string.transcribing);
|
status.setText(R.string.transcribing);
|
||||||
|
amplitude.reset();
|
||||||
record.setEnabled(false);
|
record.setEnabled(false);
|
||||||
|
record.setAlpha(0.4f);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ final class VoiceRecordingController {
|
|||||||
interface Listener {
|
interface Listener {
|
||||||
void onRecording();
|
void onRecording();
|
||||||
|
|
||||||
|
void onAmplitude(float amplitude);
|
||||||
|
|
||||||
void onTranscribing();
|
void onTranscribing();
|
||||||
|
|
||||||
void onResult(String text);
|
void onResult(String text);
|
||||||
@@ -126,6 +128,7 @@ final class VoiceRecordingController {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
appendSamples(pcm, samples, count);
|
appendSamples(pcm, samples, count);
|
||||||
|
listener.onAmplitude(calculateAmplitude(samples, count));
|
||||||
if (stopRequested.get()) break;
|
if (stopRequested.get()) break;
|
||||||
if (SystemClock.elapsedRealtime() - startedAt >= MAX_RECORDING_MS) break;
|
if (SystemClock.elapsedRealtime() - startedAt >= MAX_RECORDING_MS) break;
|
||||||
}
|
}
|
||||||
@@ -155,4 +158,16 @@ final class VoiceRecordingController {
|
|||||||
output.write((sample >>> 8) & 0xff);
|
output.write((sample >>> 8) & 0xff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static float calculateAmplitude(short[] samples, int count) {
|
||||||
|
double squaredTotal = 0;
|
||||||
|
for (int index = 0; index < count; index++) {
|
||||||
|
double normalized = samples[index] / (double) Short.MAX_VALUE;
|
||||||
|
squaredTotal += normalized * normalized;
|
||||||
|
}
|
||||||
|
double rootMeanSquare = Math.sqrt(squaredTotal / count);
|
||||||
|
if (rootMeanSquare <= 0) return 0;
|
||||||
|
double decibels = 20 * Math.log10(rootMeanSquare);
|
||||||
|
return (float) Math.max(0, Math.min(1, (decibels + 50) / 50));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M20,5H4C2.89,5 2,5.89 2,7v10c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V7c0,-1.11 -0.89,-2 -2,-2zM11,8h2v2h-2V8zM11,11h2v2h-2v-2zM8,8h2v2H8V8zM8,11h2v2H8v-2zM7,13H5v-2h2v2zM7,10H5V8h2v2zM17,17H7v-2h10v2zM17,13h-3v-2h3v2zM17,10h-3V8h3v2zM19,13h-1v-2h1v2zM19,10h-1V8h1v2z" />
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M12,14c1.66,0 3,-1.34 3,-3V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17,11c0,2.76 -2.24,5 -5,5s-5,-2.24 -5,-5H5c0,3.53 2.61,6.43 6,6.92V21h2v-3.08c3.39,-0.49 6,-3.39 6,-6.92h-2z" />
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M6,6h12v12H6z" />
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="@color/ripple_on_signal">
|
||||||
|
<item>
|
||||||
|
<shape android:shape="oval">
|
||||||
|
<solid android:color="@color/signal" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="@color/ripple_signal">
|
||||||
|
<item>
|
||||||
|
<shape android:shape="oval">
|
||||||
|
<solid android:color="@color/mist" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
@@ -41,12 +41,28 @@
|
|||||||
android:textColor="@color/slate"
|
android:textColor="@color/slate"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<io.github.transui.android.AmplitudeView
|
||||||
|
android:id="@+id/recognition_amplitude"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:paddingStart="12dp"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:paddingBottom="10dp" />
|
||||||
|
|
||||||
<io.github.transui.android.PushToTalkButton
|
<io.github.transui.android.PushToTalkButton
|
||||||
android:id="@+id/recognition_record"
|
android:id="@+id/recognition_record"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="56dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="56dp"
|
||||||
android:layout_marginTop="22dp"
|
android:layout_gravity="center_horizontal"
|
||||||
android:text="@string/stop" />
|
android:layout_marginTop="14dp"
|
||||||
|
android:background="@drawable/voice_primary_button"
|
||||||
|
android:contentDescription="@string/stop"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:src="@drawable/ic_stop"
|
||||||
|
android:tint="@color/paper" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/recognition_cancel"
|
android:id="@+id/recognition_cancel"
|
||||||
|
|||||||
@@ -1,59 +1,82 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/paper"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingStart="20dp"
|
tools:ignore="Overdraw">
|
||||||
android:paddingTop="18dp"
|
|
||||||
android:paddingEnd="20dp"
|
<View
|
||||||
android:paddingBottom="18dp">
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="@color/line" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="20dp"
|
||||||
|
android:paddingTop="12dp"
|
||||||
|
android:paddingEnd="20dp">
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="5dp"
|
android:layout_width="4dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="28dp"
|
||||||
android:background="@color/signal" />
|
android:background="@color/signal" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/voice_status"
|
android:id="@+id/voice_status"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:paddingStart="14dp"
|
android:paddingStart="12dp"
|
||||||
android:paddingEnd="14dp"
|
android:paddingEnd="12dp"
|
||||||
android:text="@string/listening"
|
android:text="@string/listening"
|
||||||
android:textColor="@color/ink"
|
android:textColor="@color/ink"
|
||||||
android:textSize="17sp"
|
android:textSize="16sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
style="?android:attr/buttonBarStyle"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="64dp"
|
||||||
android:layout_marginTop="14dp"
|
android:gravity="center_vertical"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="14dp"
|
||||||
|
android:paddingEnd="14dp"
|
||||||
|
android:paddingBottom="8dp">
|
||||||
|
|
||||||
<Button
|
<ImageButton
|
||||||
android:id="@+id/voice_keyboard"
|
android:id="@+id/voice_keyboard"
|
||||||
style="?android:attr/buttonBarButtonStyle"
|
android:layout_width="48dp"
|
||||||
android:layout_width="0dp"
|
android:layout_height="48dp"
|
||||||
android:layout_height="wrap_content"
|
android:background="@drawable/voice_secondary_button"
|
||||||
android:layout_weight="1"
|
android:contentDescription="@string/keyboard"
|
||||||
android:text="@string/keyboard" />
|
android:padding="12dp"
|
||||||
|
android:src="@drawable/ic_keyboard"
|
||||||
|
android:tint="@color/ink" />
|
||||||
|
|
||||||
<io.github.transui.android.PushToTalkButton
|
<io.github.transui.android.AmplitudeView
|
||||||
android:id="@+id/voice_record"
|
android:id="@+id/voice_amplitude"
|
||||||
style="?android:attr/buttonBarButtonStyle"
|
android:layout_width="0dp"
|
||||||
android:layout_width="0dp"
|
android:layout_height="44dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="@string/stop" />
|
android:importantForAccessibility="no"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingBottom="8dp" />
|
||||||
|
|
||||||
|
<io.github.transui.android.PushToTalkButton
|
||||||
|
android:id="@+id/voice_record"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:background="@drawable/voice_primary_button"
|
||||||
|
android:contentDescription="@string/stop"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:src="@drawable/ic_stop"
|
||||||
|
android:tint="@color/paper" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -6,4 +6,6 @@
|
|||||||
<color name="mist">#11191D</color>
|
<color name="mist">#11191D</color>
|
||||||
<color name="paper">#192329</color>
|
<color name="paper">#192329</color>
|
||||||
<color name="line">#34444B</color>
|
<color name="line">#34444B</color>
|
||||||
|
<color name="ripple_on_signal">#3DFFFFFF</color>
|
||||||
|
<color name="ripple_signal">#3342C9C1</color>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -6,4 +6,6 @@
|
|||||||
<color name="mist">#F2F6F7</color>
|
<color name="mist">#F2F6F7</color>
|
||||||
<color name="paper">#FFFFFF</color>
|
<color name="paper">#FFFFFF</color>
|
||||||
<color name="line">#D5E0E3</color>
|
<color name="line">#D5E0E3</color>
|
||||||
|
<color name="ripple_on_signal">#3DFFFFFF</color>
|
||||||
|
<color name="ripple_signal">#33008D87</color>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in new issue
Block a user