chore: Merge branch 'feat/developer/ldml-compiler-kmx-builder' in

This commit is contained in:
Marc Durdin 2022-08-25 08:42:23 +10:00
commit e84bd590b0
24 changed files with 247 additions and 134 deletions

View file

@ -1,5 +1,9 @@
# Keyman Version History
## 16.0.50 alpha 2022-08-23
* chore(core): Remove obsolete python keyboardprocessor (#7094)
## 16.0.49 alpha 2022-08-22
* fix: remove saving and restoring context kbd options (#7077)

View file

@ -1 +1 @@
16.0.50
16.0.51

View file

@ -18,6 +18,7 @@ import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.tavultesoft.kmea.BaseActivity;
@ -113,7 +114,11 @@ public class KMPBrowserActivity extends BaseActivity {
// All links that aren't internal Keyman keyboard links open in user's browser
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
Toast.makeText(context, getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show();
}
return true;
}
if (lowerURL.startsWith("keyman:")) {

View file

@ -26,6 +26,7 @@ import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;
import com.tavultesoft.kmea.ConfirmDialogFragment;
import com.tavultesoft.kmea.KMHelpFileActivity;
@ -160,7 +161,11 @@ public final class KeyboardSettingsActivity extends AppCompatActivity {
Bundle args = kbd.buildDownloadBundle();
Intent i = new Intent(getApplicationContext(), KMKeyboardDownloaderActivity.class);
i.putExtras(args);
startActivity(i);
if (i.resolveActivity(getPackageManager()) != null) {
startActivity(i);
} else {
Toast.makeText(getApplicationContext(), getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show();
}
finish();
// "Help" link clicked

View file

@ -762,10 +762,24 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
// Launch PlayStore to update Chrome
try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.android.chrome"));
startActivity(intent);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=com.android.chrome"));
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
Toast.makeText(getApplicationContext(), getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show();
}
}
} catch (android.content.ActivityNotFoundException e) {
// Link to Chrome if user is not signed in to Play Store
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=com.android.chrome")));
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=com.android.chrome"));
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
Toast.makeText(getApplicationContext(), getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show();
}
}
}
});

View file

@ -247,4 +247,7 @@
<!-- Context: KMP Package strings -->
<string name="minimum_keyboard_version_not_supported" comment="Notification when keyboard has functionality not supported by current Keyman">
Keyboard requires a newer version of Keyman</string>
<!-- Context: anywhere -->
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">Unable to launch web browser</string>
</resources>

View file

@ -136,7 +136,11 @@ public final class KeyboardInfoActivity extends BaseActivity {
} else {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(customHelpLink));
startActivity(i);
if (i.resolveActivity(getPackageManager()) != null) {
startActivity(i);
} else {
Toast.makeText(context, getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show();
}
}
}
}

View file

@ -3,10 +3,10 @@
<!-- Device type -->
<string name="device_type" translatable="false">AndroidMobile</string>
<!-- Application name (Keyman Engine for Android) -->
<string name="app_name" translatable="false">KMEA</string>
<!-- Context: Title for list -->
<plurals name="title_keyboards">
<item quantity="one">Keyboard</item>
@ -267,4 +267,6 @@
<!-- Context: Other strings -->
<string name="help_bubble_text">Tap here to change keyboard</string>
<!-- Context: anywhere -->
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">Unable to launch web browser</string>
</resources>

View file

@ -1,5 +1,7 @@
#pragma once
#include <stdint.h>
/*
#if defined(_WIN32) || defined(_WIN64)
#define snprintf _snprintf

View file

@ -14,27 +14,28 @@
//
#pragma once
#define LDML_KEYS_FLAGS_EXTEND 1
#define LDML_LENGTH_HEADER 8
#define LDML_LENGTH_KEYS 16
#define LDML_LENGTH_LOCA 12
#define LDML_LENGTH_META 36
#define LDML_LENGTH_SECT 16
#define LDML_LENGTH_STRS 16
#define LDML_LENGTH_VKEY 12
#define LDML_META_SETTINGS_FALLBACK_OMIT 1
#define LDML_META_SETTINGS_TRANSFORMFAILURE_OMIT 2
#define LDML_META_SETTINGS_TRANSFORMPARTIAL_HIDE 4
// Section ID
#define LDML_SECTION_KEYS ((uint32_t)'keys')
// Section ID
#define LDML_SECTION_LOCA ((uint32_t)'loca')
// Section ID
#define LDML_SECTION_META ((uint32_t)'meta')
// Section ID
#define LDML_SECTION_SECT ((uint32_t)'sect')
// Section ID
#define LDML_SECTION_STRS ((uint32_t)'strs')
// Section ID
#define LDML_SECTION_VKEY ((uint32_t)'vkey')
#define LDML_KEYS_FLAGS_EXTEND 0x1
#define LDML_LENGTH_HEADER 0x8
#define LDML_LENGTH_KEYS 0x10
#define LDML_LENGTH_LOCA 0xC
#define LDML_LENGTH_META 0x24
#define LDML_LENGTH_SECT 0x10
#define LDML_LENGTH_STRS 0x10
#define LDML_LENGTH_VKEY 0xC
#define LDML_META_SETTINGS_FALLBACK_OMIT 0x1
#define LDML_META_SETTINGS_TRANSFORMFAILURE_OMIT 0x2
#define LDML_META_SETTINGS_TRANSFORMPARTIAL_HIDE 0x4
#define LDML_SECTIONID_KEYS 0x7379656B /* "keys" */
#define LDML_SECTIONNAME_KEYS "keys"
#define LDML_SECTIONID_LOCA 0x61636F6C /* "loca" */
#define LDML_SECTIONNAME_LOCA "loca"
#define LDML_SECTIONID_META 0x6174656D /* "meta" */
#define LDML_SECTIONNAME_META "meta"
#define LDML_SECTIONID_SECT 0x74636573 /* "sect" */
#define LDML_SECTIONNAME_SECT "sect"
#define LDML_SECTIONID_STRS 0x73727473 /* "strs" */
#define LDML_SECTIONNAME_STRS "strs"
#define LDML_SECTIONID_VKEY 0x79656B76 /* "vkey" */
#define LDML_SECTIONNAME_VKEY "vkey"
#define LDML_VERSION "1.0"

View file

@ -19,7 +19,10 @@
// TODO-LDML: namespace com.keyman.core.ldml {
/**
* Constants for the KMXPlus data format
* These are shared between the data access layer and the compiler
* These are shared between the data access layer and the compiler.
* Note that the section IDs (section_keys etc.) are 32 bit hex
* values that are designed to appear as text when written in little endian
* format, so 0x7379656b = 'keys'
*/
export const constants = {
/**
@ -30,28 +33,16 @@ export const constants = {
* Length of a raw section header, in bytes
*/
length_header: 8,
/**
* Section ID for the keybag
*/
section_keys: 'keys',
/**
* Minimum length of the 'keys' section
* not including variable parts
*/
length_keys: 16,
/**
* Section ID for the locale list
*/
section_loca: 'loca',
/**
* Minimum length of the 'loca' section
* not including variable parts
*/
length_loca: 12,
/**
* Section ID for the metadata
*/
section_meta: 'meta',
/**
* length of the 'meta' section
*/
@ -68,18 +59,10 @@ export const constants = {
* bitwise or value for transformPartial=hide in meta.settings
*/
meta_settings_transformPartial_hide: 4,
/**
* Section ID for the section header
*/
section_sect: 'sect',
/**
* Minimum length of the 'sect' section, not including entries
*/
length_sect: 16,
/**
* Section ID for the string table
*/
section_strs: 'strs',
/**
* Minimum length of the 'strs' section
* not including variable parts
@ -93,14 +76,39 @@ export const constants = {
* `extend = flags & keys_flags_extend`
*/
keys_flags_extend: 1,
/**
* Section ID for the vkeys map
*/
section_vkey: 'vkey',
/**
* Minimum length of the 'vkey' section
* not including variable parts
*/
length_vkey: 12,
/**
* All section IDs.
*/
section: {
keys: 'keys',
loca: 'loca',
meta: 'meta',
sect: 'sect',
strs: 'strs',
vkey: 'vkey',
},
/**
* Use to convert 4-char string into hex
* @param id section id such as 'sect'
* @returns hex ID such as 0x74636573
*/
hex_section_id: function(id:string) {
if(!id || typeof id !== 'string' || !id.match(/[a-z][a-z][a-z][a-z]/)) {
throw Error(`hex_section_id(${id}) - need a 4-character string`);
}
let r = 0;
for (let i = 3; i>=0; i--) {
r = (r << 8 | id.charCodeAt(i));
}
return r;
},
};
// }

View file

@ -23,19 +23,29 @@ console.log(`
// based on core/include/ldml/keyboardprocessor_ldml.ts
//
#pragma once`);
#pragma once
`);
for (const key of keys) {
const value = constants[key];
const upkey = key.toUpperCase();
const type = typeof value;
if ((key.indexOf('section_') === 0) && type === 'string' && value.length === 4) {
// the 4-char section ID strings get handled specially
console.log('// Section ID');
console.log(`#define LDML_${upkey} ((uint32_t)'${value}')`);
} else if (type === 'number') {
console.log(`#define LDML_${upkey} ${value}`);
if (type === 'number') {
console.log(`#define LDML_${upkey} 0x${value.toString(16).toUpperCase()}`);
} else if (type === 'string') {
console.log(`#define LDML_${upkey} "${value}"`);
} else if (key === 'section') {
// handle section table
const subkeys = Object.keys(value);
subkeys.sort();
for (const subkey of subkeys) {
const upsubkey = subkey.toUpperCase();
const subvalue = subkeys[subkey];
const asnum = constants.hex_section_id(subkey);
console.log(`#define LDML_${upkey}ID_${upsubkey} 0x${asnum.toString(16).toUpperCase()} /* "${subkey}" */`);
console.log(`#define LDML_${upkey}NAME_${upsubkey} "${subkey}"`);
}
} else if (type !== 'function') {
console.error(`Unrecognized key ${key}`);
}
}

View file

@ -4,12 +4,14 @@
Implementation for the KMX Plus utilities
*/
#include <km_types.h>
#include <kmx_file.h>
#include <kmx/kmx_plus.h>
/**
* @def KMXPLUS_DEBUG Set to 1 to enable debug output
*/
#define KMXPLUS_DEBUG 1
#define KMXPLUS_DEBUG 0
#if KMXPLUS_DEBUG
#include <stdio.h>
@ -32,8 +34,9 @@ dump_section_name(KMX_DWORD ident) {
static void
dump_kmxplus_header(const COMP_KMXPLUS_HEADER* hdr) {
if (hdr == NULL) {
if (hdr == nullptr) {
printf("! dump_kmxplus_header: NULL header\n");
return;
}
dump_section_name(hdr->ident);
printf(": (%X) size 0x%X\n", hdr->ident, hdr->size);
@ -41,7 +44,7 @@ dump_kmxplus_header(const COMP_KMXPLUS_HEADER* hdr) {
static void
dump_kmxplus_keys(const uint8_t* /*data*/, const COMP_KMXPLUS_KEYS* keys) {
if(keys == NULL) {
if(keys == nullptr) {
printf("! could not load 'keys' section\n");
return;
}
@ -63,7 +66,7 @@ dump_kmxplus_keys(const uint8_t* /*data*/, const COMP_KMXPLUS_KEYS* keys) {
static void
dump_kmxplus_loca(const uint8_t* /*data*/, const COMP_KMXPLUS_LOCA* loca) {
if(loca == NULL) {
if(loca == nullptr) {
printf("! could not load 'loca' section\n");
return;
}
@ -76,7 +79,7 @@ dump_kmxplus_loca(const uint8_t* /*data*/, const COMP_KMXPLUS_LOCA* loca) {
static void
dump_kmxplus_meta(const uint8_t* /*data*/, const COMP_KMXPLUS_META* meta) {
if(meta == NULL) {
if(meta == nullptr) {
printf("! could not load 'meta' section\n");
return;
}
@ -92,7 +95,7 @@ dump_kmxplus_meta(const uint8_t* /*data*/, const COMP_KMXPLUS_META* meta) {
static void
dump_kmxplus_vkey(const uint8_t* /*data*/, const COMP_KMXPLUS_VKEY* vkey) {
if(vkey == NULL) {
if (vkey == nullptr) {
printf("! could not load 'vkey' section\n");
return;
}
@ -102,6 +105,10 @@ dump_kmxplus_vkey(const uint8_t* /*data*/, const COMP_KMXPLUS_VKEY* vkey) {
static void
dump_kmxplus_strs(const uint8_t* /*data*/, const COMP_KMXPLUS_STRS* strs) {
if (strs == nullptr) {
printf("! could not load 'strs' section\n");
return;
}
dump_kmxplus_header((const COMP_KMXPLUS_HEADER*)strs);
printf("strs: count 0x%X\n", strs->count);
for (KMX_DWORD i=0; i<strs->count; i++) {
@ -113,10 +120,10 @@ dump_kmxplus_strs(const uint8_t* /*data*/, const COMP_KMXPLUS_STRS* strs) {
continue;
}
for(int j=0; str[j] && j<0x30; j++) {
if (str[j] < 0x7F && str[j] != 0x0020 && str[j] > 0x20) {
if (str[j] < 0x7F && str[j] > 0x20) {
putchar(str[j]);
} else {
printf("U+%04X ", str[j]);
printf(" U+%04X ", str[j]);
}
}
printf("\n");
@ -125,6 +132,9 @@ dump_kmxplus_strs(const uint8_t* /*data*/, const COMP_KMXPLUS_STRS* strs) {
static void
dump_kmxplus_sect(const uint8_t* data, const COMP_KMXPLUS_SECT* sect) {
if (sect == nullptr) {
printf("! could not load 'sect' section\n");
}
dump_kmxplus_header((const COMP_KMXPLUS_HEADER*)sect);
printf("sect: total 0x%X\n", sect->total);
printf("sect: count 0x%X\n", sect->count);
@ -135,22 +145,22 @@ dump_kmxplus_sect(const uint8_t* data, const COMP_KMXPLUS_SECT* sect) {
printf(" sect#%d: %X @ %X\n", i, entry.sect, entry.offset);
const uint8_t* entrydata = (data+entry.offset);
switch(entry.sect) {
case LDML_SECTION_KEYS:
case LDML_SECTIONID_KEYS:
dump_kmxplus_keys(data, as_kmxplus_keys(entrydata));
break;
case LDML_SECTION_LOCA:
case LDML_SECTIONID_LOCA:
dump_kmxplus_loca(data, as_kmxplus_loca(entrydata));
break;
case LDML_SECTION_META:
case LDML_SECTIONID_META:
dump_kmxplus_meta(data, as_kmxplus_meta(entrydata));
break;
case LDML_SECTION_SECT:
printf("! Cowardly refusing to dump nested 'sect' section.\n");
case LDML_SECTIONID_SECT:
printf("! Cowardly refusing to dump invalid nested 'sect' section.\n");
break;
case LDML_SECTION_STRS:
case LDML_SECTIONID_STRS:
dump_kmxplus_strs(data, as_kmxplus_strs(entrydata));
break;
case LDML_SECTION_VKEY:
case LDML_SECTIONID_VKEY:
dump_kmxplus_vkey(data, as_kmxplus_vkey(entrydata));
break;
default:
@ -160,21 +170,31 @@ dump_kmxplus_sect(const uint8_t* data, const COMP_KMXPLUS_SECT* sect) {
}
#endif
#if !KMXPLUS_DEBUG
void
dump_kmxplus_data(const uint8_t* ) {
// no op
}
void
dump_kmxplus_data(kmx::PCOMP_KEYBOARD) {
// no op
}
#else
void
dump_kmxplus_data(const uint8_t* data) {
#if KMXPLUS_DEBUG
const COMP_KMXPLUS_SECT* sect = as_kmxplus_sect(data);
if (sect == NULL) {
printf("Err: 'sect' null from %p\n", data);
if (sect == nullptr) {
printf("Err: 'sect' NULL from %p\n", data);
return;
}
dump_kmxplus_sect(data, sect);
#endif
}
void
dump_kmxplus_data(kmx::PCOMP_KEYBOARD keyboard) {
#if KMXPLUS_DEBUG
printf("dump_kmxplus_data(): Got a PCOMP_KEYBOARD at %p\n", keyboard);
if (!(keyboard->dwFlags & KF_KMXPLUS)) {
printf("Err: flags KF_KMXPLUS not set\n");
@ -185,8 +205,8 @@ dump_kmxplus_data(kmx::PCOMP_KEYBOARD keyboard) {
printf("KMXPlus offset 0x%X, KMXPlus size 0x%X\n", ex->kmxplus.dpKMXPlus, ex->kmxplus.dwKMXPlusSize);
const uint8_t* rawdata = reinterpret_cast<const uint8_t*>(keyboard);
dump_kmxplus_data(rawdata + ex->kmxplus.dpKMXPlus);
#endif
}
#endif
const COMP_KMXPLUS_KEYS_ENTRY *COMP_KMXPLUS_KEYS::find(KMX_DWORD vkey, KMX_DWORD mod) const {
// TODO-LDML: eventually, assume sorted order & binary search
@ -195,7 +215,7 @@ const COMP_KMXPLUS_KEYS_ENTRY *COMP_KMXPLUS_KEYS::find(KMX_DWORD vkey, KMX_DWORD
return &entries[i];
}
}
return NULL;
return nullptr;
}
KMX_DWORD COMP_KMXPLUS_SECT::find(KMX_DWORD ident) const {
@ -211,7 +231,7 @@ PKMX_WCHAR
COMP_KMXPLUS_STRS::get(KMX_DWORD entry, PKMX_WCHAR buf, KMX_DWORD bufsiz) const {
assert(entry < count);
if (entry >= count) {
return NULL;
return nullptr;
}
KMX_DWORD offset = entries[entry].offset;
KMX_DWORD length = entries[entry].length;

View file

@ -7,13 +7,23 @@
#pragma once
#include <assert.h>
#include <kmx/kmx_file.h>
#include <km_types.h>
#include <kmx/kmx_base.h>
#include <kmx_file.h>
#include <ldml/keyboardprocessor_ldml.h>
namespace km {
namespace kbp {
namespace kmx {
/**
* Using C99 flexible array initializers: entries[]
* https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-levels-2-and-4-c4200
*/
#if defined(_WIN32)
#pragma warning ( disable : 4200 )
#endif
struct COMP_KMXPLUS_HEADER {
KMX_DWORD ident; // 0000 Section name
KMX_DWORD size; // 0004 Section length
@ -31,7 +41,7 @@ struct COMP_KMXPLUS_SECT {
COMP_KMXPLUS_HEADER header;
KMX_DWORD total; // 0008 KMXPlus entire length
KMX_DWORD count; // 000B number of section headers
COMP_KMXPLUS_SECT_ENTRY entries[0]; // 0010 section entries
COMP_KMXPLUS_SECT_ENTRY entries[]; // 0010 section entries
/**
* @brief Get the offset of a section, or 0
*
@ -53,7 +63,7 @@ struct COMP_KMXPLUS_STRS {
COMP_KMXPLUS_HEADER header;
KMX_DWORD count; // 0008 count of str entries
KMX_DWORD reserved; // 000C padding
COMP_KMXPLUS_STRS_ENTRY entries[0]; // 0010+ entries
COMP_KMXPLUS_STRS_ENTRY entries[]; // 0010+ entries
/**
* @brief Get a string entry
@ -61,7 +71,7 @@ struct COMP_KMXPLUS_STRS {
* @param entry entry number
* @param buf output buffer
* @param bufsiz buffer size in bytes
* @return NULL or a pointer to the output buffer
* @return nullptr or a pointer to the output buffer
*/
PKMX_WCHAR get(KMX_DWORD entry, PKMX_WCHAR buf, KMX_DWORD bufsiz) const;
};
@ -88,7 +98,7 @@ struct COMP_KMXPLUS_LOCA_ENTRY {
struct COMP_KMXPLUS_LOCA {
COMP_KMXPLUS_HEADER header;
KMX_DWORD count; // 0008 number of locales
COMP_KMXPLUS_LOCA_ENTRY entries[0];
COMP_KMXPLUS_LOCA_ENTRY entries[];
};
static_assert(sizeof(struct COMP_KMXPLUS_LOCA) == LDML_LENGTH_LOCA, "mismatched size of section loca");
@ -104,7 +114,7 @@ struct COMP_KMXPLUS_KEYS {
COMP_KMXPLUS_HEADER header;
KMX_DWORD count; // number of keys
KMX_DWORD reserved; // padding
COMP_KMXPLUS_KEYS_ENTRY entries[0];
COMP_KMXPLUS_KEYS_ENTRY entries[];
const COMP_KMXPLUS_KEYS_ENTRY *find(KMX_DWORD vkey, KMX_DWORD mod) const;
};
@ -118,11 +128,19 @@ struct COMP_KMXPLUS_VKEY_ENTRY {
struct COMP_KMXPLUS_VKEY {
COMP_KMXPLUS_HEADER header;
KMX_DWORD count;
COMP_KMXPLUS_VKEY_ENTRY entries[0];
COMP_KMXPLUS_VKEY_ENTRY entries[];
};
static_assert(sizeof(struct COMP_KMXPLUS_VKEY) == LDML_LENGTH_VKEY, "mismatched size of section vkey");
/**
* See above
*/
#if defined(_WIN32)
#pragma warning ( default : 4200 )
#endif
/**
* @brief Validate that this data is the named section.
*
@ -133,14 +151,14 @@ static_assert(sizeof(struct COMP_KMXPLUS_VKEY) == LDML_LENGTH_VKEY, "mismatched
static inline const COMP_KMXPLUS_HEADER *
validate_as_section(const uint8_t *data, uint32_t ident) {
if (!data) {
return NULL;
return nullptr;
}
const COMP_KMXPLUS_HEADER *all = reinterpret_cast<const COMP_KMXPLUS_HEADER *>(data);
// TODO-LDML these fail on 000null ..
assert(all->size >= LDML_LENGTH_HEADER);
assert(ident == all->ident);
if (ident != all->ident || (all->size < LDML_LENGTH_HEADER)) {
return NULL; // invalid header or wrong section
return nullptr; // invalid header or wrong section
}
return all;
}
@ -151,7 +169,7 @@ validate_as_section(const uint8_t *data, uint32_t ident) {
*/
static inline const COMP_KMXPLUS_SECT *
as_kmxplus_sect(const uint8_t *data) {
const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTION_SECT);
const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTIONID_SECT);
return reinterpret_cast<const COMP_KMXPLUS_SECT *>(all);
}
@ -161,7 +179,7 @@ as_kmxplus_sect(const uint8_t *data) {
*/
static inline const COMP_KMXPLUS_STRS *
as_kmxplus_strs(const uint8_t *data) {
const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTION_STRS);
const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTIONID_STRS);
return reinterpret_cast<const COMP_KMXPLUS_STRS *>(all);
}
@ -171,7 +189,7 @@ as_kmxplus_strs(const uint8_t *data) {
*/
static inline const COMP_KMXPLUS_KEYS *
as_kmxplus_keys(const uint8_t *data) {
const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTION_KEYS);
const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTIONID_KEYS);
return reinterpret_cast<const COMP_KMXPLUS_KEYS *>(all);
}
/**
@ -180,7 +198,7 @@ as_kmxplus_keys(const uint8_t *data) {
*/
static inline const COMP_KMXPLUS_LOCA *
as_kmxplus_loca(const uint8_t *data) {
const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTION_LOCA);
const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTIONID_LOCA);
return reinterpret_cast<const COMP_KMXPLUS_LOCA *>(all);
}
/**
@ -189,7 +207,7 @@ as_kmxplus_loca(const uint8_t *data) {
*/
static inline const COMP_KMXPLUS_META *
as_kmxplus_meta(const uint8_t *data) {
const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTION_META);
const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTIONID_META);
return reinterpret_cast<const COMP_KMXPLUS_META *>(all);
}
/**
@ -198,7 +216,7 @@ as_kmxplus_meta(const uint8_t *data) {
*/
static inline const COMP_KMXPLUS_VKEY *
as_kmxplus_vkey(const uint8_t *data) {
const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTION_VKEY);
const COMP_KMXPLUS_HEADER *all = validate_as_section(data, LDML_SECTIONID_VKEY);
return reinterpret_cast<const COMP_KMXPLUS_VKEY *>(all);
}

View file

@ -8,8 +8,8 @@
#include <fstream>
#include "ldml/ldml_processor.hpp"
#include "state.hpp"
#include "../kmx/kmx_file.h"
#include "../kmx/kmx_plus.h"
#include "kmx_file.h"
#include "kmx/kmx_plus.h"
#include "ldml/keyboardprocessor_ldml.h"
// extern "C" {
@ -28,6 +28,10 @@ enum ConversionFlags {
typedef KMX_WCHAR UTF16;
typedef KMX_DWORD UTF32;
/**
* This is a temporary patch for now.
* API surface somewhat modelled after ConvertUTF.h
*/
ConversionResult ConvertUTF32toUTF16(
UTF32** sourceStart, const UTF32* sourceEnd,
UTF16** targetStart, const UTF16* targetEnd, const ConversionFlags /*flags*/) {
@ -170,21 +174,21 @@ ldml_processor::process_event(
const uint8_t* kmxplusdata = rawdata.data() + ex->kmxplus.dpKMXPlus;
// Get out the SECT header
const kmx::COMP_KMXPLUS_SECT *sect = kmx::as_kmxplus_sect(kmxplusdata);
assert(sect != NULL);
assert(sect->header.ident == LDML_SECTION_SECT);
assert(sect != nullptr);
assert(sect->header.ident == LDML_SECTIONID_SECT);
KMX_DWORD offset;
// Fill out the other sections we need.
offset = sect->find(LDML_SECTION_STRS);
offset = sect->find(LDML_SECTIONID_STRS);
assert(offset != 0); // or else section not found
const kmx::COMP_KMXPLUS_STRS *strs = kmx::as_kmxplus_strs(kmxplusdata+offset);
assert(strs->header.ident == LDML_SECTION_STRS);
offset = sect->find(LDML_SECTION_KEYS);
assert(strs->header.ident == LDML_SECTIONID_STRS);
offset = sect->find(LDML_SECTIONID_KEYS);
assert(offset != 0); // or else section not found
const kmx::COMP_KMXPLUS_KEYS *keys = kmx::as_kmxplus_keys(kmxplusdata+offset);
assert(keys->header.ident == LDML_SECTION_KEYS);
assert(keys->header.ident == LDML_SECTIONID_KEYS);
// Look up the key
const kmx::COMP_KMXPLUS_KEYS_ENTRY *key = keys->find(vk, modifier_state);
assert(key != NULL);
assert(key != nullptr);
if (!key) {
return KM_KBP_STATUS_KEY_ERROR;
}
@ -193,7 +197,7 @@ ldml_processor::process_event(
KMX_WCHAR out[BUFSIZ];
if (key->flags && LDML_KEYS_FLAGS_EXTEND) {
// It's a string.
assert(NULL != strs->get(key->to, out, BUFSIZ));
assert(nullptr != strs->get(key->to, out, BUFSIZ));
// u_strlen()
for(len=0; len<BUFSIZ && out[len]; len++);
} else {
@ -211,7 +215,7 @@ ldml_processor::process_event(
// len = (targetStart - out);
assert(len>=1 && len <= 2);
}
assert(len>=0);
assert(len>0);
assert(len<BUFSIZ);
assert(out[len] == 0); // null termination

Binary file not shown.

View file

@ -1,6 +1,6 @@
#include <stdio.h>
#include <assert.h>
#include <kmx/kmx_plus.h>
#include "kmx/kmx_plus.h"
using namespace km::kbp::kmx;

View file

@ -3,32 +3,31 @@
#
# @darcywong00 @mcdurdin @ermshiperete @rc-swag @SabineSIL @sgschantz
/android/ @darcywong00 @rc-swag
/android/ @darcywong00 @mcdurdin
/common/ @mcdurdin @jahorton
/core/ @mcdurdin @jahorton
/common/ @mcdurdin @rc-swag
/core/ @mcdurdin @rc-swag
/common/lexical-model-types/ @jahorton @mcdurdin
/common/models/ @jahorton @mcdurdin
/common/predictive-text/ @jahorton @mcdurdin
/common/schemas/ @mcdurdin @jahorton
/common/test/ @mcdurdin @ermshiperete
/common/web/ @jahorton @ermshiperete @mcdurdin
/common/web/ @jahorton @sgschantz @mcdurdin
/developer/ @mcdurdin @darcywong00
/docs/ @mcdurdin @jahorton
/ios/ @sgschantz @mcdurdin
/ios/ @sgschantz @jahorton
/linux/ @ermshiperete @darcywong00
/mac/ @sgschantz @SabineSIL
/oem/firstvoices/android/ @darcywong00 @rc-swag
/oem/firstvoices/common/ @mcdurdin @jahorton
/oem/firstvoices/ios/ @sgschantz @mcdurdin
/oem/firstvoices/windows/ @rc-swag @sgschantz
/oem/firstvoices/android/ @darcywong00 @mcdurdin
/oem/firstvoices/common/ @mcdurdin @rc-swag
/oem/firstvoices/ios/ @sgschantz @jahorton
/oem/firstvoices/windows/ @rc-swag @ermshiperete
/resources/ @mcdurdin @jahorton
# Web is currently shared between Marc and Joshua:
/web/ @jahorton @ermshiperete @mcdurdin
/web/ @jahorton @sgschantz @mcdurdin
/windows/ @rc-swag @ermshiperete
/windows/ @rc-swag @sgschantz
# Override for windows/src:
/windows/src/developer/ @mcdurdin @darcywong00

View file

@ -6,6 +6,7 @@ import android.content.SharedPreferences;
import android.content.res.AssetManager;
import android.net.Uri;
import android.util.Log;
import android.widget.Toast;
import com.tavultesoft.kmea.KMManager;
import com.tavultesoft.kmea.data.Keyboard;
import com.tavultesoft.kmea.packages.PackageProcessor;
@ -252,7 +253,11 @@ final class FVShared {
String helpUrl = String.format("%s%s", FVKeyboardHelpLink, id);
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(helpUrl));
localContext.startActivity(i);
if (i.resolveActivity(localContext.getPackageManager()) != null) {
localContext.startActivity(i);
} else {
Toast.makeText(localContext, localContext.getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show();
}
}
private void updateActiveKeyboardsList() {

View file

@ -105,7 +105,11 @@ public class MainActivity extends AppCompatActivity implements OnKeyboardDownloa
}
else {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(i);
if (i.resolveActivity(getPackageManager()) != null) {
startActivity(i);
} else {
Toast.makeText(context, getString(R.string.unable_to_open_browser), Toast.LENGTH_SHORT).show();
}
}
return true;

View file

@ -42,4 +42,6 @@
<!-- Context: KMP Package strings -->
<string name="invalid_metadata" comment="kmp.json metadata file is invalid or missing in package">Invalid/Missing metadata in package</string>
<!-- Context: anywhere -->
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">Unable to launch web browser</string>
</resources>

View file

@ -190,7 +190,7 @@ BOOL ProcessHook()
}
if (!_td->TIPFUpdateable) {
ProcessActionsTestParse(&fOutputKeystroke);
ProcessActionsNonUpdatableParse(&fOutputKeystroke);
} else {
ProcessActions(&fOutputKeystroke);
}

View file

@ -167,7 +167,7 @@ BOOL ProcessActions(BOOL* emitKeyStroke)
}
BOOL
ProcessActionsTestParse(BOOL* emitKeyStroke) {
ProcessActionsNonUpdatableParse(BOOL* emitKeyStroke) {
PKEYMAN64THREADDATA _td = ThreadGlobals();
if (!_td) {
return FALSE;
@ -184,13 +184,16 @@ ProcessActionsTestParse(BOOL* emitKeyStroke) {
switch (act->type) {
case KM_KBP_IT_EMIT_KEYSTROKE:
*emitKeyStroke = TRUE;
SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessActionsTestParse EMIT_KEYSTROKE: act->type=%d", act->type);
SendDebugMessageFormat(0, sdmGlobal, 0, "ProcessActionsNonUpdatableParse EMIT_KEYSTROKE: act->type=[%d]", act->type);
continueProcessingActions = TRUE;
_td->CoreProcessEventRun = FALSE; // If we emit the key stroke on this parse we don't need the second parse
break;
case KM_KBP_IT_CAPSLOCK:
continueProcessingActions = processCapsLock(act, !_td->state.isDown, _td->TIPFUpdateable);
break;
case KM_KBP_IT_INVALIDATE_CONTEXT:
continueProcessingActions = processInvalidateContext(_td->app, _td->lpActiveKeyboard->lpCoreKeyboardState);
break;
}
if (!continueProcessingActions) {
return FALSE;

View file

@ -18,12 +18,12 @@ BOOL ProcessActions(BOOL* emitKeyStroke);
/**
* This function process the actions queued in the core processor in
* the non updateable parse of a keystroke.
* Emit keystroke and capslock are required to be processed in this phase.
* the non-updateable parse of a keystroke.
* Emit keystroke, capslock and possibly invalidate key stroke are required to be processed in this parse.
*
* @param [in, out] emitKeyStroke is set to true if requested by the core action queue
* @return BOOL True if actions were successfully processed
*/
BOOL ProcessActionsTestParse(BOOL* emitKeyStroke);
BOOL ProcessActionsNonUpdatableParse(BOOL* emitKeyStroke);
#endif