Reputation: 11
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:
moving all Input-Method related files into a separate project, and running that (same results)
debugging the project (breakpoints at the GlossaKeyboard class were never reached)
Searching the logs (as mentioned, nothing found)
Comparing my files to the ones in the SoftKeyboard sample app (no errors found in my version; haven't found anything missing except for unwanted features [such as word suggestions, spell check, etc.]).
Any help is appreciated.
Upvotes: 1
Views: 818
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