mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-21 15:17:40 +00:00
chore(ios): Merge branch 'master' into ios-dark-mode-cntd
This commit is contained in:
commit
e91ec28ae6
26 changed files with 432 additions and 93 deletions
|
|
@ -33,7 +33,7 @@ public class KMKeyboardDownloaderActivity extends AppCompatActivity {
|
|||
public static final String ARG_MODEL_ID = "KMKeyboardActivity.modelID";
|
||||
public static final String ARG_MODEL_NAME = "KMKeyboardActivity.modelName";
|
||||
public static final String ARG_MODEL_URL = "KMKeyboardActivity.modelURL";
|
||||
public static final String ARG_MODEL_CUSTOM_HELP_LINK = "KMKeyboardActivity.customHelpLink";
|
||||
public static final String ARG_CUSTOM_HELP_LINK = "KMKeyboardActivity.customHelpLink";
|
||||
|
||||
// custom keyboard
|
||||
public static final String ARG_KEYBOARD = "KMKeyboardActivity.keyboard";
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
package com.tavultesoft.kmea;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
|
|
@ -16,7 +15,6 @@ import android.graphics.Bitmap;
|
|||
import android.graphics.Typeface;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import androidx.core.content.FileProvider;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
|
@ -30,8 +28,8 @@ import android.widget.SimpleAdapter;
|
|||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.tavultesoft.kmea.util.FileUtils;
|
||||
import com.tavultesoft.kmea.util.FileProviderUtils;
|
||||
import com.tavultesoft.kmea.util.HelpFile;
|
||||
import com.tavultesoft.kmea.util.MapCompat;
|
||||
import com.tavultesoft.kmea.util.QRCodeUtil;
|
||||
|
||||
|
|
@ -52,7 +50,6 @@ public final class KeyboardInfoActivity extends AppCompatActivity {
|
|||
super.onCreate(savedInstanceState);
|
||||
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
final Context context = this;
|
||||
final String authority = FileProviderUtils.getAuthority(context);
|
||||
|
||||
setContentView(R.layout.activity_list_layout);
|
||||
toolbar = (Toolbar) findViewById(R.id.list_toolbar);
|
||||
|
|
@ -126,30 +123,15 @@ public final class KeyboardInfoActivity extends AppCompatActivity {
|
|||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (position == 1) {
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
|
||||
if (customHelpLink != null) {
|
||||
if (FileUtils.isWelcomeFile(customHelpLink) && ! KMManager.isTestMode()) {
|
||||
File customHelp = new File(new File(customHelpLink).getAbsolutePath());
|
||||
i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
// Starting with Android N, you can't pass file:// to intents, so we use FileProvider
|
||||
try {
|
||||
Uri contentUri = FileProvider.getUriForFile(
|
||||
context, authority, customHelp);
|
||||
i.setDataAndType(contentUri, "text/html");
|
||||
} catch (NullPointerException e) {
|
||||
String message = "FileProvider undefined in app to load" + customHelp.toString();
|
||||
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
|
||||
Log.e("KeyboardInfoActivity", message);
|
||||
}
|
||||
}
|
||||
else {
|
||||
i.setData(Uri.parse(customHelpLink));
|
||||
}
|
||||
// Display local welcome.htm help file, including associated assets
|
||||
Intent i = HelpFile.toActionView(context, customHelpLink, packageID);
|
||||
|
||||
if (FileProviderUtils.exists(context)|| KMManager.isTestMode()) {
|
||||
startActivity(i);
|
||||
}
|
||||
} else {
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.setData(Uri.parse(helpUrlStr));
|
||||
startActivity(i);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import android.widget.Toast;
|
|||
|
||||
import com.tavultesoft.kmea.util.FileUtils;
|
||||
import com.tavultesoft.kmea.util.FileProviderUtils;
|
||||
import com.tavultesoft.kmea.util.HelpFile;
|
||||
import com.tavultesoft.kmea.util.MapCompat;
|
||||
import com.tavultesoft.kmea.util.QRCodeUtil;
|
||||
|
||||
|
|
@ -140,33 +141,19 @@ public final class KeyboardSettingsActivity extends AppCompatActivity {
|
|||
|
||||
// "Help" link clicked
|
||||
if (itemTitle.equals(getString(R.string.help_link))) {
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
|
||||
if (customHelpLink != null) {
|
||||
if (FileUtils.isWelcomeFile(customHelpLink)) {
|
||||
File customHelp = new File(new File(customHelpLink).getAbsolutePath());
|
||||
i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
// Starting with Android N, you can't pass file:// to intents, so we use FileProvider
|
||||
try {
|
||||
Uri contentUri = FileProvider.getUriForFile(
|
||||
context, authority, customHelp);
|
||||
i.setDataAndType(contentUri, "text/html");
|
||||
} catch (NullPointerException e) {
|
||||
String message = "FileProvider undefined in app to load" + customHelp.toString();
|
||||
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
|
||||
Log.e("TAG", message);
|
||||
}
|
||||
}
|
||||
else {
|
||||
i.setData(Uri.parse(customHelpLink));
|
||||
}
|
||||
if (FileProviderUtils.exists(context)) {
|
||||
// Display local welcome.htm help file, including associated assets
|
||||
Intent i = HelpFile.toActionView(context, customHelpLink, packageID);
|
||||
|
||||
if (FileProviderUtils.exists(context) || KMManager.isTestMode()) {
|
||||
startActivity(i);
|
||||
}
|
||||
} else {
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.setData(Uri.parse(helpUrlStr));
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
// "Uninstall Keyboard" clicked
|
||||
} else if (itemTitle.equals(getString(R.string.uninstall_keyboard))) {
|
||||
// Uninstall selected keyboard
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import android.widget.Toast;
|
|||
|
||||
import com.tavultesoft.kmea.util.FileUtils;
|
||||
import com.tavultesoft.kmea.util.FileProviderUtils;
|
||||
import com.tavultesoft.kmea.util.HelpFile;
|
||||
import com.tavultesoft.kmea.util.MapCompat;
|
||||
|
||||
import static com.tavultesoft.kmea.ConfirmDialogFragment.DialogType.DIALOG_TYPE_DELETE_MODEL;
|
||||
|
|
@ -81,7 +82,7 @@ public final class ModelInfoActivity extends AppCompatActivity {
|
|||
// Currently, model help only available if custom link exists
|
||||
String icon = String.valueOf(R.drawable.ic_arrow_forward);
|
||||
// Don't show help link arrow if both custom help and File Provider don't exist
|
||||
// TODO: Update this when model help available on help.keyman.com
|
||||
// TODO: Update this when model help available on help.keyman.com
|
||||
if ( (!customHelpLink.equals("") && !FileProviderUtils.exists(context)) ||
|
||||
customHelpLink.equals("") ){
|
||||
icon = noIcon;
|
||||
|
|
@ -128,32 +129,17 @@ public final class ModelInfoActivity extends AppCompatActivity {
|
|||
|
||||
// "Help" link clicked
|
||||
if (itemTitle.equals(getString(R.string.help_link))) {
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
|
||||
if (!customHelpLink.equals("")) {
|
||||
if (FileUtils.isWelcomeFile(customHelpLink)) {
|
||||
File customHelp = new File(new File(customHelpLink).getAbsolutePath());
|
||||
i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
// Starting with Android N, you can't pass file:// to intents, so we use FileProvider
|
||||
try {
|
||||
Uri contentUri = FileProvider.getUriForFile(
|
||||
context, authority, customHelp);
|
||||
i.setDataAndType(contentUri, "text/html");
|
||||
} catch (NullPointerException e) {
|
||||
String message = "FileProvider undefined in app to load" + customHelp.toString();
|
||||
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
|
||||
Log.e("ModelInfoActivity", message);
|
||||
}
|
||||
}
|
||||
else {
|
||||
i.setData(Uri.parse(customHelpLink));
|
||||
}
|
||||
if (FileProviderUtils.exists(context)) {
|
||||
// Display local welcome.htm help file, including associated assets
|
||||
Intent i = HelpFile.toActionView(context, customHelpLink, packageID);
|
||||
|
||||
if (FileProviderUtils.exists(context) || KMManager.isTestMode()) {
|
||||
startActivity(i);
|
||||
}
|
||||
} else {
|
||||
// We should always have a help file packaged with models.
|
||||
}
|
||||
|
||||
// "Uninstall Model" clicked
|
||||
} else if (itemTitle.equals(getString(R.string.uninstall_model))) {
|
||||
// Uninstall selected model
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public class CloudDataJsonUtil {
|
|||
|
||||
public static HashMap<String,String> createKeyboardInfoMap(String aPackageId,String aLanguageId, String aLanguageName, String aKeyboardId,
|
||||
String aKeyboardName, String aKeyboardVersion, String anIsCustomKeyboard,
|
||||
String aFont, String aOskFont)
|
||||
String aFont, String aOskFont, String aCustomHelpLink)
|
||||
{
|
||||
HashMap<String, String> keyboardInfo = new HashMap<String, String>();
|
||||
keyboardInfo.put(KMManager.KMKey_PackageID, aPackageId);
|
||||
|
|
@ -48,8 +48,12 @@ public class CloudDataJsonUtil {
|
|||
keyboardInfo.put(KMManager.KMKey_KeyboardVersion, aKeyboardVersion);
|
||||
keyboardInfo.put(KMManager.KMKey_CustomKeyboard, anIsCustomKeyboard);
|
||||
keyboardInfo.put(KMManager.KMKey_Font, aFont);
|
||||
if (aOskFont != null)
|
||||
if (aOskFont != null) {
|
||||
keyboardInfo.put(KMManager.KMKey_OskFont, aOskFont);
|
||||
}
|
||||
if (aCustomHelpLink != null) {
|
||||
keyboardInfo.put(KMManager.KMKey_CustomHelpLink, aCustomHelpLink);
|
||||
}
|
||||
|
||||
return keyboardInfo;
|
||||
}
|
||||
|
|
@ -81,9 +85,10 @@ public class CloudDataJsonUtil {
|
|||
String kbName = keyboardJSON.getString(KMManager.KMKey_Name);
|
||||
String kbVersion = keyboardJSON.optString(KMManager.KMKey_KeyboardVersion, "1.0");
|
||||
String kbFont = keyboardJSON.optString(KMManager.KMKey_Font, "");
|
||||
String customHelpLink = keyboardJSON.optString(KMManager.KMKey_CustomHelpLink, null);
|
||||
|
||||
//String kbKey = String.format("%s_%s", langID, kbID);
|
||||
HashMap<String, String> hashMap = createKeyboardInfoMap(pkgID,langID,langName,kbID,kbName,kbVersion,isCustom,kbFont,null);
|
||||
HashMap<String, String> hashMap = createKeyboardInfoMap(pkgID,langID,langName,kbID,kbName,kbVersion,isCustom,kbFont,null, customHelpLink);
|
||||
|
||||
|
||||
// if (keyboardModifiedDates.get(kbID) == null) {
|
||||
|
|
|
|||
|
|
@ -271,12 +271,13 @@ public class CloudKeyboardMetaDataDownloadCallback implements ICloudDownloadCall
|
|||
|
||||
String _font = _keyboard.optString(KMManager.KMKey_Font);
|
||||
String _oskFont = _keyboard.optString(KMManager.KMKey_OskFont);
|
||||
String _customHelpLink = _keyboard.optString(KMManager.KMKey_CustomHelpLink, null);
|
||||
|
||||
|
||||
theKbData.additionalDownloadid = CloudKeyboardDataDownloadCallback.createDownloadId(_key_id);
|
||||
theKbData.keyboardInfo = CloudDataJsonUtil
|
||||
.createKeyboardInfoMap(
|
||||
_pkgID, _lang_id, _langName, _key_id, _kbName, _kbVersion, _kbIsCustom, _font, _oskFont);
|
||||
_pkgID, _lang_id, _langName, _key_id, _kbName, _kbVersion, _kbIsCustom, _font, _oskFont, _customHelpLink);
|
||||
theKbData.additionalDownloads = urls;
|
||||
}
|
||||
catch(JSONException _e)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import android.os.Bundle;
|
|||
import com.tavultesoft.kmea.KMKeyboardDownloaderActivity;
|
||||
import com.tavultesoft.kmea.KMManager;
|
||||
import com.tavultesoft.kmea.KeyboardPickerActivity;
|
||||
import com.tavultesoft.kmea.util.MapCompat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
|
@ -50,6 +51,13 @@ public class Keyboard implements Serializable, LanguageResource {
|
|||
return this.map.get(KMManager.KMKey_KeyboardName);
|
||||
}
|
||||
|
||||
public String getCustomHelpLink() {
|
||||
if (this.map.containsKey(KMManager.KMKey_CustomHelpLink)) {
|
||||
return this.map.get(KMManager.KMKey_CustomHelpLink);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return this.map.get(KMManager.KMKey_KeyboardVersion);
|
||||
}
|
||||
|
|
@ -60,6 +68,7 @@ public class Keyboard implements Serializable, LanguageResource {
|
|||
|
||||
public Bundle buildDownloadBundle() {
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
bundle.putString(KMKeyboardDownloaderActivity.ARG_PKG_ID, getPackage());
|
||||
bundle.putString(KMKeyboardDownloaderActivity.ARG_KB_ID, getResourceId());
|
||||
bundle.putString(KMKeyboardDownloaderActivity.ARG_LANG_ID, getLanguageCode());
|
||||
|
|
@ -70,9 +79,13 @@ public class Keyboard implements Serializable, LanguageResource {
|
|||
if(isCustom == null) {
|
||||
isCustom = "N";
|
||||
}
|
||||
|
||||
bundle.putBoolean(KMKeyboardDownloaderActivity.ARG_IS_CUSTOM, isCustom.equals("Y"));
|
||||
|
||||
String customHelpLink = map.get(KMManager.KMKey_CustomHelpLink);
|
||||
if (customHelpLink != null) {
|
||||
bundle.putString(KMKeyboardDownloaderActivity.ARG_CUSTOM_HELP_LINK, getCustomHelpLink());
|
||||
}
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ public interface LanguageResource {
|
|||
String getLanguageName();
|
||||
String getVersion();
|
||||
String getPackage();
|
||||
String getCustomHelpLink();
|
||||
|
||||
Bundle buildDownloadBundle();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.os.Bundle;
|
|||
|
||||
import com.tavultesoft.kmea.KMKeyboardDownloaderActivity;
|
||||
import com.tavultesoft.kmea.KMManager;
|
||||
import com.tavultesoft.kmea.util.MapCompat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
|
@ -52,6 +53,13 @@ public class LexicalModel implements Serializable, LanguageResource {
|
|||
return this.map.get(KMManager.KMKey_PackageID);
|
||||
}
|
||||
|
||||
public String getCustomHelpLink() {
|
||||
if (this.map.containsKey(KMManager.KMKey_CustomHelpLink)) {
|
||||
return this.map.get(KMManager.KMKey_CustomHelpLink);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Bundle buildDownloadBundle() {
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
|
|
@ -64,8 +72,6 @@ public class LexicalModel implements Serializable, LanguageResource {
|
|||
return null;
|
||||
}
|
||||
|
||||
String customHelpLink = map.get(KMManager.KMKey_CustomHelpLink);
|
||||
|
||||
bundle.putString(KMKeyboardDownloaderActivity.ARG_PKG_ID, getPackage());
|
||||
bundle.putString(KMKeyboardDownloaderActivity.ARG_MODEL_ID, getResourceId());
|
||||
bundle.putString(KMKeyboardDownloaderActivity.ARG_LANG_ID, getLanguageCode());
|
||||
|
|
@ -73,7 +79,11 @@ public class LexicalModel implements Serializable, LanguageResource {
|
|||
bundle.putString(KMKeyboardDownloaderActivity.ARG_LANG_NAME, getLanguageName());
|
||||
bundle.putBoolean(KMKeyboardDownloaderActivity.ARG_IS_CUSTOM, false);
|
||||
bundle.putString(KMKeyboardDownloaderActivity.ARG_MODEL_URL, modelURL);
|
||||
bundle.putString(KMKeyboardDownloaderActivity.ARG_MODEL_CUSTOM_HELP_LINK, customHelpLink);
|
||||
|
||||
String customHelpLink = map.get(KMManager.KMKey_CustomHelpLink);
|
||||
if (customHelpLink != null) {
|
||||
bundle.putString(KMKeyboardDownloaderActivity.ARG_CUSTOM_HELP_LINK, customHelpLink);
|
||||
}
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
package com.tavultesoft.kmea.util;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipDescription;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import com.tavultesoft.kmea.KMManager;
|
||||
import com.tavultesoft.kmea.util.FileProviderUtils;
|
||||
import com.tavultesoft.kmea.util.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
|
||||
public final class HelpFile {
|
||||
private static final String TAG = "HelpFile";
|
||||
private static final String[] ASSET_MIME_TYPES = {
|
||||
ClipDescription.MIMETYPE_TEXT_HTML,
|
||||
"text/css",
|
||||
"image/gif",
|
||||
"image/jpeg",
|
||||
"image/png"};
|
||||
|
||||
/**
|
||||
* Utility to pass a help file and all associated assets to an Intent for Intent.ACTION_VIEW
|
||||
* @param context
|
||||
* @param helpFile Full path string of the html file to view
|
||||
* @param packageID String of the package ID
|
||||
* @return Intent
|
||||
*/
|
||||
public static Intent toActionView(Context context, String helpFile, String packageID) {
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
|
||||
if (FileUtils.isWelcomeFile(helpFile) && ! KMManager.isTestMode()) {
|
||||
File customHelp = new File(new File(helpFile).getAbsolutePath());
|
||||
i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
// Starting with Android N, you can't pass file:// to intents, so we use FileProvider
|
||||
try {
|
||||
final String authority = FileProviderUtils.getAuthority(context);
|
||||
Uri contentUri = FileProvider.getUriForFile(
|
||||
context, authority, customHelp);
|
||||
i.setDataAndType(contentUri, "text/html");
|
||||
|
||||
// Grant read permission to all the files in the package so embedded assets can be viewed
|
||||
ClipData clipData = new ClipData(null, ASSET_MIME_TYPES, new ClipData.Item(contentUri));
|
||||
|
||||
// Exclude html help files and JS files. Treat rest of the files as assets
|
||||
FileFilter _fileFilter = new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
String name = pathname.getName();
|
||||
if (pathname.isFile() && (FileUtils.isReadmeFile(name) ||
|
||||
FileUtils.isWelcomeFile(name) || FileUtils.hasJavaScriptExtension(name))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
String base = helpFile.contains("packages") ? "packages" : "models";
|
||||
File packageDir = new File(
|
||||
context.getDir("data", Context.MODE_PRIVATE), base + File.separator + packageID + File.separator);
|
||||
File[] files = packageDir.listFiles(_fileFilter);
|
||||
for(File assetFile : files) {
|
||||
Uri assetUri = FileProvider.getUriForFile(
|
||||
context, authority, assetFile);
|
||||
clipData.addItem(new ClipData.Item(assetUri));
|
||||
}
|
||||
|
||||
// Associate assets in clipData to the intent
|
||||
i.setClipData(clipData);
|
||||
} catch (NullPointerException e) {
|
||||
String message = "FileProvider undefined in app to load" + customHelp.toString();
|
||||
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
|
||||
Log.e(TAG, message);
|
||||
}
|
||||
} else {
|
||||
i.setData(Uri.parse(helpFile));
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ public class CloudDataJsonUtilTest {
|
|||
private final String customKeyboard = "N";
|
||||
private final String aFont = "AndikaAfr";
|
||||
private final String oskFont = aFont;
|
||||
private final String customHelpLink = "";
|
||||
|
||||
@Before
|
||||
public void initializeTestPackage() {
|
||||
|
|
@ -40,7 +41,7 @@ public class CloudDataJsonUtilTest {
|
|||
public void shouldLowercaseLanguageID() {
|
||||
// Test createKeyboardInfoMap() used by processKeyboardJSON()
|
||||
HashMap<String, String> kbInfo = CloudDataJsonUtil.createKeyboardInfoMap(
|
||||
pkgID, langID, langName, keyboardID, keyboardName, keyboardVersion, customKeyboard, aFont, oskFont);
|
||||
pkgID, langID, langName, keyboardID, keyboardName, keyboardVersion, customKeyboard, aFont, oskFont, customHelpLink);
|
||||
Assert.assertEquals(langID.toLowerCase(), kbInfo.get(KMManager.KMKey_LanguageID));
|
||||
|
||||
// Test processLexicalModelJSON()
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
* Fix memory issues during build process (#2361)
|
||||
* Fix crashes when parsing JSON data from Cloud (#2393)
|
||||
* Improve compatibility with applications such as Gmail, Chrome that do not conform to the Android input APIs (#2382, #2376)
|
||||
* Propagate custom help links (#2448)
|
||||
* Fix file permissions for viewing welcome.htm assets (#2465)
|
||||
|
||||
## 2019-12-12 12.0.4214 stable
|
||||
* Bug fix:
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
|
||||
public static let shared = Manager()
|
||||
|
||||
public var fileBrowserLauncher: ((UINavigationController) -> Void)? = nil
|
||||
|
||||
/// Display the help bubble on first use.
|
||||
public var isKeymanHelpOn = true
|
||||
|
||||
|
|
@ -976,9 +978,12 @@ public class Manager: NSObject, UIGestureRecognizerDelegate {
|
|||
|
||||
// Keyboard download notification observers
|
||||
private func keyboardDownloadCompleted(_ keyboards: [InstallableKeyboard]) {
|
||||
// TODO: Only do this if it's an update. We'll need a bit of notification retooling for this first.
|
||||
// There's little harm in reloading the keyboard (and thus, KMW) for a clean reset
|
||||
// after resource downloads or updates. That said, we should avoid *directly*
|
||||
// triggering an immediate reset, as an extra reset will occur once we leave the
|
||||
// settings menu. The delay also helps any chained downloads (keyboard > lexical model)
|
||||
// to fully complete first.
|
||||
shouldReloadKeyboard = true
|
||||
inputViewController.reload()
|
||||
}
|
||||
|
||||
/*-----------------------------
|
||||
|
|
|
|||
|
|
@ -581,4 +581,8 @@ public class ResourceDownloadManager {
|
|||
|
||||
return updateQueue
|
||||
}
|
||||
|
||||
public func installLexicalModelPackage(at packageURL: URL) -> InstallableLexicalModel? {
|
||||
return downloader.installLexicalModelPackage(downloadedPackageFile: packageURL)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,12 @@ public class ResourceFileManager {
|
|||
var destinationUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
||||
destinationUrl.appendPathComponent(url.lastPathComponent)
|
||||
|
||||
// Since it's possible to request an install from a KMP in our owned document space,
|
||||
// we need to check that it's not already in place where we want it.
|
||||
if url == destinationUrl {
|
||||
return url
|
||||
}
|
||||
|
||||
do {
|
||||
try copyWithOverwrite(from: url, to: destinationUrl)
|
||||
return destinationUrl
|
||||
|
|
@ -103,7 +109,9 @@ public class ResourceFileManager {
|
|||
})
|
||||
}
|
||||
|
||||
public func promptPackageInstall(of package: KeymanPackage, in rootVC: UIViewController) {
|
||||
public func promptPackageInstall(of package: KeymanPackage,
|
||||
in rootVC: UIViewController,
|
||||
successHandler: ((KeymanPackage) -> Void)? = nil) {
|
||||
let vc = PackageInstallViewController(for: package, completionHandler: { error in
|
||||
if let err = error {
|
||||
if let kmpError = err as? KMPError {
|
||||
|
|
@ -111,7 +119,9 @@ public class ResourceFileManager {
|
|||
rootVC.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
} else {
|
||||
let alert = self.buildSimpleAlert(title: "Success", message: "Installed successfully.")
|
||||
let alert = self.buildSimpleAlert(title: "Success", message: "Installed successfully.", completionHandler: {
|
||||
successHandler?(package)
|
||||
})
|
||||
rootVC.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
})
|
||||
|
|
@ -124,12 +134,14 @@ public class ResourceFileManager {
|
|||
return buildSimpleAlert(title: "Error", message: error.rawValue)
|
||||
}
|
||||
|
||||
public func buildSimpleAlert(title: String, message: String) -> UIAlertController {
|
||||
public func buildSimpleAlert(title: String, message: String, completionHandler: (() -> Void)? = nil ) -> UIAlertController {
|
||||
let alertController = UIAlertController(title: title, message: message,
|
||||
preferredStyle: UIAlertController.Style.alert)
|
||||
alertController.addAction(UIAlertAction(title: "OK",
|
||||
style: UIAlertAction.Style.default,
|
||||
handler: nil))
|
||||
handler: { _ in
|
||||
completionHandler?()
|
||||
}))
|
||||
|
||||
//UIApplication.shared.keyWindow?.rootViewController?.present(alertController, animated: true, completion: nil)
|
||||
return alertController
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ open class SettingsViewController: UITableViewController {
|
|||
}
|
||||
|
||||
@objc func doneClicked(_ sender: Any) {
|
||||
// While the called method might should be renamed, it does the job well enough.
|
||||
// This resets KMW so that any new and/or updated resources can be properly loaded.
|
||||
Manager.shared.dismissKeyboardPicker(self)
|
||||
}
|
||||
|
||||
|
|
@ -69,6 +71,16 @@ open class SettingsViewController: UITableViewController {
|
|||
"subtitle": "",
|
||||
"reuseid" : "showgetstarted"
|
||||
])
|
||||
|
||||
// The iOS Files app is only available with 11.0+.
|
||||
if #available(iOS 11.0, *) {
|
||||
itemsArray.append([
|
||||
"title": "Install From File",
|
||||
"subtitle": "Browse for .kmp files",
|
||||
"reuseid" : "installfile"
|
||||
])
|
||||
}
|
||||
|
||||
_ = view
|
||||
}
|
||||
|
||||
|
|
@ -84,8 +96,7 @@ open class SettingsViewController: UITableViewController {
|
|||
}
|
||||
|
||||
override open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
// #warning Incomplete implementation, return the number of rows
|
||||
return 3
|
||||
return itemsArray.count
|
||||
}
|
||||
|
||||
public func frameAtRightOfCell(cell cellFrame: CGRect, controlSize: CGSize) -> CGRect {
|
||||
|
|
@ -148,6 +159,8 @@ open class SettingsViewController: UITableViewController {
|
|||
showAgainSwitch.rightAnchor.constraint(equalTo: cell.layoutMarginsGuide.rightAnchor).isActive = true
|
||||
showAgainSwitch.centerYAnchor.constraint(equalTo: cell.layoutMarginsGuide.centerYAnchor).isActive = true
|
||||
}
|
||||
case "installfile":
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
default:
|
||||
log.error("unknown cellIdentifier(\"\(cellIdentifier ?? "EMPTY")\")")
|
||||
cell.accessoryType = .none
|
||||
|
|
@ -187,6 +200,8 @@ open class SettingsViewController: UITableViewController {
|
|||
|
||||
if indexPath.row == 0 {
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
} else if indexPath.row == 3 {
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
} else {
|
||||
cell.textLabel?.isEnabled = true
|
||||
cell.detailTextLabel?.isEnabled = false
|
||||
|
|
@ -207,7 +222,18 @@ open class SettingsViewController: UITableViewController {
|
|||
private func performAction(for indexPath: IndexPath) {
|
||||
switch indexPath.section {
|
||||
case 0:
|
||||
showLanguages()
|
||||
switch indexPath.row {
|
||||
case 0:
|
||||
showLanguages()
|
||||
case 3:
|
||||
if let block = Manager.shared.fileBrowserLauncher {
|
||||
block(navigationController!)
|
||||
} else {
|
||||
log.info("Listener for framework signal to launch file browser is missing")
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
## 13.0 alpha
|
||||
* Start version 13.0
|
||||
* Adds file browsing for installable KMPs and makes KMPs for resources installed this way available to the Files app (#2457)
|
||||
* Testing for upcoming patch to stable:
|
||||
* Fixes for deprecated code, improving maintainability (#2282)
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@
|
|||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportsDocumentBrowser</key>
|
||||
<false/>
|
||||
<true/>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@
|
|||
CE6138021FB999C8009D0EF2 /* KeymanEngine.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE6138031FB999C8009D0EF2 /* KeymanEngine.framework */; };
|
||||
CE79CDB52370111200010C06 /* Themes+Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CE79CDB42370111200010C06 /* Themes+Colors.xcassets */; };
|
||||
CE7C1AE2236925D800100C2C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CE7C1AE1236925D800100C2C /* LaunchScreen.storyboard */; };
|
||||
CE7FF1F0239A0293007859D9 /* PackageBrowserViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7FF1EF239A0293007859D9 /* PackageBrowserViewController.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
|
|
@ -300,6 +301,7 @@
|
|||
CE6138031FB999C8009D0EF2 /* KeymanEngine.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = KeymanEngine.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
CE79CDB42370111200010C06 /* Themes+Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Themes+Colors.xcassets"; sourceTree = "<group>"; };
|
||||
CE7C1AE1236925D800100C2C /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
CE7FF1EF239A0293007859D9 /* PackageBrowserViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PackageBrowserViewController.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
|
@ -524,6 +526,7 @@
|
|||
9845A7C71A439A9200544E2E /* SetUpViewController */,
|
||||
98ABADBA176935E400B62590 /* Supporting Files */,
|
||||
C0FF769D1F5D4ECB00BD23C3 /* ActivityItemProvider.swift */,
|
||||
CE7FF1EF239A0293007859D9 /* PackageBrowserViewController.swift */,
|
||||
C0FF769C1F5D4ECA00BD23C3 /* Keyman-Bridging-Header.h */,
|
||||
C0E943F71F61234C00E7D98C /* UIImage+Helpers.swift */,
|
||||
C0E943F91F6124E100E7D98C /* AppDelegate.swift */,
|
||||
|
|
@ -928,6 +931,7 @@
|
|||
C055F0B11F60E8D400140735 /* GetStartedViewController.swift in Sources */,
|
||||
C055F0B51F610FB200140735 /* DropDownListView.swift in Sources */,
|
||||
C059FCC01FD927EF00BD1A64 /* Log.swift in Sources */,
|
||||
CE7FF1F0239A0293007859D9 /* PackageBrowserViewController.swift in Sources */,
|
||||
C0E943F81F61234C00E7D98C /* UIImage+Helpers.swift in Sources */,
|
||||
C0E943FE1F61377900E7D98C /* MainViewController.swift in Sources */,
|
||||
162E2C9920926C8600F40769 /* UIView+Extensions.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -122,6 +122,15 @@ class MainViewController: UIViewController, TextViewDelegate, UIActionSheetDeleg
|
|||
observer: self,
|
||||
function: MainViewController.keyboardRemoved)
|
||||
|
||||
// Unfortunately, it's the main app with the file definitions.
|
||||
// We have to gerry-rig this so that the framework-based SettingsViewController
|
||||
// can launch the app-based DocumentViewController.
|
||||
if #available(iOS 11.0, *) {
|
||||
Manager.shared.fileBrowserLauncher = { navController in
|
||||
let vc = PackageBrowserViewController()
|
||||
navController.pushViewController(vc, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
|
|
|
|||
78
ios/keyman/Keyman/Keyman/PackageBrowserViewController.swift
Normal file
78
ios/keyman/Keyman/Keyman/PackageBrowserViewController.swift
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// PackageBrowserViewController.swift
|
||||
// Keyman
|
||||
//
|
||||
// Created by Joshua Horton on 12/6/19.
|
||||
// Copyright © 2019 SIL International. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import KeymanEngine
|
||||
|
||||
@available(iOS 11.0, *)
|
||||
class PackageBrowserViewController: UIDocumentBrowserViewController, UIDocumentBrowserViewControllerDelegate {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
delegate = self
|
||||
|
||||
allowsDocumentCreation = false
|
||||
allowsPickingMultipleItems = false
|
||||
|
||||
// Update the style of the UIDocumentBrowserViewController
|
||||
// browserUserInterfaceStyle = .dark
|
||||
// view.tintColor = .white
|
||||
|
||||
// Specify the allowed content types of your application via the Info.plist.
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
// MARK: UIDocumentBrowserViewControllerDelegate
|
||||
|
||||
func documentBrowser(_ controller: UIDocumentBrowserViewController, didPickDocumentsAt documentURLs: [URL]) {
|
||||
guard let sourceURL = documentURLs.first else { return }
|
||||
|
||||
// Present the Document View Controller for the first document that was picked.
|
||||
// If you support picking multiple items, make sure you handle them all.
|
||||
doInstall(of: sourceURL)
|
||||
}
|
||||
|
||||
func documentBrowser(_ controller: UIDocumentBrowserViewController,
|
||||
didImportDocumentAt sourceURL: URL,
|
||||
toDestinationURL destinationURL: URL) {
|
||||
// Present the Document View Controller for the new newly created document
|
||||
doInstall(of: destinationURL)
|
||||
}
|
||||
|
||||
func documentBrowser(_ controller: UIDocumentBrowserViewController,
|
||||
failedToImportDocumentAt documentURL: URL,
|
||||
error: Error?) {
|
||||
// Make sure to handle the failed import appropriately, e.g., by presenting an error message to the user.
|
||||
}
|
||||
|
||||
// MARK: Document Presentation
|
||||
|
||||
func doInstall(of url: URL) {
|
||||
// Once selected, start the standard install process.
|
||||
let rfm = ResourceFileManager.shared
|
||||
|
||||
guard let destinationUrl = rfm.importFile(url) else {
|
||||
return
|
||||
}
|
||||
|
||||
rfm.prepareKMPInstall(from: destinationUrl,
|
||||
alertHost: self,
|
||||
completionHandler: { package in
|
||||
// We choose to prompt the user for comfirmation, rather
|
||||
// than automatically installing the package.
|
||||
rfm.promptPackageInstall(of: package, in: self, successHandler: { _ in
|
||||
// Auto-dismiss the document browser upon successful KMP install.
|
||||
// It's likely quite rare that someone would want to install 2+ at once.
|
||||
self.navigationController?.popViewController(animated: true)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
// Includes version-related functionality
|
||||
///<reference path="utils/version.ts"/>
|
||||
///<reference path="utils/styleConstants.ts" />
|
||||
|
||||
// The Device object definition -------------------------------------------------
|
||||
|
||||
|
|
@ -13,6 +14,10 @@ namespace com.keyman {
|
|||
version: string;
|
||||
orientation: string|number;
|
||||
browser: string;
|
||||
colorScheme: 'light' | 'dark';
|
||||
|
||||
private detected: boolean = false;
|
||||
private _styles: utils.StyleConstants;
|
||||
|
||||
// Generates a default Device value.
|
||||
constructor() {
|
||||
|
|
@ -178,6 +183,9 @@ namespace com.keyman {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.colorScheme = this.prefersDarkMode() ? 'dark' : 'light';
|
||||
this.detected = true;
|
||||
}
|
||||
|
||||
static _GetIEVersion() {
|
||||
|
|
@ -219,5 +227,27 @@ namespace com.keyman {
|
|||
|
||||
return 999;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks is a user's browser is in dark mode, if the feature is supported. Returns false otherwise.
|
||||
*
|
||||
* Thanks to https://stackoverflow.com/a/57795518 for this code.
|
||||
*/
|
||||
private prefersDarkMode(): boolean {
|
||||
// Ensure the detector exists (otherwise, returns false)
|
||||
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
|
||||
public get styles(): utils.StyleConstants {
|
||||
if(!this._styles) {
|
||||
if(!this.detected) {
|
||||
this.detect();
|
||||
}
|
||||
|
||||
this._styles = new utils.StyleConstants(this);
|
||||
}
|
||||
|
||||
return this._styles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1631,6 +1631,10 @@ namespace com.keyman {
|
|||
// Display the OSK (again) if enabled, in order to set its position correctly after
|
||||
// adding the UI to the page
|
||||
this.keyman.osk._Show();
|
||||
} else if(this.keyman.isEmbedded) {
|
||||
// UI modules aren't utilized in embedded mode. There's nothing to init, so we simply
|
||||
// return instead of waiting for a UI module that will never come.
|
||||
return;
|
||||
} else {
|
||||
window.setTimeout(this.initializeUI.bind(this),1000);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,7 +204,8 @@ namespace com.keyman.osk {
|
|||
* @param {number} edge -1 left edge, 1 right edge, else 0
|
||||
*/
|
||||
VisualKeyboard.prototype.drawPreview = function(this: VisualKeyboard, canvas: HTMLCanvasElement, w: number, h: number, edge: number) {
|
||||
let device = com.keyman.singleton.util.device;
|
||||
let util = com.keyman.singleton.util;
|
||||
let device = util.device;
|
||||
|
||||
var ctx = canvas.getContext('2d'), dx = (canvas.width - w)/2, hMax = canvas.height,
|
||||
w0 = 0, w1 = dx, w2 = w + dx, w3 = w + 2 * dx,
|
||||
|
|
@ -232,11 +233,9 @@ namespace com.keyman.osk {
|
|||
// Define appearance of preview (cannot be done directly in CSS)
|
||||
if(device.OS == 'Android') {
|
||||
var wx=(w1+w2)/2;
|
||||
w1 = w2 = wx;
|
||||
ctx.fillStyle = '#999';
|
||||
} else {
|
||||
ctx.fillStyle = '#ffffff';
|
||||
}
|
||||
w1 = w2 = wx;
|
||||
}
|
||||
ctx.fillStyle = device.styles.popupCanvasBackgroundColor;
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = '#cccccc';
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,17 @@
|
|||
.phone.ios .kmw-key.kmw-key-special-on {color:#000;background-color:#fdfdfe;}
|
||||
.phone.ios .kmw-key.kmw-key-touched {background-color:#88f;}
|
||||
|
||||
/* Probably best to make this its own CSS that can be optionally included? */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.phone.ios .kmw-key-layer-group {background-color: #0f1319}
|
||||
.phone.ios .kmw-key.kmw-key-default {color:#fff;background-color:#3d3d3e}
|
||||
.phone.ios .kmw-key.kmw-key-shift,
|
||||
.phone.ios .kmw-key.kmw-key-special {color:#fff;background-color:#595c62;}
|
||||
.phone.ios .kmw-key.kmw-key-shift-on,
|
||||
.phone.ios .kmw-key.kmw-key-special-on {color:#fff;background-color:#3d3d3e;}
|
||||
.phone.ios .kmw-key.kmw-key-touched {background-color:#447;}
|
||||
}
|
||||
|
||||
.ios .kmw-banner-bar {
|
||||
background-color: #cfd3d9;
|
||||
position: absolute;
|
||||
|
|
@ -90,7 +101,28 @@
|
|||
.phone.ios.kmw-osk-frame,
|
||||
.tablet.ios.kmw-osk-frame {
|
||||
background-color: #cfd3d9;
|
||||
}
|
||||
}
|
||||
|
||||
/* Probably best to make this its own CSS that can be optionally included? */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.ios .kmw-banner-bar {
|
||||
background-color: #0f1319;
|
||||
}
|
||||
|
||||
.ios .kmw-banner-bar .kmw-suggest-option {
|
||||
background-color: #3d3d3e;
|
||||
border-bottom: solid 1px #8a8d90;
|
||||
}
|
||||
|
||||
.ios .kmw-suggestion-text {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.phone.ios.kmw-osk-frame,
|
||||
.tablet.ios.kmw-osk-frame {
|
||||
background-color: #0f1319;
|
||||
}
|
||||
}
|
||||
|
||||
.phone.android .kmw-key-layer-group {background-color: #333;}
|
||||
.phone.android .kmw-key {border: none; border-bottom: solid 1px #8a8d90; box-shadow:none; border-radius: 3px;}
|
||||
|
|
@ -155,6 +187,17 @@
|
|||
|
||||
.tablet.ios .kmw-suggest-option.kmw-suggest-touched {background-color:#88f;}
|
||||
|
||||
/* Probably best to make this its own CSS that can be optionally included? */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.tablet.ios .kmw-key-layer-group {background-color: #0f1319}
|
||||
.tablet.ios .kmw-key.kmw-key-default {color:#fff;background-color:#3d3d3e}
|
||||
.tablet.ios .kmw-key.kmw-key-shift,
|
||||
.tablet.ios .kmw-key.kmw-key-special {color:#fff;background-color:#595c62;}
|
||||
.tablet.ios .kmw-key.kmw-key-shift-on,
|
||||
.tablet.ios .kmw-key.kmw-key-special-on {color:#fff;background-color:#3d3d3e;}
|
||||
.tablet.ios .kmw-key.kmw-key-touched {background-color:#447;}
|
||||
}
|
||||
|
||||
.tablet .kmw-key-row {-webkit-touch-callout:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:rgba(0,0,0,0);}
|
||||
|
||||
.tablet.android .kmw-key-layer-group {background-color: #b4b4b8; border-top: 1px solid #666;}
|
||||
|
|
@ -268,19 +311,31 @@ body div.kmw-key-shift-on span.kmw-key-text {font-family:SpecialOSK !important;f
|
|||
#kmw-popup-callout {
|
||||
position:fixed; display:block; background-color: #fdfdfe;
|
||||
border-radius: 0 0 6px 6px; z-index:10001; pointer-events:none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Key preview styles */
|
||||
div.ios div.kmw-keytip {
|
||||
position:fixed; left:0; top:0; width:3em; height:3em; background-color:rgb(0,0,0,0);overflow:visible;
|
||||
}
|
||||
|
||||
/* Dark mode - ensure text is colored appropriately for key tips. */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
div.ios div.kmw-keytip {
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
/* Style for callout used on phones */
|
||||
#kmw-popup-callout {
|
||||
background-color: #0f1319;
|
||||
}
|
||||
}
|
||||
|
||||
div.android div.kmw-keytip {
|
||||
position:fixed; left:0; top:0; width:3em; height:3em;
|
||||
color:#fff;
|
||||
background-color: rgb(0,0,0,0);
|
||||
overflow:visible;
|
||||
}
|
||||
}
|
||||
|
||||
div.android #keytip {background-color:#f00;}
|
||||
|
||||
|
|
@ -337,6 +392,12 @@ div.android #keytip {background-color:#f00;}
|
|||
.phone.android #kmw-popup-keys {border:none; border-radius: 2px; background-color:#ccc; padding:5px 5px 0 0;}
|
||||
.tablet.android #kmw-popup-keys {border:1px solid #eee; border-radius: 3px; background-color:#888; padding:8px 12px 4px 4px;}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
#kmw-popup-keys {
|
||||
background-color: #0f1319;
|
||||
}
|
||||
}
|
||||
|
||||
/* Filter (shim) to darken screen and highlight popup keys */
|
||||
#kmw-popup-shim {
|
||||
position: fixed; width: 100%; height: 100%; bottom: 0; left: 0;
|
||||
|
|
@ -358,6 +419,11 @@ div.android #keytip {background-color:#f00;}
|
|||
|
||||
.kmw-spacebar-caption{font:0.6em Arial !important;color:rgba(0,0,0,0.15);}
|
||||
|
||||
/* Probably best to make this its own CSS that can be optionally included? */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.kmw-spacebar-caption{color:rgba(255, 255, 255, 0.15);}
|
||||
}
|
||||
|
||||
/* Static styles */
|
||||
|
||||
.desktop-static.kmw-osk-inner-frame{margin:0;background-color:#666;border:2px solid #ad4a28;box-sizing:border-box;-moz-box-sizing:border-box;}
|
||||
|
|
|
|||
24
web/source/utils/styleConstants.ts
Normal file
24
web/source/utils/styleConstants.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// Includes Device definitions, which may play a role in constant logic.
|
||||
///<reference path="../kmwdevice.ts" />
|
||||
|
||||
/*
|
||||
* This file is intended for CSS-styling constants that see use with the OSK.
|
||||
*/
|
||||
|
||||
namespace com.keyman.utils {
|
||||
/**
|
||||
* Defines device-level constants used for CSS styling.
|
||||
*/
|
||||
export class StyleConstants {
|
||||
constructor(device: Device) {
|
||||
// popupCanvasBackgroundColor
|
||||
if(device.OS == 'Android') {
|
||||
this.popupCanvasBackgroundColor = '#999';
|
||||
} else {
|
||||
this.popupCanvasBackgroundColor = device.colorScheme == 'dark' ? '#0f1319' : '#ffffff';
|
||||
}
|
||||
}
|
||||
|
||||
public readonly popupCanvasBackgroundColor: string;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue