Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ef7e65aa authored by Nishith  Khanna's avatar Nishith Khanna
Browse files

Merge branch '3333-a14-espri-v3' into 'v3.0-a14'

Add an icon on the keyboard to give access to the STT feature (Android 14)

See merge request !34
parents 23a27ace fa07f9ea
Loading
Loading
Loading
Loading
+9 −0
Original line number Original line Diff line number Diff line
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
    android:viewportWidth="22.97"
    android:viewportHeight="31.17"
    android:width="22.97dp"
    android:height="31.17dp">
    <path
        android:pathData="M11.48 19.69c2.72 0 4.91 -2.2 4.91 -4.92l0.02 -9.84c0 -2.72 -2.2 -4.92 -4.92 -4.92s-4.92 2.2 -4.92 4.92v9.84c0 2.72 2.2 4.92 4.92 4.92ZM20.18 14.77c0 4.92 -4.17 8.37 -8.7 8.37S2.79 19.69 2.79 14.77H0c0 5.59 4.46 10.22 9.84 11.03v5.38h3.28v-5.38c5.38 -0.79 9.84 -5.41 9.84 -11.03h-2.79Z"
        android:fillColor="#DEADB5B9" />
</vector>
 No newline at end of file
+39 −19
Original line number Original line Diff line number Diff line
@@ -18,10 +18,15 @@
*/
*/
-->
-->


<LinearLayout
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_gravity="bottom"
        android:orientation="vertical">
        android:orientation="vertical">


@@ -43,3 +48,18 @@
            android:layout_width="match_parent"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
            android:layout_height="wrap_content" />
    </LinearLayout>
    </LinearLayout>

    <ImageButton
        android:id="@+id/floating_button"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_gravity="top|end"
        android:layout_marginTop="-4dp"
        android:layout_marginEnd="4dp"
        android:background="@android:color/transparent"
        android:src="@drawable/ic_ime_switch"
        android:contentDescription="@string/ime_switch_button"
        android:focusable="true"
        android:clickable="true" />

</FrameLayout>
+19 −0
Original line number Original line Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright Murena SAS 2025
  ~ 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 <https://www.gnu.org/licenses/>.
  -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="ime_switch_button">Speech to text</string>
</resources>
+57 −0
Original line number Original line Diff line number Diff line
@@ -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 static com.android.inputmethod.latin.common.Constants.ImeOption.NO_MICROPHONE_COMPAT;


import android.Manifest.permission;
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.ActivityOptions;
import android.app.AlertDialog;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.BroadcastReceiver;
@@ -56,7 +60,10 @@ import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype;
import android.widget.ImageButton;


import androidx.annotation.NonNull;
import androidx.annotation.NonNull;


@@ -105,6 +112,10 @@ import com.android.inputmethod.latin.utils.StatsUtilsManager;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
import com.android.inputmethod.latin.utils.ViewLayoutUtils;
import com.android.inputmethod.latin.utils.ViewLayoutUtils;


import android.content.ContentResolver;

import foundation.e.stt.AccountInfoContentProvider;

import java.io.FileDescriptor;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.ArrayList;
@@ -132,6 +143,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen


    private static final int MAX_SPACESLIDE_CHARS = 32;
    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.
     * A broadcast intent action to hide the software keyboard.
     */
     */
@@ -211,6 +224,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen


    public final UIHandler mHandler = new UIHandler(this);
    public final UIHandler mHandler = new UIHandler(this);


    private ImageButton floatingButton;

    public static final class UIHandler extends LeakGuardHandlerWrapper<LatinIME> {
    public static final class UIHandler extends LeakGuardHandlerWrapper<LatinIME> {
        private static final int MSG_UPDATE_SHIFT_STATE = 0;
        private static final int MSG_UPDATE_SHIFT_STATE = 0;
        private static final int MSG_PENDING_IMS_CALLBACK = 1;
        private static final int MSG_PENDING_IMS_CALLBACK = 1;
@@ -858,6 +873,23 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
                mIsHardwareAcceleratedDrawingEnabled);
                mIsHardwareAcceleratedDrawingEnabled);
    }
    }


    private boolean isSttKeyboardActivated() {
        InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        List<InputMethodInfo> 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
    @Override
    public void setInputView(final View view) {
    public void setInputView(final View view) {
        super.setInputView(view);
        super.setInputView(view);
@@ -868,6 +900,27 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
        if (hasSuggestionStripView()) {
        if (hasSuggestionStripView()) {
            mSuggestionStripView.setListener(this, view);
            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
    @Override
@@ -884,6 +937,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
    public void onStartInputView(final EditorInfo editorInfo, final boolean restarting) {
    public void onStartInputView(final EditorInfo editorInfo, final boolean restarting) {
        mHandler.onStartInputView(editorInfo, restarting);
        mHandler.onStartInputView(editorInfo, restarting);
        mStatsUtilsManager.onStartInputView();
        mStatsUtilsManager.onStartInputView();

        if (floatingButton != null) {
            manageFloatingButtonVisibility();
        }
    }
    }


    @Override
    @Override
+56 −0
Original line number Original line Diff line number Diff line
/*
 * 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 <https://www.gnu.org/licenses/>.
 *
 */
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