Merge Tap & Pay settings entries with NFC

Bug: 142231537
Test: make -j42 RunSettingsRoboTests
Change-Id: I54ea0cdf711f2c381f5bba8575fc22bd6329f3c6
This commit is contained in:
robertluo
2020-02-03 14:45:46 +08:00
parent 0a832ffe10
commit 81c79bb239
12 changed files with 536 additions and 23 deletions

View File

@@ -7108,6 +7108,8 @@
<string name="help_uri_bluetooth_screen" translatable="false"></string> <string name="help_uri_bluetooth_screen" translatable="false"></string>
<!-- url for the SIM combination warning required dialog help page --> <!-- url for the SIM combination warning required dialog help page -->
<string name="help_uri_sim_combination_warning" translatable="false"></string> <string name="help_uri_sim_combination_warning" translatable="false"></string>
<!-- url for the NFC and payment settings page -->
<string name="help_uri_nfc_and_payment_settings" translatable="false"></string>
<!-- User account title [CHAR LIMIT=30] --> <!-- User account title [CHAR LIMIT=30] -->
<string name="user_account_title">Account for content</string> <string name="user_account_title">Account for content</string>
@@ -7338,6 +7340,8 @@
<string name="connected_devices_dashboard_no_driving_mode_summary">Bluetooth, NFC</string> <string name="connected_devices_dashboard_no_driving_mode_summary">Bluetooth, NFC</string>
<!-- Summary for Connected devices settings, explaning a few important settings under it [CHAR LIMIT=NONE]--> <!-- Summary for Connected devices settings, explaning a few important settings under it [CHAR LIMIT=NONE]-->
<string name="connected_devices_dashboard_no_driving_mode_no_nfc_summary">Bluetooth</string> <string name="connected_devices_dashboard_no_driving_mode_no_nfc_summary">Bluetooth</string>
<!-- Summary for Tap & pay settings, explaning a few important settings under it [CHAR LIMIT=NONE]-->
<string name="nfc_and_payment_settings_payment_off_nfc_off_summary">Unavailable because NFC is off</string>
<!-- Title for setting tile leading to Apps & Notification settings [CHAR LIMIT=40]--> <!-- Title for setting tile leading to Apps & Notification settings [CHAR LIMIT=40]-->
<string name="app_and_notification_dashboard_title">Apps &amp; notifications</string> <string name="app_and_notification_dashboard_title">Apps &amp; notifications</string>
<!-- Summary for Apps & Notification settings, explaining a few important settings under it [CHAR LIMIT=NONE]--> <!-- Summary for Apps & Notification settings, explaining a few important settings under it [CHAR LIMIT=NONE]-->

View File

@@ -28,21 +28,13 @@
android:order="-9" android:order="-9"
settings:searchable="false"/> settings:searchable="false"/>
<SwitchPreference <Preference
android:key="toggle_nfc" android:fragment="com.android.settings.connecteddevice.NfcAndPaymentFragment"
android:key="nfc_and_payment_settings"
android:title="@string/nfc_quick_toggle_title" android:title="@string/nfc_quick_toggle_title"
android:icon="@drawable/ic_nfc" android:icon="@drawable/ic_nfc"
android:summary="@string/nfc_quick_toggle_summary" android:order="-7"
settings:controller="com.android.settings.nfc.NfcPreferenceController" settings:controller="com.android.settings.connecteddevice.NfcAndPaymentFragmentController"/>
android:order="-7"/>
<SwitchPreference
android:key="nfc_secure_settings"
android:title="@string/nfc_secure_settings_title"
settings:controller="com.android.settings.nfc.SecureNfcPreferenceController"
android:icon="@drawable/ic_nfc"
android:summary="@string/nfc_secure_toggle_summary"
android:order="-7"/>
<Preference <Preference
android:fragment="com.android.settings.wfd.WifiDisplaySettings" android:fragment="com.android.settings.wfd.WifiDisplaySettings"

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2020 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/nfc_quick_toggle_title">
<SwitchPreference
android:key="toggle_nfc"
android:title="@string/nfc_quick_toggle_title"
android:summary="@string/nfc_quick_toggle_summary"
settings:controller="com.android.settings.nfc.NfcPreferenceController"/>
<SwitchPreference
android:key="nfc_secure_settings"
android:title="@string/nfc_secure_settings_title"
settings:controller="com.android.settings.nfc.SecureNfcPreferenceController"
android:summary="@string/nfc_secure_toggle_summary"/>
<Preference
android:key="default_payment_app"
android:title="@string/nfc_payment_settings_title"
android:fragment="com.android.settings.nfc.PaymentSettings"
settings:keywords="@string/keywords_default_payment_app"
settings:controller="com.android.settings.applications.specialaccess.DefaultPaymentSettingsPreferenceController"/>
</PreferenceScreen>

View File

@@ -100,13 +100,6 @@
android:fragment="com.android.settings.applications.specialaccess.premiumsms.PremiumSmsAccess" android:fragment="com.android.settings.applications.specialaccess.premiumsms.PremiumSmsAccess"
settings:controller="com.android.settings.applications.specialaccess.premiumsms.PremiumSmsController" /> settings:controller="com.android.settings.applications.specialaccess.premiumsms.PremiumSmsController" />
<Preference
android:key="default_payment_app"
android:title="@string/nfc_payment_settings_title"
android:fragment="com.android.settings.nfc.PaymentSettings"
settings:keywords="@string/keywords_default_payment_app"
settings:controller="com.android.settings.applications.specialaccess.DefaultPaymentSettingsPreferenceController" />
<Preference <Preference
android:key="data_saver" android:key="data_saver"
android:title="@string/unrestricted_data_saver" android:title="@string/unrestricted_data_saver"

View File

@@ -21,11 +21,23 @@ import android.content.pm.PackageManager;
import android.nfc.NfcAdapter; import android.nfc.NfcAdapter;
import android.os.UserManager; import android.os.UserManager;
import com.android.settings.core.BasePreferenceController; import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
public class DefaultPaymentSettingsPreferenceController extends BasePreferenceController { import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
/**
* This Controller works with PaymentSettingsEnabler to control payment features availability
* based on NFC status
*/
public class DefaultPaymentSettingsPreferenceController extends BasePreferenceController
implements LifecycleObserver, OnResume, OnPause {
private final NfcAdapter mNfcAdapter; private final NfcAdapter mNfcAdapter;
private PaymentSettingsEnabler mPaymentSettingsEnabler;
private final PackageManager mPackageManager; private final PackageManager mPackageManager;
private final UserManager mUserManager; private final UserManager mUserManager;
@@ -37,6 +49,32 @@ public class DefaultPaymentSettingsPreferenceController extends BasePreferenceCo
mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext); mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext);
} }
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
if (!isAvailable()) {
mPaymentSettingsEnabler = null;
return;
}
final Preference preference = screen.findPreference(getPreferenceKey());
mPaymentSettingsEnabler = new PaymentSettingsEnabler(mContext, preference);
}
@Override
public void onResume() {
if (mPaymentSettingsEnabler != null) {
mPaymentSettingsEnabler.resume();
}
}
@Override
public void onPause() {
if (mPaymentSettingsEnabler != null) {
mPaymentSettingsEnabler.pause();
}
}
@Override @Override
public int getAvailabilityStatus() { public int getAvailabilityStatus() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC) if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC)
@@ -47,9 +85,12 @@ public class DefaultPaymentSettingsPreferenceController extends BasePreferenceCo
if (!mUserManager.isAdminUser()) { if (!mUserManager.isAdminUser()) {
return DISABLED_FOR_USER; return DISABLED_FOR_USER;
} }
if (mNfcAdapter == null || !mNfcAdapter.isEnabled()) { if (mNfcAdapter == null) {
return CONDITIONALLY_UNAVAILABLE; return CONDITIONALLY_UNAVAILABLE;
} }
if (!mNfcAdapter.isEnabled()) {
return DISABLED_DEPENDENT_SETTING;
}
return AVAILABLE; return AVAILABLE;
} }
} }

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2020 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.applications.specialaccess;
import android.content.Context;
import android.nfc.NfcAdapter;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.nfc.BaseNfcEnabler;
/**
* PaymentSettingsEnabler is a helper to manage the payment feature enable / disable state.
* It enables / disables payment features based on NFC state, and ensures the summary of the
* preference is updated.
*/
public class PaymentSettingsEnabler extends BaseNfcEnabler {
private final Preference mPreference;
public PaymentSettingsEnabler(Context context, Preference preference) {
super(context);
mPreference = preference;
}
@Override
protected void handleNfcStateChanged(int newState) {
switch (newState) {
case NfcAdapter.STATE_OFF:
mPreference.setSummary(
R.string.nfc_and_payment_settings_payment_off_nfc_off_summary);
mPreference.setEnabled(false);
break;
case NfcAdapter.STATE_ON:
mPreference.setSummary(null);
mPreference.setEnabled(true);
break;
}
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2020 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.connecteddevice;
import android.app.settings.SettingsEnums;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
/**
* This fragment contains NFC and payment specific settings.
*/
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class NfcAndPaymentFragment extends DashboardFragment {
private static final String TAG = "NfcAndPaymentFragment";
@Override
protected int getPreferenceScreenResId() {
return R.xml.nfc_and_payment_settings;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.CONNECTION_DEVICE_ADVANCED_NFC;
}
@Override
public int getHelpResource() {
return R.string.help_uri_nfc_and_payment_settings;
}
/**
* For Search.
*/
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.nfc_and_payment_settings);
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2020 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.connecteddevice;
import android.content.Context;
import android.content.pm.PackageManager;
import android.nfc.NfcAdapter;
import android.os.UserManager;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
/**
* Controller that used to show NFC and payment features
*/
public class NfcAndPaymentFragmentController extends BasePreferenceController {
private final NfcAdapter mNfcAdapter;
private final PackageManager mPackageManager;
private final UserManager mUserManager;
public NfcAndPaymentFragmentController(Context context, String preferenceKey) {
super(context, preferenceKey);
mPackageManager = context.getPackageManager();
mUserManager = context.getSystemService(UserManager.class);
mNfcAdapter = NfcAdapter.getDefaultAdapter(context);
}
@Override
public int getAvailabilityStatus() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC)
|| !mPackageManager.hasSystemFeature(
PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
return UNSUPPORTED_ON_DEVICE;
}
if (!mUserManager.isAdminUser()) {
return DISABLED_FOR_USER;
}
return AVAILABLE;
}
@Override
public CharSequence getSummary() {
if (mNfcAdapter != null) {
if (mNfcAdapter.isEnabled()) {
return mContext.getText(R.string.switch_on_text);
} else {
return mContext.getText(R.string.switch_off_text);
}
}
return null;
}
}

View File

@@ -76,4 +76,14 @@ public class DefaultPaymentSettingsPreferenceControllerTest {
assertThat(mController.isAvailable()).isFalse(); assertThat(mController.isAvailable()).isFalse();
} }
@Test
public void getAvailabilityStatus_NfcIsDisabled_shouldReturnDisabled() {
when(mPackageManager.hasSystemFeature(anyString())).thenReturn(true);
when(mUserManager.isAdminUser()).thenReturn(true);
when(mNfcAdapter.isEnabled()).thenReturn(false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(
DefaultPaymentSettingsPreferenceController.DISABLED_DEPENDENT_SETTING);
}
} }

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2020 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.applications.specialaccess;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import android.content.Context;
import android.nfc.NfcAdapter;
import androidx.preference.Preference;
import com.android.settings.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class PaymentSettingsEnablerTest {
private Context mContext;
private Preference mPreference;
private PaymentSettingsEnabler mEnabler;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mPreference = new Preference(mContext);
mEnabler = spy(new PaymentSettingsEnabler(mContext, mPreference));
}
@Test
public void handleNfcStateChanged_stateOff_shouldChangeSummaryAndDisablePreference() {
mEnabler.handleNfcStateChanged(NfcAdapter.STATE_OFF);
assertThat(mPreference.getSummary().toString()).contains(
mContext.getString(R.string.nfc_and_payment_settings_payment_off_nfc_off_summary));
assertThat(mPreference.isEnabled()).isFalse();
}
@Test
public void handleNfcStateChanged_stateOn_shouldClearSummaryAndEnablePreference() {
mEnabler.handleNfcStateChanged(NfcAdapter.STATE_ON);
assertThat(mPreference.getSummary()).isNull();
assertThat(mPreference.isEnabled()).isTrue();
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2020 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.connecteddevice;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.PackageManager;
import android.nfc.NfcAdapter;
import android.nfc.NfcManager;
import android.os.UserManager;
import com.android.settings.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
public class NfcAndPaymentFragmentControllerTest {
private NfcAndPaymentFragmentController mController;
private Context mContext;
@Mock
private PackageManager mPackageManager;
@Mock
private UserManager mUserManager;
@Mock
private NfcManager mNfcManager;
@Mock
private NfcAdapter mNfcAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getApplicationContext()).thenReturn(mContext);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
when(mContext.getSystemService(Context.NFC_SERVICE)).thenReturn(mNfcManager);
when(NfcAdapter.getDefaultAdapter(mContext)).thenReturn(mNfcAdapter);
mController = new NfcAndPaymentFragmentController(mContext, "fakeKey");
ReflectionHelpers.setField(mController, "mNfcAdapter", mNfcAdapter);
}
@Test
public void getAvailabilityStatus_hasNfc_shouldReturnAvailable() {
when(mPackageManager.hasSystemFeature(anyString())).thenReturn(true);
when(mUserManager.isAdminUser()).thenReturn(true);
when(mNfcAdapter.isEnabled()).thenReturn(true);
assertThat(mController.getAvailabilityStatus())
.isEqualTo(NfcAndPaymentFragmentController.AVAILABLE);
}
@Test
public void getAvailabilityStatus_noNfcAdapter_shouldReturnUnsupported() {
ReflectionHelpers.setField(mController, "mNfcAdapter", null);
assertThat(mController.getAvailabilityStatus())
.isEqualTo(NfcAndPaymentFragmentController.UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_notAdminUser_shouldReturnDisabled() {
when(mPackageManager.hasSystemFeature(anyString())).thenReturn(true);
when(mUserManager.isAdminUser()).thenReturn(false);
when(mNfcAdapter.isEnabled()).thenReturn(true);
assertThat(mController.getAvailabilityStatus())
.isEqualTo(NfcAndPaymentFragmentController.DISABLED_FOR_USER);
}
@Test
public void getSummary_nfcOn_shouldProvideOnSummary() {
when(mNfcAdapter.isEnabled()).thenReturn(true);
assertThat(mController.getSummary().toString()).contains(
mContext.getString(R.string.switch_on_text));
}
@Test
public void getSummary_nfcOff_shouldProvideOffSummary() {
when(mNfcAdapter.isEnabled()).thenReturn(false);
assertThat(mController.getSummary().toString()).contains(
mContext.getString(R.string.switch_off_text));
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2020 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.connecteddevice;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.nfc.NfcAdapter;
import android.provider.SearchIndexableResource;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class NfcAndPaymentFragmentTest {
private NfcAndPaymentFragment mFragment;
private Context mContext;
@Mock
private NfcAdapter mNfcAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mFragment = new NfcAndPaymentFragment();
mContext = spy(RuntimeEnvironment.application);
}
@Test
public void searchIndexProvider_shouldIndexResource() {
final List<SearchIndexableResource> indexRes =
NfcAndPaymentFragment.SEARCH_INDEX_DATA_PROVIDER
.getXmlResourcesToIndex(mContext, true /* enabled */);
assertThat(indexRes).isNotNull();
assertThat(indexRes.get(0).xmlResId).isEqualTo(mFragment.getPreferenceScreenResId());
}
@Test
public void searchIndexProvider_shouldIndexAllItems() {
when(mContext.getApplicationContext()).thenReturn(mContext);
when(NfcAdapter.getDefaultAdapter(mContext)).thenReturn(mNfcAdapter);
when(mNfcAdapter.isSecureNfcSupported()).thenReturn(true);
final List<String> niks = NfcAndPaymentFragment.SEARCH_INDEX_DATA_PROVIDER
.getNonIndexableKeys(mContext);
assertThat(niks).isNotNull();
assertThat(niks).isEmpty();
}
}