From f482c284238d329b7432cce4bd11d26577f84fa1 Mon Sep 17 00:00:00 2001 From: tom hsu Date: Thu, 15 Apr 2021 20:38:15 +0800 Subject: [PATCH 01/11] [Provider model] Show no connection during out of service. - When device out of service, show "No connection" on preference summary. - Modify the logic of icon and summary. Bug: 183944316 Test: atest SubscriptionsPreferenceControllerTest passed Change-Id: I179632918a9145f27cec7d9c08e1c0b675fd1ef6 --- .../SubscriptionsPreferenceController.java | 40 ++++++++++--------- ...SubscriptionsPreferenceControllerTest.java | 27 +++++++++++++ 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/com/android/settings/network/SubscriptionsPreferenceController.java b/src/com/android/settings/network/SubscriptionsPreferenceController.java index f89ccfdde80..aea30f2fe0a 100644 --- a/src/com/android/settings/network/SubscriptionsPreferenceController.java +++ b/src/com/android/settings/network/SubscriptionsPreferenceController.java @@ -257,15 +257,20 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl } private CharSequence getMobilePreferenceSummary(int subId) { - TelephonyManager tm = mTelephonyManager.createForSubscriptionId(subId); - String result = mSubsPrefCtrlInjector.getNetworkType( - mContext, mConfig, mTelephonyDisplayInfo, subId); - if (!tm.isDataEnabled()) { + final TelephonyManager tmForSubId = mTelephonyManager.createForSubscriptionId(subId); + if (!tmForSubId.isDataEnabled()) { return mContext.getString(R.string.mobile_data_off_summary); } - if (!result.isEmpty() && mSubsPrefCtrlInjector.isActiveCellularNetwork(mContext)) { + + final boolean isDataInService = tmForSubId.getDataState() + == TelephonyManager.DATA_CONNECTED; + String result = mSubsPrefCtrlInjector.getNetworkType( + mContext, mConfig, mTelephonyDisplayInfo, subId); + if (mSubsPrefCtrlInjector.isActiveCellularNetwork(mContext)) { result = mContext.getString(R.string.preference_summary_default_combination, mContext.getString(R.string.mobile_data_connection_active), result); + } else if (!isDataInService) { + result = mContext.getString(R.string.mobile_data_no_connection); } return Html.fromHtml(result, Html.FROM_HTML_MODE_LEGACY); } @@ -274,31 +279,27 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl final TelephonyManager tmForSubId = mTelephonyManager.createForSubscriptionId(subId); final SignalStrength strength = tmForSubId.getSignalStrength(); int level = (strength == null) ? 0 : strength.getLevel(); - int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS; if (shouldInflateSignalStrength(subId)) { level += 1; numLevels += 1; } - final boolean isMobileDataOn = tmForSubId.isDataEnabled(); + Drawable icon = mSubsPrefCtrlInjector.getIcon(mContext, level, numLevels, false); final boolean isActiveCellularNetwork = mSubsPrefCtrlInjector.isActiveCellularNetwork(mContext); - final boolean isMobileDataAccessible = tmForSubId.getDataState() - == TelephonyManager.DATA_CONNECTED; - final ServiceState serviceState = tmForSubId.getServiceState(); - final boolean isVoiceOutOfService = (serviceState == null) - ? true - : (serviceState.getState() == ServiceState.STATE_OUT_OF_SERVICE); - - Drawable icon = mSubsPrefCtrlInjector.getIcon(mContext, level, numLevels, false); - if (isActiveCellularNetwork) { icon.setTint(Utils.getColorAccentDefaultColor(mContext)); return icon; } - if ((isMobileDataOn && isMobileDataAccessible) - || (!isMobileDataOn && !isVoiceOutOfService)) { + + final boolean isDataInService = tmForSubId.getDataState() + == TelephonyManager.DATA_CONNECTED; + final ServiceState serviceState = tmForSubId.getServiceState(); + final boolean isVoiceInService = (serviceState == null) + ? false + : (serviceState.getState() == ServiceState.STATE_IN_SERVICE); + if (isDataInService || isVoiceInService) { return icon; } @@ -579,7 +580,8 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl String iconKey = getIconKey(telephonyDisplayInfo); int resId = mapIconSets(config).get(iconKey).dataContentDescription; return resId != 0 - ? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : ""; + ? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) + : ""; } /** diff --git a/tests/unit/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java index 699760741e8..916d4e62f3c 100644 --- a/tests/unit/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java @@ -542,6 +542,33 @@ public class SubscriptionsPreferenceControllerTest { assertThat(mPreferenceCategory.getPreference(0).getSummary()).isEqualTo(expectedSummary); } + @Test + @UiThreadTest + public void onTelephonyDisplayInfoChanged_providerAndHasMultiSimAndOutOfService_noConnection() { + final String noConnectionSummary = + ResourcesUtils.getResourcesString(mContext, "mobile_data_no_connection"); + final CharSequence expectedSummary = + Html.fromHtml(noConnectionSummary, Html.FROM_HTML_MODE_LEGACY); + final String networkType = "LTE"; + final List sub = setupMockSubscriptions(2); + final TelephonyDisplayInfo telephonyDisplayInfo = + new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN, + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE); + doReturn(true).when(sInjector).isProviderModelEnabled(mContext); + doReturn(sub.get(0)).when(mSubscriptionManager).getDefaultDataSubscriptionInfo(); + setupGetIconConditions(sub.get(0).getSubscriptionId(), false, true, + TelephonyManager.DATA_DISCONNECTED, ServiceState.STATE_OUT_OF_SERVICE); + doReturn(mock(MobileMappings.Config.class)).when(sInjector).getConfig(mContext); + doReturn(networkType) + .when(sInjector).getNetworkType(any(), any(), any(), anyInt()); + + mController.onResume(); + mController.displayPreference(mPreferenceScreen); + mController.onTelephonyDisplayInfoChanged(telephonyDisplayInfo); + + assertThat(mPreferenceCategory.getPreference(0).getSummary()).isEqualTo(expectedSummary); + } + @Test @UiThreadTest public void onAirplaneModeChanged_providerAndHasSim_noPreference() { From e217cd3480c4f9b733ca4c1d51c9c24762b1dbf9 Mon Sep 17 00:00:00 2001 From: Tsung-Mao Fang Date: Fri, 23 Apr 2021 21:33:17 +0800 Subject: [PATCH 02/11] Bring the search icon back in developer page There're too many settings in developer page, it could be helpful if we still support it in this page. Test: Rebuilt apk, and see this page. Fix: 186207763 Change-Id: If9cd6d4eb25199ee749e4058a635f9f8ce0a49b1 --- .../development/DevelopmentSettingsDashboardFragment.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index e34ae9b6ef7..7afce83a7d5 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -60,6 +60,7 @@ import com.android.settings.development.bluetooth.BluetoothSampleRateDialogPrefe import com.android.settings.development.qstile.DevelopmentTiles; import com.android.settings.development.storage.SharedDataPreferenceController; import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.search.actionbar.SearchMenuController; import com.android.settings.widget.SettingsMainSwitchBar; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; @@ -174,6 +175,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); + SearchMenuController.init(this); if (Utils.isMonkeyRunning()) { getActivity().finish(); return; From 4470faa175a6e22176fcf8f74bc3249d4f19f722 Mon Sep 17 00:00:00 2001 From: Tsung-Mao Fang Date: Tue, 27 Apr 2021 17:59:01 +0800 Subject: [PATCH 03/11] Tweak spacing for private DNS mode dialog Test: See the screen Fix: 186499972 Change-Id: Iaae7d625ef2931b9ba22e6edbd414ebe14cfdc3f --- res/layout/private_dns_mode_dialog.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/layout/private_dns_mode_dialog.xml b/res/layout/private_dns_mode_dialog.xml index 0b4f4db6c4b..12e29e693b7 100644 --- a/res/layout/private_dns_mode_dialog.xml +++ b/res/layout/private_dns_mode_dialog.xml @@ -51,7 +51,7 @@ android:inputType="textFilter|textUri" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginStart="40dp" + android:layout_marginStart="3dp" android:layout_marginEnd="8dp" android:minHeight="@dimen/developer_option_dialog_min_height"/> From 10000eb25481f3a733870fce18daf92c8679ff11 Mon Sep 17 00:00:00 2001 From: ykhung Date: Tue, 27 Apr 2021 16:16:13 +0800 Subject: [PATCH 04/11] Draw timestamp information into BatteryChartView manually (2/2) screenshot: https://screenshot.googleplex.com/8zJcMJeMDovfhdD Bug: 183921876 Test: make SettingsRoboTests Change-Id: I276f70c3cf8ebd2316f987ace0d6537ed2cf8a45 --- .../BatteryChartPreferenceController.java | 24 ++++++++ .../settings/fuelgauge/ConvertUtils.java | 5 ++ .../BatteryChartPreferenceControllerTest.java | 57 +++++++++++++++++-- 3 files changed, 82 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java index 99e6b0db666..972fda140f8 100644 --- a/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java +++ b/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java @@ -40,6 +40,7 @@ import com.android.settingslib.core.lifecycle.events.OnDestroy; import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.utils.StringUtil; +import java.time.Clock; import java.util.Arrays; import java.util.ArrayList; import java.util.Collections; @@ -256,6 +257,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll : mTrapezoidIndex; if (mBatteryChartView != null) { mBatteryChartView.setLevels(mBatteryHistoryLevels); + setTimestampLabel(); } refreshUi(refreshIndex, /*isForce=*/ true); } @@ -525,6 +527,28 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll return true; } + @VisibleForTesting + void setTimestampLabel() { + if (mBatteryChartView == null || mBatteryHistoryKeys == null) { + return; + } + long latestTimestamp = + mBatteryHistoryKeys[mBatteryHistoryKeys.length - 1]; + // Uses the current time if we don't have history data. + if (latestTimestamp == 0) { + latestTimestamp = Clock.systemUTC().millis(); + } + // Generates timestamp label for chart graph (every 8 hours). + final long timeSlotOffset = DateUtils.HOUR_IN_MILLIS * 8; + final String[] timestampLabels = new String[4]; + for (int index = 0; index < timestampLabels.length; index++) { + timestampLabels[index] = + ConvertUtils.utcToLocalTimeHour( + latestTimestamp - (3 - index) * timeSlotOffset); + } + mBatteryChartView.setTimestamps(timestampLabels); + } + private static String utcToLocalTime(long[] timestamps) { final StringBuilder builder = new StringBuilder(); for (int index = 0; index < timestamps.length; index++) { diff --git a/src/com/android/settings/fuelgauge/ConvertUtils.java b/src/com/android/settings/fuelgauge/ConvertUtils.java index 00dcb6be62b..7c3ebef52e3 100644 --- a/src/com/android/settings/fuelgauge/ConvertUtils.java +++ b/src/com/android/settings/fuelgauge/ConvertUtils.java @@ -196,6 +196,11 @@ public final class ConvertUtils { for (int index = 0; index < timeSlotSize; index++) { final Long currentTimestamp = Long.valueOf(batteryHistoryKeys[index * timestampStride]); + // Uses empty list if the timestamp is default value. + if (currentTimestamp == 0) { + resultMap.put(Integer.valueOf(index), new ArrayList()); + continue; + } final Long nextTimestamp = Long.valueOf(batteryHistoryKeys[index * timestampStride + 1]); final Long nextTwoTimestamp = diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java index d687c5dad68..35e3276f922 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java @@ -81,10 +81,7 @@ public final class BatteryChartPreferenceControllerTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); - mBatteryChartPreferenceController = - new BatteryChartPreferenceController( - mContext, "app_list", /*lifecycle=*/ null, - mSettingsActivity, mFragment); + mBatteryChartPreferenceController = createController(); mBatteryChartPreferenceController.mPrefContext = mContext; mBatteryChartPreferenceController.mAppListPrefGroup = mAppListGroup; mBatteryChartPreferenceController.mBatteryChartView = mBatteryChartView; @@ -572,6 +569,52 @@ public final class BatteryChartPreferenceControllerTest { .isEqualTo("System usage for past 24 hr"); } + @Test + public void testSetTimestampLabel_nullBatteryHistoryKeys_ignore() { + mBatteryChartPreferenceController = createController(); + mBatteryChartPreferenceController.mBatteryHistoryKeys = null; + mBatteryChartPreferenceController.mBatteryChartView = + spy(new BatteryChartView(mContext)); + mBatteryChartPreferenceController.setTimestampLabel(); + + verify(mBatteryChartPreferenceController.mBatteryChartView, never()) + .setTimestamps(any()); + } + + @Test + public void testSetTimestampLabel_setExpectedTimestampData() { + mBatteryChartPreferenceController = createController(); + mBatteryChartPreferenceController.mBatteryChartView = + spy(new BatteryChartView(mContext)); + setUpBatteryHistoryKeys(); + // Generates the expected result. + final String[] expectedResults = new String[4]; + final long timeSlotOffset = DateUtils.HOUR_IN_MILLIS * 8; + for (int index = 0; index < expectedResults.length; index++) { + expectedResults[index] = + ConvertUtils.utcToLocalTimeHour( + 1619247636826L - (3 - index) * timeSlotOffset); + } + + mBatteryChartPreferenceController.setTimestampLabel(); + + verify(mBatteryChartPreferenceController.mBatteryChartView) + .setTimestamps(expectedResults); + } + + @Test + public void testSetTimestampLabel_withoutValidTimestamp_setExpectedTimestampData() { + mBatteryChartPreferenceController = createController(); + mBatteryChartPreferenceController.mBatteryChartView = + spy(new BatteryChartView(mContext)); + mBatteryChartPreferenceController.mBatteryHistoryKeys = new long[] {0L}; + + mBatteryChartPreferenceController.setTimestampLabel(); + + verify(mBatteryChartPreferenceController.mBatteryChartView) + .setTimestamps(any()); + } + private static Map> createBatteryHistoryMap(int size) { final Map> batteryHistoryMap = new HashMap<>(); for (int index = 0; index < size; index++) { @@ -598,4 +641,10 @@ public final class BatteryChartPreferenceControllerTest { ConvertUtils.sSimpleDateFormatForHour .setTimeZone(TimeZone.getTimeZone("GMT")); } + + private BatteryChartPreferenceController createController() { + return new BatteryChartPreferenceController( + mContext, "app_list", /*lifecycle=*/ null, + mSettingsActivity, mFragment); + } } From 28d635d9aa6c78ec8b217c4b07cf589e9552215a Mon Sep 17 00:00:00 2001 From: Kevin Chyn Date: Tue, 27 Apr 2021 13:53:56 -0700 Subject: [PATCH 05/11] Update FingerprintEnrollIntro text and icons This change uses strings that have already been merged. Anything requiring string changes will be in a follow-up CL Bug: 185804815 Test: on-device Change-Id: I4e3d3631bbb9f976badf9d4a1582456b3cb7fae7 --- res/drawable/ic_device_locked_24dp.xml | 32 +++++++ res/drawable/ic_link_24dp.xml | 32 +++++++ .../fingerprint_enroll_introduction.xml | 86 ++++++++++++------- 3 files changed, 118 insertions(+), 32 deletions(-) create mode 100644 res/drawable/ic_device_locked_24dp.xml create mode 100644 res/drawable/ic_link_24dp.xml diff --git a/res/drawable/ic_device_locked_24dp.xml b/res/drawable/ic_device_locked_24dp.xml new file mode 100644 index 00000000000..f968b1c26c7 --- /dev/null +++ b/res/drawable/ic_device_locked_24dp.xml @@ -0,0 +1,32 @@ + + + + + + + \ No newline at end of file diff --git a/res/drawable/ic_link_24dp.xml b/res/drawable/ic_link_24dp.xml new file mode 100644 index 00000000000..cce1caad5a9 --- /dev/null +++ b/res/drawable/ic_link_24dp.xml @@ -0,0 +1,32 @@ + + + + + + + + \ No newline at end of file diff --git a/res/layout/fingerprint_enroll_introduction.xml b/res/layout/fingerprint_enroll_introduction.xml index 8555f1ab13a..acf516fc4c9 100644 --- a/res/layout/fingerprint_enroll_introduction.xml +++ b/res/layout/fingerprint_enroll_introduction.xml @@ -55,6 +55,56 @@ android:layout_height="wrap_content" android:orientation="vertical"> + + + + + + + + + + + + + + + + + + + android:src="@drawable/ic_trash_can"/> + android:text="@string/security_settings_fingerprint_v2_enroll_introduction_footer_message_4" /> - - + android:src="@drawable/ic_link_24dp"/> - - - - - - - + android:text="@string/security_settings_fingerprint_v2_enroll_introduction_message_learn_more" /> From 290298d58d306f4b0f173f1ae6f9cd2d515d0d56 Mon Sep 17 00:00:00 2001 From: Kevin Chyn Date: Tue, 27 Apr 2021 14:37:34 -0700 Subject: [PATCH 06/11] Update fingerprint strings Bug: 185804815 Test: manual Change-Id: I5d1431646cd320e0f72e3c5f9f22cc2c9c2d0ef6 --- res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 213b9e10728..68922df8c05 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -945,9 +945,9 @@ Fingerprint Unlock creates a unique model of your fingerprint to recognize you during authentication. To create this fingerprint model during setup, you will take images of your fingerprint from different positions. - The phone will also use images from your interactions with Fingerprint Unlock to update your fingerprint model. Your fingerprint images and model are stored securely on your phone and never leave the phone. All processing occurs securely on your phone. + The phone will also use images from your interactions with Fingerprint Unlock to update your fingerprint model. Images used to create your fingerprint model are never stored, but the fingerprint model is stored securely on your phone and never leaves the phone. All processing occurs securely on your phone. - You can delete your fingerprint images and model, or turn off Fingerprint Unlock at any time in Settings. Fingerprint images and models are stored on the phone until you delete them. + You can delete your fingerprint model, or turn off Fingerprint Unlock at any time in Settings. Fingerprint models are stored on the phone until you delete them. From a39dce70fc356411fb76eee902235eb69e9b64db Mon Sep 17 00:00:00 2001 From: ykhung Date: Wed, 28 Apr 2021 00:58:57 +0800 Subject: [PATCH 07/11] Update string for battery usage footer to include charging info https://docs.google.com/presentation/d/1IDmMZILzXFJb-IOt9zZc0m5g2fCamd9gBoacUsWhibg/edit?ts=6087fc2e&resourcekey=0-UhJZmzej-0opeJHr6yVKWQ#slide=id.gc788e97ad4_0_178 Bug: 177406865 Test: make SettingsRoboTests Change-Id: I6abba8a2c22be57a1afb2e6fae3d2e222dbb3f96 --- res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 828aa9c05df..3741f2cb78f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6466,7 +6466,7 @@ Background: %s - Battery usage data is approximate + Battery usage data is approximate and doesn\'t measure usage when phone is charging From 4d6bbc8af95ad6539afa11a85466e4b0e23123f0 Mon Sep 17 00:00:00 2001 From: Mill Chen Date: Wed, 28 Apr 2021 11:20:32 +0800 Subject: [PATCH 08/11] Make collapsing toolbar load font synchronously Since collapsing toolbar might load the font asynchronously, it caused that some settings pages are seeing a flicker on its title. This change is using TextAppearanceConfig.setShouldLoadFontSynchronously(true) method to have title font load synchronously, which would remove the flicker problem. Fixes: 182232144 Test: visual verified 1) Enable developer options 2) Settings > System > Developer options 3) See if there's a flicker on title Change-Id: I4e1db1a0fae9f69c7ff5baca7ada49c08f1bcbea --- src/com/android/settings/core/SettingsBaseActivity.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/com/android/settings/core/SettingsBaseActivity.java b/src/com/android/settings/core/SettingsBaseActivity.java index cf3cd50296b..96ee51d7f06 100644 --- a/src/com/android/settings/core/SettingsBaseActivity.java +++ b/src/com/android/settings/core/SettingsBaseActivity.java @@ -47,6 +47,7 @@ import com.android.settings.dashboard.CategoryManager; import com.android.settingslib.drawer.Tile; import com.google.android.material.appbar.CollapsingToolbarLayout; +import com.google.android.material.resources.TextAppearanceConfig; import com.google.android.setupcompat.util.WizardManagerHelper; import com.google.android.setupdesign.util.ThemeHelper; @@ -80,6 +81,7 @@ public class SettingsBaseActivity extends FragmentActivity { } final long startTime = System.currentTimeMillis(); getLifecycle().addObserver(new HideNonSystemOverlayMixin(this)); + TextAppearanceConfig.setShouldLoadFontSynchronously(true); final TypedArray theme = getTheme().obtainStyledAttributes(android.R.styleable.Theme); if (!theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) { From 8e0349a67b6aa1ae17e05011fcb1a740faff451f Mon Sep 17 00:00:00 2001 From: SongFerngWang Date: Thu, 22 Apr 2021 21:01:03 +0800 Subject: [PATCH 09/11] Ui show "No connection" when WiFi is turned on and no available network Follow this table to display the UI. https://docs.google.com/spreadsheets/d/1w7R7Q3EXkep7iO4VcgioNp00DWas3Xve5i1rWexAkz8/edit#gid=954295315 Bug: 183593681 Test: atest ProviderModelSliceTest atest ProviderModelSliceHelperTest atest InternetConnectivityPanelTest make RunSettingsRoboTests -j Change-Id: I103f9b2fafe4fc543452a74b7ee7b2e537f879b5 --- .../network/ProviderModelSliceHelper.java | 182 +++++++++--------- .../panel/InternetConnectivityPanel.java | 44 +++-- .../network/ProviderModelSliceHelperTest.java | 32 --- .../panel/InternetConnectivityPanelTest.java | 38 +++- 4 files changed, 153 insertions(+), 143 deletions(-) diff --git a/src/com/android/settings/network/ProviderModelSliceHelper.java b/src/com/android/settings/network/ProviderModelSliceHelper.java index 3a0ceeac6fb..28f857464ae 100644 --- a/src/com/android/settings/network/ProviderModelSliceHelper.java +++ b/src/com/android/settings/network/ProviderModelSliceHelper.java @@ -24,7 +24,6 @@ import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; -import android.net.wifi.WifiManager; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; @@ -75,8 +74,79 @@ public class ProviderModelSliceHelper { mTelephonyManager = context.getSystemService(TelephonyManager.class); } - private static void log(String s) { - Log.d(TAG, s); + /** + * @return whether there is the carrier item in the slice. + */ + public boolean hasCarrier() { + if (isAirplaneModeEnabled() + || mSubscriptionManager == null || mTelephonyManager == null + || mSubscriptionManager.getDefaultDataSubscriptionId() + == mSubscriptionManager.INVALID_SUBSCRIPTION_ID) { + return false; + } + return true; + } + + /** + * @return whether the MobileData's is enabled. + */ + public boolean isMobileDataEnabled() { + return mTelephonyManager.isDataEnabled(); + } + + /** + * To check the carrier data status. + * + * @return whether the carrier data is active. + */ + public boolean isDataSimActive() { + return MobileNetworkUtils.activeNetworkIsCellular(mContext); + } + + /** + * @return whether the ServiceState's data state is in-service. + */ + public boolean isDataStateInService() { + return mTelephonyManager.getDataState() == mTelephonyManager.DATA_CONNECTED; + } + + /** + * @return whether the ServiceState's voice state is in-service. + */ + public boolean isVoiceStateInService() { + final ServiceState serviceState = mTelephonyManager.getServiceState(); + return serviceState != null + && serviceState.getState() == serviceState.STATE_IN_SERVICE; + } + + /** + * To get the signal bar icon with level. + * + * @return The Drawable which is a signal bar icon with level. + */ + public Drawable getDrawableWithSignalStrength() { + final SignalStrength strength = mTelephonyManager.getSignalStrength(); + int level = (strength == null) ? 0 : strength.getLevel(); + int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS; + if (mSubscriptionManager != null && shouldInflateSignalStrength( + mSubscriptionManager.getDefaultDataSubscriptionId())) { + level += 1; + numLevels += 1; + } + return MobileNetworkUtils.getSignalStrengthIcon(mContext, level, numLevels, + NO_CELL_DATA_TYPE_ICON, false); + } + + /** + * To update the telephony with subid. + */ + public void updateTelephony() { + if (mSubscriptionManager == null || mSubscriptionManager.getDefaultDataSubscriptionId() + == mSubscriptionManager.INVALID_SUBSCRIPTION_ID) { + return; + } + mTelephonyManager = mTelephonyManager.createForSubscriptionId( + mSubscriptionManager.getDefaultDataSubscriptionId()); } protected ListBuilder createListBuilder(Uri uri) { @@ -97,19 +167,6 @@ public class ProviderModelSliceHelper { return item.isPresent() ? item.get() : null; } - /** - * @return whether there is the carrier item in the slice. - */ - public boolean hasCarrier() { - if (isAirplaneModeEnabled() - || mSubscriptionManager == null || mTelephonyManager == null - || mSubscriptionManager.getDefaultDataSubscriptionId() - == mSubscriptionManager.INVALID_SUBSCRIPTION_ID) { - return false; - } - return true; - } - protected ListBuilder.RowBuilder createCarrierRow(String networkTypeDescription) { final String title = getMobileTitle(); final String summary = getMobileSummary(networkTypeDescription); @@ -142,6 +199,18 @@ public class ProviderModelSliceHelper { ListBuilder.ICON_IMAGE, mContext.getText(R.string.summary_placeholder)); } + protected boolean isAirplaneModeEnabled() { + return WirelessUtils.isAirplaneModeOn(mContext); + } + + protected SubscriptionManager getSubscriptionManager() { + return mSubscriptionManager; + } + + private static void log(String s) { + Log.d(TAG, s); + } + private PendingIntent getPrimaryAction(String intentAction) { final Intent intent = new Intent(intentAction) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); @@ -153,40 +222,6 @@ public class ProviderModelSliceHelper { return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId); } - protected boolean isAirplaneModeEnabled() { - return WirelessUtils.isAirplaneModeOn(mContext); - } - - protected boolean isMobileDataEnabled() { - if (mTelephonyManager == null) { - return false; - } - return mTelephonyManager.isDataEnabled(); - } - - /** - * To check the carrier data status. - * - * @return whether the carrier data is active. - */ - public boolean isDataSimActive() { - return isNoCarrierData() ? false : MobileNetworkUtils.activeNetworkIsCellular(mContext); - } - - protected boolean isNoCarrierData() { - if (mTelephonyManager == null) { - return false; - } - boolean mobileDataOnAndNoData = isMobileDataEnabled() - && mTelephonyManager.getDataState() != mTelephonyManager.DATA_CONNECTED; - ServiceState serviceState = mTelephonyManager.getServiceState(); - boolean mobileDataOffAndOutOfService = !isMobileDataEnabled() && serviceState != null - && serviceState.getState() == serviceState.STATE_OUT_OF_SERVICE; - log("mobileDataOnAndNoData: " + mobileDataOnAndNoData - + ",mobileDataOffAndOutOfService: " + mobileDataOffAndOutOfService); - return mobileDataOnAndNoData || mobileDataOffAndOutOfService; - } - @VisibleForTesting Drawable getMobileDrawable(Drawable drawable) throws Throwable { // set color and drawable @@ -194,7 +229,7 @@ public class ProviderModelSliceHelper { log("mTelephonyManager == null"); return drawable; } - if (!isNoCarrierData()) { + if (isDataStateInService() || isVoiceStateInService()) { Semaphore lock = new Semaphore(0); AtomicReference shared = new AtomicReference<>(); ThreadUtils.postOnMainThread(() -> { @@ -213,35 +248,18 @@ public class ProviderModelSliceHelper { return drawable; } - /** - * To get the signal bar icon with level. - * - * @return The Drawable which is a signal bar icon with level. - */ - public Drawable getDrawableWithSignalStrength() { - final SignalStrength strength = mTelephonyManager.getSignalStrength(); - int level = (strength == null) ? 0 : strength.getLevel(); - int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS; - if (mSubscriptionManager != null && shouldInflateSignalStrength( - mSubscriptionManager.getDefaultDataSubscriptionId())) { - level += 1; - numLevels += 1; - } - return MobileNetworkUtils.getSignalStrengthIcon(mContext, level, numLevels, - NO_CELL_DATA_TYPE_ICON, false); - } - private String getMobileSummary(String networkTypeDescription) { - final WifiManager wifiManager = mContext.getSystemService(WifiManager.class); + if (!isMobileDataEnabled()) { + return mContext.getString(R.string.mobile_data_off_summary); + } + if (!isDataStateInService()) { + return mContext.getString(R.string.mobile_data_no_connection); + } String summary = networkTypeDescription; if (isDataSimActive()) { summary = mContext.getString(R.string.preference_summary_default_combination, mContext.getString(R.string.mobile_data_connection_active), networkTypeDescription); - } else if (!isMobileDataEnabled()) { - summary = mContext.getString(R.string.mobile_data_off_summary); - } else if (!wifiManager.isWifiEnabled() && !isDataSimActive()) { - summary = mContext.getString(R.string.mobile_data_no_connection); } return summary; } @@ -260,26 +278,10 @@ public class ProviderModelSliceHelper { return title; } - protected SubscriptionManager getSubscriptionManager() { - return mSubscriptionManager; - } - private Set getKeywords() { final String keywords = mContext.getString(R.string.keywords_internet); return Arrays.stream(TextUtils.split(keywords, ",")) .map(String::trim) .collect(Collectors.toSet()); } - - /** - * To update the telephony with subid. - */ - public void updateTelephony() { - if (mSubscriptionManager == null || mSubscriptionManager.getDefaultDataSubscriptionId() - == mSubscriptionManager.INVALID_SUBSCRIPTION_ID) { - return; - } - mTelephonyManager = mTelephonyManager.createForSubscriptionId( - mSubscriptionManager.getDefaultDataSubscriptionId()); - } } diff --git a/src/com/android/settings/panel/InternetConnectivityPanel.java b/src/com/android/settings/panel/InternetConnectivityPanel.java index 238cbb4ed97..f1b9f16e1f8 100644 --- a/src/com/android/settings/panel/InternetConnectivityPanel.java +++ b/src/com/android/settings/panel/InternetConnectivityPanel.java @@ -301,21 +301,37 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve } final List wifiList = mWifiManager.getScanResults(); - if (wifiList != null && wifiList.size() == 0) { - // Sub-Title: - // show non_carrier_network_unavailable - // - while Wi-Fi on + no Wi-Fi item - // show all_network_unavailable: - // - while Wi-Fi on + no Wi-Fi item + no carrier - // - while Wi-Fi on + no Wi-Fi item + no data capability - log("No Wi-Fi item."); - mSubtitle = SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE; - if (!mProviderModelSliceHelper.hasCarrier() - || !mProviderModelSliceHelper.isDataSimActive()) { - log("No carrier item or no carrier data."); - mSubtitle = SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE; - } + if (wifiList != null && wifiList.size() != 0) { + return; } + + // Sub-Title: + // show non_carrier_network_unavailable + // - while Wi-Fi on + no Wi-Fi item + // - while Wi-Fi on + no Wi-Fi item + mobile data off + // show all_network_unavailable: + // - while Wi-Fi on + no Wi-Fi item + no carrier item + // - while Wi-Fi on + no Wi-Fi item + service is out of service + // - while Wi-Fi on + no Wi-Fi item + mobile data on + no carrier data. + log("No Wi-Fi item."); + if (!mProviderModelSliceHelper.hasCarrier() + || (!mProviderModelSliceHelper.isVoiceStateInService() + && !mProviderModelSliceHelper.isDataStateInService())) { + log("no carrier or service is out of service."); + mSubtitle = SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE; + return; + } + if (!mProviderModelSliceHelper.isMobileDataEnabled()) { + log("mobile data off"); + mSubtitle = SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE; + return; + } + if (!mProviderModelSliceHelper.isDataSimActive()) { + log("no carrier data."); + mSubtitle = SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE; + return; + } + mSubtitle = SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE; } private class NetworkProviderTelephonyCallback extends TelephonyCallback implements diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java index 85d3e3afe2f..bfb3d0c510b 100644 --- a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java +++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java @@ -218,38 +218,6 @@ public class ProviderModelSliceHelperTest { assertThat(testRowBuild.getSubtitle()).isEqualTo(expectedSubtitle); } - @Test - public void isNoCarrierData_mobileDataOnAndNoData_returnTrue() { - mockConnections(true, ServiceState.STATE_IN_SERVICE, "", - mTelephonyManager.DATA_DISCONNECTED, true); - - assertThat(mProviderModelSliceHelper.isNoCarrierData()).isTrue(); - } - - @Test - public void isNoCarrierData_mobileDataOffAndOutOfService_returnTrue() { - mockConnections(false, ServiceState.STATE_OUT_OF_SERVICE, "", - mTelephonyManager.DATA_DISCONNECTED, true); - - assertThat(mProviderModelSliceHelper.isNoCarrierData()).isTrue(); - } - - @Test - public void isNoCarrierData_mobileDataOnAndDataConnected_returnFalse() { - mockConnections(true, ServiceState.STATE_IN_SERVICE, "", mTelephonyManager.DATA_CONNECTED, - true); - - assertThat(mProviderModelSliceHelper.isNoCarrierData()).isFalse(); - } - - @Test - public void isNoCarrierData_mobileDataOffAndVoiceIsInService_returnFalse() { - mockConnections(false, ServiceState.STATE_IN_SERVICE, "", - mTelephonyManager.DATA_DISCONNECTED, true); - - assertThat(mProviderModelSliceHelper.isNoCarrierData()).isFalse(); - } - @Test public void getMobileDrawable_noCarrierData_getMobileDrawable() throws Throwable { mockConnections(false, ServiceState.STATE_OUT_OF_SERVICE, "", diff --git a/tests/unit/src/com/android/settings/panel/InternetConnectivityPanelTest.java b/tests/unit/src/com/android/settings/panel/InternetConnectivityPanelTest.java index 8f0cfb31538..0da8464d95c 100644 --- a/tests/unit/src/com/android/settings/panel/InternetConnectivityPanelTest.java +++ b/tests/unit/src/com/android/settings/panel/InternetConnectivityPanelTest.java @@ -129,7 +129,7 @@ public class InternetConnectivityPanelTest { @Test public void getSubTitle_apmOffWifiOnNoWifiListHasCarrierData_NonCarrierNetworkUnavailable() { List wifiList = new ArrayList(); - mockCondition(false, true, true, true, wifiList); + mockCondition(false, true, true, true, true, true, wifiList); mPanel.updatePanelTitle(); @@ -137,9 +137,29 @@ public class InternetConnectivityPanelTest { } @Test - public void getSubTitle_apmOffWifiOnNoWifiListNoCarrierData_AllNetworkUnavailable() { + public void getSubTitle_apmOffWifiOnNoWifiListNoCarrierItem_AllNetworkUnavailable() { List wifiList = new ArrayList(); - mockCondition(false, true, false, true, wifiList); + mockCondition(false, false, false, false, false, true, wifiList); + + mPanel.updatePanelTitle(); + + assertThat(mPanel.getSubTitle()).isEqualTo(SUBTITLE_ALL_NETWORK_UNAVAILABLE); + } + + @Test + public void getSubTitle_apmOffWifiOnNoWifiListNoDataSimActive_AllNetworkUnavailable() { + List wifiList = new ArrayList(); + mockCondition(false, true, false, true, true, true, wifiList); + + mPanel.updatePanelTitle(); + + assertThat(mPanel.getSubTitle()).isEqualTo(SUBTITLE_ALL_NETWORK_UNAVAILABLE); + } + + @Test + public void getSubTitle_apmOffWifiOnNoWifiListNoService_AllNetworkUnavailable() { + List wifiList = new ArrayList(); + mockCondition(false, true, false, true, false, true, wifiList); mPanel.updatePanelTitle(); @@ -151,7 +171,7 @@ public class InternetConnectivityPanelTest { List wifiList = new ArrayList(); wifiList.add(new ScanResult()); wifiList.add(new ScanResult()); - mockCondition(false, true, false, true, wifiList); + mockCondition(false, true, false, true, true, true, wifiList); mPanel.updatePanelTitle(); @@ -184,7 +204,7 @@ public class InternetConnectivityPanelTest { @Test public void getSlices_providerModelDisabled_containsNecessarySlices() { mPanel.mIsProviderModelEnabled = false; - final List uris = mPanel.getSlices(); + List uris = mPanel.getSlices(); assertThat(uris).containsExactly( AirplaneModePreferenceController.SLICE_URI, @@ -194,7 +214,7 @@ public class InternetConnectivityPanelTest { @Test public void getSlices_providerModelEnabled_containsNecessarySlices() { - final List uris = mPanel.getSlices(); + List uris = mPanel.getSlices(); assertThat(uris).containsExactly( CustomSliceRegistry.PROVIDER_MODEL_SLICE_URI, @@ -291,10 +311,14 @@ public class InternetConnectivityPanelTest { } private void mockCondition(boolean airplaneMode, boolean hasCarrier, - boolean isDataSimActive, boolean isWifiEnabled, List wifiItems) { + boolean isDataSimActive, boolean isMobileDataEnabled, boolean isServiceInService, + boolean isWifiEnabled, List wifiItems) { doReturn(airplaneMode).when(mInternetUpdater).isAirplaneModeOn(); when(mProviderModelSliceHelper.hasCarrier()).thenReturn(hasCarrier); when(mProviderModelSliceHelper.isDataSimActive()).thenReturn(isDataSimActive); + when(mProviderModelSliceHelper.isMobileDataEnabled()).thenReturn(isMobileDataEnabled); + when(mProviderModelSliceHelper.isDataStateInService()).thenReturn(isServiceInService); + when(mProviderModelSliceHelper.isVoiceStateInService()).thenReturn(isServiceInService); doReturn(isWifiEnabled).when(mInternetUpdater).isWifiEnabled(); doReturn(wifiItems).when(mWifiManager).getScanResults(); } From ddafb202bd33bd268fb1bf8a50ef37f20006755b Mon Sep 17 00:00:00 2001 From: Hugh Chen Date: Mon, 26 Apr 2021 17:45:46 +0800 Subject: [PATCH 10/11] Fix usb tethering switch back to none after off/on the screen This CL adds conditions to check whether usb is connected or not. If usb is not connected or tethering is not started don't update the state. Bug: 157709648 Test: make RunSettingsRoboTests -j56 Change-Id: Ifbf5900923419d6abf35834b196aab12601076e4 --- .../usb/UsbDefaultFragment.java | 12 ++++++--- .../usb/UsbDetailsFunctionsController.java | 3 ++- .../usb/UsbDefaultFragmentTest.java | 26 +++++++++++++++---- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/com/android/settings/connecteddevice/usb/UsbDefaultFragment.java b/src/com/android/settings/connecteddevice/usb/UsbDefaultFragment.java index de08f38524f..390faa45d07 100644 --- a/src/com/android/settings/connecteddevice/usb/UsbDefaultFragment.java +++ b/src/com/android/settings/connecteddevice/usb/UsbDefaultFragment.java @@ -63,7 +63,7 @@ public class UsbDefaultFragment extends RadioButtonPickerFragment { boolean mIsStartTethering = false; private UsbConnectionBroadcastReceiver mUsbReceiver; - private Handler mHandler = new Handler(); + private Handler mHandler; private boolean mIsConnected = false; @VisibleForTesting @@ -71,14 +71,17 @@ public class UsbDefaultFragment extends RadioButtonPickerFragment { (connected, functions, powerRole, dataRole) -> { final long defaultFunctions = mUsbBackend.getDefaultUsbFunctions(); Log.d(TAG, "UsbConnectionListener() connected : " + connected + ", functions : " - + functions + ", defaultFunctions : " + defaultFunctions); - if (connected && !mIsConnected && defaultFunctions == UsbManager.FUNCTION_RNDIS) { + + functions + ", defaultFunctions : " + defaultFunctions + + ", mIsStartTethering : " + mIsStartTethering); + if (connected && !mIsConnected && defaultFunctions == UsbManager.FUNCTION_RNDIS + && !mIsStartTethering) { startTethering(); } - if (mIsStartTethering) { + if (mIsStartTethering && connected) { mCurrentFunctions = functions; refresh(functions); + mUsbBackend.setDefaultUsbFunctions(functions); mIsStartTethering = false; } mIsConnected = connected; @@ -91,6 +94,7 @@ public class UsbDefaultFragment extends RadioButtonPickerFragment { mTetheringManager = context.getSystemService(TetheringManager.class); mUsbReceiver = new UsbConnectionBroadcastReceiver(context, mUsbConnectionListener, mUsbBackend); + mHandler = new Handler(context.getMainLooper()); getSettingsLifecycle().addObserver(mUsbReceiver); mCurrentFunctions = mUsbBackend.getDefaultUsbFunctions(); } diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java index 40626fbaf04..4698bc77992 100644 --- a/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java +++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java @@ -57,7 +57,7 @@ public class UsbDetailsFunctionsController extends UsbDetailsController private PreferenceCategory mProfilesContainer; private TetheringManager mTetheringManager; - private Handler mHandler = new Handler(); + private Handler mHandler; @VisibleForTesting OnStartTetheringCallback mOnStartTetheringCallback; @VisibleForTesting @@ -69,6 +69,7 @@ public class UsbDetailsFunctionsController extends UsbDetailsController mTetheringManager = context.getSystemService(TetheringManager.class); mOnStartTetheringCallback = new OnStartTetheringCallback(); mPreviousFunction = mUsbBackend.getCurrentFunctions(); + mHandler = new Handler(context.getMainLooper()); } @Override diff --git a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDefaultFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDefaultFragmentTest.java index 31081507bf9..77d47312a85 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDefaultFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDefaultFragmentTest.java @@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -172,7 +173,7 @@ public class UsbDefaultFragmentTest { mFragment.onPause(); - verify(mUsbBackend).setDefaultUsbFunctions(UsbManager.FUNCTION_RNDIS); + verify(mUsbBackend, times(2)).setDefaultUsbFunctions(UsbManager.FUNCTION_RNDIS); assertThat(mFragment.mCurrentFunctions).isEqualTo(UsbManager.FUNCTION_RNDIS); } @@ -184,7 +185,7 @@ public class UsbDefaultFragmentTest { mFragment.onPause(); - verify(mUsbBackend).setDefaultUsbFunctions(UsbManager.FUNCTION_NONE); + verify(mUsbBackend, times(2)).setDefaultUsbFunctions(UsbManager.FUNCTION_NONE); assertThat(mFragment.mCurrentFunctions).isEqualTo(UsbManager.FUNCTION_NONE); } @@ -196,7 +197,7 @@ public class UsbDefaultFragmentTest { mFragment.onPause(); - verify(mUsbBackend).setDefaultUsbFunctions(UsbManager.FUNCTION_MTP); + verify(mUsbBackend, times(2)).setDefaultUsbFunctions(UsbManager.FUNCTION_MTP); assertThat(mFragment.mCurrentFunctions).isEqualTo(UsbManager.FUNCTION_MTP); } @@ -208,7 +209,7 @@ public class UsbDefaultFragmentTest { mFragment.onPause(); - verify(mUsbBackend).setDefaultUsbFunctions(UsbManager.FUNCTION_PTP); + verify(mUsbBackend, times(2)).setDefaultUsbFunctions(UsbManager.FUNCTION_PTP); assertThat(mFragment.mCurrentFunctions).isEqualTo(UsbManager.FUNCTION_PTP); } @@ -220,7 +221,7 @@ public class UsbDefaultFragmentTest { mFragment.onPause(); - verify(mUsbBackend).setDefaultUsbFunctions(UsbManager.FUNCTION_MIDI); + verify(mUsbBackend, times(2)).setDefaultUsbFunctions(UsbManager.FUNCTION_MIDI); assertThat(mFragment.mCurrentFunctions).isEqualTo(UsbManager.FUNCTION_MIDI); } @@ -250,6 +251,21 @@ public class UsbDefaultFragmentTest { eq(mFragment.mOnStartTetheringCallback)); } + @Test + public void usbIsPluginAndUsbTetheringIsAlreadyStarted_startTetheringIsNotInvoked() { + mFragment.mIsStartTethering = true; + when(mUsbBackend.getDefaultUsbFunctions()).thenReturn(UsbManager.FUNCTION_RNDIS); + + mFragment.mUsbConnectionListener.onUsbConnectionChanged(false /* connected */, + UsbManager.FUNCTION_RNDIS, POWER_ROLE_SINK, DATA_ROLE_DEVICE); + mFragment.mUsbConnectionListener.onUsbConnectionChanged(true /* connected */, + UsbManager.FUNCTION_RNDIS, POWER_ROLE_SINK, DATA_ROLE_DEVICE); + + verify(mTetheringManager, never()).startTethering(eq(TetheringManager.TETHERING_USB), + any(), + eq(mFragment.mOnStartTetheringCallback)); + } + public static class TestFragment extends UsbDefaultFragment { public final PreferenceScreen mScreen; From 58813f7b6ce29b3baa1d6b14f1405da395991cef Mon Sep 17 00:00:00 2001 From: Yi-Ling Chuang Date: Fri, 23 Apr 2021 17:10:53 +0800 Subject: [PATCH 11/11] Apply settings page transition This change applies Shared X-Axis transition across settings pages. For injected settings, the injected apps have to also apply the same transition to take effect. Fixes: 177479937 Test: robotests and navigate through settings pages Change-Id: I6241860766f5969428faf9a5b3730a7614fdcd92 --- src/com/android/settings/Utils.java | 13 ++++ .../settings/core/SettingsBaseActivity.java | 71 ++++++++++++++++++- .../settings/core/SubSettingLauncher.java | 18 ++++- .../homepage/SettingsHomepageActivity.java | 21 ++++++ .../settings/core/SubSettingLauncherTest.java | 10 ++- .../SettingsHomepageActivityTest.java | 4 +- .../testutils/shadow/ShadowUtils.java | 11 +++ 7 files changed, 143 insertions(+), 5 deletions(-) diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 031fb8a9d92..fd67aa813b7 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -77,6 +77,7 @@ import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.Profile; import android.provider.ContactsContract.RawContacts; +import android.provider.Settings; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.Spannable; @@ -109,6 +110,7 @@ import androidx.preference.PreferenceGroup; import com.android.internal.app.UnlaunchableAppActivity; import com.android.internal.util.ArrayUtils; import com.android.internal.widget.LockPatternUtils; +import com.android.settings.core.FeatureFlags; import com.android.settings.dashboard.profileselector.ProfileFragmentBridge; import com.android.settings.dashboard.profileselector.ProfileSelectFragment; import com.android.settings.password.ChooseLockSettingsHelper; @@ -159,6 +161,9 @@ public final class Utils extends com.android.settingslib.Utils { /** Whether or not app hibernation is enabled on the device **/ public static final String PROPERTY_APP_HIBERNATION_ENABLED = "app_hibernation_enabled"; + /** Whether or not Settings Shared Axis transition is enabled */ + public static final String SETTINGS_SHARED_AXIS_ENABLED = "settings_shared_axis_enabled"; + /** * Finds a matching activity for a preference's intent. If a matching * activity is not found, it will remove the preference. @@ -1196,4 +1201,12 @@ public final class Utils extends com.android.settingslib.Utils { public static boolean isProviderModelEnabled(Context context) { return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL); } + + public static boolean isPageTransitionEnabled(Context context) { + final boolean isSilkyHome = FeatureFlagUtils.isEnabled(context, FeatureFlags.SILKY_HOME); + final boolean isTransitionEnabled = Settings.Global.getInt(context.getContentResolver(), + SETTINGS_SHARED_AXIS_ENABLED, 0) == 1; + + return isSilkyHome && isTransitionEnabled; + } } diff --git a/src/com/android/settings/core/SettingsBaseActivity.java b/src/com/android/settings/core/SettingsBaseActivity.java index cf3cd50296b..6215dc244e1 100644 --- a/src/com/android/settings/core/SettingsBaseActivity.java +++ b/src/com/android/settings/core/SettingsBaseActivity.java @@ -18,6 +18,7 @@ package com.android.settings.core; import android.annotation.LayoutRes; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityOptions; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -27,6 +28,7 @@ import android.content.pm.PackageManager; import android.content.res.TypedArray; import android.os.AsyncTask; import android.os.Bundle; +import android.os.UserHandle; import android.text.TextUtils; import android.util.ArraySet; import android.util.FeatureFlagUtils; @@ -43,8 +45,10 @@ import androidx.fragment.app.FragmentActivity; import com.android.settings.R; import com.android.settings.SubSettings; +import com.android.settings.Utils; import com.android.settings.dashboard.CategoryManager; import com.android.settingslib.drawer.Tile; +import com.android.settingslib.transition.SettingsTransitionHelper; import com.google.android.material.appbar.CollapsingToolbarLayout; import com.google.android.setupcompat.util.WizardManagerHelper; @@ -60,6 +64,7 @@ public class SettingsBaseActivity extends FragmentActivity { protected static final boolean DEBUG_TIMING = false; private static final String TAG = "SettingsBaseActivity"; private static final String DATA_SCHEME_PKG = "package"; + private static final int DEFAULT_REQUEST = -1; // Serves as a temporary list of tiles to ignore until we heard back from the PM that they // are disabled. @@ -73,6 +78,13 @@ public class SettingsBaseActivity extends FragmentActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { + if (Utils.isPageTransitionEnabled(this)) { + // Enable Activity transitions + getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS); + SettingsTransitionHelper.applyForwardTransition(this); + SettingsTransitionHelper.applyBackwardTransition(this); + } + super.onCreate(savedInstanceState); if (isLockTaskModePinned() && !isSettingsRunOnTop()) { Log.w(TAG, "Devices lock task mode pinned."); @@ -122,6 +134,57 @@ public class SettingsBaseActivity extends FragmentActivity { return true; } + @Override + public void startActivity(Intent intent) { + if (!Utils.isPageTransitionEnabled(this)) { + super.startActivity(intent); + return; + } + super.startActivity(intent, getActivityOptionsBundle()); + } + + @Override + public void startActivity(Intent intent, @androidx.annotation.Nullable Bundle options) { + if (!Utils.isPageTransitionEnabled(this) || options != null) { + super.startActivity(intent, options); + return; + } + super.startActivity(intent, getActivityOptionsBundle()); + } + + @Override + public void startActivityForResult(Intent intent, int requestCode) { + // startActivity() will eventually calls startActivityForResult() with requestCode -1. + // Adding this condition to avoid multiple calls. + if (!Utils.isPageTransitionEnabled(this) || requestCode == DEFAULT_REQUEST) { + super.startActivityForResult(intent, requestCode); + return; + } + super.startActivityForResult(intent, requestCode, getActivityOptionsBundle()); + } + + @Override + public void startActivityForResult(Intent intent, int requestCode, + @androidx.annotation.Nullable Bundle options) { + if (!Utils.isPageTransitionEnabled(this) || requestCode == DEFAULT_REQUEST + || options != null) { + super.startActivityForResult(intent, requestCode, options); + return; + } + super.startActivityForResult(intent, requestCode, getActivityOptionsBundle()); + } + + @Override + public void startActivityForResultAsUser(Intent intent, int requestCode, + UserHandle userHandle) { + if (!Utils.isPageTransitionEnabled(this) || requestCode == DEFAULT_REQUEST) { + super.startActivityForResultAsUser(intent, requestCode, userHandle); + return; + } + super.startActivityForResultAsUser(intent, requestCode, getActivityOptionsBundle(), + userHandle); + } + @Override protected void onResume() { super.onResume(); @@ -267,10 +330,16 @@ public class SettingsBaseActivity extends FragmentActivity { } } + private Bundle getActivityOptionsBundle() { + final Toolbar toolbar = findViewById(R.id.action_bar); + return ActivityOptions.makeSceneTransitionAnimation(this, toolbar, + "shared_element_view").toBundle(); + } + public interface CategoryListener { /** * @param categories the changed categories that have to be refreshed, or null to force - * refreshing all. + * refreshing all. */ void onCategoriesChanged(@Nullable Set categories); } diff --git a/src/com/android/settings/core/SubSettingLauncher.java b/src/com/android/settings/core/SubSettingLauncher.java index 5d9a5282021..545fcdad4ab 100644 --- a/src/com/android/settings/core/SubSettingLauncher.java +++ b/src/com/android/settings/core/SubSettingLauncher.java @@ -17,18 +17,22 @@ package com.android.settings.core; import android.annotation.StringRes; +import android.app.Activity; +import android.app.ActivityOptions; import android.content.Context; import android.content.Intent; -import android.os.Build; import android.os.Bundle; import android.os.UserHandle; import android.text.TextUtils; +import android.widget.Toolbar; import androidx.annotation.VisibleForTesting; import androidx.fragment.app.Fragment; +import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.SubSettings; +import com.android.settings.Utils; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; public class SubSettingLauncher { @@ -183,7 +187,16 @@ public class SubSettingLauncher { resultListener.getActivity().startActivityForResultAsUser(intent, requestCode, userHandle); } - private void launchForResult(Fragment listener, Intent intent, int requestCode) { + @VisibleForTesting + void launchForResult(Fragment listener, Intent intent, int requestCode) { + if (Utils.isPageTransitionEnabled(mContext)) { + final Activity activity = listener.getActivity(); + final Toolbar toolbar = activity.findViewById(R.id.action_bar); + final Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(activity, toolbar, + "shared_element_view").toBundle(); + listener.startActivityForResult(intent, requestCode, bundle); + return; + } listener.startActivityForResult(intent, requestCode); } @@ -192,6 +205,7 @@ public class SubSettingLauncher { intent.replaceExtras(mLaunchRequest.extras); } } + /** * Simple container that has information about how to launch a subsetting. */ diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java index 881e39ce2cb..cd980f32a6f 100644 --- a/src/com/android/settings/homepage/SettingsHomepageActivity.java +++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java @@ -18,11 +18,14 @@ package com.android.settings.homepage; import android.animation.LayoutTransition; import android.app.ActivityManager; +import android.app.ActivityOptions; import android.app.settings.SettingsEnums; +import android.content.Intent; import android.os.Bundle; import android.util.FeatureFlagUtils; import android.util.Log; import android.view.View; +import android.view.Window; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.Toolbar; @@ -33,11 +36,13 @@ import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import com.android.settings.R; +import com.android.settings.Utils; import com.android.settings.accounts.AvatarViewMixin; import com.android.settings.core.FeatureFlags; import com.android.settings.core.HideNonSystemOverlayMixin; import com.android.settings.homepage.contextualcards.ContextualCardsFragment; import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.transition.SettingsTransitionHelper; public class SettingsHomepageActivity extends FragmentActivity { @@ -64,6 +69,12 @@ public class SettingsHomepageActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { + if (Utils.isPageTransitionEnabled(this)) { + // Enable Activity transitions + getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS); + SettingsTransitionHelper.applyForwardTransition(this); + SettingsTransitionHelper.applyBackwardTransition(this); + } super.onCreate(savedInstanceState); setContentView(R.layout.settings_homepage_container); @@ -101,6 +112,16 @@ public class SettingsHomepageActivity extends FragmentActivity { .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING); } + @Override + public void startActivity(Intent intent) { + if (Utils.isPageTransitionEnabled(this)) { + final Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(this).toBundle(); + super.startActivity(intent, bundle); + return; + } + super.startActivity(intent); + } + private void showSuggestionFragment() { final Class fragment = FeatureFactory.getFactory(this) .getSuggestionFeatureProvider(this).getContextualSuggestionFragment(); diff --git a/tests/robotests/src/com/android/settings/core/SubSettingLauncherTest.java b/tests/robotests/src/com/android/settings/core/SubSettingLauncherTest.java index 8f9c33142d7..7a83cd01bb0 100644 --- a/tests/robotests/src/com/android/settings/core/SubSettingLauncherTest.java +++ b/tests/robotests/src/com/android/settings/core/SubSettingLauncherTest.java @@ -28,12 +28,14 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.Intent; +import android.os.Bundle; import android.os.UserHandle; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import com.android.settings.SettingsActivity; +import com.android.settings.testutils.shadow.ShadowUtils; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.Before; @@ -42,10 +44,12 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) +@Config(shadows = ShadowUtils.class) public class SubSettingLauncherTest { @Mock @@ -109,10 +113,13 @@ public class SubSettingLauncherTest { @Test public void launch_hasRequestListener_shouldStartActivityForResult() { + ShadowUtils.setIsPageTransitionEnabled(true); final int requestCode = 123123; when(mFragment.getActivity()).thenReturn(mActivity); final SubSettingLauncher launcher = spy(new SubSettingLauncher(mContext)); + doNothing().when(launcher).launchForResult(any(Fragment.class), any(Intent.class), + anyInt()); launcher.setTitleText("123") .setDestination(SubSettingLauncherTest.class.getName()) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) @@ -120,7 +127,8 @@ public class SubSettingLauncherTest { .setResultListener(mFragment, requestCode) .launch(); - verify(mFragment).startActivityForResult(any(Intent.class), eq(requestCode)); + verify(launcher) + .launchForResult(eq(mFragment), any(Intent.class), eq(requestCode)); } @Test diff --git a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java index c7a2650be54..620f6d74b17 100644 --- a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java +++ b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java @@ -39,6 +39,7 @@ import com.android.settings.core.HideNonSystemOverlayMixin; import com.android.settings.dashboard.suggestions.SuggestionFeatureProviderImpl; import com.android.settings.homepage.contextualcards.slices.BatteryFixSliceTest; import com.android.settings.testutils.shadow.ShadowUserManager; +import com.android.settings.testutils.shadow.ShadowUtils; import org.junit.Before; import org.junit.Test; @@ -58,12 +59,13 @@ import org.robolectric.util.ReflectionHelpers; @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowUserManager.class, - SettingsHomepageActivityTest.ShadowSuggestionFeatureProviderImpl.class}) + SettingsHomepageActivityTest.ShadowSuggestionFeatureProviderImpl.class, ShadowUtils.class}) public class SettingsHomepageActivityTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); + ShadowUtils.setIsPageTransitionEnabled(false); } @Test diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java index 5a32f349d06..750640b225c 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java @@ -47,6 +47,7 @@ public class ShadowUtils { private static boolean sIsVoiceCapable; private static ArraySet sResultLinks = new ArraySet<>(); private static boolean sIsBatteryPresent; + private static boolean sIsPageTransitionEnabled; @Implementation protected static int enforceSameOwner(Context context, int userId) { @@ -69,6 +70,7 @@ public class ShadowUtils { sIsVoiceCapable = false; sResultLinks = new ArraySet<>(); sIsBatteryPresent = true; + sIsPageTransitionEnabled = true; } public static void setIsDemoUser(boolean isDemoUser) { @@ -166,4 +168,13 @@ public class ShadowUtils { public static void setIsBatteryPresent(boolean isBatteryPresent) { sIsBatteryPresent = isBatteryPresent; } + + @Implementation + protected static boolean isPageTransitionEnabled(Context context) { + return sIsPageTransitionEnabled; + } + + public static void setIsPageTransitionEnabled(boolean isPageTransitionEnabled) { + sIsPageTransitionEnabled = isPageTransitionEnabled; + } }