diff options
Diffstat (limited to 'java/src/com')
7 files changed, 128 insertions, 10 deletions
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryPackConstants.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryPackConstants.java index 0c8b466a4..69615887f 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryPackConstants.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryPackConstants.java @@ -25,16 +25,34 @@ package com.android.inputmethod.dictionarypack; */ public class DictionaryPackConstants { /** + * The root domain for the dictionary pack, upon which authorities and actions will append + * their own distinctive strings. + */ + private static final String DICTIONARY_DOMAIN = "com.android.inputmethod.dictionarypack"; + + /** * Authority for the ContentProvider protocol. */ // TODO: find some way to factorize this string with the one in the resources - public static final String AUTHORITY = "com.android.inputmethod.dictionarypack.aosp"; + public static final String AUTHORITY = DICTIONARY_DOMAIN + ".aosp"; /** * The action of the intent for publishing that new dictionary data is available. */ // TODO: make this different across different packages. A suggested course of action is // to use the package name inside this string. - public static final String NEW_DICTIONARY_INTENT_ACTION = - "com.android.inputmethod.dictionarypack.newdict"; + // NOTE: The appended string should be uppercase like all other actions, but it's not for + // historical reasons. + public static final String NEW_DICTIONARY_INTENT_ACTION = DICTIONARY_DOMAIN + ".newdict"; + + /** + * The action of the intent sent by the dictionary pack to ask for a client to make + * itself known. This is used when the settings activity is brought up for a client the + * dictionary pack does not know about. + */ + public static final String UNKNOWN_DICTIONARY_PROVIDER_CLIENT = DICTIONARY_DOMAIN + + ".UNKNOWN_CLIENT"; + // In the above intents, the name of the string extra that contains the name of the client + // we want information about. + public static final String DICTIONARY_PROVIDER_CLIENT_EXTRA = "client"; } diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java index 77b3b8e2e..f8d1c4fc9 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java @@ -509,6 +509,11 @@ public final class DictionaryProvider extends ContentProvider { } catch (final BadFormatException e) { Log.w(TAG, "Not enough information to insert this dictionary " + values, e); } + // We just received new information about the list of dictionary for this client. + // For all intents and purposes, this is new metadata, so we should publish it + // so that any listeners (like the Settings interface for example) can update + // themselves. + UpdateHandler.publishUpdateMetadataCompleted(getContext(), true); break; case DICTIONARY_V1_WHOLE_LIST: case DICTIONARY_V1_DICT_INFO: diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java index e85bb0d4a..9e27c1f3f 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java @@ -110,6 +110,15 @@ public final class DictionarySettingsFragment extends PreferenceFragment super.onResume(); mChangedSettings = false; UpdateHandler.registerUpdateEventListener(this); + final Activity activity = getActivity(); + if (!MetadataDbHelper.isClientKnown(activity, mClientId)) { + Log.i(TAG, "Unknown dictionary pack client: " + mClientId + ". Requesting info."); + final Intent unknownClientBroadcast = + new Intent(DictionaryPackConstants.UNKNOWN_DICTIONARY_PROVIDER_CLIENT); + unknownClientBroadcast.putExtra( + DictionaryPackConstants.DICTIONARY_PROVIDER_CLIENT_EXTRA, mClientId); + activity.sendBroadcast(unknownClientBroadcast); + } final IntentFilter filter = new IntentFilter(); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); getActivity().registerReceiver(mConnectivityChangedReceiver, filter); @@ -363,7 +372,12 @@ public final class DictionarySettingsFragment extends PreferenceFragment getActivity(), android.R.anim.fade_out)); preferenceView.startAnimation(AnimationUtils.loadAnimation( getActivity(), android.R.anim.fade_in)); - mUpdateNowMenu.setTitle(R.string.check_for_updates_now); + // The menu is created by the framework asynchronously after the activity, + // which means it's possible to have the activity running but the menu not + // created yet - hence the necessity for a null check here. + if (null != mUpdateNowMenu) { + mUpdateNowMenu.setTitle(R.string.check_for_updates_now); + } } }); } diff --git a/java/src/com/android/inputmethod/dictionarypack/EventHandler.java b/java/src/com/android/inputmethod/dictionarypack/EventHandler.java index 96c4a8305..d8aa33bb8 100644 --- a/java/src/com/android/inputmethod/dictionarypack/EventHandler.java +++ b/java/src/com/android/inputmethod/dictionarypack/EventHandler.java @@ -16,13 +16,9 @@ package com.android.inputmethod.dictionarypack; -import com.android.inputmethod.latin.LatinIME; -import com.android.inputmethod.latin.R; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.util.Log; public final class EventHandler extends BroadcastReceiver { private static final String TAG = EventHandler.class.getName(); diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java index b4727509c..e05a79b7b 100644 --- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java +++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java @@ -444,7 +444,19 @@ public final class UpdateHandler { manager.remove(fileId); } - private static void publishUpdateMetadataCompleted(final Context context, + /** + * Sends a broadcast informing listeners that the dictionaries were updated. + * + * This will call all local listeners through the UpdateEventListener#downloadedMetadata + * callback (for example, the dictionary provider interface uses this to stop the Loading + * animation) and send a broadcast about the metadata having been updated. For a client of + * the dictionary pack like Latin IME, this means it should re-query the dictionary pack + * for any relevant new data. + * + * @param context the context, to send the broadcast. + * @param downloadSuccessful whether the download of the metadata was successful or not. + */ + public static void publishUpdateMetadataCompleted(final Context context, final boolean downloadSuccessful) { // We need to warn all listeners of what happened. But some listeners may want to // remove themselves or re-register something in response. Hence we should take a diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index 4bec99c04..562e1d0b7 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -450,4 +450,25 @@ public final class BinaryDictionaryFileDumper { info.toContentValues()); } } + + /** + * Initialize a client record with the dictionary content provider. + * + * This merely acquires the content provider and calls + * #reinitializeClientRecordInDictionaryContentProvider. + * + * @param context the context for resources and providers. + * @param clientId the client ID to use. + */ + public static void initializeClientRecordHelper(final Context context, + final String clientId) { + try { + final ContentProviderClient client = context.getContentResolver(). + acquireContentProviderClient(getProviderUriBuilder("").build()); + if (null == client) return; + reinitializeClientRecordInDictionaryContentProvider(context, client, clientId); + } catch (RemoteException e) { + Log.e(TAG, "Cannot contact the dictionary content provider", e); + } + } } diff --git a/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java b/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java index 35f3119ea..41fcb83e6 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java +++ b/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java @@ -25,14 +25,35 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.net.Uri; +import android.util.Log; /** - * Takes action to reload the necessary data when a dictionary pack was added/removed. + * Receives broadcasts pertaining to dictionary management and takes the appropriate action. + * + * This object receives three types of broadcasts. + * - Package installed/added. When a dictionary provider application is added or removed, we + * need to query the dictionaries. + * - New dictionary broadcast. The dictionary provider broadcasts new dictionary availability. When + * this happens, we need to re-query the dictionaries. + * - Unknown client. If the dictionary provider is in urgent need of data about some client that + * it does not know, it sends this broadcast. When we receive this, we need to tell the dictionary + * provider about ourselves. This happens when the settings for the dictionary pack are accessed, + * but Latin IME never got a chance to register itself. */ public final class DictionaryPackInstallBroadcastReceiver extends BroadcastReceiver { + private static final String TAG = DictionaryPackInstallBroadcastReceiver.class.getSimpleName(); final LatinIME mService; + public DictionaryPackInstallBroadcastReceiver() { + // This empty constructor is necessary for the system to instantiate this receiver. + // This happens when the dictionary pack says it can't find a record for our client, + // which happens when the dictionary pack settings are called before the keyboard + // was ever started once. + Log.i(TAG, "Latin IME dictionary broadcast receiver instantiated from the framework."); + mService = null; + } + public DictionaryPackInstallBroadcastReceiver(final LatinIME service) { mService = service; } @@ -44,6 +65,11 @@ public final class DictionaryPackInstallBroadcastReceiver extends BroadcastRecei // We need to reread the dictionary if a new dictionary package is installed. if (action.equals(Intent.ACTION_PACKAGE_ADDED)) { + if (null == mService) { + Log.e(TAG, "Called with intent " + action + " but we don't know the service: this " + + "should never happen"); + return; + } final Uri packageUri = intent.getData(); if (null == packageUri) return; // No package name : we can't do anything final String packageName = packageUri.getSchemeSpecificPart(); @@ -71,6 +97,11 @@ public final class DictionaryPackInstallBroadcastReceiver extends BroadcastRecei return; } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED) && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + if (null == mService) { + Log.e(TAG, "Called with intent " + action + " but we don't know the service: this " + + "should never happen"); + return; + } // When the dictionary package is removed, we need to reread dictionary (to use the // next-priority one, or stop using a dictionary at all if this was the only one, // since this is the user request). @@ -82,7 +113,28 @@ public final class DictionaryPackInstallBroadcastReceiver extends BroadcastRecei // read dictionary from? mService.resetSuggestMainDict(); } else if (action.equals(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION)) { + if (null == mService) { + Log.e(TAG, "Called with intent " + action + " but we don't know the service: this " + + "should never happen"); + return; + } mService.resetSuggestMainDict(); + } else if (action.equals(DictionaryPackConstants.UNKNOWN_DICTIONARY_PROVIDER_CLIENT)) { + if (null != mService) { + // Careful! This is returning if the service is NOT null. This is because we + // should come here instantiated by the framework in reaction to a broadcast of + // the above action, so we should gave gone through the no-args constructor. + Log.e(TAG, "Called with intent " + action + " but we have a reference to the " + + "service: this should never happen"); + return; + } + // The dictionary provider does not know about some client. We check that it's really + // us that it needs to know about, and if it's the case, we register with the provider. + final String wantedClientId = + intent.getStringExtra(DictionaryPackConstants.DICTIONARY_PROVIDER_CLIENT_EXTRA); + final String myClientId = context.getString(R.string.dictionary_pack_client_id); + if (!wantedClientId.equals(myClientId)) return; // Not for us + BinaryDictionaryFileDumper.initializeClientRecordHelper(context, myClientId); } } } |