Merge branch 'master' into feat/core/9450-backspace-epic-ldml

This commit is contained in:
Steven R. Loomis 2023-11-21 15:01:51 -06:00 committed by GitHub
commit d82f64fadc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 954 additions and 125 deletions

View file

@ -1,5 +1,14 @@
# Keyman Version History
## 17.0.214 alpha 2023-11-20
* fix(ios): fv: replace Zip framework to prevent crash on startup (#10018)
## 17.0.213 alpha 2023-11-17
* fix(linux): Fix packaging GHA (#10020)
* fix(android): Always display HTML banner when suggestions aren't available (#9696)
## 17.0.212 alpha 2023-11-16
* chore(web): splits banner.ts into separate files per banner type (#9987)

View file

@ -1 +1 @@
17.0.213
17.0.215

View file

@ -0,0 +1,11 @@
<!-- Custom banner theme -->
<div style="background: white; width: 100%; height: 100%; position: absolute; left: 0; top: 0; display: flex">
<!-- svg path is relative to assets/ folder -->
<img src="banner/keyman_banner.svg" style="height:60%; background: white; display: block; margin: auto 2%;">
<!-- Tri-color line -->
<div style='height: 7%; left: 0; position: absolute; bottom: 0; width: 56%; background: #F68924'></div>
<div style='height: 7%; left: 56%; position: absolute; bottom: 0; width: 23%; background: #CC3846'></div>
<div style='height: 7%; left: 79%; position: absolute; bottom: 0; width: 21%; background: #79C3DA'></div>
</div>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 64 KiB

View file

@ -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);
}
}

View file

@ -6,6 +6,7 @@ package com.keyman.android;
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 class SystemKeyboard extends InputMethodService implements OnKeyboardEven
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 class SystemKeyboard extends InputMethodService implements OnKeyboardEven
@Override
public void onInitializeInterface() {
super.onInitializeInterface();
// KeymanWeb reloaded, so we have to pass the banner again
BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_SYSTEM);
}
/**

View file

@ -18,6 +18,7 @@ import java.util.List;
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 class MainActivity extends BaseActivity implements OnKeyboardEventListene
@Override
public void onKeyboardShown() {
// Refresh banner theme
BannerController.setHTMLBanner(this, KeyboardType.KEYBOARD_TYPE_INAPP);
resizeTextView(true);
}

View file

@ -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();
}

View file

@ -7,13 +7,11 @@ package com.keyman.engine;
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.FileUtils;
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.Gravity;
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.WebChromeClient;
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<OnKeyboardEventListener> 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 @@ final class KMKeyboard extends WebView {
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 @@ final class KMKeyboard extends WebView {
return currentKeyboard;
}
public static void setCurrentBanner(String banner) {
currentBanner = banner;
}
public static String currentBanner() { return currentBanner; }
protected void toggleSuggestionBanner(HashMap<String, String> 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 @@ final class KMKeyboard extends WebView {
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;
}

View file

@ -173,8 +173,7 @@ public final class KMKeyboardWebViewClient extends WebViewClient {
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) {

View file

@ -25,32 +25,21 @@ import android.content.SharedPreferences;
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.JSONUtils;
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 final class KMManager {
}
};
// 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 final class KMManager {
public static final String KMFilename_LexicalModelsList = "lexical_models_list.dat";
public static final String KMBLACK_BANNER = "<div style=\"background: black; width: 100%; height: 100%; position: absolute; left: 0; top: 0\"></div>";
public static final String KMGRAY_BANNER = "<div style=\"background: #b4b4b8; width: 100%; height: 100%; position: absolute; left: 0; top: 0\"></div>";
private static Context appContext;
public static String getResourceRoot() {
@ -636,6 +655,16 @@ public final class KMManager {
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 final class KMManager {
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 final class KMManager {
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 final class KMManager {
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 final class KMManager {
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;

View file

@ -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 final class FileUtils {
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

View file

@ -128,16 +128,9 @@ apply_action(
assert(context.back().character == ch);
context.pop_back();
} else {
assert(act.backspace.expected_type == KM_CORE_BT_UNKNOWN); // else it must be unknown
// pop to first character or empty
while (!context.empty() && context.back().type != KM_CORE_CT_CHAR) {
// pop off all non-character entries
context.pop_back();
}
if (!context.empty()) {
// pop off the next character
context.pop_back();
}
// assume it's otherwise KM-coRE_BT_UNKNOWN
assert(act.backspace.expected_type == KM_CORE_BT_UNKNOWN);
assert(context.empty()); // if KM_CORE_BT_UNKNOWN, context should be empty.
}
}
break;

View file

@ -1,4 +1,4 @@
github "marmelroy/Zip"
github "weichsel/ZIPFoundation" ~> 0.9
github "keymanapp/dependency-XCGLogger" "master"
github "devicekit/DeviceKit" ~> 5.0
github "ashleymills/Reachability.swift"

View file

@ -1,5 +1,5 @@
github "ashleymills/Reachability.swift" "v5.1.0"
github "devicekit/DeviceKit" "5.0.0"
github "getsentry/sentry-cocoa" "8.7.0"
github "devicekit/DeviceKit" "5.1.0"
github "getsentry/sentry-cocoa" "8.15.2"
github "keymanapp/dependency-XCGLogger" "57a7b975dbb6fe4fe90cef3d1bc52b8adbd89113"
github "marmelroy/Zip" "2.1.2"
github "weichsel/ZIPFoundation" "0.9.17"

View file

@ -25,6 +25,7 @@
1645D5952036C6FF0076C51B /* KeymanPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1645D5942036C6FF0076C51B /* KeymanPackage.swift */; };
1645D5972036C9F80076C51B /* KMPKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1645D5962036C9F80076C51B /* KMPKeyboard.swift */; };
165EB3A12098993900040A69 /* KeyboardError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165EB3A02098993900040A69 /* KeyboardError.swift */; };
296EF2C72AFA26C700E3E384 /* ZIPFoundation.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 296EF2C62AFA26C700E3E384 /* ZIPFoundation.xcframework */; };
377D10DE26846B8900467431 /* SpacebarTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377D10DD26846B8900467431 /* SpacebarTextViewController.swift */; };
6CD5DFAA150F6DC8007A5DDE /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 6CD5DFA8150F6DC8007A5DDE /* icon.png */; };
6CD5DFAB150F6DC8007A5DDE /* icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6CD5DFA9150F6DC8007A5DDE /* icon@2x.png */; };
@ -121,7 +122,6 @@
CE5C8BE324B5B3BA00FAFB7F /* Queries+LexicalModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE5C8BE224B5B3BA00FAFB7F /* Queries+LexicalModel.swift */; };
CE5C8BE524B5BD1C00FAFB7F /* QueryModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE5C8BE424B5BD1C00FAFB7F /* QueryModelTests.swift */; };
CE5EDDC526522EAA001733AC /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDC026522EA9001733AC /* XCGLogger.xcframework */; };
CE5EDDC626522EAA001733AC /* Zip.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDC126522EA9001733AC /* Zip.xcframework */; };
CE5EDDC726522EAA001733AC /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDC226522EAA001733AC /* Sentry.xcframework */; };
CE5EDDC826522EAA001733AC /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDC326522EAA001733AC /* Reachability.xcframework */; };
CE5EDDC926522EAA001733AC /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE5EDDC426522EAA001733AC /* ObjcExceptionBridging.xcframework */; };
@ -308,6 +308,7 @@
2949146F2738DA6700400732 /* ff-NG */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ff-NG"; path = "ff-NG.lproj/ResourceInfoView.strings"; sourceTree = "<group>"; };
294914702738DD7400400732 /* ff-NG */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ff-NG"; path = "ff-NG.lproj/Localizable.strings"; sourceTree = "<group>"; };
294914712738DD9700400732 /* ff-NG */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "ff-NG"; path = "ff-NG.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
296EF2C62AFA26C700E3E384 /* ZIPFoundation.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ZIPFoundation.xcframework; path = ../../Carthage/Build/ZIPFoundation.xcframework; sourceTree = "<group>"; };
297810FE297FAEDF007C886D /* kn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kn; path = kn.lproj/ResourceInfoView.strings; sourceTree = "<group>"; };
297810FF297FAEF8007C886D /* kn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kn; path = kn.lproj/Localizable.strings; sourceTree = "<group>"; };
29781100297FAF06007C886D /* kn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = kn; path = kn.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
@ -436,7 +437,6 @@
CE5C8BE424B5BD1C00FAFB7F /* QueryModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryModelTests.swift; sourceTree = "<group>"; };
CE5EDDBB26522EA3001733AC /* DeviceKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = DeviceKit.xcframework; path = ../../Carthage/Build/DeviceKit.xcframework; sourceTree = "<group>"; };
CE5EDDC026522EA9001733AC /* XCGLogger.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = XCGLogger.xcframework; path = ../../Carthage/Build/XCGLogger.xcframework; sourceTree = "<group>"; };
CE5EDDC126522EA9001733AC /* Zip.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Zip.xcframework; path = ../../Carthage/Build/Zip.xcframework; sourceTree = "<group>"; };
CE5EDDC226522EAA001733AC /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = ../../Carthage/Build/Sentry.xcframework; sourceTree = "<group>"; };
CE5EDDC326522EAA001733AC /* Reachability.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Reachability.xcframework; path = ../../Carthage/Build/Reachability.xcframework; sourceTree = "<group>"; };
CE5EDDC426522EAA001733AC /* ObjcExceptionBridging.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ObjcExceptionBridging.xcframework; path = ../../Carthage/Build/ObjcExceptionBridging.xcframework; sourceTree = "<group>"; };
@ -560,8 +560,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
296EF2C72AFA26C700E3E384 /* ZIPFoundation.xcframework in Frameworks */,
CE5EDDC526522EAA001733AC /* XCGLogger.xcframework in Frameworks */,
CE5EDDC626522EAA001733AC /* Zip.xcframework in Frameworks */,
CE5EDDD52652372A001733AC /* DeviceKit.xcframework in Frameworks */,
CE5EDDC726522EAA001733AC /* Sentry.xcframework in Frameworks */,
CE5EDDC826522EAA001733AC /* Reachability.xcframework in Frameworks */,
@ -949,11 +949,11 @@
F243888114BBD43000A3E055 /* Frameworks */ = {
isa = PBXGroup;
children = (
296EF2C62AFA26C700E3E384 /* ZIPFoundation.xcframework */,
CE5EDDC426522EAA001733AC /* ObjcExceptionBridging.xcframework */,
CE5EDDC326522EAA001733AC /* Reachability.xcframework */,
CE5EDDC226522EAA001733AC /* Sentry.xcframework */,
CE5EDDC026522EA9001733AC /* XCGLogger.xcframework */,
CE5EDDC126522EA9001733AC /* Zip.xcframework */,
CE5EDDBB26522EA3001733AC /* DeviceKit.xcframework */,
9A079DE52231A69D00581263 /* Foundation.framework */,
);

View file

@ -7,7 +7,7 @@
//
import Foundation
import Zip
import ZIPFoundation
// KMPErrors may be passed to UIAlertControllers, so they need localization.
public enum KMPError : String, Error {
@ -443,13 +443,28 @@ public class KeymanPackage {
@available(*, deprecated, message: "Use of the completion block is unnecessary; this method now returns synchronously.")
static public func extract(fileUrl: URL, destination: URL, complete: @escaping (KeymanPackage?) -> Void) throws {
try unzipFile(fileUrl: fileUrl, destination: destination) {
do {
let package = try KeymanPackage.parse(destination)
complete(package)
} catch {
SentryManager.captureAndLog(error, sentryLevel: .info)
complete(nil)
let fileManager = FileManager()
do {
try fileManager.unzipItem(at: fileUrl, to: destination)
let package = try KeymanPackage.parse(destination)
complete(package)
} catch {
SentryManager.captureAndLog(error, sentryLevel: .info)
complete(nil)
}
}
static public func clearDirectory(destination: URL) throws {
// First check to see if directory exists. If not, then do nothing.
var isDirectory: ObjCBool = false
if(FileManager.default.fileExists(atPath: destination.path, isDirectory: &isDirectory)){
if (isDirectory.boolValue) {
// it exists and is actually a directory, so remove every file it contains
let fileArray = try FileManager.default.contentsOfDirectory(atPath: destination.path)
try fileArray.forEach { file in
let fileUrl = destination.appendingPathComponent(file)
try FileManager.default.removeItem(atPath: fileUrl.path)
}
}
}
}
@ -460,7 +475,8 @@ public class KeymanPackage {
}
static public func unzipFile(fileUrl: URL, destination: URL, complete: @escaping () -> Void = {}) throws {
try Zip.unzipFile(fileUrl, destination: destination, overwrite: true, password: nil)
let fileManager = FileManager()
try fileManager.unzipItem(at: fileUrl, to: destination)
complete()
}

View file

@ -183,6 +183,9 @@ public class ResourceFileManager {
var extractionFolder = cacheDirectory
extractionFolder.appendPathComponent("temp/\(archiveUrl.lastPathComponent)")
// first clear extraction folder to avoid creating duplicates
try KeymanPackage.clearDirectory(destination: extractionFolder)
do {
if let package = try KeymanPackage.extract(fileUrl: archiveUrl, destination: extractionFolder) {
return package

View file

@ -29,14 +29,13 @@ class KeymanPackageTests: XCTestCase {
}
}
func testKeyboardPackageExtraction() throws {
func testKeyboardPackage_extractWithoutKmpExtension_succeeds() throws {
let cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let khmerPackageZip = cacheDirectory.appendingPathComponent("khmer_angkor.kmp.zip")
let khmerPackageZip = cacheDirectory.appendingPathComponent("khmer_angkor.kmp")
try ResourceFileManager.shared.copyWithOverwrite(from: TestUtils.Keyboards.khmerAngkorKMP, to: khmerPackageZip)
let destinationFolderURL = cacheDirectory.appendingPathComponent("khmer_angkor")
// Requires that the source file is already .zip, not .kmp. It's a ZipUtils limitation.
do {
if let kmp = try KeymanPackage.extract(fileUrl: khmerPackageZip, destination: destinationFolderURL) {
// Run assertions on the package's kmp.info.
@ -58,14 +57,104 @@ class KeymanPackageTests: XCTestCase {
}
}
func testKeyboardPackage_clearNonexistentDirectory_doesNothing() throws {
let cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let destinationDirectory = cacheDirectory.appendingPathComponent("doesnotexist")
do {
// clear directory
try KeymanPackage.clearDirectory(destination: destinationDirectory)
} catch {
XCTFail("error clearing the nonexistent directory \(error)")
}
}
func testKeyboardPackage_clearEmptyDirectory_throwsNoError() throws {
let cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let destinationDirectory = cacheDirectory.appendingPathComponent("destination")
do {
// create directory
try FileManager.default.createDirectory(
atPath: destinationDirectory.path,
withIntermediateDirectories: false,
attributes: nil
)
// clear directory
try KeymanPackage.clearDirectory(destination: destinationDirectory)
} catch {
XCTFail("error clearing the empty directory \(error)")
}
let fileArray = try FileManager.default.contentsOfDirectory(atPath: destinationDirectory.path)
XCTAssert(fileArray.count == 0, "directory still contains \(fileArray.count) items")
}
func testKeyboardPackage_clearNonEmptyDirectory_directoryIsEmpty() throws {
let cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let destinationDirectory = cacheDirectory.appendingPathComponent("destination")
do {
// create directory
try FileManager.default.createDirectory(
atPath: destinationDirectory.path,
withIntermediateDirectories: false,
attributes: nil
)
// add some files
FileManager.default.createFile(atPath: destinationDirectory.appendingPathComponent("fileone").path, contents: nil)
FileManager.default.createFile(atPath: destinationDirectory.appendingPathComponent("filetwo").path, contents: nil)
FileManager.default.createFile(atPath: destinationDirectory.appendingPathComponent("filethree").path, contents: nil)
// clear directory
try KeymanPackage.clearDirectory(destination: destinationDirectory)
} catch {
XCTFail("error clearing the empty directory \(error)")
}
let fileArray = try FileManager.default.contentsOfDirectory(atPath: destinationDirectory.path)
XCTAssert(fileArray.count == 0, "directory still contains \(fileArray.count) items")
}
func testKeyboardPackage_extractTwice_noDuplicateFileError() throws {
let cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let khmerPackageZip = cacheDirectory.appendingPathComponent("khmer_angkor.kmp")
try ResourceFileManager.shared.copyWithOverwrite(from: TestUtils.Keyboards.khmerAngkorKMP, to: khmerPackageZip)
let destinationFolderURL = cacheDirectory.appendingPathComponent("khmer_angkor")
do {
if let kmp = try KeymanPackage.extract(fileUrl: khmerPackageZip, destination: destinationFolderURL) {
log.info("*** first unzip of \(kmp.id)")
do {
// clear directory before second extract
try KeymanPackage.clearDirectory(destination: destinationFolderURL)
if let secondKmp = try KeymanPackage.extract(fileUrl: khmerPackageZip, destination: destinationFolderURL) {
log.info("*** second unzip of \(secondKmp.id)")
} else {
XCTAssert(false, "*** second unzip failed")
}
} catch {
XCTFail("unzip 2 failure with error \(error)")
}
} else {
XCTAssert(false, "*** first unzip failed")
}
} catch {
XCTFail("unzip 1 failure with error \(error)")
}
}
func testLexicalModelPackageExtraction() throws {
let cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let mtntZip = cacheDirectory.appendingPathComponent("nrc.en.mtnt.kmp.zip")
let mtntZip = cacheDirectory.appendingPathComponent("nrc.en.mtnt.kmp")
try ResourceFileManager.shared.copyWithOverwrite(from: TestUtils.LexicalModels.mtntKMP, to: mtntZip)
let destinationFolderURL = cacheDirectory.appendingPathComponent("nrc.en.mtnt.model")
// Requires that the source file is already .zip, not .kmp. It's a ZipUtils limitation.
do {
if let kmp = try KeymanPackage.extract(fileUrl: mtntZip, destination: destinationFolderURL) {
// Run assertions on the package's kmp.info.

View file

@ -8,7 +8,8 @@
import Foundation
import XCTest
import Zip
import ZIPFoundation
@testable import KeymanEngine
extension TestUtils {
@ -64,12 +65,17 @@ extension TestUtils {
try FileManager.default.moveItem(at: pListPath.appendingPathComponent(testEngineFilename), to: pListPath.appendingPathComponent(appGroupFilename))
}
let attachmentFile = try Zip.quickZipFiles([bundleConstructionURL], fileName: "bundleArchive")
log.info("Archive source: \(bundleConstructionURL)")
let attachment = XCTAttachment(contentsOfFile: attachmentFile)
attachment.lifetime = .keepAlways
return attachment
}
let archiveURL = bundleConstructionURL.appendingPathComponent("bundleArchive.zip")
do {
_ = try Archive(url: archiveURL, accessMode: .create)
log.info("archiveURL: \(archiveURL)")
let attachment = XCTAttachment(contentsOfFile: archiveURL)
attachment.lifetime = .keepAlways
return attachment
} catch let error {
print (error.localizedDescription)
return XCTAttachment(string: error.localizedDescription)
}
}
}
}

View file

@ -14,6 +14,8 @@
162E2C9F2092D36800F40769 /* UIDevice+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 162E2C9E2092D36800F40769 /* UIDevice+Extensions.swift */; };
165EB39A2097165B00040A69 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165EB3992097165B00040A69 /* UIColor+Extensions.swift */; };
165EB39C2097181D00040A69 /* KMView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 165EB39B2097181D00040A69 /* KMView.swift */; };
296EF2C42AFA267500E3E384 /* ZIPFoundation.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 296EF2C32AFA267500E3E384 /* ZIPFoundation.xcframework */; };
296EF2C52AFA267500E3E384 /* ZIPFoundation.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 296EF2C32AFA267500E3E384 /* ZIPFoundation.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
981AFA8F19EF44DE006706BF /* 724-info@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9815725518E4F0930014DF0C /* 724-info@2x.png */; };
981AFA9619EF44DE006706BF /* MainViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 98ABADCE176935E500B62590 /* MainViewController_iPhone.xib */; };
981AFA9819EF44DE006706BF /* 724-info.png in Resources */ = {isa = PBXBuildFile; fileRef = 9815725418E4F0930014DF0C /* 724-info.png */; };
@ -155,8 +157,6 @@
CEBD34422654FEB400EB2EA8 /* Sentry.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34382654FEB400EB2EA8 /* Sentry.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CEBD34432654FEB400EB2EA8 /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34392654FEB400EB2EA8 /* XCGLogger.xcframework */; };
CEBD34442654FEB400EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD34392654FEB400EB2EA8 /* XCGLogger.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CEBD34452654FEB400EB2EA8 /* Zip.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD343A2654FEB400EB2EA8 /* Zip.xcframework */; };
CEBD34462654FEB400EB2EA8 /* Zip.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD343A2654FEB400EB2EA8 /* Zip.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CEF4E55623E95B7B0065B9C7 /* ImageBanner.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEF4E55523E95B7B0065B9C7 /* ImageBanner.xib */; };
CEF4E55723E95B7B0065B9C7 /* ImageBanner.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEF4E55523E95B7B0065B9C7 /* ImageBanner.xib */; };
CEF4E55923E967140065B9C7 /* ImageBannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF4E55823E967140065B9C7 /* ImageBannerViewController.swift */; };
@ -183,11 +183,11 @@
files = (
CEBD34402654FEB400EB2EA8 /* Reachability.xcframework in Embed Frameworks */,
CEBD343E2654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */,
296EF2C52AFA267500E3E384 /* ZIPFoundation.xcframework in Embed Frameworks */,
CEBD343C2654FEB400EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */,
CEBD34422654FEB400EB2EA8 /* Sentry.xcframework in Embed Frameworks */,
CEBD34442654FEB400EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */,
CE80AD34257F2B4B008D2150 /* KeymanEngine.framework in Embed Frameworks */,
CEBD34462654FEB400EB2EA8 /* Zip.xcframework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
@ -205,6 +205,7 @@
16A9229D20325253003CC98E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Keyman/Info.plist; sourceTree = SOURCE_ROOT; };
293EA3E027059C6900545EED /* ha */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ha; path = ha.lproj/Localizable.strings; sourceTree = "<group>"; };
294914742738DF7700400732 /* ff-NG */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ff-NG"; path = "ff-NG.lproj/Localizable.strings"; sourceTree = "<group>"; };
296EF2C32AFA267500E3E384 /* ZIPFoundation.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ZIPFoundation.xcframework; path = ../../Carthage/Build/ZIPFoundation.xcframework; sourceTree = "<group>"; };
297810FD297FA818007C886D /* kn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kn; path = kn.lproj/Localizable.strings; sourceTree = "<group>"; };
298566B829802828004ACA95 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; };
298566C42980C493004ACA95 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
@ -344,7 +345,6 @@
CEBD34372654FEB400EB2EA8 /* Reachability.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Reachability.xcframework; path = ../../Carthage/Build/Reachability.xcframework; sourceTree = "<absolute>"; };
CEBD34382654FEB400EB2EA8 /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = ../../Carthage/Build/Sentry.xcframework; sourceTree = "<absolute>"; };
CEBD34392654FEB400EB2EA8 /* XCGLogger.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = XCGLogger.xcframework; path = ../../Carthage/Build/XCGLogger.xcframework; sourceTree = "<absolute>"; };
CEBD343A2654FEB400EB2EA8 /* Zip.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Zip.xcframework; path = ../../Carthage/Build/Zip.xcframework; sourceTree = "<absolute>"; };
CEBD3458265511B700EB2EA8 /* am */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = am; path = am.lproj/Localizable.strings; sourceTree = "<group>"; };
CEEC468226F2F7BA009A5B7D /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
CEEF81B92673019600EE6A07 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/Localizable.strings"; sourceTree = "<group>"; };
@ -359,11 +359,11 @@
files = (
CEBD343F2654FEB400EB2EA8 /* Reachability.xcframework in Frameworks */,
CEBD343D2654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */,
296EF2C42AFA267500E3E384 /* ZIPFoundation.xcframework in Frameworks */,
CEBD343B2654FEB400EB2EA8 /* DeviceKit.xcframework in Frameworks */,
CEBD34412654FEB400EB2EA8 /* Sentry.xcframework in Frameworks */,
CEBD34432654FEB400EB2EA8 /* XCGLogger.xcframework in Frameworks */,
CE80AD33257F2B4A008D2150 /* KeymanEngine.framework in Frameworks */,
CEBD34452654FEB400EB2EA8 /* Zip.xcframework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -544,12 +544,12 @@
98ABADB2176935E400B62590 /* Frameworks */ = {
isa = PBXGroup;
children = (
296EF2C32AFA267500E3E384 /* ZIPFoundation.xcframework */,
CEBD34352654FEB400EB2EA8 /* DeviceKit.xcframework */,
CEBD34362654FEB400EB2EA8 /* ObjcExceptionBridging.xcframework */,
CEBD34372654FEB400EB2EA8 /* Reachability.xcframework */,
CEBD34382654FEB400EB2EA8 /* Sentry.xcframework */,
CEBD34392654FEB400EB2EA8 /* XCGLogger.xcframework */,
CEBD343A2654FEB400EB2EA8 /* Zip.xcframework */,
CE80AD32257F2B4A008D2150 /* KeymanEngine.framework */,
);
name = Frameworks;

View file

@ -40,7 +40,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "NO">
shouldUseLaunchSchemeArgsEnv = "NO"
codeCoverageEnabled = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"

View file

@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
290FB45B2AFB234900249D58 /* ZIPFoundation.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 290FB45A2AFB234800249D58 /* ZIPFoundation.xcframework */; };
290FB45C2AFB234900249D58 /* ZIPFoundation.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 290FB45A2AFB234800249D58 /* ZIPFoundation.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
98ABFCEC1AD39165005534F0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 98ABFCEA1AD39165005534F0 /* Main.storyboard */; };
98ABFCEE1AD39165005534F0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 98ABFCED1AD39165005534F0 /* Images.xcassets */; };
98ABFCF11AD39165005534F0 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 98ABFCEF1AD39165005534F0 /* LaunchScreen.xib */; };
@ -24,8 +26,6 @@
CEBD33CD2654CB1500EB2EA8 /* Sentry.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BE2654CA5900EB2EA8 /* Sentry.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CEBD33CE2654CB1600EB2EA8 /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BD2654CA5900EB2EA8 /* XCGLogger.xcframework */; };
CEBD33CF2654CB1700EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BD2654CA5900EB2EA8 /* XCGLogger.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CEBD33D02654CB1900EB2EA8 /* Zip.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BB2654CA5900EB2EA8 /* Zip.xcframework */; };
CEBD33D12654CB1900EB2EA8 /* Zip.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33BB2654CA5900EB2EA8 /* Zip.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CED2AC85260AFADC00A408D2 /* ekwtamil99uni.kmp in Resources */ = {isa = PBXBuildFile; fileRef = CED2AC84260AFADC00A408D2 /* ekwtamil99uni.kmp */; };
/* End PBXBuildFile section */
@ -38,11 +38,11 @@
files = (
CEBD33CB2654CB1300EB2EA8 /* Reachability.xcframework in Embed Frameworks */,
CEBD33C92654CB1100EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */,
290FB45C2AFB234900249D58 /* ZIPFoundation.xcframework in Embed Frameworks */,
CEBD33C72654CB0F00EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */,
CEBD33CD2654CB1500EB2EA8 /* Sentry.xcframework in Embed Frameworks */,
CEBD33CF2654CB1700EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */,
CEBD337E26549EC100EB2EA8 /* KeymanEngine.xcframework in Embed Frameworks */,
CEBD33D12654CB1900EB2EA8 /* Zip.xcframework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
@ -50,6 +50,7 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
290FB45A2AFB234800249D58 /* ZIPFoundation.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ZIPFoundation.xcframework; path = ../../Carthage/Build/ZIPFoundation.xcframework; sourceTree = "<group>"; };
98ABFCDD1AD39165005534F0 /* KMSample1.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KMSample1.app; sourceTree = BUILT_PRODUCTS_DIR; };
98ABFCE11AD39165005534F0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
98ABFCEB1AD39165005534F0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
@ -59,7 +60,6 @@
C0324B831F87172900AF3785 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
CEBD337C26549EC100EB2EA8 /* KeymanEngine.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = KeymanEngine.xcframework; sourceTree = "<group>"; };
CEBD33B92654CA4B00EB2EA8 /* DeviceKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = DeviceKit.xcframework; path = ../../Carthage/Build/DeviceKit.xcframework; sourceTree = "<group>"; };
CEBD33BB2654CA5900EB2EA8 /* Zip.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Zip.xcframework; path = ../../Carthage/Build/Zip.xcframework; sourceTree = "<group>"; };
CEBD33BC2654CA5900EB2EA8 /* ObjcExceptionBridging.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ObjcExceptionBridging.xcframework; path = ../../Carthage/Build/ObjcExceptionBridging.xcframework; sourceTree = "<group>"; };
CEBD33BD2654CA5900EB2EA8 /* XCGLogger.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = XCGLogger.xcframework; path = ../../Carthage/Build/XCGLogger.xcframework; sourceTree = "<group>"; };
CEBD33BE2654CA5900EB2EA8 /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = ../../Carthage/Build/Sentry.xcframework; sourceTree = "<group>"; };
@ -74,11 +74,11 @@
files = (
CEBD33C82654CB1000EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */,
CEBD33CA2654CB1300EB2EA8 /* Reachability.xcframework in Frameworks */,
290FB45B2AFB234900249D58 /* ZIPFoundation.xcframework in Frameworks */,
CEBD33CC2654CB1400EB2EA8 /* Sentry.xcframework in Frameworks */,
CEBD33C62654CB0E00EB2EA8 /* DeviceKit.xcframework in Frameworks */,
CEBD337D26549EC100EB2EA8 /* KeymanEngine.xcframework in Frameworks */,
CEBD33CE2654CB1600EB2EA8 /* XCGLogger.xcframework in Frameworks */,
CEBD33D02654CB1900EB2EA8 /* Zip.xcframework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -127,11 +127,11 @@
98ABFD171AD391B0005534F0 /* Frameworks */ = {
isa = PBXGroup;
children = (
290FB45A2AFB234800249D58 /* ZIPFoundation.xcframework */,
CEBD33BC2654CA5900EB2EA8 /* ObjcExceptionBridging.xcframework */,
CEBD33BF2654CA5900EB2EA8 /* Reachability.xcframework */,
CEBD33BE2654CA5900EB2EA8 /* Sentry.xcframework */,
CEBD33BD2654CA5900EB2EA8 /* XCGLogger.xcframework */,
CEBD33BB2654CA5900EB2EA8 /* Zip.xcframework */,
CEBD33B92654CA4B00EB2EA8 /* DeviceKit.xcframework */,
CEBD337C26549EC100EB2EA8 /* KeymanEngine.xcframework */,
);

View file

@ -7,6 +7,9 @@
objects = {
/* Begin PBXBuildFile section */
290FB45E2AFB270D00249D58 /* ZIPFoundation.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 290FB45D2AFB270D00249D58 /* ZIPFoundation.xcframework */; };
290FB45F2AFB270D00249D58 /* ZIPFoundation.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 290FB45D2AFB270D00249D58 /* ZIPFoundation.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
290FB4602AFB271C00249D58 /* ZIPFoundation.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 290FB45D2AFB270D00249D58 /* ZIPFoundation.xcframework */; };
98ABFD461AD3A047005534F0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 98ABFD441AD3A047005534F0 /* Main.storyboard */; };
98ABFD481AD3A047005534F0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 98ABFD471AD3A047005534F0 /* Images.xcassets */; };
98ABFD4B1AD3A047005534F0 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 98ABFD491AD3A047005534F0 /* LaunchScreen.xib */; };
@ -18,8 +21,6 @@
CEBD33D42654CB6A00EB2EA8 /* KeymanEngine.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33D22654CB6A00EB2EA8 /* KeymanEngine.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CEBD33D82654CB7800EB2EA8 /* DeviceKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33D72654CB7800EB2EA8 /* DeviceKit.xcframework */; };
CEBD33D92654CB7800EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33D72654CB7800EB2EA8 /* DeviceKit.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CEBD33E02654CB8700EB2EA8 /* Zip.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DB2654CB8700EB2EA8 /* Zip.xcframework */; };
CEBD33E12654CB8700EB2EA8 /* Zip.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DB2654CB8700EB2EA8 /* Zip.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CEBD33E22654CB8700EB2EA8 /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DC2654CB8700EB2EA8 /* Reachability.xcframework */; };
CEBD33E32654CB8700EB2EA8 /* Reachability.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DC2654CB8700EB2EA8 /* Reachability.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CEBD33E42654CB8800EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DD2654CB8700EB2EA8 /* ObjcExceptionBridging.xcframework */; };
@ -38,7 +39,6 @@
CEDB327B265C9B33000A2009 /* Reachability.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DC2654CB8700EB2EA8 /* Reachability.xcframework */; };
CEDB327C265C9B33000A2009 /* Sentry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DE2654CB8700EB2EA8 /* Sentry.xcframework */; };
CEDB327D265C9B33000A2009 /* XCGLogger.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DF2654CB8700EB2EA8 /* XCGLogger.xcframework */; };
CEDB327E265C9B33000A2009 /* Zip.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEBD33DB2654CB8700EB2EA8 /* Zip.xcframework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -71,9 +71,9 @@
files = (
CEBD33E32654CB8700EB2EA8 /* Reachability.xcframework in Embed Frameworks */,
CEBD33E52654CB8800EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */,
290FB45F2AFB270D00249D58 /* ZIPFoundation.xcframework in Embed Frameworks */,
CEBD33E72654CB8800EB2EA8 /* Sentry.xcframework in Embed Frameworks */,
CEBD33D92654CB7800EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */,
CEBD33E12654CB8700EB2EA8 /* Zip.xcframework in Embed Frameworks */,
CEBD33D42654CB6A00EB2EA8 /* KeymanEngine.xcframework in Embed Frameworks */,
CEBD33E92654CB8800EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */,
);
@ -94,6 +94,7 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
290FB45D2AFB270D00249D58 /* ZIPFoundation.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ZIPFoundation.xcframework; path = ../../Carthage/Build/ZIPFoundation.xcframework; sourceTree = "<group>"; };
98ABFD371AD3A047005534F0 /* KMSample2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KMSample2.app; sourceTree = BUILT_PRODUCTS_DIR; };
98ABFD3B1AD3A047005534F0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
98ABFD451AD3A047005534F0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
@ -108,7 +109,6 @@
C045148C1F85E33300D88416 /* KeyboardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardViewController.swift; sourceTree = "<group>"; };
CEBD33D22654CB6A00EB2EA8 /* KeymanEngine.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = KeymanEngine.xcframework; sourceTree = "<group>"; };
CEBD33D72654CB7800EB2EA8 /* DeviceKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = DeviceKit.xcframework; path = ../../Carthage/Build/DeviceKit.xcframework; sourceTree = "<group>"; };
CEBD33DB2654CB8700EB2EA8 /* Zip.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Zip.xcframework; path = ../../Carthage/Build/Zip.xcframework; sourceTree = "<group>"; };
CEBD33DC2654CB8700EB2EA8 /* Reachability.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Reachability.xcframework; path = ../../Carthage/Build/Reachability.xcframework; sourceTree = "<group>"; };
CEBD33DD2654CB8700EB2EA8 /* ObjcExceptionBridging.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ObjcExceptionBridging.xcframework; path = ../../Carthage/Build/ObjcExceptionBridging.xcframework; sourceTree = "<group>"; };
CEBD33DE2654CB8700EB2EA8 /* Sentry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Sentry.xcframework; path = ../../Carthage/Build/Sentry.xcframework; sourceTree = "<group>"; };
@ -125,9 +125,9 @@
files = (
CEBD33E22654CB8700EB2EA8 /* Reachability.xcframework in Frameworks */,
CEBD33E42654CB8800EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */,
290FB45E2AFB270D00249D58 /* ZIPFoundation.xcframework in Frameworks */,
CEBD33E62654CB8800EB2EA8 /* Sentry.xcframework in Frameworks */,
CEBD33D82654CB7800EB2EA8 /* DeviceKit.xcframework in Frameworks */,
CEBD33E02654CB8700EB2EA8 /* Zip.xcframework in Frameworks */,
CEBD33E82654CB8800EB2EA8 /* XCGLogger.xcframework in Frameworks */,
CEBD33D32654CB6A00EB2EA8 /* KeymanEngine.xcframework in Frameworks */,
);
@ -139,9 +139,9 @@
files = (
CEDB327A265C9B33000A2009 /* ObjcExceptionBridging.xcframework in Frameworks */,
CEDB327B265C9B33000A2009 /* Reachability.xcframework in Frameworks */,
290FB4602AFB271C00249D58 /* ZIPFoundation.xcframework in Frameworks */,
CEDB327C265C9B33000A2009 /* Sentry.xcframework in Frameworks */,
CEDB327D265C9B33000A2009 /* XCGLogger.xcframework in Frameworks */,
CEDB327E265C9B33000A2009 /* Zip.xcframework in Frameworks */,
CEDB3279265C9B15000A2009 /* DeviceKit.xcframework in Frameworks */,
CEDB3276265C953A000A2009 /* KeymanEngine.xcframework in Frameworks */,
);
@ -195,11 +195,11 @@
98ABFD601AD3A087005534F0 /* Frameworks */ = {
isa = PBXGroup;
children = (
290FB45D2AFB270D00249D58 /* ZIPFoundation.xcframework */,
CEBD33DD2654CB8700EB2EA8 /* ObjcExceptionBridging.xcframework */,
CEBD33DC2654CB8700EB2EA8 /* Reachability.xcframework */,
CEBD33DE2654CB8700EB2EA8 /* Sentry.xcframework */,
CEBD33DF2654CB8700EB2EA8 /* XCGLogger.xcframework */,
CEBD33DB2654CB8700EB2EA8 /* Zip.xcframework */,
CEBD33D72654CB7800EB2EA8 /* DeviceKit.xcframework */,
CEBD33D22654CB6A00EB2EA8 /* KeymanEngine.xcframework */,
);

View file

@ -0,0 +1,10 @@
<!-- Custom banner theme -->
<div style="background: #aa1225; width: 100%; height: 100%; position: absolute; left: 0; top: 0; display: flex">
<!-- svg path is relative to assets/ folder -->
<img src="banner/red-logo.svg" style="height:60%; background: #aa1225; display: block; margin: auto 2%;">
<!-- Gold Band -->
<div style="height: 7%; left: 0; position: absolute; bottom: 0; width: 100%; background: #a77a24"></div>
<!-- teal #224f65 red #b40000 -->
</div>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 96 KiB

View file

@ -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);
}
}

View file

@ -20,6 +20,7 @@ import android.view.inputmethod.ExtractedTextRequest;
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.android.core.SentryAndroid;
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 class SystemKeyboard extends InputMethodService implements OnKeyboardEven
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 class SystemKeyboard extends InputMethodService implements OnKeyboardEven
* 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

View file

@ -1,3 +1,4 @@
github "weichsel/ZIPFoundation" ~> 0.9
github "keymanapp/dependency-XCGLogger" "master"
github "devicekit/DeviceKit" ~> 5.0
github "ashleymills/Reachability.swift"

View file

@ -1,4 +1,5 @@
github "ashleymills/Reachability.swift" "v5.1.0"
github "devicekit/DeviceKit" "5.0.0"
github "getsentry/sentry-cocoa" "8.7.0"
github "devicekit/DeviceKit" "5.1.0"
github "getsentry/sentry-cocoa" "8.15.2"
github "keymanapp/dependency-XCGLogger" "57a7b975dbb6fe4fe90cef3d1bc52b8adbd89113"
github "weichsel/ZIPFoundation" "0.9.17"

View file

@ -7,6 +7,9 @@
objects = {
/* Begin PBXBuildFile section */
290FB4632AFB358F00249D58 /* ZIPFoundation.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 290FB4622AFB358E00249D58 /* ZIPFoundation.xcframework */; };
290FB4642AFB358F00249D58 /* ZIPFoundation.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 290FB4622AFB358E00249D58 /* ZIPFoundation.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
290FB4652AFB359A00249D58 /* ZIPFoundation.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 290FB4622AFB358E00249D58 /* ZIPFoundation.xcframework */; };
294D40E9279FE62600DB37F6 /* KeyboardRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294D40E8279FE62600DB37F6 /* KeyboardRepository.swift */; };
294D40EC27A110BC00DB37F6 /* KeyboardSettingsRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294D40EB27A110BC00DB37F6 /* KeyboardSettingsRepository.swift */; };
29694DC227A27EC300EA6C18 /* LexicalModelRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29694DC127A27EC300EA6C18 /* LexicalModelRepository.swift */; };
@ -74,6 +77,7 @@
files = (
CEBD34202654D76900EB2EA8 /* Reachability.xcframework in Embed Frameworks */,
CEBD341E2654D76800EB2EA8 /* ObjcExceptionBridging.xcframework in Embed Frameworks */,
290FB4642AFB358F00249D58 /* ZIPFoundation.xcframework in Embed Frameworks */,
CEBD341B2654D76600EB2EA8 /* DeviceKit.xcframework in Embed Frameworks */,
CEBD34232654D76B00EB2EA8 /* Sentry.xcframework in Embed Frameworks */,
CEBD34262654D76C00EB2EA8 /* XCGLogger.xcframework in Embed Frameworks */,
@ -96,6 +100,7 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
290FB4622AFB358E00249D58 /* ZIPFoundation.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ZIPFoundation.xcframework; path = Carthage/Build/ZIPFoundation.xcframework; sourceTree = "<group>"; };
294D40E8279FE62600DB37F6 /* KeyboardRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardRepository.swift; sourceTree = "<group>"; };
294D40EB27A110BC00DB37F6 /* KeyboardSettingsRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardSettingsRepository.swift; sourceTree = "<group>"; };
29694DC127A27EC300EA6C18 /* LexicalModelRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModelRepository.swift; sourceTree = "<group>"; };
@ -153,6 +158,7 @@
files = (
CEBD341D2654D76800EB2EA8 /* ObjcExceptionBridging.xcframework in Frameworks */,
CEBD341F2654D76900EB2EA8 /* Reachability.xcframework in Frameworks */,
290FB4632AFB358F00249D58 /* ZIPFoundation.xcframework in Frameworks */,
CEBD34222654D76B00EB2EA8 /* Sentry.xcframework in Frameworks */,
CEBD341A2654D76600EB2EA8 /* DeviceKit.xcframework in Frameworks */,
CEBD34022654D41200EB2EA8 /* KeymanEngine.xcframework in Frameworks */,
@ -166,6 +172,7 @@
files = (
CEDB327F265C9C58000A2009 /* DeviceKit.xcframework in Frameworks */,
CEDB3280265C9C58000A2009 /* ObjcExceptionBridging.xcframework in Frameworks */,
290FB4652AFB359A00249D58 /* ZIPFoundation.xcframework in Frameworks */,
CEDB3281265C9C58000A2009 /* Reachability.xcframework in Frameworks */,
CEDB3282265C9C58000A2009 /* Sentry.xcframework in Frameworks */,
CEDB3283265C9C58000A2009 /* XCGLogger.xcframework in Frameworks */,
@ -284,6 +291,7 @@
98C9A9FB1BFC19ED009E9A4F /* Frameworks */ = {
isa = PBXGroup;
children = (
290FB4622AFB358E00249D58 /* ZIPFoundation.xcframework */,
CEBD34082654D5AA00EB2EA8 /* ObjcExceptionBridging.xcframework */,
CEBD340C2654D5AB00EB2EA8 /* Reachability.xcframework */,
CEBD34092654D5AA00EB2EA8 /* Sentry.xcframework */,