GlobeKey Action on older KeymanEngines

Hello again.
Most of the other Keyman engine-related issues were solved, but here’s a new one.

I have 2 KM keyboards in a custom android app, and the user will have other input methods. I know that I can set the Globe Key action to several values (open menu, next KM keyboard, next input method, etc.)

In both KM engine 13 and 14, I can use:
GLOBE_KEY_ACTION_SHOW_MENU , GLOBE_KEY_ACTION_SWITCH_TO_NEXT_KEYBOARD , or GLOBE_KEY_ACTION_DO_NOTHING

Here’s another minimal example:

In KMEngine 14 (see project KMSample2-14): the fourth option works well:
GLOBE_KEY_ACTION_ADVANCE_TO_NEXT_SYSTEM_KEYBOARD

In KMEngine 13 (see project KMSample2-11-13) , it still opens the menu, even though it has been instructed to switch to the next IME.

In my “real” app, the user has an option to choose the keyboard, and any one user is unlikely to need both, so a simple next Input Method may be sufficient. Can you help me figure out why GLOBE_KEY_ACTION_ADVANCE_TO_NEXT_SYSTEM_KEYBOARD doesn’t work in KME 13 (KMSample2-11-13) or earlier. If it’s my mistake, great! If it is a bug, I guess you’ll have to decide whether to backport it to stable 13. I’m leaning toward submitting my next app version with 14 anyway, since everything I want works there now.

Eventually, if the user selects both KM keyboards, I may want the first KM keyboard’s globe to switch KM keyboards, and the second’s globe to switch Input methods. I have an idea how I would set up this in the KMmanager lifecycle.

ah, this is fixed in 14 via PR 3140. I’ve highlighted the offending line where the globe button was getting reverted to GlobeKeyAction.GLOBE_KEY_ACTION_SHOW_MENU.

I’ll cherry-pick this to 13.0

Thank you, thank you!

@darcy, please let me know when and which 13 version will contain this fix. I want to embed it and push an update to my app.

It turns out the fix was already cherry-picked to stable-13.0 in 13.0.6212

2020-05-25 13.0.6212 stable

  • Bug fix:
    • Fix system keyboard globe button override (#3161)

When I rebuilt your project with 13.0.6220, the globe button switched to the next system keyboard.

Great, OK. I had 13.0.6060.

Ok, so I have been able to use GLOBE_KEY_ACTION_ADVANCE_TO_NEXT_SYSTEM_KEYBOARD the 14 beta in my copy of the sample (kmsample2-14) https://github.com/MattGyverLee/keyman-engine-android-testing. It even works with minify turned on in Gradle. So, this isn’t a Keyman Bug anymore.

Unfortunately, everything works on my CameroonKeyboard app, except I cannot get GLOBE_KEY_ACTION_ADVANCE_TO_NEXT_SYSTEM_KEYBOARD to work in my real project.
I can enable and activate the keyboard in Android settings. I can use the keyboard in any app. I can use the android IME switcher to enable my keybaord. All of the other globe-key options work, even the new GLOBE_KEY_ACTION_SHOW_SYSTEM_KEYBOARDS,
and I can’t find any functional differences between keyman2-14 and my app. I’m using the same version of KMEngine, the same verison of android (29), the same phone, I’ve wiped the memory, it calls the same functions with the same parameters, the same keyboards, and everything except the project name on SystemKeyboard.java is the same.

If someone can help, I will gladly add them to my private repository. I was really hoping to release this version before the new year.

KMSample and CameroonKeyboard

If I set GLOBE_KEY_ACTION_ADVANCE_TO_NEXT_SYSTEM_KEYBOARD and add several other keyboards to the emulator. the Globe on the KM keyboard calls this code in KMManager.java:

 case GLOBE_KEY_ACTION_ADVANCE_TO_NEXT_SYSTEM_KEYBOARD:
                                KMManager.advanceToNextInputMode();
                                break;

and then this code:

public static void advanceToNextInputMode() {
        if (VERSION.SDK_INT >= 28) {
            if (IMService != null) {
                IMService.switchToNextInputMethod(false);
            }

then goes to inputMethodservice.java

public final boolean switchToNextInputMethod(boolean onlyCurrentIme) {
        return mPrivOps.switchToNextInputMethod(onlyCurrentIme);
    }

then it loops through this in parcel.java:

@NonNull
    public static Parcel obtain() {
        final Parcel[] pool = sOwnedPool;
        synchronized (pool) {
            Parcel p;
            for (int i=0; i<POOL_SIZE; i++) {
                p = pool[i];
                if (p != null) {
                    pool[i] = null;
                    if (DEBUG_RECYCLE) {
                        p.mStack = new RuntimeException();
                    }
                    p.mReadWriteHelper = ReadWriteHelper.DEFAULT;
                    return p;
                }
            }
        }
        return new Parcel(0);

KMSample2-14
KMSample2-14 loops through Parcel several times, and one of the loops changes the IME. I can loop through the IMEs at will.

CameroonKeyboard
My project loops through Parcel and does not throw an error, but fails to change to the next IME, eventually returning to KMManager. It almost seems like KMManager in my app isn’t registered in the same group as Gboard and others.

The weird thing is that with CameroonKeyboard, Keyman’s globe does not work with GLOBE_KEY_ACTION_ADVANCE_TO_NEXT_SYSTEM_KEYBOARD, but the globes in my other IMEs (i.e. gboard) toggle through all other IMEs except my project. The only way to get back to my keyboard is to use image.

Edit: My old version of the app with KME13 (https://github.com/MattGyverLee/CamKeyAndroid/tree/13GlobeTrial) and my trial KME14 (https://github.com/MattGyverLee/CamKeyAndroid/tree/KM14) version fail to switch to the Globe. I have added @darcy to the repository.

I can’t get my CameroonKeyboard app to switch IME’s with the globe, and I can’t figure out how to break the KMSample2-14 in the same way. I don’t know what to try, other than starting my app again from scratch to get this working!

This sounds like something @darcy should be able to help you with :slight_smile:

Awesome. I have a feeling that whatever’s blocking it must be blocking both 13 and 14, but getting the globe working with 14 is my priority. One person suggested it was the order of imports. I thought it might have been minification, but I was able to minify KMSample2-14 without breaking it.

The globe key is triggering KMManager.advanceToNextInputMode().

I’m puzzled why the Android call IMService.switchToNextInputMethod(false) fails (returns false, with nothing happening).

Maybe a possible workaround when switchToNextInputMethod() fails is to try IMService.switchToPreviousInputMethod(). That worked in my emulator with Android P.

I just wonder if it would possibly frustrate a user with 3 IME’s if the globe button only toggled between 2 of them.

Now you see why this is so puzzling to me!

One clue may be the fact that if you have 3 IMEs and switch to gBoard, gBoard’s globe steps between all non-keyman keyboards and skips my CamKey app. This leads me to believe that my app is having some problem registering itself in the list (or seeing the list of IMEs). Maybe something is not properly registered, too-easily disposed, calling a conflicting command, or double registered. It’s odd that this seems to be a deep code issue, but it doesn’t happen in KMSample2-14.

I’ve stepped through the Android code, and the parcel with code 11 seems to be the keyboard swap that fires properly in KMSample2-14, but not my app.

Does this help you debug?
https://developer.android.com/reference/android/inputmethodservice/InputMethodService#shouldOfferSwitchingToNextInputMethod()
Maybe Android is reading my IME as failing to have a swap feature.

My App has 2 keyboards installed, chosen from the radio button. Is it trying to swap to the other KM keyboard and failing? Does another KM keyboard count as a “subtype” in Android lingo?

If it returns false, this guy reverts to showing the IM picker, which seems like a safe option (https://www.codota.com/web/assistant/code/rs/5c7c188c2ef5570001d94afc#L825) makes sense. But I still want to solve why it is failing.

Progress… When I get to the code below, I put a watch on IMService.shouldOfferSwitchingToNextInputMethod().

 public static void advanceToNextInputMode() {
        if (VERSION.SDK_INT >= 28) {
            if (IMService != null) {
                IMService.switchToNextInputMethod(false);
            }

If I break on IMService.switchToNextInputMethod(false); in KMsample2-14, IMService.shouldOfferSwitchingToNextInputMethod() resolves to true and switches IMEs.
In CamKeyboard, the same variable resolves to `false’, and it doesn’t switch IMEs.

The question is now why it thinks it shouldn’t.

What are your minSDKVersion and targetSDKVersion set to?

minSdkVersion 21
targetSdkVersion 29

…and I do most of my testing on 29 and 27.

I’ve added you to the CamKey project.

I think @darcy is on vacation now so he’ll pick this up with you after he returns in the New Year. (I am not going to have the chance to investigate before Christmas, sorry.)

I’m back on deck, so I’ll try to investigate some more this week

Thank you, Thank you! I’m definitely willing to chat with you if that helps (GMT+1). I’m out of ideas!

Is there anything I can do to help? I’d really like to get this out as there’s a publicity event at the end of the month.

Darcy pointed me to listing the IMEs which gave some helpful info.
In the terminal:

adb -e shell
ime list -a

This led me to some variables that I had configured when Keyman 10 didn’t support all the Globe key options in app/src/main/res/xml/method.xml along with this note:
<!-- TODO Change supportsSwitching when supported by Keyman: https://github .com/keymanapp/keyman/issues/943 and https://developer.android.com/guide/topics/text/creating-input-method?hl=lt-->

One variable specifically disabled keyboard switching: android:supportsSwitchingToNextInputMethod='false'.

So…my issue is solved, and it had nothing to do with mainActivity.java or systemKeyboard.java. During the investigation, I corrected <service android:name=".keyboard.SystemKeyboard">
to <service android:name="info.langtechcameroon.keyboard.SystemKeyboard">, and ironed out some other switching logic issues.

A frustratingly simple fix in code I didn’t even remember existed.
android:supportsSwitchingToNextInputMethod='true'
:man_facepalming:

Thanks KM Team for your time!

1 Like

This was on my agenda to review with @Darcy today (I got back from holidays two days ago)!

Well done. Glad you solved it, and glad it was something simple and clear in the end. But must have been frustrating all the same. :slight_smile: