mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-24 08:37:42 +00:00
Merge branch 'master' into feat/core/9450-backspace-epic-ldml
This commit is contained in:
commit
d82f64fadc
31 changed files with 954 additions and 125 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
17.0.213
|
||||
17.0.215
|
||||
11
android/KMAPro/kMAPro/src/main/assets/banner.html
Normal file
11
android/KMAPro/kMAPro/src/main/assets/banner.html
Normal 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>
|
||||
372
android/KMAPro/kMAPro/src/main/assets/banner/keyman_banner.svg
Normal file
372
android/KMAPro/kMAPro/src/main/assets/banner/keyman_banner.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 64 KiB |
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 */,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 */,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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 */,
|
||||
);
|
||||
|
|
|
|||
10
oem/firstvoices/android/app/src/main/assets/banner.html
Normal file
10
oem/firstvoices/android/app/src/main/assets/banner.html
Normal 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 |
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
github "weichsel/ZIPFoundation" ~> 0.9
|
||||
github "keymanapp/dependency-XCGLogger" "master"
|
||||
github "devicekit/DeviceKit" ~> 5.0
|
||||
github "ashleymills/Reachability.swift"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 */,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue