1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.inputmethod.latin; 18 19 import android.content.Context; 20 import android.content.res.AssetFileDescriptor; 21 import android.content.res.Resources; 22 import android.util.Log; 23 24 import java.io.File; 25 import java.util.ArrayList; 26 import java.util.LinkedList; 27 import java.util.Locale; 28 29 /** 30 * Factory for dictionary instances. 31 */ 32 public final class DictionaryFactory { 33 private static final String TAG = DictionaryFactory.class.getSimpleName(); 34 35 /** 36 * Initializes a main dictionary collection from a dictionary pack, with explicit flags. 37 * 38 * This searches for a content provider providing a dictionary pack for the specified 39 * locale. If none is found, it falls back to the built-in dictionary - if any. 40 * @param context application context for reading resources 41 * @param locale the locale for which to create the dictionary 42 * @param useFullEditDistance whether to use the full edit distance in suggestions 43 * @return an initialized instance of DictionaryCollection 44 */ 45 public static DictionaryCollection createMainDictionaryFromManager(final Context context, 46 final Locale locale, final boolean useFullEditDistance) { 47 if (null == locale) { 48 Log.e(TAG, "No locale defined for dictionary"); 49 return new DictionaryCollection(Dictionary.TYPE_MAIN, 50 createBinaryDictionary(context, locale)); 51 } 52 53 final LinkedList<Dictionary> dictList = CollectionUtils.newLinkedList(); 54 final ArrayList<AssetFileAddress> assetFileList = 55 BinaryDictionaryGetter.getDictionaryFiles(locale, context); 56 if (null != assetFileList) { 57 for (final AssetFileAddress f : assetFileList) { 58 final BinaryDictionary binaryDictionary = new BinaryDictionary(f.mFilename, 59 f.mOffset, f.mLength, useFullEditDistance, locale, Dictionary.TYPE_MAIN); 60 if (binaryDictionary.isValidDictionary()) { 61 dictList.add(binaryDictionary); 62 } 63 } 64 } 65 66 // If the list is empty, that means we should not use any dictionary (for example, the user 67 // explicitly disabled the main dictionary), so the following is okay. dictList is never 68 // null, but if for some reason it is, DictionaryCollection handles it gracefully. 69 return new DictionaryCollection(Dictionary.TYPE_MAIN, dictList); 70 } 71 72 /** 73 * Initializes a main dictionary collection from a dictionary pack, with default flags. 74 * 75 * This searches for a content provider providing a dictionary pack for the specified 76 * locale. If none is found, it falls back to the built-in dictionary, if any. 77 * @param context application context for reading resources 78 * @param locale the locale for which to create the dictionary 79 * @return an initialized instance of DictionaryCollection 80 */ 81 public static DictionaryCollection createMainDictionaryFromManager(final Context context, 82 final Locale locale) { 83 return createMainDictionaryFromManager(context, locale, false /* useFullEditDistance */); 84 } 85 86 /** 87 * Initializes a dictionary from a raw resource file 88 * @param context application context for reading resources 89 * @param locale the locale to use for the resource 90 * @return an initialized instance of BinaryDictionary 91 */ 92 protected static BinaryDictionary createBinaryDictionary(final Context context, 93 final Locale locale) { 94 AssetFileDescriptor afd = null; 95 try { 96 final int resId = DictionaryInfoUtils.getMainDictionaryResourceIdIfAvailableForLocale( 97 context.getResources(), locale); 98 if (0 == resId) return null; 99 afd = context.getResources().openRawResourceFd(resId); 100 if (afd == null) { 101 Log.e(TAG, "Found the resource but it is compressed. resId=" + resId); 102 return null; 103 } 104 final String sourceDir = context.getApplicationInfo().sourceDir; 105 final File packagePath = new File(sourceDir); 106 // TODO: Come up with a way to handle a directory. 107 if (!packagePath.isFile()) { 108 Log.e(TAG, "sourceDir is not a file: " + sourceDir); 109 return null; 110 } 111 return new BinaryDictionary(sourceDir, afd.getStartOffset(), afd.getLength(), 112 false /* useFullEditDistance */, locale, Dictionary.TYPE_MAIN); 113 } catch (android.content.res.Resources.NotFoundException e) { 114 Log.e(TAG, "Could not find the resource"); 115 return null; 116 } finally { 117 if (null != afd) { 118 try { 119 afd.close(); 120 } catch (java.io.IOException e) { 121 /* IOException on close ? What am I supposed to do ? */ 122 } 123 } 124 } 125 } 126 127 /** 128 * Create a dictionary from passed data. This is intended for unit tests only. 129 * @param dictionary the file to read 130 * @param startOffset the offset in the file where the data starts 131 * @param length the length of the data 132 * @param useFullEditDistance whether to use the full edit distance in suggestions 133 * @return the created dictionary, or null. 134 */ 135 public static Dictionary createDictionaryForTest(File dictionary, long startOffset, long length, 136 final boolean useFullEditDistance, Locale locale) { 137 if (dictionary.isFile()) { 138 return new BinaryDictionary(dictionary.getAbsolutePath(), startOffset, length, 139 useFullEditDistance, locale, Dictionary.TYPE_MAIN); 140 } else { 141 Log.e(TAG, "Could not find the file. path=" + dictionary.getAbsolutePath()); 142 return null; 143 } 144 } 145 146 /** 147 * Find out whether a dictionary is available for this locale. 148 * @param context the context on which to check resources. 149 * @param locale the locale to check for. 150 * @return whether a (non-placeholder) dictionary is available or not. 151 */ 152 public static boolean isDictionaryAvailable(Context context, Locale locale) { 153 final Resources res = context.getResources(); 154 return 0 != DictionaryInfoUtils.getMainDictionaryResourceIdIfAvailableForLocale( 155 res, locale); 156 } 157 } 158