diff --git a/android/KMAPro/kMAPro/src/main/assets/banner.html b/android/KMAPro/kMAPro/src/main/assets/banner.html
new file mode 100644
index 00000000000..41107f11424
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/assets/banner.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/KMAPro/kMAPro/src/main/assets/banner/keyman_banner.svg b/android/KMAPro/kMAPro/src/main/assets/banner/keyman_banner.svg
new file mode 100644
index 00000000000..3888a0937f9
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/assets/banner/keyman_banner.svg
@@ -0,0 +1,372 @@
+
+
diff --git a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/BannerController.java b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/BannerController.java
new file mode 100644
index 00000000000..f716faa9aab
--- /dev/null
+++ b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/BannerController.java
@@ -0,0 +1,29 @@
+package com.keyman.android;
+
+import android.content.Context;
+
+import com.keyman.engine.KMManager;
+import com.keyman.engine.util.FileUtils;
+
+import java.io.File;
+
+public class BannerController {
+
+ // Paths relative to assets folder for banner themes
+ public static final String KM_BANNER_DIR = "banner";
+ public static final String KM_BANNER_THEME_KEYMAN = "banner.html";
+
+ public static void setHTMLBanner(Context context, KMManager.KeyboardType keyboardType) {
+ if (keyboardType == KMManager.KeyboardType.KEYBOARD_TYPE_UNDEFINED) {
+ return;
+ }
+
+ KMManager.copyHTMLBannerAssets(context, KM_BANNER_DIR);
+
+ // Always use Keyman banner theme
+ String contents = FileUtils.readContents(context, KM_BANNER_THEME_KEYMAN);
+ KMManager.setHTMLBanner(keyboardType, contents);
+ KMManager.setBanner(keyboardType, KMManager.BannerType.HTML);
+ KMManager.showBanner(true);
+ }
+}
diff --git a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java
index 697825094bf..cf919deddb4 100644
--- a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java
+++ b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java
@@ -6,6 +6,7 @@
import com.tavultesoft.kmapro.BuildConfig;
import com.tavultesoft.kmapro.KeymanSettingsActivity;
+import com.keyman.android.BannerController;
import com.keyman.engine.KMManager;
import com.keyman.engine.KMManager.KeyboardType;
import com.keyman.engine.KMHardwareKeyboardInterpreter;
@@ -71,6 +72,9 @@ public void onCreate() {
KMManager.SpacebarText spacebarText = KMManager.SpacebarText.fromString(prefs.getString(KeymanSettingsActivity.spacebarTextKey, KMManager.SpacebarText.LANGUAGE_KEYBOARD.toString()));
KMManager.setSpacebarText(spacebarText);
+ // Set the system keyboard HTML banner
+ BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM);
+
boolean mayHaveHapticFeedback = prefs.getBoolean(KeymanSettingsActivity.hapticFeedbackKey, false);
KMManager.setHapticFeedback(mayHaveHapticFeedback);
@@ -93,6 +97,9 @@ public void onDestroy() {
@Override
public void onInitializeInterface() {
super.onInitializeInterface();
+
+ // KeymanWeb reloaded, so we have to pass the banner again
+ BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM);
}
/**
diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java
index 6b92d4c2eb5..16899f7c2d9 100644
--- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java
+++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java
@@ -18,6 +18,7 @@
import java.util.Map;
import com.keyman.android.CheckInstallReferrer;
+import com.keyman.android.BannerController;
import com.keyman.engine.BaseActivity;
import com.keyman.engine.KMHelpFileActivity;
import com.keyman.engine.KMKeyboardDownloaderActivity;
@@ -499,6 +500,8 @@ public void onKeyboardChanged(String newKeyboard) {
@Override
public void onKeyboardShown() {
+ // Refresh banner theme
+ BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_INAPP);
resizeTextView(true);
}
diff --git a/android/KMEA/app/src/main/assets/android-host.js b/android/KMEA/app/src/main/assets/android-host.js
index afa2825e33d..7930c76f11f 100644
--- a/android/KMEA/app/src/main/assets/android-host.js
+++ b/android/KMEA/app/src/main/assets/android-host.js
@@ -8,6 +8,9 @@ if(window.parent && window.parent.jsInterface && !window.jsInterface) {
var device = window.jsInterface.getDeviceType();
var oskHeight = Math.ceil(window.jsInterface.getKeyboardHeight() / window.devicePixelRatio);
var oskWidth = 0;
+var bannerHeight = 0;
+var bannerImagePath = '';
+var bannerHTMLContents = '';
var fragmentToggle = 0;
var sentryManager = new KeymanSentryManager({
@@ -37,11 +40,13 @@ function init() {
oninserttext: insertText,
root:'./'
}).then(function () { // Note: For non-upgraded API 21, arrow functions will break the keyboard!
- const bannerHeight = Math.ceil(window.jsInterface.getDefaultBannerHeight() / window.devicePixelRatio);
+ bannerHeight = Math.ceil(window.jsInterface.getDefaultBannerHeight() / window.devicePixelRatio);
+ if (bannerHeight > 0) {
- // The OSK is not available until initialization is complete.
- keyman.osk.bannerView.activeBannerHeight = bannerHeight;
- keyman.refreshOskLayout();
+ // The OSK is not available until initialization is complete.
+ keyman.osk.bannerView.activeBannerHeight = bannerHeight;
+ keyman.refreshOskLayout();
+ }
});
keyman.addEventListener('keyboardloaded', setIsChiral);
@@ -53,6 +58,29 @@ function init() {
notifyHost('pageLoaded');
}
+function showBanner(flag) {
+ console_debug("Setting banner display for dictionaryless keyboards to " + flag);
+ console_debug("bannerHTMLContents: " + bannerHTMLContents);
+ var bc = keyman.osk.bannerController;
+ if (bc) {
+ if (bannerHTMLContents != '') {
+ bc.inactiveBanner = flag ? new bc.HTMLBanner(bannerHTMLContents) : null;
+ } else {
+ bc.inactiveBanner = flag ? new bc.ImageBanner(bannerImagePath) : null;
+ }
+ }
+}
+
+function setBannerImage(path) {
+ bannerImagePath = path;
+}
+
+// Set the HTML banner to use when predictive-text is not available
+// contents - HTML content to use for the banner
+function setBannerHTML(contents) {
+ bannerHTMLContents = contents;
+}
+
function notifyHost(event, params) {
console_debug('notifyHost(event='+event+',params='+params+')');
// TODO: Update all other host notifications to use notifyHost instead of directly setting window.location.hash
@@ -65,12 +93,19 @@ function notifyHost(event, params) {
}
// Update the KMW banner height
+// h is in dpi (different from iOS)
function setBannerHeight(h) {
if (h > 0) {
- var osk = keyman.osk;
- osk.banner.height = Math.ceil(h / window.devicePixelRatio);
+ // The banner itself may not be loaded yet. This will preemptively help set
+ // its eventual display height.
+ bannerHeight = Math.ceil(h / window.devicePixelRatio);
+
+ if (keyman.osk) {
+ keyman.osk.bannerView.activeBannerHeight = bannerHeight;
+ }
}
- // Refresh KMW OSK
+
+ // Refresh KMW's OSK
keyman.refreshOskLayout();
}
diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java
index 6471e875101..215630cd03d 100644
--- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java
+++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboard.java
@@ -7,13 +7,11 @@
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
-import com.keyman.engine.BaseActivity;
import com.keyman.engine.data.Keyboard;
import com.keyman.engine.data.KeyboardController;
import com.keyman.engine.KMManager.KeyboardType;
@@ -25,19 +23,11 @@
import com.keyman.engine.util.KMLog;
import com.keyman.engine.util.KMString;
-import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.res.Configuration;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Typeface;
-import android.os.Build;
-import android.os.Bundle;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -46,7 +36,6 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
@@ -56,12 +45,9 @@
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Button;
-import android.widget.FrameLayout;
-import android.widget.GridLayout;
import android.widget.PopupWindow;
import android.widget.PopupWindow.OnDismissListener;
import android.widget.RelativeLayout;
-import android.widget.TextView;
import android.widget.Toast;
import io.sentry.Breadcrumb;
@@ -84,19 +70,10 @@ final class KMKeyboard extends WebView {
private static String currentKeyboard = null;
- /**
- * Banner state value: "blank" - no banner available.
- */
- protected static final String KM_BANNER_STATE_BLANK = "blank";
- /**
- * Banner state value: "suggestion" - dictionary suggestions are shown.
- */
- protected static final String KM_BANNER_STATE_SUGGESTION = "suggestion";
-
/**
* Current banner state.
*/
- protected static String currentBanner = KM_BANNER_STATE_BLANK;
+ protected static KMManager.BannerType currentBanner = KMManager.BannerType.HTML;
private static String txtFont = "";
private static String oskFont = null;
@@ -105,6 +82,10 @@ final class KMKeyboard extends WebView {
private GestureDetector gestureDetector;
private static ArrayList kbEventListeners = null;
+ // Stores the current html string for use by the Banner
+ // when predictive text is not active
+ protected String htmlBannerString = "";
+
// Facilitates a 'lazy init' - we'll only check the preference when it matters,
// rather than at construction time.
private Boolean _shouldShowHelpBubble = null;
@@ -400,6 +381,9 @@ public void onConfigurationChanged(Configuration newConfig) {
int bannerHeight = KMManager.getBannerHeight(context);
int oskHeight = KMManager.getKeyboardHeight(context);
+ if (this.htmlBannerString != null && !this.htmlBannerString.isEmpty()) {
+ setHTMLBanner(this.htmlBannerString);
+ }
loadJavascript(KMString.format("setBannerHeight(%d)", bannerHeight));
loadJavascript(KMString.format("setOskWidth(%d)", newConfig.screenWidthDp));
loadJavascript(KMString.format("setOskHeight(%d)", oskHeight));
@@ -425,22 +409,15 @@ public static String currentKeyboard() {
return currentKeyboard;
}
- public static void setCurrentBanner(String banner) {
- currentBanner = banner;
- }
-
- public static String currentBanner() { return currentBanner; }
-
protected void toggleSuggestionBanner(HashMap associatedLexicalModel, boolean keyboardChanged) {
//reset banner state if new language has no lexical model
- if (currentBanner != null && currentBanner.equals(KM_BANNER_STATE_SUGGESTION)
+ if (currentBanner == KMManager.BannerType.SUGGESTION
&& associatedLexicalModel == null) {
- setCurrentBanner(KMKeyboard.KM_BANNER_STATE_BLANK);
+ currentBanner = KMManager.BannerType.HTML;
}
- if(keyboardChanged) {
- setLayoutParams(KMManager.getKeyboardLayoutParams());
- }
+ showBanner(true);
+ // Since there's always a banner, no need to update setLayoutParams()
}
/**
@@ -653,6 +630,30 @@ public boolean setKeyboard(String packageID, String keyboardID, String languageI
return retVal;
}
+ public void showBanner(boolean flag) {
+ String jsString = KMString.format("showBanner(%b)", flag);
+ loadJavascript(jsString);
+ }
+
+ public KMManager.BannerType getBanner() {
+ return currentBanner;
+ }
+
+ public void setBanner(KMManager.BannerType bannerType) {
+ currentBanner = bannerType;
+ }
+
+ public String getHTMLBanner() {
+ return this.htmlBannerString;
+ }
+
+ public void setHTMLBanner(String contents) {
+ this.htmlBannerString = contents;
+ String jsString = KMString.format("setBannerHTML(%s)",
+ JSONObject.quote(this.htmlBannerString));
+ loadJavascript(jsString);
+ }
+
public void setChirality(boolean flag) {
this.isChiral = flag;
}
diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardWebViewClient.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardWebViewClient.java
index e7188a94f96..4d458bd0616 100644
--- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardWebViewClient.java
+++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardWebViewClient.java
@@ -173,8 +173,7 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (KMManager.currentLexicalModel != null) {
modelPredictionPref = prefs.getBoolean(KMManager.getLanguagePredictionPreferenceKey(KMManager.currentLexicalModel.get(KMManager.KMKey_LanguageID)), true);
}
- kmKeyboard.setCurrentBanner((isModelActive && modelPredictionPref) ?
- KMKeyboard.KM_BANNER_STATE_SUGGESTION : KMKeyboard.KM_BANNER_STATE_BLANK);
+ KMManager.setBannerOptions(isModelActive && modelPredictionPref);
RelativeLayout.LayoutParams params = KMManager.getKeyboardLayoutParams();
kmKeyboard.setLayoutParams(params);
} else if (url.indexOf("suggestPopup") >= 0) {
diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java
index cbcb28053a1..df90f1d869d 100644
--- a/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java
+++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMManager.java
@@ -25,32 +25,21 @@
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.RectF;
import android.graphics.Typeface;
import android.inputmethodservice.InputMethodService;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
-import android.net.Uri;
import android.os.Build;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.text.InputType;
import android.util.Log;
-import android.view.HapticFeedbackConstants;
-import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.ExtractedText;
-import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
-import android.webkit.JavascriptInterface;
import android.webkit.WebView;
-import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
@@ -71,7 +60,6 @@
import com.keyman.engine.packages.LexicalModelPackageProcessor;
import com.keyman.engine.packages.PackageProcessor;
import com.keyman.engine.util.BCP47;
-import com.keyman.engine.util.CharSequenceUtil;
import com.keyman.engine.util.DependencyUtil;
import com.keyman.engine.util.DependencyUtil.LibraryType;
import com.keyman.engine.util.FileUtils;
@@ -157,6 +145,34 @@ public String toString() {
}
};
+ // Maps to enum BannerType in bannerView.ts
+ public enum BannerType {
+ BLANK,
+ IMAGE,
+ SUGGESTION,
+ HTML;
+
+ public static BannerType fromString(String mode) {
+ if (mode == null) return BLANK;
+ switch (mode) {
+ case "BLANK":
+ return BLANK;
+ case "image":
+ return IMAGE;
+ case "suggestion":
+ return SUGGESTION;
+ case "html":
+ return HTML;
+ }
+ return BLANK;
+ }
+
+ public String toString() {
+ String modes[] = { "blank", "image", "suggestion", "html"};
+ return modes[this.ordinal()];
+ }
+ }
+
protected static InputMethodService IMService;
private static boolean debugMode = false;
@@ -287,6 +303,9 @@ public String toString() {
public static final String KMFilename_LexicalModelsList = "lexical_models_list.dat";
+ public static final String KMBLACK_BANNER = "";
+ public static final String KMGRAY_BANNER = "";
+
private static Context appContext;
public static String getResourceRoot() {
@@ -636,6 +655,16 @@ private static void initKeyboard(Context appContext, KeyboardType keyboardType)
keyboard.addJavascriptInterface(new KMKeyboardJSHandler(appContext, keyboard), "jsInterface");
keyboard.loadKeyboard();
+ if (!isTestMode()) {
+ // For apps that don't specify an HTML banner, specify a default phone/tablet HTML banner
+ if (getFormFactor() == FormFactor.PHONE) {
+ keyboard.setHTMLBanner(KMBLACK_BANNER);
+ } else {
+ keyboard.setHTMLBanner(KMGRAY_BANNER);
+ }
+ keyboard.setBanner(KMManager.BannerType.HTML);
+ keyboard.showBanner(true);
+ }
setEngineWebViewVersionStatus(appContext, keyboard);
}
@@ -793,6 +822,31 @@ public static boolean hasInternetPermission(Context context) {
return hasPermission(context, Manifest.permission.INTERNET);
}
+ /**
+ * Copy HTML banner assets to the app
+ * @param context - The context
+ * @param path - Folder relative to assets/ containing the banner file.
+ * @return boolean - true if assets copied
+ */
+ public static boolean copyHTMLBannerAssets(Context context, String path) {
+ AssetManager assetManager = context.getAssets();
+ try {
+ File bannerDir = new File(getResourceRoot() + File.separator + path);
+ if (!bannerDir.exists()) {
+ bannerDir.mkdir();
+ }
+
+ String[] bannerFiles = assetManager.list(path);
+ for (String bannerFile : bannerFiles) {
+ copyAsset(context, bannerFile, path, true);
+ }
+ return true;
+ } catch (Exception e) {
+ KMLog.LogException(TAG, "copyHTMLBannerAssets() failed. Error: ", e);
+ }
+ return false;
+ }
+
private static void copyAssets(Context context) {
AssetManager assetManager = context.getAssets();
try {
@@ -1370,7 +1424,13 @@ public static void deleteLexicalModel(Context context, int position, boolean sil
KeyboardPickerActivity.deleteLexicalModel(context, position, silenceNotification);
}
- public static boolean setBannerOptions(boolean mayPredict) {
+ /**
+ * setBannerOptions - Update KMW whether to generate predictions.
+ * For now, also display banner
+ * @param mayPredict - boolean whether KMW should generate predictions
+ * @return boolean - Success
+ */
+ public static boolean setBannerOptions(boolean mayPredict) {
String url = KMString.format("setBannerOptions(%s)", mayPredict);
if (InAppKeyboard != null) {
InAppKeyboard.loadJavascript(url);
@@ -1379,6 +1439,73 @@ public static boolean setBannerOptions(boolean mayPredict) {
if (SystemKeyboard != null) {
SystemKeyboard.loadJavascript(url);
}
+
+ return true;
+ }
+
+ /**
+ * Update KeymanWeb banner type
+ * @param {KeyboardType} keyboard
+ * @param {BannerType} bannerType
+ * @return status
+ */
+ public static boolean setBanner(KeyboardType keyboard, BannerType bannerType) {
+ if (keyboard == KeyboardType.KEYBOARD_TYPE_INAPP && InAppKeyboard != null) {
+ InAppKeyboard.setBanner(bannerType);
+ } else if (keyboard == KeyboardType.KEYBOARD_TYPE_SYSTEM && SystemKeyboard != null) {
+ SystemKeyboard.setBanner(bannerType);
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Set the HTML content to use with the HTML banner
+ * @param {KeyboardType} keyboard
+ * @param {String} HTMl string
+ * @return {boolean}
+ */
+ public static boolean setHTMLBanner(KeyboardType keyboard, String htmlContent) {
+ if (keyboard == KeyboardType.KEYBOARD_TYPE_INAPP && InAppKeyboard != null) {
+ InAppKeyboard.setHTMLBanner(htmlContent);
+ } else if (keyboard == KeyboardType.KEYBOARD_TYPE_SYSTEM && SystemKeyboard != null) {
+ SystemKeyboard.setHTMLBanner(htmlContent);
+ } else {
+ Log.d(TAG, "setHTMLBanner() but keyboard is null");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Get the HTML content associated with the HTML banner
+ * @param {KeyboardType} keyboard
+ * @return {String}
+ */
+ public static String getHTMLBanner(KeyboardType keyboard) {
+ if (keyboard == KeyboardType.KEYBOARD_TYPE_INAPP && InAppKeyboard != null) {
+ return InAppKeyboard.getHTMLBanner();
+ } else if (keyboard == KeyboardType.KEYBOARD_TYPE_SYSTEM && SystemKeyboard != null) {
+ return SystemKeyboard.getHTMLBanner();
+ }
+ return "";
+ }
+
+ /**
+ * showBanner - Update KMW whether to display banner.
+ * For now, always keep displaying banner
+ * @param flag - boolean whether KMW should display banner
+ * @return boolean - Success
+ */
+ public static boolean showBanner(boolean flag) {
+ if (InAppKeyboard != null) {
+ InAppKeyboard.showBanner(flag);
+ }
+
+ if (SystemKeyboard != null) {
+ SystemKeyboard.showBanner(flag);
+ }
return true;
}
@@ -1845,9 +1972,9 @@ public static void removeKeyboardEventListener(OnKeyboardEventListener listener)
public static int getBannerHeight(Context context) {
int bannerHeight = 0;
- if (InAppKeyboard != null && InAppKeyboard.currentBanner().equals(KMKeyboard.KM_BANNER_STATE_SUGGESTION)) {
+ if (InAppKeyboard != null && InAppKeyboard.getBanner() != BannerType.BLANK) {
bannerHeight = (int) context.getResources().getDimension(R.dimen.banner_height);
- } else if (SystemKeyboard != null && SystemKeyboard.currentBanner().equals(KMKeyboard.KM_BANNER_STATE_SUGGESTION)) {
+ } else if (SystemKeyboard != null && SystemKeyboard.getBanner() != BannerType.BLANK) {
bannerHeight = (int) context.getResources().getDimension(R.dimen.banner_height);
}
return bannerHeight;
diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/util/FileUtils.java b/android/KMEA/app/src/main/java/com/keyman/engine/util/FileUtils.java
index d5610dedec2..6b04b974f8a 100644
--- a/android/KMEA/app/src/main/java/com/keyman/engine/util/FileUtils.java
+++ b/android/KMEA/app/src/main/java/com/keyman/engine/util/FileUtils.java
@@ -4,16 +4,22 @@
package com.keyman.engine.util;
import android.content.Context;
+import android.content.res.AssetManager;
+import android.util.Log;
+
+import com.keyman.engine.KMManager;
import org.json.JSONObject;
import org.json.JSONArray;
import java.io.BufferedInputStream;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
@@ -248,6 +254,35 @@ public static boolean saveList(File filepath, Object obj) {
return result;
}
+ /**
+ * Read the contents of asset file as a string
+ * Reference: https://stackoverflow.com/questions/16110002/read-assets-file-as-string
+ * @param context
+ * @param path - path of file relative to assets folder
+ * @return String
+ */
+ public static String readContents(Context context, String path) {
+ StringBuilder sb = new StringBuilder();
+ String str = "";
+ AssetManager assetManager = context.getAssets();
+ try {
+ InputStream inputStream = assetManager.open(path);
+ if (inputStream == null) {
+ KMLog.LogInfo(TAG, "Unable to read contents of asset: " + path);
+ return str;
+ }
+ BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
+ while ((str = reader.readLine()) != null) {
+ sb.append(str);
+ }
+ reader.close();
+ } catch (Exception e) {
+ KMLog.LogException(TAG, "Error reading asset file", e);
+ return str;
+ }
+ return sb.toString();
+ }
+
/**
* Utility to parse a URL and extract the filename
* @param urlStr String
diff --git a/oem/firstvoices/android/app/src/main/assets/banner.html b/oem/firstvoices/android/app/src/main/assets/banner.html
new file mode 100644
index 00000000000..b0c30219cbe
--- /dev/null
+++ b/oem/firstvoices/android/app/src/main/assets/banner.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/oem/firstvoices/android/app/src/main/assets/banner/red-logo.svg b/oem/firstvoices/android/app/src/main/assets/banner/red-logo.svg
new file mode 100644
index 00000000000..cf89f67db51
--- /dev/null
+++ b/oem/firstvoices/android/app/src/main/assets/banner/red-logo.svg
@@ -0,0 +1,38 @@
+
+
+
+
diff --git a/oem/firstvoices/android/app/src/main/java/com/firstvoices/android/BannerController.java b/oem/firstvoices/android/app/src/main/java/com/firstvoices/android/BannerController.java
new file mode 100644
index 00000000000..a0df15b2f1e
--- /dev/null
+++ b/oem/firstvoices/android/app/src/main/java/com/firstvoices/android/BannerController.java
@@ -0,0 +1,29 @@
+package com.firstvoices.android;
+
+import android.content.Context;
+
+import com.keyman.engine.KMManager;
+import com.keyman.engine.util.FileUtils;
+
+import java.io.File;
+
+public class BannerController {
+
+ // Paths relative to assets folder for banner themes
+ public static final String FV_BANNER_DIR = "banner";
+ public static final String FV_BANNER_THEME = "banner.html";
+
+ public static void setHTMLBanner(Context context, KMManager.KeyboardType keyboardType) {
+ if (keyboardType == KMManager.KeyboardType.KEYBOARD_TYPE_UNDEFINED) {
+ return;
+ }
+
+ KMManager.copyHTMLBannerAssets(context, FV_BANNER_DIR);
+
+ // Always use FirstVoices banner theme
+ String contents = FileUtils.readContents(context, FV_BANNER_THEME);
+ KMManager.setHTMLBanner(keyboardType, contents);
+ KMManager.setBanner(keyboardType, KMManager.BannerType.HTML);
+ KMManager.showBanner(true);
+ }
+}
diff --git a/oem/firstvoices/android/app/src/main/java/com/firstvoices/keyboards/SystemKeyboard.java b/oem/firstvoices/android/app/src/main/java/com/firstvoices/keyboards/SystemKeyboard.java
index 95d0c51b372..0fb770c9b51 100644
--- a/oem/firstvoices/android/app/src/main/java/com/firstvoices/keyboards/SystemKeyboard.java
+++ b/oem/firstvoices/android/app/src/main/java/com/firstvoices/keyboards/SystemKeyboard.java
@@ -20,6 +20,7 @@
import android.view.inputmethod.InputConnection;
import android.widget.FrameLayout;
+import com.firstvoices.android.BannerController;
import com.keyman.engine.KMManager;
import com.keyman.engine.KMManager.KeyboardType;
import com.keyman.engine.KMHardwareKeyboardInterpreter;
@@ -33,7 +34,6 @@
import io.sentry.Sentry;
public class SystemKeyboard extends InputMethodService implements OnKeyboardEventListener {
-
private View inputView = null;
private static ExtractedText exText = null;
private KMHardwareKeyboardInterpreter interpreter = null;
@@ -64,6 +64,9 @@ public void onCreate() {
interpreter = new KMHardwareKeyboardInterpreter(getApplicationContext(), KeyboardType.KEYBOARD_TYPE_SYSTEM);
KMManager.setInputMethodService(this); // for HW interface
+
+ // Set the system keyboard HTML banner
+ BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM);
}
@Override
@@ -79,7 +82,10 @@ public void onDestroy() {
* is called after creation and any configuration change. */
@Override
public void onInitializeInterface() {
- super.onInitializeInterface();
+ super.onInitializeInterface();
+
+ // KeymanWeb reloaded, so we have to pass the banner again
+ BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM);
}
/** Called by the framework when your view for creating input needs to