/* * Copyright (C) 2011 The Android Open Source Project * modified * SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only */ package helium314.keyboard.latin import android.content.Context import helium314.keyboard.latin.common.FileUtils import helium314.keyboard.latin.common.LocaleUtils import helium314.keyboard.latin.common.LocaleUtils.constructLocale import helium314.keyboard.latin.settings.USER_DICTIONARY_SUFFIX import helium314.keyboard.latin.utils.DictionaryInfoUtils import helium314.keyboard.latin.utils.Log import java.io.File import java.util.LinkedList import java.util.Locale /** * Initializes a main dictionary collection from a dictionary pack, with explicit flags. * * * This searches for a content provider providing a dictionary pack for the specified * locale. If none is found, it falls back to the built-in dictionary - if any. * @param context application context for reading resources * @param locale the locale for which to create the dictionary * @return an initialized instance of DictionaryCollection */ fun createMainDictionary(context: Context, locale: Locale): DictionaryCollection { val cacheDir = DictionaryInfoUtils.getCacheDirectoryForLocale(locale, context) val dictList = LinkedList() // get cached dict files val (userDicts, extractedDicts) = DictionaryInfoUtils.getCachedDictsForLocale(locale, context) .partition { it.name.endsWith(USER_DICTIONARY_SUFFIX) } // add user dicts to list userDicts.forEach { checkAndAddDictionaryToListIfNotExisting(it, dictList, locale) } // add extracted dicts to list (after userDicts, to skip extracted dicts of same type) extractedDicts.forEach { checkAndAddDictionaryToListIfNotExisting(it, dictList, locale) } if (dictList.any { it.mDictType == Dictionary.TYPE_MAIN }) return DictionaryCollection(Dictionary.TYPE_MAIN, locale, dictList) // no main dict found -> check assets val assetsDicts = DictionaryInfoUtils.getAssetsDictionaryList(context) // file name is _.dict val dictsByType = assetsDicts?.groupBy { it.substringBefore("_") } // for each type find the best match dictsByType?.forEach { (dictType, dicts) -> val bestMatch = LocaleUtils.getBestMatch(locale, dicts) { it.substringAfter("_") .substringBefore(".").constructLocale() } ?: return@forEach // extract dict and add extracted file val targetFile = File(cacheDir, "$dictType.dict") FileUtils.copyStreamToNewFile( context.assets.open(DictionaryInfoUtils.ASSETS_DICTIONARY_FOLDER + File.separator + bestMatch), targetFile ) checkAndAddDictionaryToListIfNotExisting(targetFile, dictList, locale) } // If the list is empty, that means we should not use any dictionary (for example, the user // explicitly disabled the main dictionary), so the following is okay. dictList is never // null, but if for some reason it is, DictionaryCollection handles it gracefully. return DictionaryCollection(Dictionary.TYPE_MAIN, locale, dictList) } /** * add dictionary created from [file] to [dicts] * if [file] cannot be loaded it is deleted * if the dictionary type already exists in [dicts], the [file] is skipped */ private fun checkAndAddDictionaryToListIfNotExisting(file: File, dicts: MutableList, locale: Locale) { if (!file.isFile) return val header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(file) ?: return killDictionary(file) val dictType = header.mIdString.split(":").first() if (dicts.any { it.mDictType == dictType }) return val readOnlyBinaryDictionary = ReadOnlyBinaryDictionary( file.absolutePath, 0, file.length(), false, locale, dictType ) if (readOnlyBinaryDictionary.isValidDictionary) { if (locale.language == "ko") { // Use KoreanDictionary for Korean locale dicts.add(KoreanDictionary(readOnlyBinaryDictionary)) } else { dicts.add(readOnlyBinaryDictionary) } } else { readOnlyBinaryDictionary.close() killDictionary(file) } } private fun killDictionary(file: File) { Log.e("DictionaryFactory", "could not load dictionary ${file.parentFile?.name}/${file.name}, deleting") file.delete() }