diff --git a/.gitea/workflows/android.yml b/.gitea/workflows/android.yml index 966b294..668cb21 100644 --- a/.gitea/workflows/android.yml +++ b/.gitea/workflows/android.yml @@ -1,8 +1,6 @@ name: Android on: - push: - pull_request: release: types: [published] workflow_dispatch: @@ -22,16 +20,11 @@ jobs: - uses: android-actions/setup-android@v3 # Gitea's act resolver cannot peel the annotated v4 tag alias. - uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3 - with: - cache-read-only: ${{ gitea.event_name == 'pull_request' }} - name: Install Android SDK packages run: sdkmanager "platforms;android-35" "build-tools;35.0.0" - name: Lint and test working-directory: android run: ./gradlew --no-daemon lintDebug testDebugUnitTest - - name: Build debug APK - working-directory: android - run: ./gradlew --no-daemon assembleDebug - name: Build signed release APK working-directory: android env: @@ -52,12 +45,13 @@ jobs: working-directory: android run: | mkdir -p ../artifacts - cp app/build/outputs/apk/debug/app-debug.apk ../artifacts/transui-android-debug.apk if [ -f app/build/outputs/apk/release/app-release.apk ]; then cp app/build/outputs/apk/release/app-release.apk ../artifacts/transui-android-release.apk + cd ../artifacts + sha256sum ./*.apk > SHA256SUMS-android + else + echo "No release APK to publish" fi - cd ../artifacts - sha256sum ./*.apk > SHA256SUMS-android - name: Attach APKs to the Gitea release if: gitea.event_name == 'release' env: @@ -72,4 +66,4 @@ jobs: with: name: transui-android path: artifacts/ - if-no-files-found: error + if-no-files-found: warn diff --git a/android/app/src/main/java/io/github/transui/android/PushToTalkButton.java b/android/app/src/main/java/io/github/transui/android/PushToTalkButton.java index a24542c..cce2859 100644 --- a/android/app/src/main/java/io/github/transui/android/PushToTalkButton.java +++ b/android/app/src/main/java/io/github/transui/android/PushToTalkButton.java @@ -1,17 +1,34 @@ package io.github.transui.android; import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.ImageButton; public final class PushToTalkButton extends ImageButton { + enum IconMode { + APPS, + STOP_CIRCLE + } + + private final Paint iconPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private Runnable pressedAction; private Runnable releasedAction; private boolean trackingPress; + private IconMode iconMode = IconMode.STOP_CIRCLE; public PushToTalkButton(Context context, AttributeSet attributes) { super(context, attributes); + iconPaint.setColor(context.getColor(R.color.on_signal)); + setImageDrawable(null); + } + + void setIconMode(IconMode iconMode) { + this.iconMode = iconMode; + setImageDrawable(null); + invalidate(); } void setPushToTalkActions(Runnable pressedAction, Runnable releasedAction) { @@ -55,4 +72,48 @@ public final class PushToTalkButton extends ImageButton { public boolean performClick() { return super.performClick(); } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + float contentWidth = getWidth() - getPaddingLeft() - getPaddingRight(); + float contentHeight = getHeight() - getPaddingTop() - getPaddingBottom(); + float size = Math.min(contentWidth, contentHeight); + float centerX = getWidth() / 2f; + float centerY = getHeight() / 2f; + if (iconMode == IconMode.APPS) { + drawAppsIcon(canvas, centerX, centerY, size); + } else { + drawStopCircleIcon(canvas, centerX, centerY, size); + } + } + + private void drawAppsIcon(Canvas canvas, float centerX, float centerY, float size) { + iconPaint.setStyle(Paint.Style.FILL); + float spacing = size * 0.27f; + float radius = size * 0.09f; + for (int row = -1; row <= 1; row++) { + for (int column = -1; column <= 1; column++) { + canvas.drawCircle( + centerX + column * spacing, + centerY + row * spacing, + radius, + iconPaint); + } + } + } + + private void drawStopCircleIcon(Canvas canvas, float centerX, float centerY, float size) { + iconPaint.setStyle(Paint.Style.STROKE); + iconPaint.setStrokeWidth(size * 0.12f); + canvas.drawCircle(centerX, centerY, size * 0.42f, iconPaint); + iconPaint.setStyle(Paint.Style.FILL); + float squareHalfSize = size * 0.17f; + canvas.drawRect( + centerX - squareHalfSize, + centerY - squareHalfSize, + centerX + squareHalfSize, + centerY + squareHalfSize, + iconPaint); + } } diff --git a/android/app/src/main/java/io/github/transui/android/SpeechRecognitionActivity.java b/android/app/src/main/java/io/github/transui/android/SpeechRecognitionActivity.java index d001519..a30145f 100644 --- a/android/app/src/main/java/io/github/transui/android/SpeechRecognitionActivity.java +++ b/android/app/src/main/java/io/github/transui/android/SpeechRecognitionActivity.java @@ -80,25 +80,29 @@ public final class SpeechRecognitionActivity extends Activity { private void configureRecordButton() { record.setAlpha(1); - resizeRecordButton(config.pushToTalk ? 80 : 64, config.pushToTalk ? 23 : 19); if (config.pushToTalk) { - record.setImageResource(R.drawable.ic_mic); + record.setBackgroundResource(R.drawable.voice_pill_button); + resizeRecordButton(132, 64, 16); + record.setIconMode(PushToTalkButton.IconMode.APPS); record.setContentDescription(getString(R.string.hold_to_talk)); record.setPushToTalkActions(this::startRecording, this::stopRecording); } else { - record.setImageResource(R.drawable.ic_stop); + record.setBackgroundResource(R.drawable.voice_primary_button); + resizeRecordButton(64, 64, 19); + record.setIconMode(PushToTalkButton.IconMode.STOP_CIRCLE); record.setContentDescription(getString(R.string.stop)); record.setOnClickListener(view -> stopRecording()); } } - private void resizeRecordButton(int sizeDp, int paddingDp) { + private void resizeRecordButton(int widthDp, int heightDp, int paddingDp) { float density = getResources().getDisplayMetrics().density; - int size = Math.round(sizeDp * density); + int width = Math.round(widthDp * density); + int height = Math.round(heightDp * density); int padding = Math.round(paddingDp * density); ViewGroup.LayoutParams parameters = record.getLayoutParams(); - parameters.width = size; - parameters.height = size; + parameters.width = width; + parameters.height = height; record.setLayoutParams(parameters); record.setPadding(padding, padding, padding, padding); } @@ -194,7 +198,9 @@ public final class SpeechRecognitionActivity extends Activity { public void onRecording() { runOnUiThread(() -> { status.setText(R.string.listening); - if (!config.pushToTalk) record.setImageResource(R.drawable.ic_stop); + if (!config.pushToTalk) { + record.setIconMode(PushToTalkButton.IconMode.STOP_CIRCLE); + } recordingStartedAt = SystemClock.elapsedRealtime(); elapsed.setText(R.string.elapsed_zero); handler.post(updateElapsed); diff --git a/android/app/src/main/java/io/github/transui/android/TransuiInputMethodService.java b/android/app/src/main/java/io/github/transui/android/TransuiInputMethodService.java index 68a8856..6a947cb 100644 --- a/android/app/src/main/java/io/github/transui/android/TransuiInputMethodService.java +++ b/android/app/src/main/java/io/github/transui/android/TransuiInputMethodService.java @@ -109,25 +109,29 @@ public final class TransuiInputMethodService extends InputMethodService { record.setAlpha(1); record.setOnClickListener(null); record.setPushToTalkActions(null, null); - resizeRecordButton(config.pushToTalk ? 76 : 64, config.pushToTalk ? 22 : 19); if (config.pushToTalk) { - record.setImageResource(R.drawable.ic_mic); + record.setBackgroundResource(R.drawable.voice_pill_button); + resizeRecordButton(132, 64, 16); + record.setIconMode(PushToTalkButton.IconMode.APPS); record.setContentDescription(getString(R.string.hold_to_talk)); record.setPushToTalkActions(this::startRecording, this::stopRecording); } else { - record.setImageResource(R.drawable.ic_stop); + record.setBackgroundResource(R.drawable.voice_primary_button); + resizeRecordButton(64, 64, 19); + record.setIconMode(PushToTalkButton.IconMode.STOP_CIRCLE); record.setContentDescription(getString(R.string.stop)); record.setOnClickListener(view -> stopRecording()); } } - private void resizeRecordButton(int sizeDp, int paddingDp) { + private void resizeRecordButton(int widthDp, int heightDp, int paddingDp) { float density = getResources().getDisplayMetrics().density; - int size = Math.round(sizeDp * density); + int width = Math.round(widthDp * density); + int height = Math.round(heightDp * density); int padding = Math.round(paddingDp * density); ViewGroup.LayoutParams parameters = record.getLayoutParams(); - parameters.width = size; - parameters.height = size; + parameters.width = width; + parameters.height = height; record.setLayoutParams(parameters); record.setPadding(padding, padding, padding, padding); } @@ -176,7 +180,9 @@ public final class TransuiInputMethodService extends InputMethodService { public void onRecording() { handler.post(() -> { status.setText(R.string.listening); - if (!config.pushToTalk) record.setImageResource(R.drawable.ic_stop); + if (!config.pushToTalk) { + record.setIconMode(PushToTalkButton.IconMode.STOP_CIRCLE); + } }); } diff --git a/android/app/src/main/res/drawable/ic_mic.xml b/android/app/src/main/res/drawable/ic_mic.xml deleted file mode 100644 index c0e8e76..0000000 --- a/android/app/src/main/res/drawable/ic_mic.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/android/app/src/main/res/drawable/ic_stop.xml b/android/app/src/main/res/drawable/ic_stop.xml deleted file mode 100644 index 8af5ca0..0000000 --- a/android/app/src/main/res/drawable/ic_stop.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - diff --git a/android/app/src/main/res/drawable/voice_pill_button.xml b/android/app/src/main/res/drawable/voice_pill_button.xml new file mode 100644 index 0000000..ae61418 --- /dev/null +++ b/android/app/src/main/res/drawable/voice_pill_button.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/android/app/src/main/res/layout/recognition_panel.xml b/android/app/src/main/res/layout/recognition_panel.xml index 527994f..be50110 100644 --- a/android/app/src/main/res/layout/recognition_panel.xml +++ b/android/app/src/main/res/layout/recognition_panel.xml @@ -60,9 +60,7 @@ 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" /> + android:padding="16dp" />