2011-04-26 12:49:09 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2011 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
|
|
* use this file except in compliance with the License. You may obtain a copy of
|
|
|
|
* the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
* License for the specific language governing permissions and limitations under
|
|
|
|
* the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package com.android.inputmethod.latin;
|
|
|
|
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.res.AssetFileDescriptor;
|
|
|
|
import android.content.res.Resources;
|
|
|
|
import android.util.Log;
|
|
|
|
|
2012-04-03 05:28:56 +00:00
|
|
|
import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
|
|
|
|
|
2011-04-26 12:49:09 +00:00
|
|
|
import java.io.File;
|
2012-03-16 15:50:51 +00:00
|
|
|
import java.util.ArrayList;
|
2011-04-27 14:13:11 +00:00
|
|
|
import java.util.LinkedList;
|
2011-04-26 12:49:09 +00:00
|
|
|
import java.util.Locale;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Factory for dictionary instances.
|
|
|
|
*/
|
|
|
|
public class DictionaryFactory {
|
|
|
|
private static String TAG = DictionaryFactory.class.getSimpleName();
|
|
|
|
|
|
|
|
/**
|
2011-09-29 12:07:22 +00:00
|
|
|
* Initializes a dictionary from a dictionary pack, with explicit flags.
|
2011-04-26 12:49:09 +00:00
|
|
|
*
|
|
|
|
* This searches for a content provider providing a dictionary pack for the specified
|
|
|
|
* locale. If none is found, it falls back to using the resource passed as fallBackResId
|
|
|
|
* as a dictionary.
|
|
|
|
* @param context application context for reading resources
|
|
|
|
* @param locale the locale for which to create the dictionary
|
|
|
|
* @param fallbackResId the id of the resource to use as a fallback if no pack is found
|
2011-09-29 12:07:22 +00:00
|
|
|
* @param flagArray an array of flags to use
|
2011-08-18 10:29:29 +00:00
|
|
|
* @return an initialized instance of DictionaryCollection
|
2011-04-26 12:49:09 +00:00
|
|
|
*/
|
2011-09-29 12:07:22 +00:00
|
|
|
public static DictionaryCollection createDictionaryFromManager(final Context context,
|
|
|
|
final Locale locale, final int fallbackResId, final Flag[] flagArray) {
|
2011-04-26 12:49:09 +00:00
|
|
|
if (null == locale) {
|
|
|
|
Log.e(TAG, "No locale defined for dictionary");
|
2011-07-21 08:36:57 +00:00
|
|
|
return new DictionaryCollection(createBinaryDictionary(context, fallbackResId, locale));
|
2011-04-26 12:49:09 +00:00
|
|
|
}
|
|
|
|
|
2012-03-16 15:50:51 +00:00
|
|
|
final LinkedList<Dictionary> dictList = new LinkedList<Dictionary>();
|
|
|
|
final ArrayList<AssetFileAddress> assetFileList =
|
2011-07-19 09:16:34 +00:00
|
|
|
BinaryDictionaryGetter.getDictionaryFiles(locale, context, fallbackResId);
|
|
|
|
if (null != assetFileList) {
|
|
|
|
for (final AssetFileAddress f : assetFileList) {
|
2011-07-20 10:02:29 +00:00
|
|
|
final BinaryDictionary binaryDictionary =
|
2012-04-04 12:39:23 +00:00
|
|
|
new BinaryDictionary(context, f.mFilename, f.mOffset, f.mLength, flagArray,
|
|
|
|
locale);
|
2011-07-20 10:02:29 +00:00
|
|
|
if (binaryDictionary.isValidDictionary()) {
|
|
|
|
dictList.add(binaryDictionary);
|
|
|
|
}
|
2011-07-19 09:16:34 +00:00
|
|
|
}
|
2011-04-27 14:13:11 +00:00
|
|
|
}
|
|
|
|
|
2011-08-23 08:55:46 +00:00
|
|
|
// 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 new DictionaryCollection(dictList);
|
2011-04-26 12:49:09 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 12:07:22 +00:00
|
|
|
/**
|
|
|
|
* Initializes a dictionary from a dictionary pack, with default flags.
|
|
|
|
*
|
|
|
|
* This searches for a content provider providing a dictionary pack for the specified
|
|
|
|
* locale. If none is found, it falls back to using the resource passed as fallBackResId
|
|
|
|
* as a dictionary.
|
|
|
|
* @param context application context for reading resources
|
|
|
|
* @param locale the locale for which to create the dictionary
|
|
|
|
* @param fallbackResId the id of the resource to use as a fallback if no pack is found
|
|
|
|
* @return an initialized instance of DictionaryCollection
|
|
|
|
*/
|
|
|
|
public static DictionaryCollection createDictionaryFromManager(final Context context,
|
|
|
|
final Locale locale, final int fallbackResId) {
|
|
|
|
return createDictionaryFromManager(context, locale, fallbackResId, null);
|
|
|
|
}
|
|
|
|
|
2011-04-26 12:49:09 +00:00
|
|
|
/**
|
|
|
|
* Initializes a dictionary from a raw resource file
|
|
|
|
* @param context application context for reading resources
|
|
|
|
* @param resId the resource containing the raw binary dictionary
|
2011-07-21 08:36:57 +00:00
|
|
|
* @param locale the locale to use for the resource
|
2011-04-26 12:49:09 +00:00
|
|
|
* @return an initialized instance of BinaryDictionary
|
|
|
|
*/
|
2011-07-21 08:36:57 +00:00
|
|
|
protected static BinaryDictionary createBinaryDictionary(final Context context,
|
|
|
|
final int resId, final Locale locale) {
|
2011-04-26 12:49:09 +00:00
|
|
|
AssetFileDescriptor afd = null;
|
|
|
|
try {
|
2012-04-03 05:28:56 +00:00
|
|
|
final RunInLocale<AssetFileDescriptor> job = new RunInLocale<AssetFileDescriptor>() {
|
|
|
|
@Override
|
|
|
|
protected AssetFileDescriptor job(Resources res) {
|
|
|
|
return res.openRawResourceFd(resId);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
afd = job.runInLocale(context.getResources(), locale);
|
2011-04-26 12:49:09 +00:00
|
|
|
if (afd == null) {
|
|
|
|
Log.e(TAG, "Found the resource but it is compressed. resId=" + resId);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if (!isFullDictionary(afd)) return null;
|
|
|
|
final String sourceDir = context.getApplicationInfo().sourceDir;
|
|
|
|
final File packagePath = new File(sourceDir);
|
|
|
|
// TODO: Come up with a way to handle a directory.
|
|
|
|
if (!packagePath.isFile()) {
|
|
|
|
Log.e(TAG, "sourceDir is not a file: " + sourceDir);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return new BinaryDictionary(context,
|
2012-04-04 12:39:23 +00:00
|
|
|
sourceDir, afd.getStartOffset(), afd.getLength(), null, locale);
|
2011-04-26 12:49:09 +00:00
|
|
|
} catch (android.content.res.Resources.NotFoundException e) {
|
|
|
|
Log.e(TAG, "Could not find the resource. resId=" + resId);
|
|
|
|
return null;
|
|
|
|
} finally {
|
|
|
|
if (null != afd) {
|
|
|
|
try {
|
|
|
|
afd.close();
|
|
|
|
} catch (java.io.IOException e) {
|
|
|
|
/* IOException on close ? What am I supposed to do ? */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a dictionary from passed data. This is intended for unit tests only.
|
|
|
|
* @param context the test context to create this data from.
|
|
|
|
* @param dictionary the file to read
|
|
|
|
* @param startOffset the offset in the file where the data starts
|
|
|
|
* @param length the length of the data
|
|
|
|
* @param flagArray the flags to use with this data for testing
|
|
|
|
* @return the created dictionary, or null.
|
|
|
|
*/
|
|
|
|
public static Dictionary createDictionaryForTest(Context context, File dictionary,
|
|
|
|
long startOffset, long length, Flag[] flagArray) {
|
|
|
|
if (dictionary.isFile()) {
|
|
|
|
return new BinaryDictionary(context, dictionary.getAbsolutePath(), startOffset, length,
|
2012-04-04 12:39:23 +00:00
|
|
|
flagArray, null);
|
2011-04-26 12:49:09 +00:00
|
|
|
} else {
|
|
|
|
Log.e(TAG, "Could not find the file. path=" + dictionary.getAbsolutePath());
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find out whether a dictionary is available for this locale.
|
|
|
|
* @param context the context on which to check resources.
|
|
|
|
* @param locale the locale to check for.
|
|
|
|
* @return whether a (non-placeholder) dictionary is available or not.
|
|
|
|
*/
|
|
|
|
public static boolean isDictionaryAvailable(Context context, Locale locale) {
|
2012-04-03 05:28:56 +00:00
|
|
|
final RunInLocale<Boolean> job = new RunInLocale<Boolean>() {
|
|
|
|
@Override
|
|
|
|
protected Boolean job(Resources res) {
|
|
|
|
final int resourceId = getMainDictionaryResourceId(res);
|
|
|
|
final AssetFileDescriptor afd = res.openRawResourceFd(resourceId);
|
|
|
|
final boolean hasDictionary = isFullDictionary(afd);
|
|
|
|
try {
|
|
|
|
if (null != afd) afd.close();
|
|
|
|
} catch (java.io.IOException e) {
|
|
|
|
/* Um, what can we do here exactly? */
|
|
|
|
}
|
|
|
|
return hasDictionary;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return job.runInLocale(context.getResources(), locale);
|
2011-04-26 12:49:09 +00:00
|
|
|
}
|
|
|
|
|
2011-05-11 06:19:24 +00:00
|
|
|
// TODO: Do not use the size of the dictionary as an unique dictionary ID.
|
2011-08-26 11:22:47 +00:00
|
|
|
public static Long getDictionaryId(final Context context, final Locale locale) {
|
2012-04-03 05:28:56 +00:00
|
|
|
final RunInLocale<Long> job = new RunInLocale<Long>() {
|
|
|
|
@Override
|
|
|
|
protected Long job(Resources res) {
|
|
|
|
final int resourceId = getMainDictionaryResourceId(res);
|
|
|
|
final AssetFileDescriptor afd = res.openRawResourceFd(resourceId);
|
|
|
|
final Long size = (afd != null && afd.getLength() > PLACEHOLDER_LENGTH)
|
|
|
|
? afd.getLength()
|
|
|
|
: null;
|
|
|
|
try {
|
|
|
|
if (null != afd) afd.close();
|
|
|
|
} catch (java.io.IOException e) {
|
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return job.runInLocale(context.getResources(), locale);
|
2011-05-11 06:19:24 +00:00
|
|
|
}
|
|
|
|
|
2011-04-26 12:49:09 +00:00
|
|
|
// TODO: Find the Right Way to find out whether the resource is a placeholder or not.
|
|
|
|
// Suggestion : strip the locale, open the placeholder file and store its offset.
|
|
|
|
// Upon opening the file, if it's the same offset, then it's the placeholder.
|
|
|
|
private static final long PLACEHOLDER_LENGTH = 34;
|
|
|
|
/**
|
|
|
|
* Finds out whether the data pointed out by an AssetFileDescriptor is a full
|
|
|
|
* dictionary (as opposed to null, or to a place holder).
|
|
|
|
* @param afd the file descriptor to test, or null
|
|
|
|
* @return true if the dictionary is a real full dictionary, false if it's null or a placeholder
|
|
|
|
*/
|
|
|
|
protected static boolean isFullDictionary(final AssetFileDescriptor afd) {
|
|
|
|
return (afd != null && afd.getLength() > PLACEHOLDER_LENGTH);
|
|
|
|
}
|
2012-03-08 08:07:02 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a main dictionary resource id
|
|
|
|
* @return main dictionary resource id
|
|
|
|
*/
|
|
|
|
public static int getMainDictionaryResourceId(Resources res) {
|
|
|
|
final String MAIN_DIC_NAME = "main";
|
|
|
|
String packageName = LatinIME.class.getPackage().getName();
|
|
|
|
return res.getIdentifier(MAIN_DIC_NAME, "raw", packageName);
|
|
|
|
}
|
2011-04-26 12:49:09 +00:00
|
|
|
}
|