Add account listing and preference integration for current account
Bug: 17464068 Change-Id: Idb68a6012b285d6bc4632414bb6d11131148cf67
This commit is contained in:
parent
27bb70d6a0
commit
84185148c5
6 changed files with 356 additions and 8 deletions
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.utils;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Utility class for retrieving accounts that may be used for login.
|
||||
*/
|
||||
public class LoginAccountUtils {
|
||||
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
|
||||
public static String[] getAccountsForLogin(final Context context) {
|
||||
return new String[0];
|
||||
}
|
||||
}
|
|
@ -158,5 +158,9 @@
|
|||
<action android:name="android.intent.action.MAIN"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Unexported activity used for tests. -->
|
||||
<activity android:name=".settings.TestFragmentActivity"
|
||||
android:exported="false" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
|
@ -181,14 +181,20 @@
|
|||
|
||||
<!-- Title of the preference item for switching accounts [CHAR LIMIT=30] -->
|
||||
<string name="switch_accounts">Switch accounts</string>
|
||||
|
||||
<!-- Summary of the preference item for switching accounts when no accounts
|
||||
are selected [CHAR LIMIT=65] -->
|
||||
<string name="no_accounts_selected">No accounts selected</string>
|
||||
|
||||
<!-- Summary of the preference item for switching accounts when an account
|
||||
is selected [CHAR LIMIT=65] -->
|
||||
<string name="account_selected">Currently using <xliff:g id="EMAIL_ADDRESS" example="someone@example.com">%1$s</xliff:g></string>
|
||||
<!-- Positive text for selecting an account -->
|
||||
<string name="account_select_ok">OK</string>
|
||||
<!-- Negative text for selecting an account -->
|
||||
<string name="account_select_cancel">Cancel</string>
|
||||
<!-- Text for signing out of an account -->
|
||||
<string name="account_select_sign_out">Sign out</string>
|
||||
<!-- Title of the account picker dialog for selecting an account [CHAR LIMIT=40] -->
|
||||
<string name="account_select_title">Select an account to use</string>
|
||||
|
||||
<!-- Description for English (UK) keyboard subtype [CHAR LIMIT=25]
|
||||
(UK) should be an abbreviation of United Kingdom to fit in the CHAR LIMIT. -->
|
||||
|
|
|
@ -16,25 +16,41 @@
|
|||
|
||||
package com.android.inputmethod.latin.settings;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.inputmethod.latin.R;
|
||||
import com.android.inputmethod.latin.SubtypeSwitcher;
|
||||
import com.android.inputmethod.latin.define.ProductionFlags;
|
||||
import com.android.inputmethod.latin.utils.LoginAccountUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* "Accounts & Privacy" settings sub screen.
|
||||
*
|
||||
* This settings sub screen handles the following preferences:
|
||||
* - TODO: Account selection/management for IME
|
||||
* - TODO: Sync preferences
|
||||
* - TODO: Privacy preferences
|
||||
* <li> Account selection/management for IME
|
||||
* <li> TODO: Sync preferences
|
||||
* <li> TODO: Privacy preferences
|
||||
*/
|
||||
public final class AccountsSettingsFragment extends SubScreenFragment {
|
||||
static final String PREF_ACCCOUNT_SWITCHER = "account_switcher";
|
||||
|
||||
private final DialogInterface.OnClickListener mAccountSelectedListener =
|
||||
new AccountSelectedListener();
|
||||
private final DialogInterface.OnClickListener mAccountSignedOutListener =
|
||||
new AccountSignedOutListener();
|
||||
|
||||
@Override
|
||||
public void onCreate(final Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
@ -74,9 +90,104 @@ public final class AccountsSettingsFragment extends SubScreenFragment {
|
|||
}
|
||||
|
||||
private void refreshAccountSelection() {
|
||||
// TODO: Fetch the currently selected account.
|
||||
// Set the summary for the account preference.
|
||||
// Depending on the account selection, enable/disable preferences that
|
||||
final String currentAccount = getCurrentlySelectedAccount();
|
||||
final Preference accountSwitcher = findPreference(PREF_ACCCOUNT_SWITCHER);
|
||||
if (currentAccount == null) {
|
||||
// No account is currently selected.
|
||||
accountSwitcher.setSummary(getString(R.string.no_accounts_selected));
|
||||
} else {
|
||||
// Set the currently selected account.
|
||||
accountSwitcher.setSummary(getString(R.string.account_selected, currentAccount));
|
||||
}
|
||||
final Context context = getActivity();
|
||||
final String[] accountsForLogin = LoginAccountUtils.getAccountsForLogin(context);
|
||||
accountSwitcher.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
if (accountsForLogin.length == 0) {
|
||||
// TODO: Handle account addition.
|
||||
Toast.makeText(getActivity(),
|
||||
getString(R.string.account_select_cancel), Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
createAccountPicker(accountsForLogin, currentAccount).show();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Depending on the account selection, enable/disable preferences that
|
||||
// depend on an account.
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getCurrentlySelectedAccount() {
|
||||
return getSharedPreferences().getString(Settings.PREF_ACCOUNT_NAME, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an account picker dialog showing the given accounts in a list and selecting
|
||||
* the selected account by default.
|
||||
* The list of accounts must not be null/empty.
|
||||
*
|
||||
* Package-private for testing.
|
||||
*/
|
||||
AlertDialog createAccountPicker(final String[] accounts,
|
||||
final String selectedAccount) {
|
||||
if (accounts == null || accounts.length == 0) {
|
||||
throw new IllegalArgumentException("List of accounts must not be empty");
|
||||
}
|
||||
|
||||
// See if the currently selected account is in the list.
|
||||
// If it is, the entry is selected, and a sign-out button is provided.
|
||||
// If it isn't, select the 0th account by default which will get picked up
|
||||
// if the user presses OK.
|
||||
int index = 0;
|
||||
boolean isSignedIn = false;
|
||||
for (int i = 0; i < accounts.length; i++) {
|
||||
if (TextUtils.equals(accounts[i], selectedAccount)) {
|
||||
index = i;
|
||||
isSignedIn = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.account_select_title)
|
||||
.setSingleChoiceItems(accounts, index, null)
|
||||
.setPositiveButton(R.string.account_select_ok, mAccountSelectedListener)
|
||||
.setNegativeButton(R.string.account_select_cancel, null);
|
||||
if (isSignedIn) {
|
||||
builder.setNeutralButton(R.string.account_select_sign_out, mAccountSignedOutListener);
|
||||
}
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for an account being selected from the picker.
|
||||
* Persists the account to shared preferences.
|
||||
*/
|
||||
class AccountSelectedListener implements DialogInterface.OnClickListener {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
final ListView lv = ((AlertDialog)dialog).getListView();
|
||||
final Object selectedItem = lv.getItemAtPosition(lv.getCheckedItemPosition());
|
||||
getSharedPreferences()
|
||||
.edit()
|
||||
.putString(Settings.PREF_ACCOUNT_NAME, (String) selectedItem)
|
||||
.apply();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for sign-out being initiated from from the picker.
|
||||
* Removed the account from shared preferences.
|
||||
*/
|
||||
class AccountSignedOutListener implements DialogInterface.OnClickListener {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
getSharedPreferences()
|
||||
.edit()
|
||||
.remove(Settings.PREF_ACCOUNT_NAME)
|
||||
.apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.settings;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* Test activity to use when testing preference fragments. <br/>
|
||||
* Usage: <br/>
|
||||
* Create an ActivityInstrumentationTestCase2 for this activity
|
||||
* and call setIntent() with an intent that specifies the fragment to load in the activity.
|
||||
* The fragment can then be obtained from this activity and used for testing/verification.
|
||||
*/
|
||||
public final class TestFragmentActivity extends Activity {
|
||||
/**
|
||||
* The fragment name that should be loaded when starting this activity.
|
||||
* This must be specified when starting this activity, as this activity is only
|
||||
* meant to test fragments from instrumentation tests.
|
||||
*/
|
||||
public static final String EXTRA_SHOW_FRAGMENT = "show_fragment";
|
||||
|
||||
public Fragment mFragment;
|
||||
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedState) {
|
||||
super.onCreate(savedState);
|
||||
final Intent intent = getIntent();
|
||||
final String fragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
|
||||
if (fragmentName == null) {
|
||||
throw new IllegalArgumentException("No fragment name specified for testing");
|
||||
}
|
||||
|
||||
mFragment = Fragment.instantiate(this, fragmentName);
|
||||
FragmentManager fragmentManager = getFragmentManager();
|
||||
fragmentManager.beginTransaction().add(mFragment, fragmentName).commit();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* 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.settings;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Intent;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
import android.view.View;
|
||||
import android.widget.ListView;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@MediumTest
|
||||
public class AccountsSettingsFragmentTests
|
||||
extends ActivityInstrumentationTestCase2<TestFragmentActivity> {
|
||||
private static final String FRAG_NAME = AccountsSettingsFragment.class.getName();
|
||||
private static final long TEST_TIMEOUT_MILLIS = 5000;
|
||||
|
||||
private AlertDialog mDialog;
|
||||
|
||||
public AccountsSettingsFragmentTests() {
|
||||
super(TestFragmentActivity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(TestFragmentActivity.EXTRA_SHOW_FRAGMENT, FRAG_NAME);
|
||||
setActivityIntent(intent);
|
||||
}
|
||||
|
||||
public void testEmptyAccounts() {
|
||||
final AccountsSettingsFragment fragment =
|
||||
(AccountsSettingsFragment) getActivity().mFragment;
|
||||
try {
|
||||
fragment.createAccountPicker(new String[0], null);
|
||||
fail("Expected IllegalArgumentException, never thrown");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
|
||||
public void testMultipleAccounts_noCurrentAccount() {
|
||||
final AccountsSettingsFragment fragment =
|
||||
(AccountsSettingsFragment) getActivity().mFragment;
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mDialog = fragment.createAccountPicker(
|
||||
new String[] {
|
||||
"1@example.com",
|
||||
"2@example.com",
|
||||
"3@example.com",
|
||||
"4@example.com"},
|
||||
null);
|
||||
mDialog.show();
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
latch.await(TEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException ex) {
|
||||
fail();
|
||||
}
|
||||
getInstrumentation().waitForIdleSync();
|
||||
final ListView lv = mDialog.getListView();
|
||||
// The 1st account should be checked by default.
|
||||
assertEquals("checked-item", 0, lv.getCheckedItemPosition());
|
||||
// There should be 4 accounts in the list.
|
||||
assertEquals("count", 4, lv.getCount());
|
||||
// The sign-out button shouldn't exist
|
||||
assertEquals(View.GONE, mDialog.getButton(Dialog.BUTTON_NEUTRAL).getVisibility());
|
||||
assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_NEGATIVE).getVisibility());
|
||||
assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_POSITIVE).getVisibility());
|
||||
}
|
||||
|
||||
public void testMultipleAccounts_currentAccount() {
|
||||
final AccountsSettingsFragment fragment =
|
||||
(AccountsSettingsFragment) getActivity().mFragment;
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mDialog = fragment.createAccountPicker(
|
||||
new String[] {
|
||||
"1@example.com",
|
||||
"2@example.com",
|
||||
"3@example.com",
|
||||
"4@example.com"},
|
||||
"3@example.com");
|
||||
mDialog.show();
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
latch.await(TEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException ex) {
|
||||
fail();
|
||||
}
|
||||
getInstrumentation().waitForIdleSync();
|
||||
final ListView lv = mDialog.getListView();
|
||||
// The 3rd account should be checked by default.
|
||||
assertEquals("checked-item", 2, lv.getCheckedItemPosition());
|
||||
// There should be 4 accounts in the list.
|
||||
assertEquals("count", 4, lv.getCount());
|
||||
// The sign-out button should be shown
|
||||
assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_NEUTRAL).getVisibility());
|
||||
assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_NEGATIVE).getVisibility());
|
||||
assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_POSITIVE).getVisibility());
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue