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
17package com.android.inputmethod.latin;
18
19import com.android.inputmethod.dictionarypack.DictionaryPackConstants;
20import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask;
21
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.pm.PackageInfo;
26import android.content.pm.PackageManager;
27import android.content.pm.ProviderInfo;
28import android.net.Uri;
29import android.util.Log;
30
31/**
32 * Receives broadcasts pertaining to dictionary management and takes the appropriate action.
33 *
34 * This object receives three types of broadcasts.
35 * - Package installed/added. When a dictionary provider application is added or removed, we
36 * need to query the dictionaries.
37 * - New dictionary broadcast. The dictionary provider broadcasts new dictionary availability. When
38 * this happens, we need to re-query the dictionaries.
39 * - Unknown client. If the dictionary provider is in urgent need of data about some client that
40 * it does not know, it sends this broadcast. When we receive this, we need to tell the dictionary
41 * provider about ourselves. This happens when the settings for the dictionary pack are accessed,
42 * but Latin IME never got a chance to register itself.
43 */
44public final class DictionaryPackInstallBroadcastReceiver extends BroadcastReceiver {
45    private static final String TAG = DictionaryPackInstallBroadcastReceiver.class.getSimpleName();
46
47    final LatinIME mService;
48
49    public DictionaryPackInstallBroadcastReceiver() {
50        // This empty constructor is necessary for the system to instantiate this receiver.
51        // This happens when the dictionary pack says it can't find a record for our client,
52        // which happens when the dictionary pack settings are called before the keyboard
53        // was ever started once.
54        Log.i(TAG, "Latin IME dictionary broadcast receiver instantiated from the framework.");
55        mService = null;
56    }
57
58    public DictionaryPackInstallBroadcastReceiver(final LatinIME service) {
59        mService = service;
60    }
61
62    @Override
63    public void onReceive(Context context, Intent intent) {
64        final String action = intent.getAction();
65        final PackageManager manager = context.getPackageManager();
66
67        // We need to reread the dictionary if a new dictionary package is installed.
68        if (action.equals(Intent.ACTION_PACKAGE_ADDED)) {
69            if (null == mService) {
70                Log.e(TAG, "Called with intent " + action + " but we don't know the service: this "
71                        + "should never happen");
72                return;
73            }
74            final Uri packageUri = intent.getData();
75            if (null == packageUri) return; // No package name : we can't do anything
76            final String packageName = packageUri.getSchemeSpecificPart();
77            if (null == packageName) return;
78            // TODO: do this in a more appropriate place
79            TargetPackageInfoGetterTask.removeCachedPackageInfo(packageName);
80            final PackageInfo packageInfo;
81            try {
82                packageInfo = manager.getPackageInfo(packageName, PackageManager.GET_PROVIDERS);
83            } catch (android.content.pm.PackageManager.NameNotFoundException e) {
84                return; // No package info : we can't do anything
85            }
86            final ProviderInfo[] providers = packageInfo.providers;
87            if (null == providers) return; // No providers : it is not a dictionary.
88
89            // Search for some dictionary pack in the just-installed package. If found, reread.
90            for (ProviderInfo info : providers) {
91                if (DictionaryPackConstants.AUTHORITY.equals(info.authority)) {
92                    mService.resetSuggestMainDict();
93                    return;
94                }
95            }
96            // If we come here none of the authorities matched the one we searched for.
97            // We can exit safely.
98            return;
99        } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
100                && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
101            if (null == mService) {
102                Log.e(TAG, "Called with intent " + action + " but we don't know the service: this "
103                        + "should never happen");
104                return;
105            }
106            // When the dictionary package is removed, we need to reread dictionary (to use the
107            // next-priority one, or stop using a dictionary at all if this was the only one,
108            // since this is the user request).
109            // If we are replacing the package, we will receive ADDED right away so no need to
110            // remove the dictionary at the moment, since we will do it when we receive the
111            // ADDED broadcast.
112
113            // TODO: Only reload dictionary on REMOVED when the removed package is the one we
114            // read dictionary from?
115            mService.resetSuggestMainDict();
116        } else if (action.equals(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION)) {
117            if (null == mService) {
118                Log.e(TAG, "Called with intent " + action + " but we don't know the service: this "
119                        + "should never happen");
120                return;
121            }
122            mService.resetSuggestMainDict();
123        } else if (action.equals(DictionaryPackConstants.UNKNOWN_DICTIONARY_PROVIDER_CLIENT)) {
124            if (null != mService) {
125                // Careful! This is returning if the service is NOT null. This is because we
126                // should come here instantiated by the framework in reaction to a broadcast of
127                // the above action, so we should gave gone through the no-args constructor.
128                Log.e(TAG, "Called with intent " + action + " but we have a reference to the "
129                        + "service: this should never happen");
130                return;
131            }
132            // The dictionary provider does not know about some client. We check that it's really
133            // us that it needs to know about, and if it's the case, we register with the provider.
134            final String wantedClientId =
135                    intent.getStringExtra(DictionaryPackConstants.DICTIONARY_PROVIDER_CLIENT_EXTRA);
136            final String myClientId = context.getString(R.string.dictionary_pack_client_id);
137            if (!wantedClientId.equals(myClientId)) return; // Not for us
138            BinaryDictionaryFileDumper.initializeClientRecordHelper(context, myClientId);
139        }
140    }
141}
142