* commit 'e40d5633659a401e1357e1c530238ed74cc158f6': Ask the client to make itself known when it's not
This commit is contained in:
commit
873c20ba06
8 changed files with 134 additions and 10 deletions
|
@ -95,6 +95,12 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
<receiver android:name=".DictionaryPackInstallBroadcastReceiver">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="com.android.inputmethod.dictionarypack.UNKNOWN_CLIENT" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
<provider android:name="com.android.inputmethod.dictionarypack.DictionaryProvider"
|
<provider android:name="com.android.inputmethod.dictionarypack.DictionaryProvider"
|
||||||
android:grantUriPermissions="true"
|
android:grantUriPermissions="true"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
|
|
|
@ -24,17 +24,35 @@ package com.android.inputmethod.dictionarypack;
|
||||||
* is needed in particular to cleanly compile regression tests.
|
* is needed in particular to cleanly compile regression tests.
|
||||||
*/
|
*/
|
||||||
public class DictionaryPackConstants {
|
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.
|
* Authority for the ContentProvider protocol.
|
||||||
*/
|
*/
|
||||||
// TODO: find some way to factorize this string with the one in the resources
|
// 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.
|
* 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
|
// TODO: make this different across different packages. A suggested course of action is
|
||||||
// to use the package name inside this string.
|
// to use the package name inside this string.
|
||||||
public static final String NEW_DICTIONARY_INTENT_ACTION =
|
// NOTE: The appended string should be uppercase like all other actions, but it's not for
|
||||||
"com.android.inputmethod.dictionarypack.newdict";
|
// 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";
|
||||||
}
|
}
|
||||||
|
|
|
@ -509,6 +509,11 @@ public final class DictionaryProvider extends ContentProvider {
|
||||||
} catch (final BadFormatException e) {
|
} catch (final BadFormatException e) {
|
||||||
Log.w(TAG, "Not enough information to insert this dictionary " + values, 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;
|
break;
|
||||||
case DICTIONARY_V1_WHOLE_LIST:
|
case DICTIONARY_V1_WHOLE_LIST:
|
||||||
case DICTIONARY_V1_DICT_INFO:
|
case DICTIONARY_V1_DICT_INFO:
|
||||||
|
|
|
@ -110,6 +110,15 @@ public final class DictionarySettingsFragment extends PreferenceFragment
|
||||||
super.onResume();
|
super.onResume();
|
||||||
mChangedSettings = false;
|
mChangedSettings = false;
|
||||||
UpdateHandler.registerUpdateEventListener(this);
|
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();
|
final IntentFilter filter = new IntentFilter();
|
||||||
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||||
getActivity().registerReceiver(mConnectivityChangedReceiver, filter);
|
getActivity().registerReceiver(mConnectivityChangedReceiver, filter);
|
||||||
|
@ -363,8 +372,13 @@ public final class DictionarySettingsFragment extends PreferenceFragment
|
||||||
getActivity(), android.R.anim.fade_out));
|
getActivity(), android.R.anim.fade_out));
|
||||||
preferenceView.startAnimation(AnimationUtils.loadAnimation(
|
preferenceView.startAnimation(AnimationUtils.loadAnimation(
|
||||||
getActivity(), android.R.anim.fade_in));
|
getActivity(), android.R.anim.fade_in));
|
||||||
|
// 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);
|
mUpdateNowMenu.setTitle(R.string.check_for_updates_now);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,9 @@
|
||||||
|
|
||||||
package com.android.inputmethod.dictionarypack;
|
package com.android.inputmethod.dictionarypack;
|
||||||
|
|
||||||
import com.android.inputmethod.latin.LatinIME;
|
|
||||||
import com.android.inputmethod.latin.R;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
public final class EventHandler extends BroadcastReceiver {
|
public final class EventHandler extends BroadcastReceiver {
|
||||||
private static final String TAG = EventHandler.class.getName();
|
private static final String TAG = EventHandler.class.getName();
|
||||||
|
|
|
@ -444,7 +444,19 @@ public final class UpdateHandler {
|
||||||
manager.remove(fileId);
|
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) {
|
final boolean downloadSuccessful) {
|
||||||
// We need to warn all listeners of what happened. But some listeners may want to
|
// 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
|
// remove themselves or re-register something in response. Hence we should take a
|
||||||
|
|
|
@ -450,4 +450,25 @@ public final class BinaryDictionaryFileDumper {
|
||||||
info.toContentValues());
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,14 +25,35 @@ import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ProviderInfo;
|
import android.content.pm.ProviderInfo;
|
||||||
import android.net.Uri;
|
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 {
|
public final class DictionaryPackInstallBroadcastReceiver extends BroadcastReceiver {
|
||||||
|
private static final String TAG = DictionaryPackInstallBroadcastReceiver.class.getSimpleName();
|
||||||
|
|
||||||
final LatinIME mService;
|
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) {
|
public DictionaryPackInstallBroadcastReceiver(final LatinIME service) {
|
||||||
mService = 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.
|
// We need to reread the dictionary if a new dictionary package is installed.
|
||||||
if (action.equals(Intent.ACTION_PACKAGE_ADDED)) {
|
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();
|
final Uri packageUri = intent.getData();
|
||||||
if (null == packageUri) return; // No package name : we can't do anything
|
if (null == packageUri) return; // No package name : we can't do anything
|
||||||
final String packageName = packageUri.getSchemeSpecificPart();
|
final String packageName = packageUri.getSchemeSpecificPart();
|
||||||
|
@ -71,6 +97,11 @@ public final class DictionaryPackInstallBroadcastReceiver extends BroadcastRecei
|
||||||
return;
|
return;
|
||||||
} else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
|
} else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
|
||||||
&& !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
|
&& !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
|
// 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,
|
// next-priority one, or stop using a dictionary at all if this was the only one,
|
||||||
// since this is the user request).
|
// since this is the user request).
|
||||||
|
@ -82,7 +113,28 @@ public final class DictionaryPackInstallBroadcastReceiver extends BroadcastRecei
|
||||||
// read dictionary from?
|
// read dictionary from?
|
||||||
mService.resetSuggestMainDict();
|
mService.resetSuggestMainDict();
|
||||||
} else if (action.equals(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION)) {
|
} 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();
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue