Skip to content

Commit

Permalink
Merge pull request #10022 from keymanapp/change/android/flishy-flashy…
Browse files Browse the repository at this point in the history
…-mitigation-2

change(android): smoother keyboard initialization ✨
  • Loading branch information
jahorton authored Dec 1, 2023
2 parents f7f5952 + 7c9061e commit 19d3d7c
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 11 deletions.
4 changes: 4 additions & 0 deletions android/KMEA/app/src/main/assets/android-host.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,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/',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,8 @@ public boolean onConsoleMessage(ConsoleMessage cm) {
}

// Send console errors to Sentry in case they're missed by KMW sentryManager
// (Ignoring spurious message "No keyboard stubs exist = ...")
// TODO: Fix base error rather than trying to ignore it "No keyboard stubs exist"

if ((cm.messageLevel() == ConsoleMessage.MessageLevel.ERROR) && (!cm.message().startsWith("No keyboard stubs exist"))) {
if (cm.messageLevel() == ConsoleMessage.MessageLevel.ERROR) {
// Make Toast notification of error and send log about falling back to default keyboard (ignore language ID)
// Sanitize sourceId info
String NAVIGATION_PATTERN = "^(.*)?(keyboard\\.html#[^-]+)-.*$";
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -20,6 +21,7 @@
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;

Expand Down Expand Up @@ -62,6 +64,21 @@ public int getKeyboardWidth() {
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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -776,12 +776,10 @@ public static void onConfigurationChanged(Configuration newConfig) {
// KMKeyboard
if (InAppKeyboard != null) {
RelativeLayout.LayoutParams params = getKeyboardLayoutParams();
InAppKeyboard.setLayoutParams(params);
InAppKeyboard.onConfigurationChanged(newConfig);
}
if (SystemKeyboard != null) {
RelativeLayout.LayoutParams params = getKeyboardLayoutParams();
SystemKeyboard.setLayoutParams(params);
SystemKeyboard.onConfigurationChanged(newConfig);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
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";
Expand Down Expand Up @@ -171,6 +173,91 @@ public JSONObject toJSON() {
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
Expand Down
18 changes: 12 additions & 6 deletions web/src/engine/main/src/keyboardInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down

0 comments on commit 19d3d7c

Please sign in to comment.