Remove some unneeded code and permissions

This commit is contained in:
Aleksandras Kostarevas 2024-04-11 21:47:24 -05:00
parent 76231dc7a7
commit ecafe8f86a
15 changed files with 33 additions and 674 deletions

View File

@ -125,6 +125,7 @@ android {
dimension "buildType" dimension "buildType"
buildConfigField "boolean", "IS_PLAYSTORE_BUILD", "false" buildConfigField "boolean", "IS_PLAYSTORE_BUILD", "false"
buildConfigField "boolean", "UPDATE_CHECKING", "true" buildConfigField "boolean", "UPDATE_CHECKING", "true"
getIsDefault().set(true)
} }
playstore { playstore {
dimension "buildType" dimension "buildType"
@ -205,9 +206,9 @@ dependencies {
implementation 'androidx.datastore:datastore-preferences:1.0.0' implementation 'androidx.datastore:datastore-preferences:1.0.0'
implementation 'androidx.autofill:autofill:1.1.0' implementation 'androidx.autofill:autofill:1.1.0'
implementation 'ch.acra:acra-http:5.11.1' // TODO: Remove upon release stableImplementation 'ch.acra:acra-http:5.11.1' // TODO: Remove upon release
implementation 'ch.acra:acra-mail:5.11.1' stableImplementation 'ch.acra:acra-mail:5.11.1'
implementation 'ch.acra:acra-dialog:5.11.1' stableImplementation 'ch.acra:acra-dialog:5.11.1'
implementation 'com.squareup.okhttp3:okhttp:4.11.0' implementation 'com.squareup.okhttp3:okhttp:4.11.0'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1' implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1'

View File

@ -16,27 +16,16 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_CONTACTS"/> <!--<uses-permission android:name="android.permission.READ_PROFILE"/>--> <!-- ? -->
<uses-permission android:name="android.permission.READ_PROFILE"/>
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.READ_SYNC_STATS"/>
<uses-permission android:name="android.permission.READ_USER_DICTIONARY"/> <uses-permission android:name="android.permission.READ_USER_DICTIONARY"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_USER_DICTIONARY"/> <uses-permission android:name="android.permission.WRITE_USER_DICTIONARY"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!-- A signature-protected permission to ask AOSP Keyboard to close the software keyboard. <!-- A signature-protected permission to ask AOSP Keyboard to close the software keyboard.
To use this, add the following line into calling application's AndroidManifest.xml To use this, add the following line into calling application's AndroidManifest.xml
@ -77,12 +66,6 @@
android:resource="@xml/method"/> android:resource="@xml/method"/>
</service> </service>
<service
android:name="org.futo.inputmethod.updates.UpdateCheckingService"
android:label="@string/update_checking_service"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
<service android:name=".spellcheck.AndroidSpellCheckerService" <service android:name=".spellcheck.AndroidSpellCheckerService"
android:label="@string/spell_checker_service_name" android:label="@string/spell_checker_service_name"
android:permission="android.permission.BIND_TEXT_SERVICE" android:permission="android.permission.BIND_TEXT_SERVICE"
@ -177,16 +160,6 @@
</intent-filter> </intent-filter>
</receiver> </receiver>
<!-- Broadcast receiver for AccountManager#LOGIN_ACCOUNTS_CHANGED_ACTION. -->
<receiver android:name=".accounts.AccountsChangedReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED"/>
</intent-filter>
</receiver>
<receiver android:name="org.futo.inputmethod.updates.InstallReceiver" />
<service <service
android:name="androidx.work.impl.foreground.SystemForegroundService" android:name="androidx.work.impl.foreground.SystemForegroundService"
android:foregroundServiceType="specialUse" android:foregroundServiceType="specialUse"

View File

@ -1,36 +0,0 @@
/*
* Copyright (C) 2013 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 org.futo.inputmethod.compat;
import android.net.ConnectivityManager;
import java.lang.reflect.Method;
public final class ConnectivityManagerCompatUtils {
// ConnectivityManager#isActiveNetworkMetered() has been introduced
// in API level 16 (Build.VERSION_CODES.JELLY_BEAN).
private static final Method METHOD_isActiveNetworkMetered = CompatUtils.getMethod(
ConnectivityManager.class, "isActiveNetworkMetered");
public static boolean isActiveNetworkMetered(final ConnectivityManager manager) {
return (Boolean)CompatUtils.invoke(manager,
// If the API telling whether the network is metered or not is not available,
// then the closest thing is "if it's a mobile connection".
manager.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_MOBILE,
METHOD_isActiveNetworkMetered);
}
}

View File

@ -1,36 +0,0 @@
/*
* Copyright (C) 2013 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 org.futo.inputmethod.dictionarypack;
import android.app.DownloadManager;
/**
* Struct class to encapsulate the result of a completed download.
*/
public class CompletedDownloadInfo {
final String mUri;
final long mDownloadId;
final int mStatus;
public CompletedDownloadInfo(final String uri, final long downloadId, final int status) {
mUri = uri;
mDownloadId = downloadId;
mStatus = status;
}
public boolean wasSuccessful() {
return DownloadManager.STATUS_SUCCESSFUL == mStatus;
}
}

View File

@ -1,179 +0,0 @@
/*
* Copyright (C) 2012 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 org.futo.inputmethod.latin;
import android.Manifest;
import android.content.Context;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.util.Log;
import org.futo.inputmethod.annotations.ExternallyReferenced;
import org.futo.inputmethod.latin.ContactsManager.ContactsChangedListener;
import org.futo.inputmethod.latin.common.StringUtils;
import org.futo.inputmethod.latin.permissions.PermissionsUtil;
import org.futo.inputmethod.latin.personalization.AccountUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.annotation.Nullable;
public class ContactsBinaryDictionary extends ExpandableBinaryDictionary
implements ContactsChangedListener {
private static final String TAG = ContactsBinaryDictionary.class.getSimpleName();
private static final String NAME = "contacts";
private static final boolean DEBUG = false;
private static final boolean DEBUG_DUMP = false;
/**
* Whether to use "firstname lastname" in bigram predictions.
*/
private final boolean mUseFirstLastBigrams;
private final ContactsManager mContactsManager;
protected ContactsBinaryDictionary(final Context context, final Locale locale,
final File dictFile, final String name) {
super(context, getDictName(name, locale, dictFile), locale, Dictionary.TYPE_CONTACTS,
dictFile);
mUseFirstLastBigrams = ContactsDictionaryUtils.useFirstLastBigramsForLocale(locale);
mContactsManager = new ContactsManager(context);
mContactsManager.registerForUpdates(this /* listener */);
reloadDictionaryIfRequired();
}
// Note: This method is called by {@link DictionaryFacilitator} using Java reflection.
@ExternallyReferenced
public static ContactsBinaryDictionary getDictionary(final Context context, final Locale locale,
final File dictFile, final String dictNamePrefix, @Nullable final String account) {
return new ContactsBinaryDictionary(context, locale, dictFile, dictNamePrefix + NAME);
}
@Override
public synchronized void close() {
mContactsManager.close();
super.close();
}
/**
* Typically called whenever the dictionary is created for the first time or
* recreated when we think that there are updates to the dictionary.
* This is called asynchronously.
*/
@Override
public void loadInitialContentsLocked() {
loadDeviceAccountsEmailAddressesLocked();
loadDictionaryForUriLocked(ContactsContract.Profile.CONTENT_URI);
// TODO: Switch this URL to the newer ContactsContract too
loadDictionaryForUriLocked(Contacts.CONTENT_URI);
}
/**
* Loads device accounts to the dictionary.
*/
private void loadDeviceAccountsEmailAddressesLocked() {
final List<String> accountVocabulary =
AccountUtils.getDeviceAccountsEmailAddresses(mContext);
if (accountVocabulary == null || accountVocabulary.isEmpty()) {
return;
}
for (String word : accountVocabulary) {
if (DEBUG) {
Log.d(TAG, "loadAccountVocabulary: " + word);
}
runGCIfRequiredLocked(true /* mindsBlockByGC */);
addUnigramLocked(word, ContactsDictionaryConstants.FREQUENCY_FOR_CONTACTS,
null, 0,
false /* isNotAWord */, false /* isPossiblyOffensive */,
BinaryDictionary.NOT_A_VALID_TIMESTAMP);
}
}
/**
* Loads data within content providers to the dictionary.
*/
private void loadDictionaryForUriLocked(final Uri uri) {
if (!PermissionsUtil.checkAllPermissionsGranted(
mContext, Manifest.permission.READ_CONTACTS)) {
Log.i(TAG, "No permission to read contacts. Not loading the Dictionary.");
}
final ArrayList<String> validNames = mContactsManager.getValidNames(uri);
for (final String name : validNames) {
addNameLocked(name);
}
if (uri.equals(Contacts.CONTENT_URI)) {
// Since we were able to add content successfully, update the local
// state of the manager.
mContactsManager.updateLocalState(validNames);
}
}
/**
* Adds the words in a name (e.g., firstname/lastname) to the binary dictionary along with their
* bigrams depending on locale.
*/
private void addNameLocked(final String name) {
int len = StringUtils.codePointCount(name);
NgramContext ngramContext = NgramContext.getEmptyPrevWordsContext(
BinaryDictionary.MAX_PREV_WORD_COUNT_FOR_N_GRAM);
// TODO: Better tokenization for non-Latin writing systems
for (int i = 0; i < len; i++) {
if (Character.isLetter(name.codePointAt(i))) {
int end = ContactsDictionaryUtils.getWordEndPosition(name, len, i);
String word = name.substring(i, end);
if (DEBUG_DUMP) {
Log.d(TAG, "addName word = " + word);
}
i = end - 1;
// Don't add single letter words, possibly confuses
// capitalization of i.
final int wordLen = StringUtils.codePointCount(word);
if (wordLen <= MAX_WORD_LENGTH && wordLen > 1) {
if (DEBUG) {
Log.d(TAG, "addName " + name + ", " + word + ", " + ngramContext);
}
runGCIfRequiredLocked(true /* mindsBlockByGC */);
addUnigramLocked(word,
ContactsDictionaryConstants.FREQUENCY_FOR_CONTACTS,
null, 0,
false /* isNotAWord */,
false /* isPossiblyOffensive */,
BinaryDictionary.NOT_A_VALID_TIMESTAMP);
if (ngramContext.isValid() && mUseFirstLastBigrams) {
runGCIfRequiredLocked(true /* mindsBlockByGC */);
addNgramEntryLocked(ngramContext,
word,
ContactsDictionaryConstants.FREQUENCY_FOR_CONTACTS_BIGRAM,
BinaryDictionary.NOT_A_VALID_TIMESTAMP);
}
ngramContext = ngramContext.getNextNgramContext(
new NgramContext.WordInfo(word));
}
}
}
}
@Override
public void onContactsChange() {
setNeedsToRecreate();
}
}

View File

@ -80,7 +80,6 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
static { static {
DICT_TYPE_TO_CLASS.put(Dictionary.TYPE_USER_HISTORY, UserHistoryDictionary.class); DICT_TYPE_TO_CLASS.put(Dictionary.TYPE_USER_HISTORY, UserHistoryDictionary.class);
DICT_TYPE_TO_CLASS.put(Dictionary.TYPE_USER, UserBinaryDictionary.class); DICT_TYPE_TO_CLASS.put(Dictionary.TYPE_USER, UserBinaryDictionary.class);
DICT_TYPE_TO_CLASS.put(Dictionary.TYPE_CONTACTS, ContactsBinaryDictionary.class);
} }
private static final String DICT_FACTORY_METHOD_NAME = "getDictionary"; private static final String DICT_FACTORY_METHOD_NAME = "getDictionary";

View File

@ -1,81 +0,0 @@
/*
* 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 org.futo.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 org.futo.inputmethod.annotations.UsedForTesting;
import org.futo.inputmethod.latin.settings.LocalSettingsConstants;
/**
* {@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;
}
// Ideally the account preference could live in a different preferences file
// that wasn't being backed up and restored, however the preference fragments
// currently only deal with the default shared preferences which is why
// separating this out into a different file is not trivial currently.
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final String currentAccount = prefs.getString(
LocalSettingsConstants.PREF_ACCOUNT_NAME, null);
removeUnknownAccountFromPreference(prefs, getAccountsForLogin(context), currentAccount);
}
/**
* Helper method to help test this receiver.
*/
@UsedForTesting
protected String[] getAccountsForLogin(Context context) {
return LoginAccountUtils.getAccountsForLogin(context);
}
/**
* Removes the currentAccount from preferences if it's not found
* in the list of current accounts.
*/
private static void removeUnknownAccountFromPreference(final SharedPreferences prefs,
final String[] accounts, final String currentAccount) {
if (currentAccount == null) {
return;
}
for (final String account : accounts) {
if (TextUtils.equals(currentAccount, account)) {
return;
}
}
Log.i(TAG, "The current account was removed from the system: " + currentAccount);
prefs.edit()
.remove(LocalSettingsConstants.PREF_ACCOUNT_NAME)
.apply();
}
}

View File

@ -1,67 +0,0 @@
/*
* 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 org.futo.inputmethod.latin.accounts;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import java.io.IOException;
/**
* Utility class that handles generation/invalidation of auth tokens in the app.
*/
public class AuthUtils {
private final AccountManager mAccountManager;
public AuthUtils(Context context) {
mAccountManager = AccountManager.get(context);
}
/**
* @see AccountManager#invalidateAuthToken(String, String)
*/
public void invalidateAuthToken(final String accountType, final String authToken) {
mAccountManager.invalidateAuthToken(accountType, authToken);
}
/**
* @see AccountManager#getAuthToken(
* Account, String, Bundle, boolean, AccountManagerCallback, Handler)
*/
public AccountManagerFuture<Bundle> getAuthToken(final Account account,
final String authTokenType, final Bundle options, final boolean notifyAuthFailure,
final AccountManagerCallback<Bundle> callback, final Handler handler) {
return mAccountManager.getAuthToken(account, authTokenType, options, notifyAuthFailure,
callback, handler);
}
/**
* @see AccountManager#blockingGetAuthToken(Account, String, boolean)
*/
public String blockingGetAuthToken(final Account account, final String authTokenType,
final boolean notifyAuthFailure) throws OperationCanceledException,
AuthenticatorException, IOException {
return mAccountManager.blockingGetAuthToken(account, authTokenType, notifyAuthFailure);
}
}

View File

@ -1,47 +0,0 @@
/*
* 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 org.futo.inputmethod.latin.accounts;
import android.content.Context;
import javax.annotation.Nonnull;
/**
* Utility class for retrieving accounts that may be used for login.
*/
public class LoginAccountUtils {
/**
* This defines the type of account this class deals with.
* This account type is used when listing the accounts available on the device for login.
*/
public static final String ACCOUNT_TYPE = "";
private LoginAccountUtils() {
// This utility class is not publicly instantiable.
}
/**
* Get the accounts available for login.
*
* @return an array of accounts. Empty (never null) if no accounts are available for login.
*/
@Nonnull
@SuppressWarnings("unused")
public static String[] getAccountsForLogin(final Context context) {
return new String[0];
}
}

View File

@ -1,66 +0,0 @@
/*
* Copyright (C) 2013 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 org.futo.inputmethod.latin.personalization;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.Context;
import android.util.Patterns;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class AccountUtils {
private AccountUtils() {
// This utility class is not publicly instantiable.
}
private static Account[] getAccounts(final Context context) {
return AccountManager.get(context).getAccounts();
}
public static List<String> getDeviceAccountsEmailAddresses(final Context context) {
final ArrayList<String> retval = new ArrayList<>();
for (final Account account : getAccounts(context)) {
final String name = account.name;
if (Patterns.EMAIL_ADDRESS.matcher(name).matches()) {
retval.add(name);
retval.add(name.split("@")[0]);
}
}
return retval;
}
/**
* Get all device accounts having specified domain name.
* @param context application context
* @param domain domain name used for filtering
* @return List of account names that contain the specified domain name
*/
public static List<String> getDeviceAccountsWithDomain(
final Context context, final String domain) {
final ArrayList<String> retval = new ArrayList<>();
final String atDomain = "@" + domain.toLowerCase(Locale.ROOT);
for (final Account account : getAccounts(context)) {
if (account.name.toLowerCase(Locale.ROOT).endsWith(atDomain)) {
retval.add(account.name);
}
}
return retval;
}
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
</manifest>

View File

@ -0,0 +1,7 @@
package org.futo.inputmethod.latin
import android.app.Application
class CrashLoggingApplication : Application() {
}

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.INTERNET" />
<application>
<service
android:name="org.futo.inputmethod.updates.UpdateCheckingService"
android:label="@string/update_checking_service"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
<receiver android:name="org.futo.inputmethod.updates.InstallReceiver" />
</application>
</manifest>

View File

@ -1,130 +0,0 @@
/*
* 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 org.futo.inputmethod.latin.accounts;
import static org.junit.Assert.assertEquals;
import android.accounts.AccountManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.futo.inputmethod.latin.settings.LocalSettingsConstants;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Tests for {@link AccountsChangedReceiver}.
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AccountsChangedReceiverTests {
private static final String ACCOUNT_1 = "account1@example.com";
private static final String ACCOUNT_2 = "account2@example.com";
private SharedPreferences mPrefs;
private String mLastKnownAccount = null;
private Context getContext() {
return InstrumentationRegistry.getTargetContext();
}
@Before
public void setUp() throws Exception {
mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext());
// Keep track of the current account so that we restore it when the test finishes.
mLastKnownAccount = mPrefs.getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null);
}
@After
public void tearDown() throws Exception {
// Restore the account that was present before running the test.
updateAccountName(mLastKnownAccount);
}
@Test
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);
}
@Test
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);
}
@Test
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);
}
@Test
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) {
if (accountName == null) {
mPrefs.edit().remove(LocalSettingsConstants.PREF_ACCOUNT_NAME).apply();
} else {
mPrefs.edit().putString(LocalSettingsConstants.PREF_ACCOUNT_NAME, accountName).apply();
}
}
private void assertAccountName(String expectedAccountName) {
assertEquals(expectedAccountName,
mPrefs.getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null));
}
}