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"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<!-- Unexported activity used for tests. -->
|
||||||
|
<activity android:name=".settings.TestFragmentActivity"
|
||||||
|
android:exported="false" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -181,14 +181,20 @@
|
||||||
|
|
||||||
<!-- Title of the preference item for switching accounts [CHAR LIMIT=30] -->
|
<!-- Title of the preference item for switching accounts [CHAR LIMIT=30] -->
|
||||||
<string name="switch_accounts">Switch accounts</string>
|
<string name="switch_accounts">Switch accounts</string>
|
||||||
|
|
||||||
<!-- Summary of the preference item for switching accounts when no accounts
|
<!-- Summary of the preference item for switching accounts when no accounts
|
||||||
are selected [CHAR LIMIT=65] -->
|
are selected [CHAR LIMIT=65] -->
|
||||||
<string name="no_accounts_selected">No accounts selected</string>
|
<string name="no_accounts_selected">No accounts selected</string>
|
||||||
|
|
||||||
<!-- Summary of the preference item for switching accounts when an account
|
<!-- Summary of the preference item for switching accounts when an account
|
||||||
is selected [CHAR LIMIT=65] -->
|
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>
|
<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]
|
<!-- Description for English (UK) keyboard subtype [CHAR LIMIT=25]
|
||||||
(UK) should be an abbreviation of United Kingdom to fit in the CHAR LIMIT. -->
|
(UK) should be an abbreviation of United Kingdom to fit in the CHAR LIMIT. -->
|
||||||
|
|
|
@ -16,25 +16,41 @@
|
||||||
|
|
||||||
package com.android.inputmethod.latin.settings;
|
package com.android.inputmethod.latin.settings;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.Preference;
|
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.R;
|
||||||
import com.android.inputmethod.latin.SubtypeSwitcher;
|
import com.android.inputmethod.latin.SubtypeSwitcher;
|
||||||
import com.android.inputmethod.latin.define.ProductionFlags;
|
import com.android.inputmethod.latin.define.ProductionFlags;
|
||||||
|
import com.android.inputmethod.latin.utils.LoginAccountUtils;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "Accounts & Privacy" settings sub screen.
|
* "Accounts & Privacy" settings sub screen.
|
||||||
*
|
*
|
||||||
* This settings sub screen handles the following preferences:
|
* This settings sub screen handles the following preferences:
|
||||||
* - TODO: Account selection/management for IME
|
* <li> Account selection/management for IME
|
||||||
* - TODO: Sync preferences
|
* <li> TODO: Sync preferences
|
||||||
* - TODO: Privacy preferences
|
* <li> TODO: Privacy preferences
|
||||||
*/
|
*/
|
||||||
public final class AccountsSettingsFragment extends SubScreenFragment {
|
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
|
@Override
|
||||||
public void onCreate(final Bundle icicle) {
|
public void onCreate(final Bundle icicle) {
|
||||||
super.onCreate(icicle);
|
super.onCreate(icicle);
|
||||||
|
@ -74,9 +90,104 @@ public final class AccountsSettingsFragment extends SubScreenFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshAccountSelection() {
|
private void refreshAccountSelection() {
|
||||||
// TODO: Fetch the currently selected account.
|
final String currentAccount = getCurrentlySelectedAccount();
|
||||||
// Set the summary for the account preference.
|
final Preference accountSwitcher = findPreference(PREF_ACCCOUNT_SWITCHER);
|
||||||
// Depending on the account selection, enable/disable preferences that
|
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.
|
// 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