1

I need to use custom input method for specific edit texts inside my app.

Input method successfully works with all apps (when I select it from the system menus). So all OK with the method.

But when I tries to force its use explicitly:

<EditText
    android:id="@+id/edit"
    android:inputMethod="tx.android.softkeyboard.TXSoftKeyboard"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

it triggers ClassNotFoundException

Caused by: java.lang.ClassNotFoundException: tx.android.softkeyboard.TXSoftKeyboard
        at java.lang.Class.classForName(Native Method)
        at java.lang.Class.forName(Class.java:400)
        at java.lang.Class.forName(Class.java:326)
        at android.widget.TextView.<init>(TextView.java:1233)
        at android.widget.EditText.<init>(EditText.java:64) 

Class tx.android.softkeyboard.TXSoftKeyboard exists of course.

In another topic user m0skit0 reported similar behaviour but no solutions..

Dmitriy P
  • 63
  • 5
  • Just to double check: are you using ProGuard or R8? Also, have you checked your resulting APK with "Analyze APK..." option from Android Studio to make extra-sure that the class is there and that the name hasn't been obfuscated or something similar? – Xavier Rubio Jansana Mar 18 '19 at 17:12
  • Input method works (both this app and other apps), so declared class _can_ be found by the class loader in another cases. – Dmitriy P Mar 18 '19 at 17:15
  • I know, but my suspicion is more that maybe is available in other (separated) process for the same app, and that's why it can be found by the system, but not directly from inside your app. I would check "Analyze APK..." tool output to see if there's something there. Also, check AndroidManifest.xml with the same tool to see how it looks after compilation (mainly to look for added information about your IME that may hint that it's in a separated process or DEX file). – Xavier Rubio Jansana Mar 18 '19 at 17:22
  • classes.dex - ok. Manifest - ok. Class _can_ be loaded by the app in another cases (on button press, for example): `try { Class> c = Class.forName("tx.android.softkeyboard.TXSoftKeyboard"); } catch (ClassNotFoundException ex) {throw new RuntimeException(ex); }` does not throws exception – Dmitriy P Mar 18 '19 at 17:46

1 Answers1

1

I've checked in AOSP and in TextView you can clearly see that the value of the attribute is not modified (it's easy to follow, in line 1034 the attribute is fetched and later is used in the following piece of code):

    if (inputMethod != null) {
        Class<?> c;
        try {
            c = Class.forName(inputMethod.toString());
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException(ex);
        }
        try {
            createEditorIfNeeded();
            mEditor.mKeyListener = (KeyListener) c.newInstance();
        } catch (InstantiationException ex) {
            throw new RuntimeException(ex);
        } catch (IllegalAccessException ex) {
            throw new RuntimeException(ex);
        }
        try {
            mEditor.mInputType = inputType != EditorInfo.TYPE_NULL
                    ? inputType
                    : mEditor.mKeyListener.getInputType();
        } catch (IncompatibleClassChangeError e) {
            mEditor.mInputType = EditorInfo.TYPE_CLASS_TEXT;
        }
    }

So, the closest we can get to work around this is calling TextView#setKeyListener() (that basically is what this piece of code is doing, notice the cast after creating the instance of the class once it is loaded mEditor.mKeyListener = (KeyListener) c.newInstance();). That being said, from my limited understanding of this topic, KeyListener is not what you're looking for.

Xavier Rubio Jansana
  • 6,388
  • 1
  • 27
  • 50