N. Hen
N. Hen

Reputation: 11

Android won't recognize Soft Keyboard

I'm new to the android environment, so please bear with me:

I've been trying to develop a soft keyboard as part of a vocabulary-building app I'm creating. When I run the app, through Android Studio (v2.3), the app successfully launches, but the input method service seems to be ignored, and the keyboard is not listed in the system's input methods (to where I got by entering Settings -> Language & Input -> Current keyboard -> Choose Keyboards). I'm using a Nexus 5, and Android version 6.0.1, if it helps.

In order to create the keyboard I've been following the guide on Android Developers (under "Creating An Input Method"), as well as the sample keyboard referenced there ("SoftKeyboard", Link). I didn't need all the features that the SoftKeyboard example gives (for example, candidates), and so I adapted only a part of the sample project to my own; this might be the cause for problems.

To clarify, the app launches successfully, but the keyboard service seems to be ignored. I've searched the logs, and have found a single error which is related to input methods, but it seems quite minor. The full log can be found here (noted error starts at line 274).

Also, here are some code snippets I think are important to the problem (of course, I can provide any other requested file):

The AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.akuros.glossa.keyboard">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <!-- Declares the input method service -->
        <service android:name="GlossaKeyboard"
                 android:label="greek_keyboard"
                 android:permission="android.permission.BIND_INPUT_METHOD"
            >
            <intent-filter>
                <action android:name="android.view.InputMethod" />
            </intent-filter>
            <meta-data android:name="android.view.im"
                       android:resource="@xml/method" />
        </service>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

The method.xml:

<?xml version="1.0" encoding="utf-8"?>
<InputMethod
xmlns:android="http://schemas.android.com/apk/res/android"
android:supportsSwitchingToNextInputMethod="true"
>
    <Subtype
    android:label="@string/keyboard_grk_label"
    android:imeSubtypeLocale="el_Gr"
    android:imeSubtypeMode="keyboard"
    />
</InputMethod>

and the central class (which extends the InputMethodService), GlossaKeyboard:

package com.akuros.glossa.keyboard;

/*
 * This class defines the Greek Keyboard used by this application.
 *
 * Created by Nitzan on 15/02/2017.
 */

//TODO have the keyboard adapt its state according to the language needed

import android.app.Dialog;
import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.os.IBinder;
import android.text.InputType;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;

import static android.inputmethodservice.Keyboard.KEYCODE_DELETE;
import static android.inputmethodservice.Keyboard.KEYCODE_SHIFT;

public class GlossaKeyboard extends InputMethodService
        implements KeyboardView.OnKeyboardActionListener
{
    //------ Key Codes ------//
    private static final int KEYCODE_ENGLISH = -101;
    private static final int KEYCODE_LANGUAGE_SWITCH = -102;
    private static final int KEYCODE_CAPS = -103;

    //------ Fields ------ //

    private GlossaKeyboardView keyboardView;
    private StringBuilder composing;

    private InputMethodManager imm;

    private Keyboard greekKeyboard;
    private Keyboard englishKeyboard;
    private Keyboard currKeyboard;

    private int shiftState;

    @Override
    public void onCreate()
    {
        super.onCreate();
        imm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
        composing = new StringBuilder();

        shiftState = 0;
    }

    @Override
    public void onInitializeInterface()
    {
        greekKeyboard = new Keyboard(this, R.xml.qwerty_greek);
        englishKeyboard = new Keyboard(this, R.xml.qwerty_english);
    }

    @Override
    public View onCreateInputView()
    {
        keyboardView = (GlossaKeyboardView) getLayoutInflater().inflate(
                R.layout.keyboard_greek, null);
        keyboardView.setOnKeyboardActionListener(this);
        keyboardView.setKeyboard(greekKeyboard);
        return keyboardView;
    }

    @Override
    public void onStartInput(EditorInfo attribute, boolean restarting)
    {
        super.onStartInput(attribute, restarting);

        composing.setLength(0);
        shiftState = 0;

        currKeyboard = greekKeyboard;

    }


    @Override
    public void onFinishInput()
    {
        super.onFinishInput();

        composing.setLength(0);

        if (keyboardView != null)
            keyboardView.closing();
    }

    /* Interface Required Methods */

    @Override
    public void onPress(int primaryCode)
    {

    }

    @Override
    public void onRelease(int primaryCode)
    {

    }

    @Override
    //note the static import of Keyboard's KEYCODE values
    public void onKey(int primaryCode, int[] keyCodes)
    {

        switch (primaryCode)
        {
            case KEYCODE_SHIFT:
                shiftState = (shiftState == 0) ? 1 : 0;
                break;

            case KEYCODE_CAPS:
                shiftState = (shiftState == 0) ? 2 : 0;
                break;

            case KEYCODE_DELETE:
                handleBackspace();
                break;

            case KEYCODE_LANGUAGE_SWITCH:
                handleLanguageSwitch();
                break;

            case KEYCODE_ENGLISH:
                handleEnglishSwitch();
                break;

            default:
                handleCharacter(primaryCode);
                break;

        }

        updateShiftKeyState(getCurrentInputEditorInfo());

    }

    @Override
    public void onText(CharSequence text)
    {
        InputConnection ic = getCurrentInputConnection();
        if (ic == null) return;
        ic.beginBatchEdit();

        if (composing.length() > 0)
            commitTyped(ic);

        ic.commitText(text, 0); //not sure why this call needs to exist, commitText(ic) should already
        //do the work.

        ic.endBatchEdit();
        updateShiftKeyState(getCurrentInputEditorInfo());
    }


    //------ Private methods ------//

    /* Handlers */

    private void handleCharacter(int primaryCode)
    {
        if(isInputViewShown())
        {
            if(keyboardView.isShifted())
                primaryCode = Character.toUpperCase(primaryCode);
        }
        CharSequence str = String.valueOf((char) primaryCode);
        getCurrentInputConnection().commitText(str, 1);
    }

    private void handleBackspace()
    {
        final int length = composing.length();
        if(length > 1)
        {
            composing.delete(length - 1, length);
            getCurrentInputConnection().setComposingText(composing,1);
        }
        else if(length > 0)
        {
            composing.setLength(0);
            getCurrentInputConnection().commitText("", 0);
        }
        else
        {
            keyDownUp(KeyEvent.KEYCODE_DEL);
        }
        updateShiftKeyState(getCurrentInputEditorInfo());
    }

    private void handleLanguageSwitch()
    {
        imm.switchToNextInputMethod(getToken(), false);
    }

    private void handleEnglishSwitch()
    {
        if(currKeyboard != englishKeyboard )
        {
            currKeyboard = englishKeyboard;
            keyboardView.setKeyboard(englishKeyboard);
        }
    }

    /* Helper Methods */

    public IBinder getToken() {
        final Dialog dialog = getWindow();
        if (dialog == null) {
            return null;
        }
        final Window window = dialog.getWindow();
        if (window == null) {
            return null;
        }
        return window.getAttributes().token;
    }

    private void commitTyped(InputConnection ic)
    {
        if (composing.length() > 0)
        {
            ic.commitText(composing, composing.length());
            composing.setLength(0);
        }
    }

    private void keyDownUp(int keyEventCode)
    {
        getCurrentInputConnection().sendKeyEvent(
                new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode));
        getCurrentInputConnection().sendKeyEvent(
                new KeyEvent(KeyEvent.ACTION_UP, keyEventCode));
    }

    private void updateShiftKeyState(EditorInfo attr)
    {
        if (attr != null && keyboardView != null)
        {
            Keyboard keyboard = keyboardView.getKeyboard();
            if (keyboard == greekKeyboard
                    || keyboard == englishKeyboard)
            {
                int caps = 0;
                EditorInfo ei = getCurrentInputEditorInfo();
                if (ei != null && ei.inputType != InputType.TYPE_NULL)
                {
                    caps = getCurrentInputConnection().getCursorCapsMode(attr.inputType);
                }
                keyboardView.setShifted(shiftState > 0 || caps != 0);
            }
        }
        if (shiftState == 1)
            shiftState = 0;
    }


    /* Unused Methods */
    //These methods are implemented simply because the OnKeyboardActionListener interface demands it.
    //their implementation is empty.

    @Override
    public void swipeLeft()
    {
        //do nothing
    }

    @Override
    public void swipeRight()
    {
        //do nothing
    }

    @Override
    public void swipeDown()
    {
        //do nothing
    }

    @Override
    public void swipeUp()
    {
        //do nothing
    }
}

So far, I've tried:

Any help is appreciated.

Upvotes: 1

Views: 818

Answers (1)

TheRealChx101
TheRealChx101

Reputation: 1544

5 years later.

The problem is your method.xml file.

It should be defined like this:

<?xml version="1.0" encoding="utf-8"?>
<input-method 
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:supportsSwitchingToNextInputMethod="true">
    <subtype 
       android:label="@string/keyboard_grk_label"
       android:imeSubtypeLocale="el_Gr"
       android:imeSubtypeMode="keyboard" />
</input-method>

https://developer.android.com/develop/ui/views/touch-and-input/creating-input-method#IMESubTypes

Upvotes: 0

Related Questions