mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-05 00:15:32 +00:00
Merge pull request #2275 from keymanapp/feature/keyboard-download-manager
feat(android): Feature/keyboard download manager
This commit is contained in:
commit
799d1b653b
21 changed files with 1223 additions and 274 deletions
|
|
@ -856,7 +856,8 @@ public class MainActivity extends AppCompatActivity implements OnKeyboardEventLi
|
|||
kbInfo.put(KMManager.KMKey_OskFont, kOskFont);
|
||||
if (i == 0) {
|
||||
if (KMManager.addKeyboard(this, kbInfo)) {
|
||||
KMManager.setKeyboard(packageID, keyboardID, langId, keyboardName, langName, kFont, kOskFont);
|
||||
if (!KMKeyboardDownloaderActivity.USE_DOWNLOAD_MANAGER)
|
||||
KMManager.setKeyboard(packageID, keyboardID, langId, keyboardName, langName, kFont, kOskFont);
|
||||
}
|
||||
} else {
|
||||
KMManager.addKeyboard(this, kbInfo);
|
||||
|
|
@ -864,7 +865,8 @@ public class MainActivity extends AppCompatActivity implements OnKeyboardEventLi
|
|||
}
|
||||
} else {
|
||||
if (KMManager.addKeyboard(this, keyboardInfo)) {
|
||||
KMManager.setKeyboard(packageID, keyboardID, languageID, keyboardName, languageName, kFont, kOskFont);
|
||||
if (!KMKeyboardDownloaderActivity.USE_DOWNLOAD_MANAGER)
|
||||
KMManager.setKeyboard(packageID, keyboardID, languageID, keyboardName, languageName, kFont, kOskFont);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import android.content.Context;
|
|||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
|
@ -19,6 +20,12 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.tavultesoft.kmea.data.CloudApiTypes;
|
||||
import com.tavultesoft.kmea.data.CloudDataJsonUtil;
|
||||
import com.tavultesoft.kmea.data.CloudDownloadMgr;
|
||||
import com.tavultesoft.kmea.data.CloudKeyboardDataDownloadCallback;
|
||||
import com.tavultesoft.kmea.data.CloudKeyboardMetaDataDownloadCallback;
|
||||
import com.tavultesoft.kmea.data.CloudLexicalPackageDownloadCallback;
|
||||
import com.tavultesoft.kmea.packages.LexicalModelPackageProcessor;
|
||||
import com.tavultesoft.kmea.packages.PackageProcessor;
|
||||
import com.tavultesoft.kmea.util.FileUtils;
|
||||
|
|
@ -29,7 +36,11 @@ import static com.tavultesoft.kmea.KMManager.KMDefault_UndefinedPackageID;
|
|||
|
||||
public class KMKeyboardDownloaderActivity extends AppCompatActivity {
|
||||
// Bundle Keys
|
||||
|
||||
// Cloud
|
||||
//TODO: Should be removed with the old implementation when downloadmanager impl works
|
||||
public static boolean USE_DOWNLOAD_MANAGER = true;
|
||||
|
||||
public static final String ARG_PKG_ID = "KMKeyboardActivity.pkgID";
|
||||
public static final String ARG_KB_ID = "KMKeyboardActivity.kbID";
|
||||
public static final String ARG_LANG_ID = "KMKeyboardActivity.langID";
|
||||
|
|
@ -66,6 +77,7 @@ public class KMKeyboardDownloaderActivity extends AppCompatActivity {
|
|||
public static final String KMKey_KeyboardBaseURI = "keyboardBaseUri";
|
||||
public static final String KMKey_FontBaseURI = "fontBaseUri";
|
||||
|
||||
//TODO: use keyboard model class, should not be static
|
||||
private static String pkgID;
|
||||
private static String kbID;
|
||||
private static String langID;
|
||||
|
|
@ -89,43 +101,41 @@ public class KMKeyboardDownloaderActivity extends AppCompatActivity {
|
|||
super.onCreate(savedInstanceState);
|
||||
|
||||
Bundle bundle = getIntent().getExtras();
|
||||
if (bundle != null) {
|
||||
pkgID = bundle.getString(ARG_PKG_ID);
|
||||
if (pkgID == null || pkgID.isEmpty()) {
|
||||
pkgID = KMManager.KMDefault_UndefinedPackageID;
|
||||
}
|
||||
langID = bundle.getString(ARG_LANG_ID);
|
||||
langName = bundle.getString(ARG_LANG_NAME);
|
||||
|
||||
downloadOnlyLexicalModel = bundle.containsKey(ARG_MODEL_URL) &&
|
||||
bundle.getString(ARG_MODEL_URL) != null &&
|
||||
!bundle.getString(ARG_MODEL_URL).isEmpty();
|
||||
|
||||
if (downloadOnlyLexicalModel) {
|
||||
modelID = bundle.getString(ARG_MODEL_ID);
|
||||
modelName = bundle.getString(ARG_MODEL_NAME);
|
||||
isCustom = false;
|
||||
url = bundle.getString(ARG_MODEL_URL);
|
||||
} else {
|
||||
|
||||
kbID = bundle.getString(ARG_KB_ID);
|
||||
kbName = bundle.getString(ARG_KB_NAME);
|
||||
isCustom = bundle.getBoolean(ARG_IS_CUSTOM);
|
||||
|
||||
// URL parameters for custom keyboard (if they exist)
|
||||
customKeyboard = bundle.getString(ARG_KEYBOARD);
|
||||
customLanguage = bundle.getString(ARG_LANGUAGE);
|
||||
url = bundle.getString(ARG_URL);
|
||||
filename = bundle.getString(ARG_FILENAME);
|
||||
if (filename == null || filename.isEmpty()) {
|
||||
filename = "unknown";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (bundle == null)
|
||||
return;
|
||||
|
||||
pkgID = bundle.getString(ARG_PKG_ID);
|
||||
if (pkgID == null || pkgID.isEmpty()) {
|
||||
pkgID = KMManager.KMDefault_UndefinedPackageID;
|
||||
}
|
||||
langID = bundle.getString(ARG_LANG_ID);
|
||||
langName = bundle.getString(ARG_LANG_NAME);
|
||||
|
||||
downloadOnlyLexicalModel = bundle.containsKey(ARG_MODEL_URL) &&
|
||||
bundle.getString(ARG_MODEL_URL) != null &&
|
||||
!bundle.getString(ARG_MODEL_URL).isEmpty();
|
||||
|
||||
if (downloadOnlyLexicalModel) {
|
||||
modelID = bundle.getString(ARG_MODEL_ID);
|
||||
modelName = bundle.getString(ARG_MODEL_NAME);
|
||||
isCustom = false;
|
||||
url = bundle.getString(ARG_MODEL_URL);
|
||||
} else {
|
||||
|
||||
kbID = bundle.getString(ARG_KB_ID);
|
||||
kbName = bundle.getString(ARG_KB_NAME);
|
||||
isCustom = bundle.getBoolean(ARG_IS_CUSTOM);
|
||||
|
||||
// URL parameters for custom keyboard (if they exist)
|
||||
customKeyboard = bundle.getString(ARG_KEYBOARD);
|
||||
customLanguage = bundle.getString(ARG_LANGUAGE);
|
||||
url = bundle.getString(ARG_URL);
|
||||
filename = bundle.getString(ARG_FILENAME);
|
||||
if (filename == null || filename.isEmpty()) {
|
||||
filename = "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
Bundle args = new Bundle();
|
||||
String title = "";
|
||||
if (url != null) {
|
||||
title = String.format("%s: %s", getString(R.string.custom_keyboard), filename);
|
||||
|
|
@ -162,6 +172,7 @@ public class KMKeyboardDownloaderActivity extends AppCompatActivity {
|
|||
* Used by the <code>download</code> method to handle asynchronous downloading and evaluation of
|
||||
* packages and keyboards.
|
||||
*/
|
||||
@Deprecated
|
||||
static class DownloadTask extends AsyncTask<Void, Integer, DownloadTask.Result> {
|
||||
static class Result {
|
||||
public final Integer kbdResult;
|
||||
|
|
@ -247,6 +258,8 @@ public class KMKeyboardDownloaderActivity extends AppCompatActivity {
|
|||
|
||||
String remoteUrl = "",remoteLexicalModelUrl = "";
|
||||
if (isCustom) {
|
||||
//TODO: Doesn't work, because this case will end up in an exception during download???
|
||||
// See downloadNonKMPKeyboard
|
||||
remoteUrl = url;
|
||||
} else {
|
||||
// Keyman cloud
|
||||
|
|
@ -357,10 +370,9 @@ public class KMKeyboardDownloaderActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
String fontBaseUri = options.optString(KMKey_FontBaseURI, "");
|
||||
JSONObject language, keyboard = null;
|
||||
|
||||
// Keyman cloud keyboard distribution via JSON
|
||||
language = kbData.optJSONObject(KMKey_Language);
|
||||
JSONObject language = kbData.optJSONObject(KMKey_Language);
|
||||
if (language == null) {
|
||||
throw new Exception(exceptionStr);
|
||||
}
|
||||
|
|
@ -368,20 +380,8 @@ public class KMKeyboardDownloaderActivity extends AppCompatActivity {
|
|||
langName = language.optString(KMManager.KMKey_Name, "");
|
||||
|
||||
JSONArray keyboards = language.getJSONArray(KMKey_LanguageKeyboards);
|
||||
if (keyboards == null) {
|
||||
throw new Exception(exceptionStr);
|
||||
}
|
||||
|
||||
// In case keyboards array contains multiple keyboards, get the one with matching keyboard ID
|
||||
for (int index = 0; index < keyboards.length(); index++) {
|
||||
keyboard = keyboards.getJSONObject(index);
|
||||
if (keyboard != null && (kbID.equals(keyboard.getString(KMManager.KMKey_ID)))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (keyboard == null) {
|
||||
throw new Exception(exceptionStr);
|
||||
}
|
||||
JSONObject keyboard = CloudDataJsonUtil.findMatchingKeyboardByID(keyboards,kbID);
|
||||
|
||||
kbID = keyboard.getString(KMManager.KMKey_ID);
|
||||
pkgID = keyboard.optString(KMManager.KMKey_PackageID, KMManager.KMDefault_UndefinedPackageID);
|
||||
|
|
@ -400,13 +400,13 @@ public class KMKeyboardDownloaderActivity extends AppCompatActivity {
|
|||
JSONObject jsonOskFont = keyboard.optJSONObject(KMManager.KMKey_OskFont);
|
||||
|
||||
if (jsonFont != null) {
|
||||
findTTF(jsonFont);
|
||||
CloudDataJsonUtil.updateFontSourceToTTFFont(jsonFont);
|
||||
}
|
||||
if (jsonOskFont != null) {
|
||||
findTTF(jsonOskFont);
|
||||
CloudDataJsonUtil.updateFontSourceToTTFFont(jsonOskFont);
|
||||
}
|
||||
ArrayList<String> fontUrls = fontUrls(jsonFont, fontBaseUri, true);
|
||||
ArrayList<String> oskFontUrls = fontUrls(jsonOskFont, fontBaseUri, true);
|
||||
ArrayList<String> fontUrls = CloudDataJsonUtil.fontUrls(jsonFont, fontBaseUri, false);
|
||||
ArrayList<String> oskFontUrls = CloudDataJsonUtil.fontUrls(jsonOskFont, fontBaseUri, true);
|
||||
if (fontUrls != null)
|
||||
urls.addAll(fontUrls);
|
||||
if (oskFontUrls != null) {
|
||||
|
|
@ -502,17 +502,9 @@ public class KMKeyboardDownloaderActivity extends AppCompatActivity {
|
|||
*/
|
||||
protected void notifyListeners(KeyboardEventHandler.EventType eventType, int result) {
|
||||
if (kbDownloadEventListeners != null) {
|
||||
HashMap<String, String> keyboardInfo = new HashMap<String, String>();
|
||||
keyboardInfo.put(KMManager.KMKey_PackageID, pkgID);
|
||||
keyboardInfo.put(KMManager.KMKey_KeyboardID, kbID);
|
||||
keyboardInfo.put(KMManager.KMKey_LanguageID, langID);
|
||||
keyboardInfo.put(KMManager.KMKey_KeyboardName, kbName);
|
||||
keyboardInfo.put(KMManager.KMKey_LanguageName, langName);
|
||||
keyboardInfo.put(KMManager.KMKey_KeyboardVersion, kbVersion);
|
||||
keyboardInfo.put(KMManager.KMKey_CustomKeyboard, kbIsCustom);
|
||||
keyboardInfo.put(KMManager.KMKey_Font, font);
|
||||
if (oskFont != null)
|
||||
keyboardInfo.put(KMManager.KMKey_OskFont, oskFont);
|
||||
HashMap<String, String> keyboardInfo =
|
||||
CloudDataJsonUtil.createKeyboardInfoMap(pkgID,langID,langName,kbID,kbName,kbVersion,kbIsCustom,font,oskFont);
|
||||
|
||||
KeyboardEventHandler.notifyListeners(kbDownloadEventListeners, eventType, keyboardInfo, result);
|
||||
}
|
||||
}
|
||||
|
|
@ -537,14 +529,152 @@ public class KMKeyboardDownloaderActivity extends AppCompatActivity {
|
|||
* @param showProgressDialog
|
||||
*/
|
||||
public static void download(final Context context, final boolean showProgressDialog) {
|
||||
new DownloadTask(context, showProgressDialog).execute();
|
||||
if(USE_DOWNLOAD_MANAGER)
|
||||
downloadKeyboardUsingDownloadManager(context);
|
||||
else
|
||||
new DownloadTask(context, showProgressDialog).execute();
|
||||
}
|
||||
|
||||
public static void download(final Context context, final boolean showProgressDialog,
|
||||
final boolean donwloadOnlyLexicalModel) {
|
||||
new DownloadTask(context, showProgressDialog, downloadOnlyLexicalModel).execute();
|
||||
final boolean aDownloadOnlyLexicalModel) {
|
||||
if(USE_DOWNLOAD_MANAGER)
|
||||
downloadUsingDownloadManager(context,aDownloadOnlyLexicalModel);
|
||||
else
|
||||
new DownloadTask(context, showProgressDialog, aDownloadOnlyLexicalModel).execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Download in keyboards and lexical model using download manager.
|
||||
* @param context the context
|
||||
* @param aDownloadOnlyLexicalModel is lexical model download
|
||||
*/
|
||||
private static void downloadUsingDownloadManager(final Context context,
|
||||
final boolean aDownloadOnlyLexicalModel)
|
||||
{
|
||||
if(aDownloadOnlyLexicalModel)
|
||||
{
|
||||
downloadLexicalModelUsingDownloadManager(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
downloadKeyboardUsingDownloadManager(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* prepare and execute keyboard download using downloadmanager.
|
||||
* @param context the context
|
||||
*/
|
||||
private static void downloadKeyboardUsingDownloadManager(Context context)
|
||||
{
|
||||
if (pkgID == null || pkgID.trim().isEmpty() ||
|
||||
(!isCustom && (langID == null || langID.trim().isEmpty() || kbID == null || kbID.trim().isEmpty()))) {
|
||||
throw new IllegalStateException("Invalid keyboard");
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<CloudApiTypes.CloudApiParam> cloudQueries = getPrepareCloudQueriesForKeyboardDownload(context);
|
||||
|
||||
String _downloadid= CloudKeyboardMetaDataDownloadCallback.createDownloadId(langID , kbID);
|
||||
|
||||
if( CloudDownloadMgr.getInstance().alreadyDownloadingData(_downloadid)
|
||||
|| CloudDownloadMgr.getInstance().alreadyDownloadingData(
|
||||
CloudKeyboardDataDownloadCallback.createDownloadId(kbID)))
|
||||
{
|
||||
Toast.makeText(context,
|
||||
context.getString(R.string.keyboard_download_is_running_in_background),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
else
|
||||
{
|
||||
CloudKeyboardMetaDataDownloadCallback _callback = new CloudKeyboardMetaDataDownloadCallback();
|
||||
_callback.setDownloadEventListeners(kbDownloadEventListeners);
|
||||
|
||||
Toast.makeText(context,
|
||||
context.getString(R.string.keyboard_download_start_in_background),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
|
||||
CloudDownloadMgr.getInstance().executeAsDownload(
|
||||
context, _downloadid, null, _callback,
|
||||
cloudQueries.toArray(new CloudApiTypes.CloudApiParam[0]));
|
||||
}
|
||||
|
||||
((AppCompatActivity) context).finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the cloud queries for keyboard metadata download.
|
||||
* @param context the context
|
||||
* @return the result
|
||||
*/
|
||||
private static List<CloudApiTypes.CloudApiParam> getPrepareCloudQueriesForKeyboardDownload(Context context)
|
||||
{
|
||||
List<CloudApiTypes.CloudApiParam> cloudQueries = new ArrayList<>();
|
||||
|
||||
String deviceType = CloudDataJsonUtil.getDeviceTypeForCloudQuery(context);
|
||||
|
||||
if (isCustom) {
|
||||
//TODO: will end up in an exception during download???
|
||||
cloudQueries.add(new CloudApiTypes.CloudApiParam(
|
||||
CloudApiTypes.ApiTarget.KeyboardData, url));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keyman cloud
|
||||
String _remoteUrl = String.format("%s/%s/%s?version=%s&device=%s&languageidtype=bcp47",
|
||||
kKeymanApiBaseURL, langID, kbID, BuildConfig.VERSION_NAME, deviceType);
|
||||
cloudQueries.add(
|
||||
new CloudApiTypes.CloudApiParam(
|
||||
CloudApiTypes.ApiTarget.Keyboard, _remoteUrl)
|
||||
.setType(CloudApiTypes.JSONType.Object)
|
||||
.setAdditionalProperty(CloudKeyboardMetaDataDownloadCallback.PARAM_IS_CUSTOM,isCustom)
|
||||
.setAdditionalProperty(CloudKeyboardMetaDataDownloadCallback.PARAM_LANG_ID,langID)
|
||||
.setAdditionalProperty(CloudKeyboardMetaDataDownloadCallback.PARAM_KB_ID,kbID));
|
||||
|
||||
String _remoteLexicalModelUrl = String.format("%s?q=bcp47:%s", kKeymanApiModelURL, langID);
|
||||
cloudQueries.add(new CloudApiTypes.CloudApiParam(
|
||||
CloudApiTypes.ApiTarget.KeyboardLexicalModels, _remoteLexicalModelUrl)
|
||||
.setType(CloudApiTypes.JSONType.Array));
|
||||
}
|
||||
return cloudQueries;
|
||||
}
|
||||
|
||||
/**
|
||||
* prepare and execute lexical model download using downloadmanager.
|
||||
* @param context the context
|
||||
*/
|
||||
private static void downloadLexicalModelUsingDownloadManager(Context context) {
|
||||
|
||||
|
||||
String _downloadid= CloudLexicalPackageDownloadCallback.createDownloadId(modelID);
|
||||
|
||||
if( CloudDownloadMgr.getInstance().alreadyDownloadingData(_downloadid))
|
||||
{
|
||||
Toast.makeText(context,
|
||||
context.getString(R.string.dictionary_download_is_running_in_background),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
else
|
||||
{
|
||||
CloudLexicalPackageDownloadCallback _callback = new CloudLexicalPackageDownloadCallback();
|
||||
_callback.setDownloadEventListeners(kbDownloadEventListeners);
|
||||
|
||||
CloudApiTypes.CloudApiParam _param = new CloudApiTypes.CloudApiParam(
|
||||
CloudApiTypes.ApiTarget.LexicalModelPackage, url);
|
||||
|
||||
Toast.makeText(context,
|
||||
context.getString(R.string.dictionary_download_start_in_background),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
|
||||
CloudDownloadMgr.getInstance().executeAsDownload(
|
||||
context, _downloadid, null, _callback, _param);
|
||||
}
|
||||
|
||||
((AppCompatActivity) context).finish();
|
||||
}
|
||||
|
||||
|
||||
public static boolean isCustom(String u) {
|
||||
boolean ret = false;
|
||||
if (u != null && !u.contains(KMKeyboardDownloaderActivity.kKeymanApiBaseURL) &&
|
||||
|
|
@ -554,108 +684,7 @@ public class KMKeyboardDownloaderActivity extends AppCompatActivity {
|
|||
return ret;
|
||||
}
|
||||
|
||||
// If a font JSONObject contains multiple font font files, only keep the .ttf source
|
||||
private static void findTTF(JSONObject jsonFont) {
|
||||
boolean updateJsonFont = false;
|
||||
try {
|
||||
JSONArray fontSource = jsonFont.optJSONArray(KMManager.KMKey_FontSource);
|
||||
if ((fontSource != null) && hasTTFFont(fontSource)) {
|
||||
for (int i = fontSource.length() - 1; i >= 0; i--) {
|
||||
String s = fontSource.getString(i);
|
||||
if (!FileUtils.isTTFFont(s)) {
|
||||
updateJsonFont = true;
|
||||
// remove() was added in API 19
|
||||
// https://developer.android.com/reference/org/json/JSONArray#remove(int)
|
||||
if (Build.VERSION.SDK_INT > 19) {
|
||||
fontSource.remove(i);
|
||||
} else {
|
||||
fontSource = removeJsonObjectAtIndex(fontSource, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updateJsonFont) {
|
||||
JSONArray copy = fontSource;
|
||||
jsonFont.remove(KMManager.KMKey_FontSource);
|
||||
jsonFont.put(KMManager.KMKey_FontSource, copy);
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "findTTF exception" + e);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the fontSource JSONArray to see if it contains a .ttf font
|
||||
private static boolean hasTTFFont(JSONArray fontSource) {
|
||||
try {
|
||||
for (int i = 0; i < fontSource.length(); i++) {
|
||||
String s = fontSource.getString(i);
|
||||
if (FileUtils.isTTFFont(s)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "hasTTFFont exception" + e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// From https://stackoverflow.com/questions/27427999/remove-jsonobeject-before-android-api-lvl-19
|
||||
private static JSONArray removeJsonObjectAtIndex(JSONArray source, int index) throws JSONException {
|
||||
if (index < 0 || index > source.length() - 1) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
final JSONArray copy = new JSONArray();
|
||||
for (int i=0, count = source.length(); i<count; i++) {
|
||||
if (i != index) {
|
||||
copy.put(source.get(i));
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
private static ArrayList<String> fontUrls(JSONObject jsonFont, String baseUri, boolean isOskFont) {
|
||||
if (jsonFont == null)
|
||||
return null;
|
||||
|
||||
ArrayList<String> urls = new ArrayList<String>();
|
||||
JSONArray fontSource = jsonFont.optJSONArray(KMManager.KMKey_FontSource);
|
||||
if (fontSource != null) {
|
||||
int fcCount = fontSource.length();
|
||||
for (int i = 0; i < fcCount; i++) {
|
||||
String fontSourceString;
|
||||
try {
|
||||
fontSourceString = fontSource.getString(i);
|
||||
|
||||
if (FileUtils.hasFontExtension(fontSourceString)) {
|
||||
urls.add(baseUri + fontSourceString);
|
||||
} else if (isOskFont && FileUtils.hasSVGViewBox(fontSourceString)) {
|
||||
String fontFilename = FileUtils.getSVGFilename(fontSourceString);
|
||||
urls.add(baseUri + fontFilename);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String fontSourceString;
|
||||
try {
|
||||
fontSourceString = jsonFont.getString(KMManager.KMKey_FontSource);
|
||||
if (FileUtils.hasFontExtension(fontSourceString)) {
|
||||
urls.add(baseUri + fontSourceString);
|
||||
} else if (isOskFont && FileUtils.hasSVGViewBox(fontSourceString)) {
|
||||
String fontFilename = FileUtils.getSVGFilename(fontSourceString);
|
||||
urls.add(baseUri + fontFilename);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return urls;
|
||||
}
|
||||
|
||||
public static void addKeyboardDownloadEventListener(KeyboardEventHandler.OnKeyboardDownloadEventListener listener) {
|
||||
if (kbDownloadEventListeners == null) {
|
||||
|
|
|
|||
|
|
@ -74,8 +74,12 @@ final class KMKeyboardPickerAdapter extends NestedAdapter<Keyboard, Dataset.Keyb
|
|||
holder = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
holder.textLang.setText(kbd.map.get(KMManager.KMKey_LanguageName));
|
||||
holder.textKbd.setText(kbd.map.get(KMManager.KMKey_KeyboardName));
|
||||
if(kbd.isNewKeyboard())
|
||||
holder.textLang.setText(getContext().getText(R.string.keyboard_picker_new_keyboard_prefix)
|
||||
+ " " + kbd.getLanguageName());
|
||||
else
|
||||
holder.textLang.setText(kbd.getLanguageName());
|
||||
holder.textKbd.setText(kbd.getResourceName());
|
||||
|
||||
if (listFont != null) {
|
||||
holder.textLang.setTypeface(listFont, Typeface.BOLD);
|
||||
|
|
|
|||
|
|
@ -145,7 +145,8 @@ public final class KeyboardListActivity extends AppCompatActivity implements OnK
|
|||
String kOskFont = keyboardInfo.get(KMManager.KMKey_OskFont);
|
||||
|
||||
KeyboardPickerActivity.addKeyboard(this, keyboardInfo);
|
||||
KMManager.setKeyboard(packageID, keyboardID, languageID, keyboardName, languageName, kFont, kOskFont);
|
||||
if (!KMKeyboardDownloaderActivity.USE_DOWNLOAD_MANAGER)
|
||||
KMManager.setKeyboard(packageID, keyboardID, languageID, keyboardName, languageName, kFont, kOskFont);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,34 @@
|
|||
|
||||
package com.tavultesoft.kmea;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemLongClickListener;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ListView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import com.tavultesoft.kmea.data.CloudDataJsonUtil;
|
||||
import com.tavultesoft.kmea.data.CloudRepository;
|
||||
import com.tavultesoft.kmea.data.Dataset;
|
||||
import com.tavultesoft.kmea.data.Keyboard;
|
||||
import com.tavultesoft.kmea.data.LexicalModel;
|
||||
import com.tavultesoft.kmea.util.MapCompat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
|
|
@ -14,42 +42,17 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.tavultesoft.kmea.data.CloudDataJsonUtil;
|
||||
import com.tavultesoft.kmea.data.CloudRepository;
|
||||
import com.tavultesoft.kmea.data.Dataset;
|
||||
import com.tavultesoft.kmea.data.Keyboard;
|
||||
import com.tavultesoft.kmea.data.LexicalModel;
|
||||
import com.tavultesoft.kmea.util.MapCompat;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemLongClickListener;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ListView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
public final class KeyboardPickerActivity extends AppCompatActivity {
|
||||
|
||||
//TODO: view instances should not be static
|
||||
private static Toolbar toolbar = null;
|
||||
private static ListView listView = null;
|
||||
private static Button closeButton = null;
|
||||
private static KMKeyboardPickerAdapter listAdapter = null;
|
||||
|
||||
public static final String KMKEY_INTERNAL_NEW_KEYBOARD = "_internal_new_keyboard_";
|
||||
|
||||
// TODO: Refactoring to remove keyboard selection into own keyboard manager class (MVC)
|
||||
// Lists of installed keyboards and installed lexical models
|
||||
private static ArrayList<HashMap<String, String>> keyboardsList = null;
|
||||
private static ArrayList<HashMap<String, String>> lexicalModelsList = null;
|
||||
|
|
@ -176,6 +179,39 @@ public final class KeyboardPickerActivity extends AppCompatActivity {
|
|||
|
||||
int curKbPos = getCurrentKeyboardIndex();
|
||||
setSelection(curKbPos);
|
||||
|
||||
KMKeyboard.addOnKeyboardEventListener(new KeyboardEventHandler.OnKeyboardEventListener() {
|
||||
@Override
|
||||
public void onKeyboardLoaded(KMManager.KeyboardType keyboardType) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKeyboardChanged(String newKeyboard) {
|
||||
int _index = getKeyboardIndex(context,newKeyboard);
|
||||
if(_index>=0)
|
||||
{
|
||||
Map<String,String> _keyboard = keyboardsList.get(_index);
|
||||
if(_keyboard==null)
|
||||
return;
|
||||
if(_keyboard.get(KMKEY_INTERNAL_NEW_KEYBOARD)==null)
|
||||
return;
|
||||
_keyboard.remove(KMKEY_INTERNAL_NEW_KEYBOARD);
|
||||
saveList(context, KMManager.KMFilename_KeyboardsList);
|
||||
notifyKeyboardsUpdate(context);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKeyboardShown() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKeyboardDismissed() {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -343,9 +379,12 @@ public final class KeyboardPickerActivity extends AppCompatActivity {
|
|||
if (kbKey.length() >= 3) {
|
||||
int x = getKeyboardIndex(context, kbKey);
|
||||
if (x >= 0) {
|
||||
if(keyboardsList.get(x).get(KMKEY_INTERNAL_NEW_KEYBOARD)!=null)
|
||||
keyboardInfo.put(KMKEY_INTERNAL_NEW_KEYBOARD,KMKEY_INTERNAL_NEW_KEYBOARD);
|
||||
keyboardsList.set(x, keyboardInfo);
|
||||
result = saveList(context, KMManager.KMFilename_KeyboardsList);
|
||||
} else {
|
||||
keyboardInfo.put(KMKEY_INTERNAL_NEW_KEYBOARD,KMKEY_INTERNAL_NEW_KEYBOARD);
|
||||
keyboardsList.add(keyboardInfo);
|
||||
result = saveList(context, KMManager.KMFilename_KeyboardsList);
|
||||
if (!result) {
|
||||
|
|
|
|||
|
|
@ -137,7 +137,9 @@ public final class LanguageListActivity extends AppCompatActivity implements OnK
|
|||
if (!pkgID.equals(KMManager.KMDefault_UndefinedPackageID)) {
|
||||
// Custom keyboard already exists in packages/ so just add the language association
|
||||
KeyboardPickerActivity.addKeyboard(context, kbInfo);
|
||||
KMManager.setKeyboard(pkgID, kbID, langID, kbName, language.name, kFont, kOskFont);
|
||||
|
||||
if (!KMKeyboardDownloaderActivity.USE_DOWNLOAD_MANAGER)
|
||||
KMManager.setKeyboard(pkgID, kbID, langID, kbName, language.name, kFont, kOskFont);
|
||||
Toast.makeText(context, "Keyboard installed", Toast.LENGTH_SHORT).show();
|
||||
setResult(RESULT_OK);
|
||||
((AppCompatActivity) context).finish();
|
||||
|
|
@ -207,7 +209,8 @@ public final class LanguageListActivity extends AppCompatActivity implements OnK
|
|||
String kOskFont = keyboardInfo.get(KMManager.KMKey_OskFont);
|
||||
|
||||
KeyboardPickerActivity.addKeyboard(this, keyboardInfo);
|
||||
KMManager.setKeyboard(packageID, keyboardID, languageID, keyboardName, languageName, kFont, kOskFont);
|
||||
if (!KMKeyboardDownloaderActivity.USE_DOWNLOAD_MANAGER)
|
||||
KMManager.setKeyboard(packageID, keyboardID, languageID, keyboardName, languageName, kFont, kOskFont);
|
||||
|
||||
if (result == 2) {
|
||||
Toast.makeText(context, context.getString(R.string.font_failed_to_download), Toast.LENGTH_LONG).show();
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ public final class LanguagesSettingsActivity extends AppCompatActivity
|
|||
|
||||
// ********* ONLY USED BY UPDATE CODE ***********
|
||||
|
||||
//TODO: Refactoring to separate update logic from view
|
||||
private static boolean updateCheckFailed = false;
|
||||
private static boolean updateFailed = false;
|
||||
private static Calendar lastUpdateCheck = null;
|
||||
|
|
|
|||
|
|
@ -9,8 +9,10 @@ import org.json.JSONObject;
|
|||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class CloudApiTypes {
|
||||
protected static class CloudApiReturns {
|
||||
|
|
@ -33,25 +35,64 @@ public class CloudApiTypes {
|
|||
|
||||
}
|
||||
|
||||
protected enum ApiTarget {
|
||||
public enum ApiTarget {
|
||||
/**
|
||||
* Catalog download: available keyboards including meta data.
|
||||
*/
|
||||
Keyboards,
|
||||
LexicalModels
|
||||
/**
|
||||
* Catalog download: available lexical models including meta data.
|
||||
*/
|
||||
LexicalModels,
|
||||
/**
|
||||
* Keyboard download: keyboard meta data for the selected keyboard.
|
||||
*/
|
||||
Keyboard,
|
||||
/**
|
||||
* Keyboard download: lexical models meta data for the language of the selected keyboard.
|
||||
*/
|
||||
KeyboardLexicalModels,
|
||||
/**
|
||||
* Keyboard download: download keyboard data package and fonts.
|
||||
*/
|
||||
KeyboardData,
|
||||
/**
|
||||
* Lexical download: download lexical model package
|
||||
* Used for single lexical model download and
|
||||
* automatic lexical model download during keyboard download
|
||||
*/
|
||||
LexicalModelPackage,
|
||||
}
|
||||
|
||||
protected enum JSONType {
|
||||
public enum JSONType {
|
||||
Array,
|
||||
Object
|
||||
}
|
||||
|
||||
protected static class CloudApiParam {
|
||||
public static class CloudApiParam {
|
||||
public final ApiTarget target;
|
||||
public final String url;
|
||||
public final JSONType type;
|
||||
public JSONType type;
|
||||
private Map<String,Object> additionalProperties = new HashMap<>();
|
||||
|
||||
CloudApiParam(ApiTarget target, String url, JSONType type) {
|
||||
public CloudApiParam(ApiTarget target, String url) {
|
||||
this.target = target;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public CloudApiParam setType(JSONType type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
public CloudApiParam setAdditionalProperty(String aProperty, Object aValue)
|
||||
{
|
||||
additionalProperties.put(aProperty,aValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
public <T> T getAdditionalProperty(String aProperty,Class<T> aType)
|
||||
{
|
||||
return (T)additionalProperties.get(aProperty);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -61,8 +102,7 @@ public class CloudApiTypes {
|
|||
private boolean downloadFinished =false;
|
||||
private long downloadId;
|
||||
private File destinationFile;
|
||||
private CloudApiTypes.JSONType type;
|
||||
private CloudApiTypes.ApiTarget target;
|
||||
private CloudApiParam cloudParams;
|
||||
|
||||
public SingleCloudDownload(DownloadManager.Request aRequest,File aDestinationFile)
|
||||
{
|
||||
|
|
@ -74,13 +114,8 @@ public class CloudApiTypes {
|
|||
return this;
|
||||
}
|
||||
|
||||
public SingleCloudDownload setJsonType(JSONType type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SingleCloudDownload setTarget(ApiTarget target) {
|
||||
this.target = target;
|
||||
public SingleCloudDownload setCloudParams(CloudApiParam params) {
|
||||
this.cloudParams = params;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -96,12 +131,8 @@ public class CloudApiTypes {
|
|||
return destinationFile;
|
||||
}
|
||||
|
||||
public JSONType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public ApiTarget getTarget() {
|
||||
return target;
|
||||
public CloudApiParam getCloudParams() {
|
||||
return cloudParams;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -229,6 +229,11 @@ public class CloudCatalogDownloadCallback implements ICloudDownloadCallback<Data
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeContext(Context context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyCloudDownloadToModel(Context aContext, Dataset aDataSet, CloudCatalogDownloadReturns aCloudResult)
|
||||
{
|
||||
|
|
@ -245,35 +250,14 @@ public class CloudCatalogDownloadCallback implements ICloudDownloadCallback<Data
|
|||
{
|
||||
List<CloudApiTypes.CloudApiReturns> retrievedJSON = new ArrayList<>(aDownload.getSingleDownloads().size());
|
||||
|
||||
|
||||
for (CloudApiTypes.SingleCloudDownload _d : aDownload.getSingleDownloads()) {
|
||||
JSONParser jsonParser = new JSONParser();
|
||||
JSONArray dataArray = null;
|
||||
JSONObject dataObject = null;
|
||||
|
||||
if (_d.getDestinationFile() != null && _d.getDestinationFile().length() > 0) {
|
||||
try {
|
||||
CloudApiTypes.CloudApiReturns _json_result = CloudDataJsonUtil.retrieveJsonFromDownload(_d);
|
||||
|
||||
if (_d.getType() == CloudApiTypes.JSONType.Array) {
|
||||
dataArray = jsonParser.getJSONObjectFromFile(_d.getDestinationFile(),JSONArray.class);
|
||||
} else {
|
||||
dataObject = jsonParser.getJSONObjectFromFile(_d.getDestinationFile(),JSONObject.class);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, e.getMessage());
|
||||
} finally {
|
||||
_d.getDestinationFile().delete();
|
||||
}
|
||||
} else {
|
||||
// Offline trouble! That said, we can't get anything, so we simply shouldn't add anything.
|
||||
}
|
||||
|
||||
if (_d.getType() == CloudApiTypes.JSONType.Array) {
|
||||
retrievedJSON.add(new CloudApiTypes.CloudApiReturns(_d.getTarget(), dataArray)); // Null if offline.
|
||||
} else {
|
||||
retrievedJSON.add(new CloudApiTypes.CloudApiReturns(_d.getTarget(), dataObject)); // Null if offline.
|
||||
}
|
||||
if (_json_result!=null)
|
||||
retrievedJSON.add(_json_result); // Null if offline.
|
||||
}
|
||||
|
||||
return new CloudCatalogDownloadReturns(retrievedJSON);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package com.tavultesoft.kmea.data;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.tavultesoft.kmea.JSONParser;
|
||||
import com.tavultesoft.kmea.KMKeyboardDownloaderActivity;
|
||||
import com.tavultesoft.kmea.KMManager;
|
||||
import com.tavultesoft.kmea.R;
|
||||
|
|
@ -29,6 +31,27 @@ public class CloudDataJsonUtil {
|
|||
{
|
||||
//no instances
|
||||
}
|
||||
|
||||
public static HashMap<String,String> createKeyboardInfoMap(String aPackageId,String aLanguageId, String aLanguageName, String aKeyboardId,
|
||||
String aKeyboardName, String aKeyboardVersion, String anIsCustomKeyboard,
|
||||
String aFont, String aOskFont)
|
||||
{
|
||||
HashMap<String, String> keyboardInfo = new HashMap<String, String>();
|
||||
if(aPackageId!=null)
|
||||
keyboardInfo.put(KMManager.KMKey_PackageID, aPackageId);
|
||||
keyboardInfo.put(KMManager.KMKey_KeyboardID, aKeyboardId);
|
||||
keyboardInfo.put(KMManager.KMKey_LanguageID, aLanguageId);
|
||||
keyboardInfo.put(KMManager.KMKey_KeyboardName, aKeyboardName);
|
||||
keyboardInfo.put(KMManager.KMKey_LanguageName, aLanguageName);
|
||||
keyboardInfo.put(KMManager.KMKey_KeyboardVersion, aKeyboardVersion);
|
||||
keyboardInfo.put(KMManager.KMKey_CustomKeyboard, anIsCustomKeyboard);
|
||||
keyboardInfo.put(KMManager.KMKey_Font, aFont);
|
||||
if (aOskFont != null)
|
||||
keyboardInfo.put(KMManager.KMKey_OskFont, aOskFont);
|
||||
|
||||
return keyboardInfo;
|
||||
}
|
||||
|
||||
static List<Keyboard> processKeyboardJSON(JSONObject query, boolean fromKMP) {
|
||||
List<Keyboard> keyboardsList = new ArrayList<>();
|
||||
//keyboardModifiedDates = new HashMap<String, String>();
|
||||
|
|
@ -55,14 +78,8 @@ public class CloudDataJsonUtil {
|
|||
String kbFont = keyboardJSON.optString(KMManager.KMKey_Font, "");
|
||||
|
||||
//String kbKey = String.format("%s_%s", langID, kbID);
|
||||
HashMap<String, String> hashMap = new HashMap<String, String>();
|
||||
hashMap.put(KMManager.KMKey_KeyboardName, kbName);
|
||||
hashMap.put(KMManager.KMKey_KeyboardID, kbID);
|
||||
hashMap.put(KMManager.KMKey_LanguageName, langName);
|
||||
hashMap.put(KMManager.KMKey_LanguageID, langID);
|
||||
hashMap.put(KMManager.KMKey_KeyboardVersion, kbVersion);
|
||||
hashMap.put(KMManager.KMKey_CustomKeyboard, isCustom);
|
||||
hashMap.put(KMManager.KMKey_Font, kbFont);
|
||||
HashMap<String, String> hashMap = createKeyboardInfoMap(null,langID,langName,kbID,kbName,kbVersion,isCustom,kbFont,null);
|
||||
|
||||
|
||||
// if (keyboardModifiedDates.get(kbID) == null) {
|
||||
// keyboardModifiedDates.put(kbID, keyboardJSON.getString(KMManager.KMKey_KeyboardModified));
|
||||
|
|
@ -220,4 +237,187 @@ public class CloudDataJsonUtil {
|
|||
final String jsonLexicalCacheFilename = "jsonLexicalModelsCache.dat";
|
||||
return new File(context.getCacheDir(), jsonLexicalCacheFilename);
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve a json object from a downloaded file.
|
||||
* @param aDownload the download
|
||||
* @return the result
|
||||
*/
|
||||
public static CloudApiTypes.CloudApiReturns retrieveJsonFromDownload( CloudApiTypes.SingleCloudDownload aDownload)
|
||||
{
|
||||
JSONParser jsonParser = new JSONParser();
|
||||
JSONArray dataArray = null;
|
||||
JSONObject dataObject = null;
|
||||
|
||||
if (aDownload.getDestinationFile() != null && aDownload.getDestinationFile().length() > 0) {
|
||||
try {
|
||||
|
||||
if (aDownload.getCloudParams().type == CloudApiTypes.JSONType.Array) {
|
||||
dataArray = jsonParser.getJSONObjectFromFile(aDownload.getDestinationFile(),JSONArray.class);
|
||||
} else {
|
||||
dataObject = jsonParser.getJSONObjectFromFile(aDownload.getDestinationFile(),JSONObject.class);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, e.getMessage());
|
||||
} finally {
|
||||
aDownload.getDestinationFile().delete();
|
||||
}
|
||||
} else {
|
||||
// Offline trouble! That said, we can't get anything, so we simply shouldn't add anything.
|
||||
}
|
||||
|
||||
if (aDownload.getCloudParams().type == CloudApiTypes.JSONType.Array)
|
||||
{
|
||||
if(dataArray!=null)
|
||||
return new CloudApiTypes.CloudApiReturns(aDownload.getCloudParams().target, dataArray); // Null if offline.
|
||||
return null;
|
||||
}
|
||||
if(dataObject!=null)
|
||||
return new CloudApiTypes.CloudApiReturns(aDownload.getCloudParams().target, dataObject); // Null if offline.
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// If a font JSONObject contains multiple font font files, only keep the .ttf source
|
||||
public static void updateFontSourceToTTFFont(JSONObject jsonFont) {
|
||||
boolean updateJsonFont = false;
|
||||
try {
|
||||
JSONArray fontSource = jsonFont.optJSONArray(KMManager.KMKey_FontSource);
|
||||
if ((fontSource != null) && hasTTFFont(fontSource)) {
|
||||
for (int i = fontSource.length() - 1; i >= 0; i--) {
|
||||
String s = fontSource.getString(i);
|
||||
if (!FileUtils.isTTFFont(s)) {
|
||||
updateJsonFont = true;
|
||||
// remove() was added in API 19
|
||||
// https://developer.android.com/reference/org/json/JSONArray#remove(int)
|
||||
if (Build.VERSION.SDK_INT > 19) {
|
||||
fontSource.remove(i);
|
||||
} else {
|
||||
fontSource = removeJsonObjectAtIndex(fontSource, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updateJsonFont) {
|
||||
JSONArray copy = fontSource;
|
||||
jsonFont.remove(KMManager.KMKey_FontSource);
|
||||
jsonFont.put(KMManager.KMKey_FontSource, copy);
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "findTTF exception" + e);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the fontSource JSONArray to see if it contains a .ttf font
|
||||
private static boolean hasTTFFont(JSONArray fontSource) {
|
||||
try {
|
||||
for (int i = 0; i < fontSource.length(); i++) {
|
||||
String s = fontSource.getString(i);
|
||||
if (FileUtils.isTTFFont(s)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "hasTTFFont exception" + e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// From https://stackoverflow.com/questions/27427999/remove-jsonobeject-before-android-api-lvl-19
|
||||
private static JSONArray removeJsonObjectAtIndex(JSONArray source, int index) throws JSONException {
|
||||
if (index < 0 || index > source.length() - 1) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
final JSONArray copy = new JSONArray();
|
||||
for (int i=0, count = source.length(); i<count; i++) {
|
||||
if (i != index) {
|
||||
copy.put(source.get(i));
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
public static ArrayList<String> fontUrls(JSONObject jsonFont, String baseUri, boolean isOskFont) {
|
||||
if (jsonFont == null)
|
||||
return null;
|
||||
|
||||
ArrayList<String> urls = new ArrayList<String>();
|
||||
JSONArray fontSource = jsonFont.optJSONArray(KMManager.KMKey_FontSource);
|
||||
if (fontSource != null) {
|
||||
int fcCount = fontSource.length();
|
||||
for (int i = 0; i < fcCount; i++) {
|
||||
String fontSourceString;
|
||||
try {
|
||||
fontSourceString = fontSource.getString(i);
|
||||
|
||||
if (FileUtils.hasFontExtension(fontSourceString)) {
|
||||
urls.add(baseUri + fontSourceString);
|
||||
} else if (isOskFont && FileUtils.hasSVGViewBox(fontSourceString)) {
|
||||
String fontFilename = FileUtils.getSVGFilename(fontSourceString);
|
||||
urls.add(baseUri + fontFilename);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String fontSourceString;
|
||||
try {
|
||||
fontSourceString = jsonFont.getString(KMManager.KMKey_FontSource);
|
||||
if (FileUtils.hasFontExtension(fontSourceString)) {
|
||||
urls.add(baseUri + fontSourceString);
|
||||
} else if (isOskFont && FileUtils.hasSVGViewBox(fontSourceString)) {
|
||||
String fontFilename = FileUtils.getSVGFilename(fontSourceString);
|
||||
urls.add(baseUri + fontFilename);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return urls;
|
||||
}
|
||||
|
||||
public static JSONObject findMatchingKeyboardByID(JSONArray aKeyboards, String aKbId)
|
||||
throws IllegalStateException, JSONException
|
||||
{
|
||||
if (aKeyboards == null) {
|
||||
throw new IllegalStateException("Keyboard array is empty");
|
||||
}
|
||||
|
||||
JSONObject keyboard = null;
|
||||
// In case keyboards array contains multiple keyboards, get the one with matching keyboard ID
|
||||
for (int index = 0; index < aKeyboards.length(); index++) {
|
||||
keyboard = aKeyboards.getJSONObject(index);
|
||||
if (keyboard != null && (aKbId.equals(keyboard.getString(KMManager.KMKey_ID)))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (keyboard == null) {
|
||||
throw new IllegalStateException("could not find matching keyboard");
|
||||
}
|
||||
|
||||
return keyboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* select device type for api queries.
|
||||
* @param aContext a context
|
||||
* @return the result
|
||||
*/
|
||||
public static String getDeviceTypeForCloudQuery(Context aContext)
|
||||
{
|
||||
String deviceType = aContext.getString(R.string.device_type);
|
||||
if (deviceType.equals("AndroidTablet")) {
|
||||
deviceType = "androidtablet";
|
||||
} else {
|
||||
deviceType = "androidphone";
|
||||
}
|
||||
return deviceType;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,6 +189,9 @@ public class CloudDownloadMgr{
|
|||
|
||||
DownloadManager downloadManager = (DownloadManager) aContext.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
|
||||
|
||||
aCallback.initializeContext(aContext);
|
||||
|
||||
CloudApiTypes.CloudDownloadSet<ModelType,ResultType> _downloadSet =
|
||||
new CloudApiTypes.CloudDownloadSet<ModelType,ResultType>(
|
||||
aDownloadIdentifier,aTargetModel);
|
||||
|
|
@ -232,7 +235,6 @@ public class CloudDownloadMgr{
|
|||
//.setAllowedOverRoaming(true);// Set if download is allowed on roaming network
|
||||
|
||||
return new CloudApiTypes.SingleCloudDownload(_request,_file)
|
||||
.setJsonType(aParam.type)
|
||||
.setTarget(aParam.target);
|
||||
.setCloudParams(aParam);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,138 @@
|
|||
package com.tavultesoft.kmea.data;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.tavultesoft.kmea.KeyboardEventHandler;
|
||||
import com.tavultesoft.kmea.R;
|
||||
import com.tavultesoft.kmea.util.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static com.tavultesoft.kmea.KMManager.KMDefault_UndefinedPackageID;
|
||||
|
||||
/**
|
||||
* Install keyboard data, when download is finished.
|
||||
* Could be keyboard packages or fonts.
|
||||
*/
|
||||
public class CloudKeyboardDataDownloadCallback implements ICloudDownloadCallback<
|
||||
Void, CloudKeyboardDownloadReturns>
|
||||
{
|
||||
|
||||
private static final String TAG = "CloudkbDataDldCb";
|
||||
|
||||
private File dataDir;
|
||||
|
||||
private ArrayList<KeyboardEventHandler.OnKeyboardDownloadEventListener> downloadEventListeners = new ArrayList<>();
|
||||
private HashMap<String,String> keyboardInfo;
|
||||
|
||||
/**
|
||||
* Additional Cloud API parameter:
|
||||
* Parameter to force a special destination file name during installation.
|
||||
* Default name is the file name of the url.
|
||||
*/
|
||||
public static final String PARAM_DESTINATION_FILE_NAME = "destination_file_name";
|
||||
|
||||
/**
|
||||
* Additional Cloud API parameter:
|
||||
* Parameter to force a special destination file name during installation.
|
||||
* Default name is the file name of the url.
|
||||
*/
|
||||
public static final String PARAM_PACKAGE = "package";
|
||||
|
||||
/**
|
||||
* listeners to inform after installation is completed.
|
||||
* @param aDownloadEventListeners the listeners
|
||||
*/
|
||||
public void setDownloadEventListeners(ArrayList<KeyboardEventHandler.OnKeyboardDownloadEventListener> aDownloadEventListeners)
|
||||
{
|
||||
downloadEventListeners.clear();
|
||||
downloadEventListeners.addAll(aDownloadEventListeners);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keyboard meta data.
|
||||
* @param aKeyboardInfo the keyboard
|
||||
*/
|
||||
public void setKeyboardInfo(HashMap<String, String> aKeyboardInfo) {
|
||||
this.keyboardInfo = aKeyboardInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeContext(Context context)
|
||||
{
|
||||
dataDir = context.getDir("data", Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloudKeyboardDownloadReturns extractCloudResultFromDownloadSet(
|
||||
CloudApiTypes.CloudDownloadSet<Void, CloudKeyboardDownloadReturns> aDownload)
|
||||
{
|
||||
int _result = FileUtils.DOWNLOAD_SUCCESS;
|
||||
for(CloudApiTypes.SingleCloudDownload _d:aDownload.getSingleDownloads())
|
||||
{
|
||||
if (_d.getDestinationFile() != null && _d.getDestinationFile().length() > 0)
|
||||
{
|
||||
|
||||
try {
|
||||
|
||||
String _pkg = _d.getCloudParams().getAdditionalProperty(PARAM_PACKAGE,String.class);
|
||||
String destination = dataDir.toString() +
|
||||
File.separator + _pkg + File.separator;
|
||||
|
||||
String _filename = _d.getCloudParams().getAdditionalProperty(PARAM_DESTINATION_FILE_NAME,String.class);
|
||||
if (_filename==null) {
|
||||
|
||||
_filename = FileUtils.getFilename(_d.getCloudParams().url);
|
||||
}
|
||||
|
||||
File _data_file = new File(destination,_filename);
|
||||
FileUtils.copy(_d.getDestinationFile(), _data_file);
|
||||
|
||||
}
|
||||
catch (IOException _e)
|
||||
{
|
||||
Log.e(TAG,_e.getLocalizedMessage(),_e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FileUtils.hasFontExtension(_d.getCloudParams().url))
|
||||
_result = 2;
|
||||
else
|
||||
_result = FileUtils.DOWNLOAD_ERROR;
|
||||
}
|
||||
}
|
||||
return new CloudKeyboardDownloadReturns(_result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void applyCloudDownloadToModel(Context aContext, Void aModel, CloudKeyboardDownloadReturns aCloudResult)
|
||||
{
|
||||
Toast.makeText(aContext,
|
||||
aContext.getString(R.string.keyboard_download_finished),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
|
||||
if(keyboardInfo!=null)
|
||||
{
|
||||
KeyboardEventHandler.notifyListeners(downloadEventListeners, KeyboardEventHandler.EventType.KEYBOARD_DOWNLOAD_FINISHED,
|
||||
keyboardInfo, aCloudResult.kbdResult);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create a download id for the keyboard data.
|
||||
* @param aKeyboardId the keyboard id
|
||||
* @return the result
|
||||
*/
|
||||
public static String createDownloadId(String aKeyboardId)
|
||||
{
|
||||
return "keyboarddata_" + aKeyboardId;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tavultesoft.kmea.data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* key board and lexical model download result.
|
||||
*/
|
||||
public class CloudKeyboardDownloadReturns {
|
||||
public final Integer kbdResult;
|
||||
public final List<Map<String, String>> installedLexicalModels;
|
||||
|
||||
public CloudKeyboardDownloadReturns(Integer i) {
|
||||
this(i, null);
|
||||
}
|
||||
|
||||
public CloudKeyboardDownloadReturns(Integer i, List<Map<String, String>> installedLexicalModels) {
|
||||
this.kbdResult = i;
|
||||
this.installedLexicalModels = installedLexicalModels;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,349 @@
|
|||
package com.tavultesoft.kmea.data;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.tavultesoft.kmea.KMKeyboardDownloaderActivity;
|
||||
import com.tavultesoft.kmea.KMManager;
|
||||
import com.tavultesoft.kmea.KeyboardEventHandler;
|
||||
import com.tavultesoft.kmea.R;
|
||||
import com.tavultesoft.kmea.util.FileUtils;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Start the keyboard download when keyboard metadata is downloaded.
|
||||
*/
|
||||
public class CloudKeyboardMetaDataDownloadCallback implements ICloudDownloadCallback<Void,
|
||||
List<CloudKeyboardMetaDataDownloadCallback.MetaDataResult>>
|
||||
{
|
||||
/**
|
||||
* the metadata result and all necessary downloads which should be started.
|
||||
*/
|
||||
public static class MetaDataResult
|
||||
{
|
||||
CloudApiTypes.CloudApiReturns returnjson;
|
||||
CloudApiTypes.CloudApiParam params;
|
||||
HashMap<String,String> keyboardInfo;
|
||||
String additionalDownloadid;
|
||||
List<CloudApiTypes.CloudApiParam> additionalDownloads;
|
||||
}
|
||||
|
||||
private static final String TAG = "CloudKeyboardMetaDldCb";
|
||||
|
||||
private ArrayList<KeyboardEventHandler.OnKeyboardDownloadEventListener> downloadEventListeners = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Additional Cloud API parameter: Is custom keyboard.
|
||||
*/
|
||||
public static final String PARAM_IS_CUSTOM = "is_custom";
|
||||
/**
|
||||
* Additional Cloud API parameter: language id.
|
||||
*/
|
||||
public static final String PARAM_LANG_ID = "lang_id";
|
||||
/**
|
||||
* Additional Cloud API parameter: keyboard id.
|
||||
*/
|
||||
public static final String PARAM_KB_ID = "kb_id";
|
||||
|
||||
/**
|
||||
* Listeners to notify about starting the data download.
|
||||
* @param aDownloadEventListeners
|
||||
*/
|
||||
public void setDownloadEventListeners(ArrayList<KeyboardEventHandler.OnKeyboardDownloadEventListener> aDownloadEventListeners) {
|
||||
downloadEventListeners.clear();
|
||||
downloadEventListeners.addAll(aDownloadEventListeners);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeContext(Context context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CloudKeyboardMetaDataDownloadCallback.MetaDataResult> extractCloudResultFromDownloadSet(
|
||||
CloudApiTypes.CloudDownloadSet<Void, List<CloudKeyboardMetaDataDownloadCallback.MetaDataResult>> aDownload) {
|
||||
|
||||
List<CloudKeyboardMetaDataDownloadCallback.MetaDataResult> _result = new ArrayList<>(aDownload.getSingleDownloads().size());
|
||||
|
||||
for (CloudApiTypes.SingleCloudDownload _d : aDownload.getSingleDownloads()) {
|
||||
|
||||
CloudApiTypes.CloudApiReturns _json_result = CloudDataJsonUtil.retrieveJsonFromDownload(_d);
|
||||
|
||||
if (_json_result!=null) {
|
||||
|
||||
CloudKeyboardMetaDataDownloadCallback.MetaDataResult _data = new CloudKeyboardMetaDataDownloadCallback.MetaDataResult();
|
||||
_data.returnjson = _json_result;
|
||||
_data.params = _d.getCloudParams();
|
||||
_result.add(_data); // Null if offline.
|
||||
}
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void applyCloudDownloadToModel(Context aContext, Void aModel, List<CloudKeyboardMetaDataDownloadCallback.MetaDataResult> aCloudResult)
|
||||
{
|
||||
if(aCloudResult.isEmpty()) {
|
||||
String msg = aContext.getString(R.string.catalog_unavailable);
|
||||
Toast.makeText(aContext, msg, Toast.LENGTH_SHORT).show();
|
||||
//throw new IllegalStateException("Could not reach server");
|
||||
return;
|
||||
}
|
||||
|
||||
processCloudResults(aCloudResult);
|
||||
|
||||
startDownloads(aContext, aCloudResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the keyboard data and lexical model download.
|
||||
* @param aContext the context
|
||||
* @param aMetaDataResult the meta data result
|
||||
*/
|
||||
private void startDownloads(Context aContext, List<MetaDataResult> aMetaDataResult) {
|
||||
for(MetaDataResult _r:aMetaDataResult)
|
||||
{
|
||||
if(_r.additionalDownloads!=null)
|
||||
{
|
||||
if(_r.returnjson.target== CloudApiTypes.ApiTarget.Keyboard)
|
||||
{
|
||||
CloudKeyboardDataDownloadCallback _callback = new CloudKeyboardDataDownloadCallback();
|
||||
_callback.setDownloadEventListeners(downloadEventListeners);
|
||||
_callback.setKeyboardInfo(_r.keyboardInfo);
|
||||
|
||||
if( CloudDownloadMgr.getInstance().alreadyDownloadingData(_r.additionalDownloadid))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
KeyboardEventHandler.notifyListeners(downloadEventListeners,
|
||||
KeyboardEventHandler.EventType.KEYBOARD_DOWNLOAD_STARTED, _r.keyboardInfo, 0);
|
||||
|
||||
CloudDownloadMgr.getInstance().executeAsDownload(aContext, _r.additionalDownloadid, null, _callback,
|
||||
_r.additionalDownloads.toArray(new CloudApiTypes.CloudApiParam[0]));
|
||||
}
|
||||
else if(_r.returnjson.target== CloudApiTypes.ApiTarget.KeyboardLexicalModels) {
|
||||
|
||||
if( CloudDownloadMgr.getInstance().alreadyDownloadingData(_r.additionalDownloadid))
|
||||
{
|
||||
Toast.makeText(aContext,
|
||||
aContext.getString(R.string.dictionary_download_is_running_in_background),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
continue;
|
||||
}
|
||||
CloudLexicalPackageDownloadCallback _callback = new CloudLexicalPackageDownloadCallback();
|
||||
_callback.setDownloadEventListeners(downloadEventListeners);
|
||||
|
||||
Toast.makeText(aContext,
|
||||
aContext.getString(R.string.dictionary_download_start_in_background),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
|
||||
CloudDownloadMgr.getInstance().executeAsDownload(aContext,
|
||||
_r.additionalDownloadid, null, _callback,
|
||||
_r.additionalDownloads.toArray(new CloudApiTypes.CloudApiParam[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* process the meta data result and prepare the additional downloads.
|
||||
* @param aMetaDataResult the meta data results
|
||||
*/
|
||||
private void processCloudResults(List<MetaDataResult> aMetaDataResult) {
|
||||
for(MetaDataResult _r:aMetaDataResult)
|
||||
{
|
||||
if(_r.returnjson.target== CloudApiTypes.ApiTarget.Keyboard)
|
||||
{
|
||||
handleKeyboardMetaData(_r);
|
||||
}
|
||||
if(_r.returnjson.target== CloudApiTypes.ApiTarget.KeyboardLexicalModels)
|
||||
{
|
||||
JSONArray lmData = _r.returnjson.jsonArray;
|
||||
if (lmData != null && lmData.length() > 0) {
|
||||
try
|
||||
{
|
||||
JSONObject modelInfo = lmData.getJSONObject(0);
|
||||
|
||||
if (modelInfo.has("packageFilename") && modelInfo.has("id"))
|
||||
{
|
||||
String _modelID = modelInfo.getString("id");
|
||||
ArrayList<CloudApiTypes.CloudApiParam> urls = new ArrayList<>();
|
||||
urls.add(new CloudApiTypes.CloudApiParam(
|
||||
CloudApiTypes.ApiTarget.LexicalModelPackage,
|
||||
modelInfo.getString("packageFilename")));
|
||||
_r.additionalDownloadid = CloudLexicalPackageDownloadCallback.createDownloadId(_modelID);
|
||||
_r.additionalDownloads= urls;
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Error parsing lexical model from api.keyman.com. " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* handle meta data result.
|
||||
* @param theKbData the data
|
||||
*/
|
||||
private void handleKeyboardMetaData(MetaDataResult theKbData)
|
||||
{
|
||||
if (theKbData.params.getAdditionalProperty(PARAM_IS_CUSTOM,Boolean.class)) {
|
||||
throw new IllegalStateException("Cannot download custom non-KMP keyboard");
|
||||
}
|
||||
|
||||
String _key_id = theKbData.params.getAdditionalProperty(PARAM_KB_ID,String.class);
|
||||
String _lang_id = theKbData.params.getAdditionalProperty(PARAM_LANG_ID,String.class);
|
||||
|
||||
String _kbIsCustom =
|
||||
theKbData.params.getAdditionalProperty(PARAM_IS_CUSTOM,Boolean.class) ? "Y" : "N";
|
||||
JSONObject _kb_data = theKbData.returnjson.jsonObject;
|
||||
|
||||
try {
|
||||
JSONObject options = _kb_data.optJSONObject(
|
||||
KMKeyboardDownloaderActivity.KMKey_Options);
|
||||
if (options == null) {
|
||||
throw new IllegalStateException("JSON file does not contain a valid \"options\" object");
|
||||
}
|
||||
|
||||
|
||||
// Keyman cloud _keyboard distribution via JSON
|
||||
JSONObject language = _kb_data.optJSONObject(
|
||||
KMKeyboardDownloaderActivity.KMKey_Language);
|
||||
if (language == null) {
|
||||
throw new IllegalStateException("JSON file does not contain a valid \"language\" object");
|
||||
}
|
||||
|
||||
String _langName = language.optString(KMManager.KMKey_Name, "");
|
||||
|
||||
JSONArray keyboards = language.getJSONArray(KMKeyboardDownloaderActivity.KMKey_LanguageKeyboards);
|
||||
|
||||
JSONObject _keyboard = CloudDataJsonUtil.findMatchingKeyboardByID(keyboards, _key_id);
|
||||
|
||||
_key_id = _keyboard.getString(KMManager.KMKey_ID);
|
||||
String _pkgID = _keyboard.optString(KMManager.KMKey_PackageID, KMManager.KMDefault_UndefinedPackageID);
|
||||
|
||||
String _kbName = _keyboard.optString(KMManager.KMKey_Name, "");
|
||||
String _kbVersion = _keyboard.optString(KMManager.KMKey_KeyboardVersion, "1.0");
|
||||
|
||||
if (_kbName.isEmpty() || _langName.isEmpty())
|
||||
throw new IllegalStateException("JSON file does not contain a valid base values for _keyboard object");
|
||||
|
||||
ArrayList<CloudApiTypes.CloudApiParam> urls = new ArrayList<>();
|
||||
|
||||
urls.add(prepareKeyboardPackageDownload(options, _keyboard));
|
||||
|
||||
JSONObject jsonFont = _keyboard.optJSONObject(KMManager.KMKey_Font);
|
||||
JSONObject jsonOskFont = _keyboard.optJSONObject(KMManager.KMKey_OskFont);
|
||||
|
||||
if (jsonFont != null) {
|
||||
CloudDataJsonUtil.updateFontSourceToTTFFont(jsonFont);
|
||||
}
|
||||
if (jsonOskFont != null) {
|
||||
CloudDataJsonUtil.updateFontSourceToTTFFont(jsonOskFont);
|
||||
}
|
||||
String fontBaseUri = options.optString(KMKeyboardDownloaderActivity.KMKey_FontBaseURI, "");
|
||||
|
||||
ArrayList<String> fontUrls = CloudDataJsonUtil.fontUrls(jsonFont, fontBaseUri, false);
|
||||
ArrayList<String> oskFontUrls = CloudDataJsonUtil.fontUrls(jsonOskFont, fontBaseUri, true);
|
||||
if (fontUrls != null) {
|
||||
for (String url : fontUrls) {
|
||||
urls.add(new CloudApiTypes.CloudApiParam(CloudApiTypes.ApiTarget.KeyboardData, url)
|
||||
.setAdditionalProperty(
|
||||
CloudKeyboardDataDownloadCallback.PARAM_PACKAGE, _pkgID));
|
||||
}
|
||||
}
|
||||
if (oskFontUrls != null) {
|
||||
for (String url : oskFontUrls) {
|
||||
if (!urls.contains(url))
|
||||
urls.add(new CloudApiTypes.CloudApiParam(CloudApiTypes.ApiTarget.KeyboardData, url)
|
||||
.setAdditionalProperty(
|
||||
CloudKeyboardDataDownloadCallback.PARAM_PACKAGE, _pkgID));
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
String _font = _keyboard.optString(KMManager.KMKey_Font);
|
||||
String _oskFont = _keyboard.optString(KMManager.KMKey_OskFont);
|
||||
|
||||
|
||||
theKbData.additionalDownloadid = CloudKeyboardDataDownloadCallback.createDownloadId(_key_id);
|
||||
theKbData.keyboardInfo = CloudDataJsonUtil
|
||||
.createKeyboardInfoMap(
|
||||
_pkgID, _lang_id, _langName, _key_id, _kbName, _kbVersion, _kbIsCustom, _font, _oskFont);
|
||||
theKbData.additionalDownloads = urls;
|
||||
}
|
||||
catch(JSONException _e)
|
||||
{
|
||||
Log.e(TAG,_e.getLocalizedMessage(),_e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* prepare keyboard package download.
|
||||
* @param aOptions the options from json
|
||||
* @param aKeyboard the keyboard
|
||||
* @return the result
|
||||
*/
|
||||
private CloudApiTypes.CloudApiParam prepareKeyboardPackageDownload(JSONObject aOptions, JSONObject aKeyboard)
|
||||
{
|
||||
String kbBaseUri = aOptions.optString(KMKeyboardDownloaderActivity.KMKey_KeyboardBaseURI, "");
|
||||
if (kbBaseUri.isEmpty()) {
|
||||
throw new IllegalStateException("JSON file does not contain a valid \"keyboardBaseUri\" object");
|
||||
}
|
||||
|
||||
String _kbVersion = aKeyboard.optString(KMManager.KMKey_KeyboardVersion, "1.0");
|
||||
String _kbFilename = aKeyboard.optString(KMKeyboardDownloaderActivity.KMKey_Filename, "");
|
||||
|
||||
if (_kbFilename.isEmpty())
|
||||
throw new IllegalStateException("JSON file does not contain a valid \"filename\" object");
|
||||
|
||||
String _js_filename = _kbFilename;
|
||||
if (FileUtils.hasJavaScriptExtension(_kbFilename)) {
|
||||
|
||||
int start = _kbFilename.lastIndexOf("/");
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
} else {
|
||||
start++;
|
||||
}
|
||||
if (!_kbFilename.contains("-")) {
|
||||
_js_filename = _kbFilename.substring(start, _kbFilename.length() - 3) + "-" + _kbVersion + ".js";
|
||||
} else {
|
||||
_js_filename = _kbFilename.substring(start);
|
||||
}
|
||||
}
|
||||
|
||||
String _pkgID = aKeyboard.optString(KMManager.KMKey_PackageID, KMManager.KMDefault_UndefinedPackageID);
|
||||
String kbUrl = kbBaseUri + _kbFilename;
|
||||
|
||||
return new CloudApiTypes.CloudApiParam(CloudApiTypes.ApiTarget.KeyboardData, kbUrl)
|
||||
.setAdditionalProperty(
|
||||
CloudKeyboardDataDownloadCallback.PARAM_PACKAGE, _pkgID)
|
||||
.setAdditionalProperty(
|
||||
CloudKeyboardDataDownloadCallback.PARAM_DESTINATION_FILE_NAME, _js_filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* create a download id for the keyboard metadata.
|
||||
* @param aLanguageId the language id
|
||||
* @param aKeyboardId the keyboard id
|
||||
* @return the result
|
||||
*/
|
||||
public static String createDownloadId(String aLanguageId, String aKeyboardId)
|
||||
{
|
||||
return "metadata_" + aLanguageId + "_" + aKeyboardId;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
package com.tavultesoft.kmea.data;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.tavultesoft.kmea.KeyboardEventHandler;
|
||||
import com.tavultesoft.kmea.R;
|
||||
import com.tavultesoft.kmea.packages.LexicalModelPackageProcessor;
|
||||
import com.tavultesoft.kmea.packages.PackageProcessor;
|
||||
import com.tavultesoft.kmea.util.FileUtils;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.tavultesoft.kmea.KMManager.KMDefault_UndefinedPackageID;
|
||||
|
||||
/**
|
||||
* Install lexical model.
|
||||
*/
|
||||
public class CloudLexicalPackageDownloadCallback implements ICloudDownloadCallback<
|
||||
Void, CloudKeyboardDownloadReturns>
|
||||
{
|
||||
|
||||
private static final String TAG = "CloudLexModelPKGDldCb";
|
||||
|
||||
private File resourceRoot;
|
||||
private File cacheDir;
|
||||
|
||||
private ArrayList<KeyboardEventHandler.OnKeyboardDownloadEventListener> downloadEventListeners = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* listeners to inform after installation is completed.
|
||||
* @param aDownloadEventListeners the listeners
|
||||
*/
|
||||
public void setDownloadEventListeners(ArrayList<KeyboardEventHandler.OnKeyboardDownloadEventListener> aDownloadEventListeners)
|
||||
{
|
||||
downloadEventListeners.clear();
|
||||
downloadEventListeners.addAll(aDownloadEventListeners);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeContext(Context context)
|
||||
{
|
||||
resourceRoot = new File(context.getDir("data", Context.MODE_PRIVATE).toString() + File.separator);
|
||||
cacheDir = context.getCacheDir();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloudKeyboardDownloadReturns extractCloudResultFromDownloadSet(
|
||||
CloudApiTypes.CloudDownloadSet<Void, CloudKeyboardDownloadReturns> aDownload)
|
||||
{
|
||||
LexicalModelPackageProcessor kmpProcessor = new LexicalModelPackageProcessor(resourceRoot);
|
||||
List<Map<String, String>> installedLexicalModels = null;
|
||||
|
||||
int _result = FileUtils.DOWNLOAD_SUCCESS;
|
||||
for(CloudApiTypes.SingleCloudDownload _d:aDownload.getSingleDownloads())
|
||||
{
|
||||
if (_d.getDestinationFile() != null && _d.getDestinationFile().length() > 0)
|
||||
{
|
||||
|
||||
try {
|
||||
|
||||
if (_d.getCloudParams().target== CloudApiTypes.ApiTarget.LexicalModelPackage) {
|
||||
installedLexicalModels = new LinkedList<>();
|
||||
// Extract the kmp. Validate it contains only lexical models, and then process the lexical model package
|
||||
File kmpFile = new File(cacheDir, FileUtils.getFilename(_d.getCloudParams().url));
|
||||
|
||||
FileUtils.copy(_d.getDestinationFile(), kmpFile);
|
||||
|
||||
String pkgTarget = kmpProcessor.getPackageTarget(kmpFile);
|
||||
if (pkgTarget.equals(PackageProcessor.PP_TARGET_LEXICAL_MODELS)) {
|
||||
File unzipPath = kmpProcessor.unzipKMP(kmpFile);
|
||||
installedLexicalModels.addAll(kmpProcessor.processKMP(kmpFile, unzipPath, PackageProcessor.PP_LEXICAL_MODELS_KEY));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException | JSONException _e)
|
||||
{
|
||||
Log.e(TAG,_e.getLocalizedMessage(),_e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_result = FileUtils.DOWNLOAD_ERROR;
|
||||
}
|
||||
}
|
||||
return new CloudKeyboardDownloadReturns(_result,installedLexicalModels);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void applyCloudDownloadToModel(Context aContext, Void aModel, CloudKeyboardDownloadReturns aCloudResult)
|
||||
{
|
||||
Toast.makeText(aContext,
|
||||
aContext.getString(R.string.dictionary_download_finished),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
|
||||
if(aCloudResult.installedLexicalModels != null)
|
||||
{
|
||||
KeyboardEventHandler.notifyListeners(downloadEventListeners, KeyboardEventHandler.EventType.LEXICAL_MODEL_INSTALLED,
|
||||
aCloudResult.installedLexicalModels, aCloudResult.kbdResult);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create a download id for the model.
|
||||
* @param aModelId the lexical model id
|
||||
* @return the result
|
||||
*/
|
||||
public static String createDownloadId(String aModelId)
|
||||
{
|
||||
return "dictionary_" + aModelId;
|
||||
}
|
||||
}
|
||||
|
|
@ -28,7 +28,9 @@ public class CloudRepository {
|
|||
static public final CloudRepository shared = new CloudRepository();
|
||||
private static final String TAG = "CloudRepository";
|
||||
|
||||
//TODO: Should be removed with the old implementation when downloadmanager impl works
|
||||
public static final boolean USE_DOWNLOAD_MANAGER = true;
|
||||
|
||||
public static final String DOWNLOAD_IDENTIFIER_CATALOGUE = "catalogue";
|
||||
|
||||
private Dataset memCachedDataset;
|
||||
|
|
@ -117,19 +119,15 @@ public class CloudRepository {
|
|||
|
||||
private CloudApiTypes.CloudApiParam prepareKeyboardUpdateQuery(Context aContext)
|
||||
{
|
||||
String deviceType = aContext.getString(R.string.device_type);
|
||||
if (deviceType.equals("AndroidTablet")) {
|
||||
deviceType = "androidtablet";
|
||||
} else {
|
||||
deviceType = "androidphone";
|
||||
}
|
||||
String deviceType = CloudDataJsonUtil.getDeviceTypeForCloudQuery(aContext);
|
||||
|
||||
// Retrieves the cloud-based keyboard catalog in Android's preferred format.
|
||||
String keyboardURL = String.format("%s?version=%s&device=%s&languageidtype=bcp47",
|
||||
KMKeyboardDownloaderActivity.kKeymanApiBaseURL, BuildConfig.VERSION_NAME, deviceType);
|
||||
|
||||
//cloudQueries[cloudQueryEntries++] = new CloudApiParam(ApiTarget.Keyboards, keyboardURL, JSONType.Object);
|
||||
return new CloudApiTypes.CloudApiParam(CloudApiTypes.ApiTarget.Keyboards, keyboardURL, CloudApiTypes.JSONType.Object);
|
||||
return new CloudApiTypes.CloudApiParam(
|
||||
CloudApiTypes.ApiTarget.Keyboards, keyboardURL).setType(CloudApiTypes.JSONType.Object);
|
||||
}
|
||||
|
||||
private CloudApiTypes.CloudApiParam prepareLexicalModellUpdateQuery(Context aContext)
|
||||
|
|
@ -139,7 +137,8 @@ public class CloudRepository {
|
|||
// query is ready!
|
||||
String lexicalURL = String.format("%s?q", KMKeyboardDownloaderActivity.kKeymanApiModelURL);
|
||||
|
||||
return new CloudApiTypes.CloudApiParam(CloudApiTypes.ApiTarget.LexicalModels, lexicalURL, CloudApiTypes.JSONType.Array);
|
||||
return new CloudApiTypes.CloudApiParam(CloudApiTypes.ApiTarget.LexicalModels, lexicalURL)
|
||||
.setType(CloudApiTypes.JSONType.Array);
|
||||
|
||||
|
||||
// TODO: We want a list of lexical models for every language with an installed resource (kbd, lex model)
|
||||
|
|
@ -167,7 +166,7 @@ public class CloudRepository {
|
|||
preCacheDataSet(context,updateHandler,onSuccess,onFailure);
|
||||
|
||||
if(USE_DOWNLOAD_MANAGER)
|
||||
downloadCatalogFromServer(context,updateHandler,onSuccess,onFailure);
|
||||
downloadMetaDataFromServer(context,updateHandler,onSuccess,onFailure);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -191,7 +190,7 @@ public class CloudRepository {
|
|||
|
||||
preCacheDataSet(context,updateHandler,onSuccess,onFailure);
|
||||
|
||||
downloadCatalogFromServer(context,updateHandler,onSuccess,onFailure);
|
||||
downloadMetaDataFromServer(context,updateHandler,onSuccess,onFailure);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -310,7 +309,7 @@ public class CloudRepository {
|
|||
* @param onFailure A callback to be triggered upon failure of a query.
|
||||
* @return A Dataset object implementing the Adapter interface to be asynchronously filled.
|
||||
*/
|
||||
private void downloadCatalogFromServer(@NonNull Context context, UpdateHandler updateHandler, Runnable onSuccess, Runnable onFailure) {
|
||||
private void downloadMetaDataFromServer(@NonNull Context context, UpdateHandler updateHandler, Runnable onSuccess, Runnable onFailure) {
|
||||
boolean loadKeyboardsFromCache = this.shouldUseCache(context, CloudDataJsonUtil.getKeyboardCacheFile(context));
|
||||
boolean loadLexicalModelsFromCache = this.shouldUseCache(context, CloudDataJsonUtil.getLexicalModelCacheFile(context));
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,11 @@ import android.content.Context;
|
|||
*/
|
||||
public interface ICloudDownloadCallback<ModelType,ResultType> {
|
||||
|
||||
/**
|
||||
* Initialize callback using context.
|
||||
* @param context the context
|
||||
*/
|
||||
void initializeContext(Context context);
|
||||
|
||||
/**
|
||||
* extract download result object from download set
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.os.Bundle;
|
|||
|
||||
import com.tavultesoft.kmea.KMKeyboardDownloaderActivity;
|
||||
import com.tavultesoft.kmea.KMManager;
|
||||
import com.tavultesoft.kmea.KeyboardPickerActivity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
|
@ -26,6 +27,12 @@ public class Keyboard implements Serializable, LanguageResource {
|
|||
this.map = kbdData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public boolean isNewKeyboard() {
|
||||
return map.get(KeyboardPickerActivity.KMKEY_INTERNAL_NEW_KEYBOARD)!=null;
|
||||
}
|
||||
|
||||
public String getResourceId() {
|
||||
return this.map.get(KMManager.KMKey_KeyboardID);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import org.json.JSONObject;
|
|||
* KMEA engine. This is primarily for installing keyboard packages.
|
||||
*/
|
||||
public class PackageProcessor {
|
||||
protected static File resourceRoot = null;
|
||||
protected File resourceRoot = null;
|
||||
|
||||
public static final String PP_DEFAULT_VERSION = "1.0";
|
||||
public static final String PP_DEFAULT_METADATA = "kmp.json";
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
<string name="keyboard_version" translatable="false">Keyboard version</string>
|
||||
<string name="help_link" translatable="false">Help link</string>
|
||||
<string name="uninstall_keyboard" translatable="false">Uninstall keyboard</string>
|
||||
<string name="keyboard_picker_new_keyboard_prefix" translatable="false">[new]</string>
|
||||
|
||||
<!-- Model Updates -->
|
||||
<string name="getting_model_catalog" translatable="false">Getting dictionary catalog.\nThis may take a while…</string>
|
||||
|
|
@ -58,10 +59,18 @@
|
|||
<string name="checking_model_download" translatable="false">Checking to download dictionary…</string>
|
||||
<string name="downloading_model" translatable="false">Downloading dictionary…</string>
|
||||
<string name="catalog_unavailable" translatable="false">The resource catalog is unavailable\n</string>
|
||||
|
||||
<!-- Background download messages-->
|
||||
<string name="catalog_download_start_in_background" translatable="false">Catalog update started in background.\n</string>
|
||||
<string name="catalog_download_is_running_in_background" translatable="false">The catalog is still downloading; please try again in a moment!\n</string>
|
||||
<string name="keyboard_download_start_in_background" translatable="false">Downloading keyboard started in Background</string>
|
||||
<string name="keyboard_download_is_running_in_background" translatable="false">The selected keyboard is already downloading; please try again in a moment!</string>
|
||||
<string name="keyboard_download_finished" translatable="false">Keyboard download is finished!</string>
|
||||
<string name="dictionary_download_start_in_background" translatable="false">Downloading dictionary started in Background</string>
|
||||
<string name="dictionary_download_is_running_in_background" translatable="false">The selected dictionary is already downloading; please try again in a moment!</string>
|
||||
<string name="dictionary_download_finished" translatable="false">Dictionary download is finished.</string>
|
||||
|
||||
<!-- General Updates -->
|
||||
<!-- General Updates -->
|
||||
<string name="update_check_unavailable" translatable="false">Failed to access server!\n</string>
|
||||
<string name="update_check_current" translatable="false">"All resources are up to date!"</string>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
## 13.0 alpha
|
||||
* Start version 13.0
|
||||
* New Features:
|
||||
* Adding a download manager to execute downloads in background (#2247)
|
||||
* Adding a download manager to execute downloads in background (#2247,#2275)
|
||||
* Improve custom package installation: Show readme.htm before starting installation process (#2286)
|
||||
* Update target Android SDK version to 29 (#2279)
|
||||
* Add linting to Debug builds and resolve lint errors (#2305)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue