change(android): host-page init now retrieves current stub before KMW init

This commit is contained in:
Joshua A. Horton 2023-11-17 09:54:40 +07:00
parent 33c69969b5
commit c5b3bafcc7
4 changed files with 120 additions and 6 deletions

View file

@ -31,6 +31,10 @@ function init() {
keyman.getOskHeight = getOskHeight;
keyman.getOskWidth = getOskWidth;
keyman.beepKeyboard = beepKeyboard;
// Readies the keyboard stub for instant loading during the init process.
KeymanWeb.registerStub(JSON.parse(jsInterface.initialKeyboard()));
keyman.init({
'embeddingApp':device,
'fonts':'packages/',

View file

@ -1,6 +1,7 @@
package com.keyman.engine;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@ -20,6 +21,7 @@ import android.webkit.JavascriptInterface;
import static android.content.Context.VIBRATOR_SERVICE;
import com.keyman.engine.KMManager.KeyboardType;
import com.keyman.engine.data.Keyboard;
import com.keyman.engine.util.CharSequenceUtil;
import com.keyman.engine.util.KMLog;
@ -62,6 +64,21 @@ public class KMKeyboardJSHandler {
return kbWidth;
}
@JavascriptInterface
public String initialKeyboard() {
// Note: KMManager.getCurrentKeyboard() (and similar) will throw errors until the host-page is first fully
// loaded and has set a keyboard. To allow the host-page to have earlier access, we instead get the stored
// keyboard index directly.
SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
int index = prefs.getInt(KMManager.KMKey_UserKeyboardIndex, 0);
if (index < 0) {
index = 0;
}
Keyboard kbd = KMManager.getKeyboardInfo(this.context, index);
return kbd.toStub(context);
}
// This annotation is required in Jelly Bean and later:
@JavascriptInterface
public void beepKeyboard() {

View file

@ -16,11 +16,13 @@ import com.keyman.engine.util.FileUtils;
import com.keyman.engine.util.KMLog;
import com.keyman.engine.util.KMString;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
public class Keyboard extends LanguageResource implements Serializable {
private static final String TAG = "Keyboard";
@ -171,6 +173,91 @@ public class Keyboard extends LanguageResource implements Serializable {
return o;
}
private String getKeyboardRoot(Context context) {
String keyboardRoot = context.getDir("data", Context.MODE_PRIVATE).toString() +
File.separator;
if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
return keyboardRoot + KMManager.KMDefault_UndefinedPackageID + File.separator;
} else {
return keyboardRoot + KMManager.KMDefault_AssetPackages + File.separator + packageID + File.separator;
}
}
public String getKeyboardPath(Context context) {
String keyboardID = this.getKeyboardID();
String keyboardVersion = this.getVersion();
if (packageID.equals(KMManager.KMDefault_UndefinedPackageID)) {
return getKeyboardRoot(context) + keyboardID + "-" + keyboardVersion + ".js";
} else {
return getKeyboardRoot(context) + keyboardID + ".js";
}
}
public String toStub(Context context) {
JSONObject stubObj = new JSONObject();
try {
stubObj.put("KN", this.getKeyboardName());
stubObj.put("KI", "Keyboard_" + this.getKeyboardID());
stubObj.put("KLC", this.getLanguageID());
stubObj.put("KL", this.getLanguageName());
stubObj.put("KF", this.getKeyboardPath(context));
stubObj.put("KP", this.getPackageID());
String displayFont = this.getFont();
if(displayFont != null) {
stubObj.put("KFont", this.buildDisplayFontObject(displayFont, context));
}
String oskFont = this.getOSKFont();
if(oskFont != null) {
stubObj.put("KOskFont", this.buildDisplayFontObject(oskFont, context));
}
String displayName = this.getDisplayName();
if(displayName != null) {
stubObj.put("displayName", displayName);
}
return stubObj.toString();
} catch(JSONException e) {
KMLog.LogException(TAG, "", e);
return null;
}
}
/**
* Take a font JSON object and adjust to pass to JS
* 1. Replace "source" keys for "files" keys
* 2. Create full font paths for .ttf or .svg
* @param font String font JSON object as a string
* @return JSONObject of modified font information with full paths. If font is invalid, return `null`
*/
private JSONObject buildDisplayFontObject(String font, Context context) {
if(font == null || font.equals("")) {
return null;
}
String keyboardRoot = this.getKeyboardRoot(context);
try {
if (FileUtils.hasFontExtension(font)) {
JSONObject jfont = new JSONObject();
jfont.put(KMManager.KMKey_FontFamily, font.substring(0, font.length() - 4));
JSONArray jfiles = new JSONArray();
jfiles.put(keyboardRoot + font);
jfont.put(KMManager.KMKey_FontFiles, jfiles);
return jfont;
} else {
return null;
}
} catch (JSONException e) {
KMLog.LogException(TAG, "Failed to make font for '"+font+"'", e);
return null;
}
}
/**
* Get the fallback keyboard. If never specified, use sil_euro_latin
* @param context Context

View file

@ -88,15 +88,21 @@ export default class KeyboardInterface<ContextManagerType extends ContextManager
//
// The mobile apps typically have fully-preconfigured paths, but Developer's
// test-host page does not.
const pathConfig = this.engine.config.paths;
const stub = new KeyboardStub(Pstub, pathConfig.keyboards, pathConfig.fonts);
if(this.engine.keyboardRequisitioner?.cache.findMatchingStub(stub)) {
return 1;
}
const buildStub = () => {
const pathConfig = this.engine.config.paths;
return new KeyboardStub(Pstub, pathConfig.keyboards, pathConfig.fonts);
};
if(!this.engine.config.deferForInitialization.hasFinalized) {
this.engine.config.deferForInitialization.then(() => this.engine.keyboardRequisitioner.cache.addStub(stub));
// pathConfig is not ready until KMW initializes, which prevents proper stub-building.
this.engine.config.deferForInitialization.then(() => this.engine.keyboardRequisitioner.cache.addStub(buildStub()));
} else {
const stub = buildStub();
if(this.engine.keyboardRequisitioner?.cache.findMatchingStub(stub)) {
return 1;
}
this.engine.keyboardRequisitioner.cache.addStub(stub);
}