diff --git a/res/layout/support_account_spinner_item.xml b/res/layout/support_account_spinner_item.xml new file mode 100644 index 00000000000..fe37a8589ca --- /dev/null +++ b/res/layout/support_account_spinner_item.xml @@ -0,0 +1,26 @@ + + + + diff --git a/res/layout/support_escalation_options.xml b/res/layout/support_escalation_options.xml index 63cc85dadfb..17e03ff0344 100644 --- a/res/layout/support_escalation_options.xml +++ b/res/layout/support_escalation_options.xml @@ -34,11 +34,25 @@ android:gravity="center_horizontal" android:paddingTop="8dp" android:paddingBottom="30dp" - android:textAppearance="@style/TextAppearance.Small" - android:textColor="?android:attr/textColorSecondary"/> + android:textAppearance="?android:attr/textAppearanceSmall"/> + + + android:textAppearance="?android:attr/textAppearanceSmall"/> + android:textAppearance="?android:attr/textAppearanceSmall"/> diff --git a/res/values/strings.xml b/res/values/strings.xml index 9aa7608f39c..fe214f45dc0 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -7797,6 +7797,12 @@ Do not show again + + Requesting as + + + Add account + Work profile settings diff --git a/src/com/android/settings/dashboard/SupportFragment.java b/src/com/android/settings/dashboard/SupportFragment.java index 63983ce5f68..163becec3a3 100644 --- a/src/com/android/settings/dashboard/SupportFragment.java +++ b/src/com/android/settings/dashboard/SupportFragment.java @@ -138,8 +138,8 @@ public final class SupportFragment extends InstrumentedFragment implements View. @Override public void onAccountsUpdated(Account[] accounts) { // Account changed, update support items. - mSupportItemAdapter.setAccount( - mSupportFeatureProvider.getSupportEligibleAccount(mActivity)); + mSupportItemAdapter.setAccounts( + mSupportFeatureProvider.getSupportEligibleAccounts(mActivity)); } @Override diff --git a/src/com/android/settings/dashboard/SupportItemAdapter.java b/src/com/android/settings/dashboard/SupportItemAdapter.java index 517d8d006f6..4e1ae7806a8 100644 --- a/src/com/android/settings/dashboard/SupportItemAdapter.java +++ b/src/com/android/settings/dashboard/SupportItemAdapter.java @@ -26,6 +26,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; +import android.support.annotation.VisibleForTesting; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.view.LayoutInflater; @@ -38,6 +39,7 @@ import android.widget.Spinner; import android.widget.TextView; import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.util.ArrayUtils; import com.android.settings.R; import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.overlay.SupportFeatureProvider; @@ -46,8 +48,8 @@ import com.android.settings.support.SupportPhone; import com.android.settings.support.SupportPhoneDialogFragment; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -import java.util.Objects; import static com.android.settings.overlay.SupportFeatureProvider.SupportType.CHAT; import static com.android.settings.overlay.SupportFeatureProvider.SupportType.PHONE; @@ -58,6 +60,7 @@ import static com.android.settings.overlay.SupportFeatureProvider.SupportType.PH public final class SupportItemAdapter extends RecyclerView.Adapter { private static final String STATE_SELECTED_COUNTRY = "STATE_SELECTED_COUNTRY"; + private static final String ACCOUNT_SELECTED_INDEX = "ACCOUNT_SELECTED_INDEX"; private static final int TYPE_ESCALATION_OPTIONS = R.layout.support_escalation_options; private static final int TYPE_ESCALATION_OPTIONS_OFFLINE = R.layout.support_offline_escalation_options; @@ -67,7 +70,8 @@ public final class SupportItemAdapter extends RecyclerView.Adapter(); // Optimistically assume we have Internet access. It will be updated later to correct value. mHasInternet = true; if (savedInstanceState != null) { mSelectedCountry = savedInstanceState.getString(STATE_SELECTED_COUNTRY); + mSelectedAccountIndex = savedInstanceState.getInt(ACCOUNT_SELECTED_INDEX); } else { mSelectedCountry = mSupportFeatureProvider.getCurrentCountryCodeIfHasConfig(PHONE); + mSelectedAccountIndex = 0; } - mAccount = mSupportFeatureProvider.getSupportEligibleAccount(mActivity); + + mAccounts = mSupportFeatureProvider.getSupportEligibleAccounts(mActivity); refreshData(); } @@ -159,9 +168,11 @@ public final class SupportItemAdapter extends RecyclerView.Adapter adapter = new ArrayAdapter( + mActivity, R.layout.support_account_spinner_item, + extractAccountNames(mAccounts)); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinner.setAdapter(adapter); + spinner.setOnItemSelectedListener(mOnlineSpinnerItemSelectListener); + spinner.setSelection(mSelectedAccountIndex); } private void bindOfflineEscalationOptions(ViewHolder holder, OfflineEscalationData data) { @@ -360,7 +387,7 @@ public final class SupportItemAdapter extends RecyclerView.Adapter parent, View view, int position, long id) { @@ -508,6 +548,26 @@ public final class SupportItemAdapter extends RecyclerView.Adapter parent, View view, int position, long id) { + if (position == mAccounts.length) { + mActivity.startActivity(mSupportFeatureProvider.getAccountLoginIntent()); + // Make sure "Add account" is not shown as selected item + parent.setSelection(mSelectedAccountIndex); + } else if (position != mSelectedAccountIndex) { + mSelectedAccountIndex = position; + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + // Do nothing. + } + } + /** * {@link RecyclerView.ViewHolder} for support items. */ diff --git a/src/com/android/settings/overlay/SupportFeatureProvider.java b/src/com/android/settings/overlay/SupportFeatureProvider.java index 506d1bc8399..24ec7b58e06 100644 --- a/src/com/android/settings/overlay/SupportFeatureProvider.java +++ b/src/com/android/settings/overlay/SupportFeatureProvider.java @@ -18,6 +18,7 @@ package com.android.settings.overlay; import android.accounts.Account; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.StringRes; import android.app.Activity; import android.content.Context; @@ -111,15 +112,16 @@ public interface SupportFeatureProvider { void setShouldShowDisclaimerDialog(Context context, boolean shouldShow); /** - * Returns an {@link Account} that's eligible for support options. + * Returns array of {@link Account} that's eligible for support options. */ - Account getSupportEligibleAccount(Context context); + @NonNull + Account[] getSupportEligibleAccounts(Context context); /** * Starts support activity of specified type * * @param activity Calling activity - * @param account A account returned by {@link #getSupportEligibleAccount} + * @param account A account that selected by user * @param type The type of support account needs. */ void startSupport(Activity activity, Account account, @SupportType int type); diff --git a/tests/robotests/src/com/android/settings/dashboard/SupportItemAdapterTest.java b/tests/robotests/src/com/android/settings/dashboard/SupportItemAdapterTest.java new file mode 100644 index 00000000000..71bb4f657ce --- /dev/null +++ b/tests/robotests/src/com/android/settings/dashboard/SupportItemAdapterTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2016 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.settings.dashboard; + +import android.accounts.Account; +import android.app.Activity; +import android.content.Intent; +import android.provider.Settings; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.Spinner; +import android.widget.SpinnerAdapter; +import com.android.settings.TestConfig; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; +import com.android.settings.overlay.SupportFeatureProvider; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import com.android.settings.R; +import org.robolectric.shadows.ShadowActivity; + +import static org.mockito.Mockito.verify; +import static org.robolectric.Shadows.shadowOf; +import static org.mockito.Mockito.when; +import static com.google.common.truth.Truth.assertThat; + +@RunWith(RobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class SupportItemAdapterTest { + private static final String ACCOUNT_TYPE = "com.google"; + private final Account USER_1 = new Account("user1", ACCOUNT_TYPE); + private final Account USER_2 = new Account("user2", ACCOUNT_TYPE); + private final Account TWO_ACCOUNTS[] = {USER_1, USER_2}; + private final Account ONE_ACCOUNT[] = {USER_1}; + + private ShadowActivity mShadowActivity; + private Activity mActivity; + private SupportItemAdapter mSupportItemAdapter; + private SupportItemAdapter.ViewHolder mViewHolder; + @Mock + private SupportFeatureProvider mSupportFeatureProvider; + @Mock + private MetricsFeatureProvider mMetricsFeatureProvider; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mActivity = Robolectric.setupActivity(Activity.class); + mShadowActivity = shadowOf(mActivity); + + final View itemView = LayoutInflater.from(mActivity).inflate( + R.layout.support_escalation_options, null); + mViewHolder = new SupportItemAdapter.ViewHolder(itemView); + + // Mock this to prevent crash in testing + when(mSupportFeatureProvider.getAccountLoginIntent()).thenReturn( + new Intent(Settings.ACTION_ADD_ACCOUNT)); + } + + @Test + public void testBindAccountPicker_TwoAccounts_ShouldHaveTwoAccounts() { + testBindAccountPickerInner(mViewHolder, TWO_ACCOUNTS); + } + + @Test + public void testBindAccountPicker_OneAccount_ShouldHaveOneAccount() { + testBindAccountPickerInner(mViewHolder, ONE_ACCOUNT); + } + + @Test + public void testOnSpinnerItemClick_AddAccountClicked_AccountLoginIntentInvoked() { + bindAccountPickerInner(mViewHolder, TWO_ACCOUNTS); + + final Spinner spinner = (Spinner) mViewHolder.itemView.findViewById(R.id.account_spinner); + spinner.setSelection(TWO_ACCOUNTS.length); + + Robolectric.flushForegroundThreadScheduler(); + + verify(mSupportFeatureProvider).getAccountLoginIntent(); + } + + /** + * Check after {@link SupportItemAdapter#bindAccountPicker(SupportItemAdapter.ViewHolder)} is + * invoked, whether the spinner in {@paramref viewHolder} has all the data from {@paramref + * accounts} + * + * @param viewHolder holds the view that contains the spinner to test + * @param accounts holds the accounts info to be showed in spinner. + */ + private void testBindAccountPickerInner(SupportItemAdapter.ViewHolder viewHolder, + Account accounts[]) { + bindAccountPickerInner(viewHolder, accounts); + + final Spinner spinner = (Spinner) viewHolder.itemView.findViewById(R.id.account_spinner); + final SpinnerAdapter adapter = spinner.getAdapter(); + + // Contains "Add account" option, so should be 'count+1' + assertThat(adapter.getCount()).isEqualTo(accounts.length + 1); + for (int i = 0; i < accounts.length; i++) { + assertThat(adapter.getItem(i)).isEqualTo(accounts[i].name); + } + } + + /** + * Create {@link SupportItemAdapter} and bind the account picker view into + * {@paramref viewholder} + * + * @param viewHolder holds the view that contains the spinner to test + * @param accounts holds the accounts info to be showed in spinner. + */ + private void bindAccountPickerInner(SupportItemAdapter.ViewHolder viewHolder, + Account accounts[]) { + when(mSupportFeatureProvider.getSupportEligibleAccounts(mActivity)).thenReturn(accounts); + mSupportItemAdapter = new SupportItemAdapter(mActivity, null, mSupportFeatureProvider, + mMetricsFeatureProvider, null); + + mSupportItemAdapter.bindAccountPicker(viewHolder); + } + +}