diff --git a/java/res/drawable/ic_ime_switch.xml b/java/res/drawable/ic_ime_switch.xml new file mode 100644 index 0000000000000000000000000000000000000000..2302161c1c25ccb8a22cee698953551e94b27131 --- /dev/null +++ b/java/res/drawable/ic_ime_switch.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/java/res/layout/main_keyboard_frame.xml b/java/res/layout/main_keyboard_frame.xml index ebf746679c603d9899ed78f591c665bfcf1c806d..89e0f3a96a930fafddb98174e69fb29c1166bf24 100644 --- a/java/res/layout/main_keyboard_frame.xml +++ b/java/res/layout/main_keyboard_frame.xml @@ -18,28 +18,48 @@ */ --> - + android:layout_gravity="bottom"> - - + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:orientation="vertical"> - - - + + + + + + + + + + diff --git a/java/res/values/e-strings.xml b/java/res/values/e-strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..b4012e6a6c8db24c980ba6af44a8a61467c3bbe0 --- /dev/null +++ b/java/res/values/e-strings.xml @@ -0,0 +1,19 @@ + + + + Speech to text + diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 45ebcbc21b0e71b9b9303e56774f4105002f5db6..eb7bfcbebd16f94ddec07eaac67486be64a3aa5e 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -23,6 +23,10 @@ import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_MICROP import static com.android.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE_COMPAT; import android.Manifest.permission; +import android.accounts.Account; +import android.accounts.AccountManager; +import android.content.pm.PackageManager; +import androidx.core.content.ContextCompat; import android.app.ActivityOptions; import android.app.AlertDialog; import android.content.BroadcastReceiver; @@ -56,7 +60,10 @@ import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; +import android.widget.ImageButton; import androidx.annotation.NonNull; @@ -104,6 +111,9 @@ import com.android.inputmethod.latin.utils.StatsUtilsManager; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import com.android.inputmethod.latin.utils.ViewLayoutUtils; +import android.content.ContentResolver; + +import foundation.e.stt.AccountInfoContentProvider; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; @@ -131,6 +141,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int MAX_SPACESLIDE_CHARS = 32; + private static final String KEY_STT_KEYBOARD = "foundation.e.stt/.MurenaWhisperInputService"; + /** * A broadcast intent action to hide the software keyboard. */ @@ -210,6 +222,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public final UIHandler mHandler = new UIHandler(this); + private ImageButton floatingButton; + public static final class UIHandler extends LeakGuardHandlerWrapper { private static final int MSG_UPDATE_SHIFT_STATE = 0; private static final int MSG_PENDING_IMS_CALLBACK = 1; @@ -841,6 +855,23 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mIsHardwareAcceleratedDrawingEnabled); } + private boolean isSttKeyboardActivated() { + InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + List enabledImeList = inputMethodManager.getEnabledInputMethodList(); + for (InputMethodInfo inputMethodInfo : enabledImeList) { + if (inputMethodInfo.getId().equals(KEY_STT_KEYBOARD)) { + return true; + } + } + return false; + } + + private void switchToSttIme() { + if (isSttKeyboardActivated()) { + switchInputMethod(KEY_STT_KEYBOARD); + } + } + @Override public void setInputView(final View view) { super.setInputView(view); @@ -851,6 +882,27 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (hasSuggestionStripView()) { mSuggestionStripView.setListener(this, view); } + + floatingButton = mInputView.findViewById(R.id.floating_button); + floatingButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + switchToSttIme(); + } + }); + manageFloatingButtonVisibility(); + } + + private void manageFloatingButtonVisibility() { + if (shouldShowFloatingButton()) { + floatingButton.setVisibility(View.VISIBLE); + } else { + floatingButton.setVisibility(View.GONE); + } + } + + private boolean shouldShowFloatingButton() { + return AccountInfoContentProvider.isPremiumAccount(getApplicationContext()) && isSttKeyboardActivated(); } @Override @@ -867,6 +919,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void onStartInputView(final EditorInfo editorInfo, final boolean restarting) { mHandler.onStartInputView(editorInfo, restarting); mStatsUtilsManager.onStartInputView(); + + if (floatingButton != null) { + manageFloatingButtonVisibility(); + } } @Override diff --git a/java/src/foundation/e/stt/AccountInfoContentProvider.java b/java/src/foundation/e/stt/AccountInfoContentProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..e38aef6ff8a812ee9afd389b6e48acfdae019e74 --- /dev/null +++ b/java/src/foundation/e/stt/AccountInfoContentProvider.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2025 MURENA SAS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package foundation.e.stt; + +import android.content.ContentResolver; +import android.database.Cursor; +import android.net.Uri; +import android.content.Context; +import android.util.Log; + +public class AccountInfoContentProvider { + + private static final String TAG = "AccountInfoContentProvider"; + private static final Uri ACCOUNT_URI = Uri.parse("content://foundation.e.stt.provider/account"); + private static final String COLUMN_IS_PREMIUM = "is_premium"; + private static final int PREMIUM_VALUE = 1; + + public static boolean isPremiumAccount(Context context) { + Cursor cursor = null; + try { + ContentResolver resolver = context.getContentResolver(); + String[] projection = new String[] { COLUMN_IS_PREMIUM }; + cursor = resolver.query(ACCOUNT_URI, projection, null, null, null); + + if (cursor != null && cursor.moveToFirst()) { + int columnIndex = cursor.getColumnIndex(COLUMN_IS_PREMIUM); + if (columnIndex != -1) { + int value = cursor.getInt(columnIndex); + return value == PREMIUM_VALUE; + } + } + } catch (Exception e) { + Log.e(TAG, "Error failed to read status", e); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return false; + } +} \ No newline at end of file