diff --git a/java-overridable/src/com/android/inputmethod/latin/utils/LoginAccountUtils.java b/java-overridable/src/com/android/inputmethod/latin/accounts/LoginAccountUtils.java similarity index 96% rename from java-overridable/src/com/android/inputmethod/latin/utils/LoginAccountUtils.java rename to java-overridable/src/com/android/inputmethod/latin/accounts/LoginAccountUtils.java index faada29b85720ebc92dbb75475cef114886fd255..2dc001c83ee828eef226bc7a88da9717f26cffaf 100644 --- a/java-overridable/src/com/android/inputmethod/latin/utils/LoginAccountUtils.java +++ b/java-overridable/src/com/android/inputmethod/latin/accounts/LoginAccountUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.latin.utils; +package com.android.inputmethod.latin.accounts; import android.content.Context; diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml index b29a6e2866e88e1b663b305101a15ed03e032a17..2249be7f4dda48ae7383dc73fca124ad7b9b1538 100644 --- a/java/AndroidManifest.xml +++ b/java/AndroidManifest.xml @@ -37,6 +37,7 @@ android:supportsRtl="true" android:allowBackup="true"> + <!-- Services --> <service android:name="LatinIME" android:label="@string/english_ime_name" android:permission="android.permission.BIND_INPUT_METHOD"> @@ -56,6 +57,11 @@ android:resource="@xml/spellchecker" /> </service> + <service android:name="com.android.inputmethod.dictionarypack.DictionaryService" + android:label="@string/dictionary_service_name"> + </service> + + <!-- Activities --> <activity android:name=".setup.SetupActivity" android:theme="@style/platformActivityTheme" android:label="@string/english_ime_name" @@ -77,15 +83,6 @@ </intent-filter> </activity> - <receiver android:name="SystemBroadcastReceiver"> - <intent-filter> - <action android:name="android.intent.action.MY_PACKAGE_REPLACED" /> - <action android:name="android.intent.action.BOOT_COMPLETED" /> - <action android:name="android.intent.action.USER_INITIALIZE" /> - <action android:name="android.intent.action.LOCALE_CHANGED" /> - </intent-filter> - </receiver> - <activity android:name=".settings.SettingsActivity" android:theme="@style/platformSettingsTheme" android:label="@string/english_ime_settings" @@ -103,9 +100,34 @@ </intent-filter> </activity> - <receiver android:name="SuggestionSpanPickedNotificationReceiver" android:enabled="true"> + <activity android:name="com.android.inputmethod.dictionarypack.DictionarySettingsActivity" + android:theme="@style/platformSettingsTheme" + android:label="@string/dictionary_settings_title" + android:uiOptions="splitActionBarWhenNarrow"> <intent-filter> - <action android:name="android.text.style.SUGGESTION_PICKED" /> + <action android:name="android.intent.action.MAIN"/> + </intent-filter> + </activity> + + <activity android:name="com.android.inputmethod.dictionarypack.DownloadOverMeteredDialog" + android:theme="@style/platformActivityTheme" + android:label="@string/dictionary_install_over_metered_network_prompt"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + </intent-filter> + </activity> + + <!-- Unexported activity used for tests. --> + <activity android:name=".settings.TestFragmentActivity" + android:exported="false" /> + + <!-- Broadcast receivers --> + <receiver android:name="SystemBroadcastReceiver"> + <intent-filter> + <action android:name="android.intent.action.MY_PACKAGE_REPLACED" /> + <action android:name="android.intent.action.BOOT_COMPLETED" /> + <action android:name="android.intent.action.USER_INITIALIZE" /> + <action android:name="android.intent.action.LOCALE_CHANGED" /> </intent-filter> </receiver> @@ -122,17 +144,11 @@ </intent-filter> </receiver> - <provider android:name="com.android.inputmethod.dictionarypack.DictionaryProvider" - android:grantUriPermissions="true" - android:exported="false" - android:authorities="@string/authority" - android:multiprocess="false" - android:label="@string/dictionary_provider_name"> - </provider> - - <service android:name="com.android.inputmethod.dictionarypack.DictionaryService" - android:label="@string/dictionary_service_name"> - </service> + <receiver android:name="SuggestionSpanPickedNotificationReceiver" android:enabled="true"> + <intent-filter> + <action android:name="android.text.style.SUGGESTION_PICKED" /> + </intent-filter> + </receiver> <receiver android:name="com.android.inputmethod.dictionarypack.EventHandler"> <intent-filter> @@ -142,25 +158,21 @@ </intent-filter> </receiver> - <activity android:name="com.android.inputmethod.dictionarypack.DictionarySettingsActivity" - android:theme="@style/platformSettingsTheme" - android:label="@string/dictionary_settings_title" - android:uiOptions="splitActionBarWhenNarrow"> + <!-- Broadcast receiver for AccountManager#LOGIN_ACCOUNTS_CHANGED_ACTION. --> + <receiver + android:name=".accounts.AccountsChangedReceiver"> <intent-filter> - <action android:name="android.intent.action.MAIN"/> + <action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED" /> </intent-filter> - </activity> - - <activity android:name="com.android.inputmethod.dictionarypack.DownloadOverMeteredDialog" - android:theme="@style/platformActivityTheme" - android:label="@string/dictionary_install_over_metered_network_prompt"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - </intent-filter> - </activity> + </receiver> - <!-- Unexported activity used for tests. --> - <activity android:name=".settings.TestFragmentActivity" - android:exported="false" /> + <!-- Content providers --> + <provider android:name="com.android.inputmethod.dictionarypack.DictionaryProvider" + android:grantUriPermissions="true" + android:exported="false" + android:authorities="@string/authority" + android:multiprocess="false" + android:label="@string/dictionary_provider_name"> + </provider> </application> </manifest> diff --git a/java/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiver.java b/java/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiver.java new file mode 100644 index 0000000000000000000000000000000000000000..9445ce4c3d2410176978807c336c9accb91071a7 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiver.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 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.accounts; + +import android.accounts.AccountManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.text.TextUtils; +import android.util.Log; + +import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.settings.Settings; + +/** + * {@link BroadcastReceiver} for {@link AccountManager#LOGIN_ACCOUNTS_CHANGED_ACTION}. + */ +public class AccountsChangedReceiver extends BroadcastReceiver { + static final String TAG = "AccountsChangedReceiver"; + + @Override + public void onReceive(Context context, Intent intent) { + if (!AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION.equals(intent.getAction())) { + Log.w(TAG, "Received unknown broadcast: " + intent); + return; + } + + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + final String currentAccount = prefs.getString(Settings.PREF_ACCOUNT_NAME, null); + if (currentAccount != null) { + final String[] accounts = getAccountsForLogin(context); + boolean accountFound = false; + for (String account : accounts) { + if (TextUtils.equals(currentAccount, account)) { + accountFound = true; + break; + } + } + // The current account was not found in the list of accounts, remove it. + if (!accountFound) { + Log.i(TAG, "The current account was removed from the system: " + currentAccount); + prefs.edit() + .remove(Settings.PREF_ACCOUNT_NAME) + .apply(); + } + } + } + + /** + * Helper method to help test this receiver. + */ + @UsedForTesting + protected String[] getAccountsForLogin(Context context) { + return LoginAccountUtils.getAccountsForLogin(context); + } +} diff --git a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java index 06ab1e2d29784933539bc2184c0cd409add00648..ffb0ad7bf8d6741daae503b96501534651b7fc33 100644 --- a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java @@ -30,8 +30,8 @@ import android.widget.Toast; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SubtypeSwitcher; +import com.android.inputmethod.latin.accounts.LoginAccountUtils; import com.android.inputmethod.latin.define.ProductionFlags; -import com.android.inputmethod.latin.utils.LoginAccountUtils; import javax.annotation.Nullable; diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 3339ab57fff82894271f7eded2c5157578b36bd4..964d5ce33c679c6212f79f52081e8215815afae8 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -64,6 +64,7 @@ public class SettingsValues { public final boolean mSoundOn; public final boolean mKeyPreviewPopupOn; public final boolean mShowsVoiceInputKey; + public final String mAccountName; public final boolean mIncludesOtherImesInLanguageSwitchList; public final boolean mShowsLanguageSwitchKey; public final boolean mUseContactsDict; @@ -140,6 +141,7 @@ public class SettingsValues { mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res) && mInputAttributes.mShouldShowVoiceInputKey && SubtypeSwitcher.getInstance().isShortcutImeEnabled(); + mAccountName = prefs.getString(Settings.PREF_ACCOUNT_NAME, null); final String autoCorrectionThresholdRawValue = prefs.getString( Settings.PREF_AUTO_CORRECTION_THRESHOLD, res.getString(R.string.auto_correction_threshold_mode_index_modest)); @@ -383,6 +385,8 @@ public class SettingsValues { sb.append("" + mKeyPreviewPopupOn); sb.append("\n mShowsVoiceInputKey = "); sb.append("" + mShowsVoiceInputKey); + sb.append("\n mAccountName = "); + sb.append("" + mAccountName); sb.append("\n mIncludesOtherImesInLanguageSwitchList = "); sb.append("" + mIncludesOtherImesInLanguageSwitchList); sb.append("\n mShowsLanguageSwitchKey = "); diff --git a/tests/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiverTests.java b/tests/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiverTests.java new file mode 100644 index 0000000000000000000000000000000000000000..3319eec3b8b235912e231ea3a5960c48199e0bfa --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/accounts/AccountsChangedReceiverTests.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2014 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.accounts; + +import android.accounts.AccountManager; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.test.AndroidTestCase; + +import com.android.inputmethod.latin.settings.Settings; + +/** + * Tests for {@link AccountsChangedReceiver}. + */ +public class AccountsChangedReceiverTests extends AndroidTestCase { + private static final String ACCOUNT_1 = "account1@example.com"; + private static final String ACCOUNT_2 = "account2@example.com"; + + private SharedPreferences mPrefs; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + // Remove all preferences before the next test. + mPrefs.edit().clear(); + } + + public void testUnknownIntent() { + updateAccountName(ACCOUNT_1); + AccountsChangedReceiver reciever = new AccountsChangedReceiver(); + reciever.onReceive(getContext(), new Intent("some-random-action")); + // Account should *not* be removed from preferences. + assertAccountName(ACCOUNT_1); + } + + public void testAccountRemoved() { + updateAccountName(ACCOUNT_1); + AccountsChangedReceiver reciever = new AccountsChangedReceiver() { + @Override + protected String[] getAccountsForLogin(Context context) { + return new String[] {ACCOUNT_2}; + } + }; + reciever.onReceive(getContext(), new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION)); + // Account should be removed from preferences. + assertAccountName(null); + } + + public void testAccountRemoved_noAccounts() { + updateAccountName(ACCOUNT_2); + AccountsChangedReceiver reciever = new AccountsChangedReceiver() { + @Override + protected String[] getAccountsForLogin(Context context) { + return new String[0]; + } + }; + reciever.onReceive(getContext(), new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION)); + // Account should be removed from preferences. + assertAccountName(null); + } + + public void testAccountNotRemoved() { + updateAccountName(ACCOUNT_2); + AccountsChangedReceiver reciever = new AccountsChangedReceiver() { + @Override + protected String[] getAccountsForLogin(Context context) { + return new String[] {ACCOUNT_1, ACCOUNT_2}; + } + }; + reciever.onReceive(getContext(), new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION)); + // Account should *not* be removed from preferences. + assertAccountName(ACCOUNT_2); + } + + private void updateAccountName(String accountName) { + mPrefs.edit() + .putString(Settings.PREF_ACCOUNT_NAME, accountName) + .commit(); + } + + private void assertAccountName(String expectedAccountName) { + assertEquals(expectedAccountName, mPrefs.getString(Settings.PREF_ACCOUNT_NAME, null)); + } +}