From 1a253baae4a0212a344db5a3f9a04f74c8083e04 Mon Sep 17 00:00:00 2001 From: Calvin Pan Date: Wed, 18 May 2022 09:32:52 +0000 Subject: [PATCH 1/8] Avoid getting app locale config for each app Getting app locale config will spend a lot of time. Removing the log to prevent getting locale-config for each system app. Bug: 232557302 Test: Saving about 80% under java stack tracing mode Change-Id: I2e755286a4e493be7fd73028ade8d190f6571e46 --- .../settings/applications/AppLocaleUtil.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/com/android/settings/applications/AppLocaleUtil.java b/src/com/android/settings/applications/AppLocaleUtil.java index 31357968a07..8c3671ed943 100644 --- a/src/com/android/settings/applications/AppLocaleUtil.java +++ b/src/com/android/settings/applications/AppLocaleUtil.java @@ -49,17 +49,17 @@ public class AppLocaleUtil { boolean isDisallowedPackage = isDisallowedPackage(context, packageName); boolean hasLauncherEntry = hasLauncherEntry(packageName, infos); boolean isSignedWithPlatformKey = isSignedWithPlatformKey(context, packageName); - boolean isAppLocaleSupported = isAppLocaleSupported(context, packageName); + boolean canDisplay = !isDisallowedPackage + && !isSignedWithPlatformKey + && hasLauncherEntry + && isAppLocaleSupported(context, packageName); + Log.i(TAG, "Can display preference - [" + packageName + "] :" + " isDisallowedPackage : " + isDisallowedPackage + " / isSignedWithPlatformKey : " + isSignedWithPlatformKey + " / hasLauncherEntry : " + hasLauncherEntry - + " / isAppLocaleSupported : " + isAppLocaleSupported); - - return !isDisallowedPackage - && !isSignedWithPlatformKey - && hasLauncherEntry - && isAppLocaleSupported; + + " / canDisplay : " + canDisplay); + return canDisplay; } private static boolean isDisallowedPackage(Context context, String packageName) { From 692068d5351462c334a13a1ff529333a64ffa736 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Fri, 6 May 2022 01:17:20 +0800 Subject: [PATCH 2/8] Add the jank detection to Settings toggles Add jank detection when click the following preferences, - SwitchPreference Single target, detect click in InstrumentedPreferenceFragment - PrimarySwitchPreference Two target, only detect click switch in switch's onClick() - MainSwitchPreference Single target, detect click in TogglePreferenceController - SettingsMainSwitchPreference Single target, detect click in its onSwitchChanged() Bug: 230285829 Test: manual & robo tests Change-Id: I97a13e05a601237b16cd2d903ba2fb6ec4a69a74 --- .../core/InstrumentedPreferenceFragment.java | 16 +++++++++++++++- .../core/TogglePreferenceController.java | 13 +++++++++++++ .../widget/SettingsMainSwitchPreference.java | 2 ++ ...bbleNotificationPreferenceControllerTest.java | 11 +++++++++-- .../app/BlockPreferenceControllerTest.java | 4 +++- .../shadow/ShadowInteractionJankMonitor.java | 4 ++++ 6 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/core/InstrumentedPreferenceFragment.java b/src/com/android/settings/core/InstrumentedPreferenceFragment.java index 48e5176c891..bff405b8e6d 100644 --- a/src/com/android/settings/core/InstrumentedPreferenceFragment.java +++ b/src/com/android/settings/core/InstrumentedPreferenceFragment.java @@ -27,6 +27,7 @@ import android.util.Log; import androidx.annotation.XmlRes; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; +import androidx.preference.SwitchPreference; import androidx.recyclerview.widget.RecyclerView; import com.android.internal.jank.InteractionJankMonitor; @@ -34,6 +35,7 @@ import com.android.settings.overlay.FeatureFactory; import com.android.settings.survey.SurveyMixin; import com.android.settingslib.core.instrumentation.Instrumentable; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; +import com.android.settingslib.core.instrumentation.SettingsJankMonitor; import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; import com.android.settingslib.core.lifecycle.ObservablePreferenceFragment; @@ -45,7 +47,6 @@ public abstract class InstrumentedPreferenceFragment extends ObservablePreferenc private static final String TAG = "InstrumentedPrefFrag"; - protected MetricsFeatureProvider mMetricsFeatureProvider; // metrics placeholder value. Only use this for development. @@ -65,6 +66,19 @@ public abstract class InstrumentedPreferenceFragment extends ObservablePreferenc super.onAttach(context); } + @Override + public void onStart() { + super.onStart(); + // Override the OnPreferenceTreeClickListener in super.onStart() to inject jank detection. + getPreferenceManager().setOnPreferenceTreeClickListener((preference) -> { + if (preference instanceof SwitchPreference) { + SettingsJankMonitor.detectSwitchPreferenceClickJank( + getListView(), (SwitchPreference) preference); + } + return onPreferenceTreeClick(preference); + }); + } + @Override public void onResume() { mVisibilityLoggerMixin.setSourceMetricsCategory(getActivity()); diff --git a/src/com/android/settings/core/TogglePreferenceController.java b/src/com/android/settings/core/TogglePreferenceController.java index fa83aeacdf1..f14e0b23b87 100644 --- a/src/com/android/settings/core/TogglePreferenceController.java +++ b/src/com/android/settings/core/TogglePreferenceController.java @@ -16,12 +16,15 @@ package com.android.settings.core; import android.content.Context; import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; import androidx.preference.TwoStatePreference; import com.android.settings.overlay.FeatureFactory; import com.android.settings.slices.SliceData; import com.android.settings.widget.TwoStateButtonPreference; import com.android.settingslib.PrimarySwitchPreference; +import com.android.settingslib.core.instrumentation.SettingsJankMonitor; +import com.android.settingslib.widget.MainSwitchPreference; /** * Abstract class that consolidates logic for updating toggle controllers. @@ -50,6 +53,16 @@ public abstract class TogglePreferenceController extends BasePreferenceControlle */ public abstract boolean setChecked(boolean isChecked); + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + Preference preference = screen.findPreference(getPreferenceKey()); + if (preference instanceof MainSwitchPreference) { + ((MainSwitchPreference) preference).addOnSwitchChangeListener((switchView, isChecked) -> + SettingsJankMonitor.detectToggleJank(getPreferenceKey(), switchView)); + } + } + @Override public void updateState(Preference preference) { if (preference instanceof TwoStatePreference) { diff --git a/src/com/android/settings/widget/SettingsMainSwitchPreference.java b/src/com/android/settings/widget/SettingsMainSwitchPreference.java index 92649113d82..ce91c9e693c 100644 --- a/src/com/android/settings/widget/SettingsMainSwitchPreference.java +++ b/src/com/android/settings/widget/SettingsMainSwitchPreference.java @@ -32,6 +32,7 @@ import androidx.preference.TwoStatePreference; import com.android.settings.R; import com.android.settings.widget.SettingsMainSwitchBar.OnBeforeCheckedChangeListener; import com.android.settingslib.RestrictedPreferenceHelper; +import com.android.settingslib.core.instrumentation.SettingsJankMonitor; import com.android.settingslib.widget.OnMainSwitchChangeListener; import com.google.android.setupdesign.util.LayoutStyler; @@ -153,6 +154,7 @@ public class SettingsMainSwitchPreference extends TwoStatePreference implements @Override public void onSwitchChanged(Switch switchView, boolean isChecked) { super.setChecked(isChecked); + SettingsJankMonitor.detectToggleJank(getKey(), switchView); } /** diff --git a/tests/robotests/src/com/android/settings/notification/BubbleNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BubbleNotificationPreferenceControllerTest.java index 8d643ad4613..3cb3abfc4a0 100644 --- a/tests/robotests/src/com/android/settings/notification/BubbleNotificationPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/BubbleNotificationPreferenceControllerTest.java @@ -31,9 +31,11 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.content.Context; import android.provider.Settings; +import android.widget.Switch; import androidx.preference.PreferenceScreen; +import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor; import com.android.settingslib.widget.MainSwitchPreference; import org.junit.Before; @@ -44,16 +46,21 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowActivityManager; @RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowInteractionJankMonitor.class}) public class BubbleNotificationPreferenceControllerTest { private Context mContext; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private PreferenceScreen mScreen; + @Mock + private Switch mSwitch; + private BubbleNotificationPreferenceController mController; private MainSwitchPreference mPreference; @@ -102,7 +109,7 @@ public class BubbleNotificationPreferenceControllerTest { public void onSwitchChanged_true_settingIsOff_flagShouldOn() { Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, OFF); - mController.onSwitchChanged(null, true); + mController.onSwitchChanged(mSwitch, true); assertThat(Settings.Global.getInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, OFF)).isEqualTo(ON); @@ -112,7 +119,7 @@ public class BubbleNotificationPreferenceControllerTest { public void onSwitchChanged_false_settingIsOn_flagShouldOff() { Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, ON); - mController.onSwitchChanged(null, false); + mController.onSwitchChanged(mSwitch, false); assertThat(Settings.Global.getInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, ON)).isEqualTo(OFF); diff --git a/tests/robotests/src/com/android/settings/notification/app/BlockPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/BlockPreferenceControllerTest.java index 0c2eebe8070..9e9e655e3af 100644 --- a/tests/robotests/src/com/android/settings/notification/app/BlockPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/app/BlockPreferenceControllerTest.java @@ -17,7 +17,6 @@ package com.android.settings.notification.app; import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID; -import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_NONE; @@ -49,6 +48,7 @@ import androidx.preference.PreferenceViewHolder; import com.android.settings.notification.NotificationBackend; import com.android.settings.widget.SettingsMainSwitchPreference; +import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor; import org.junit.Before; import org.junit.Test; @@ -57,11 +57,13 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowApplication; import java.util.ArrayList; @RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowInteractionJankMonitor.class}) public class BlockPreferenceControllerTest { private Context mContext; diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowInteractionJankMonitor.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowInteractionJankMonitor.java index 3ec07c515ce..9eb08d79246 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowInteractionJankMonitor.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowInteractionJankMonitor.java @@ -23,6 +23,10 @@ import com.android.internal.jank.InteractionJankMonitor; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +/** + * @deprecated use {@link com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor} + */ +@Deprecated @Implements(InteractionJankMonitor.class) public class ShadowInteractionJankMonitor { From 26588fc2e5a3ac164746029bc7fbcab283872d91 Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Fri, 20 May 2022 12:05:32 +0800 Subject: [PATCH 3/8] Fix the work profile's deep link problem - Settings shows the work Homepage while starting the work profile's icon, which is not allowed. - Clicking work apps' App info plays an unsmooth window transition animation. Fix: 233296965 Test: manual, robotest Change-Id: I3d12f6c59692c4a4cc718ea7022b60be50235abb --- .../android/settings/SettingsActivity.java | 3 ++- .../homepage/SettingsHomepageActivity.java | 27 ++++++++++++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java index f5fb26cae30..bb217a0d20a 100644 --- a/src/com/android/settings/SettingsActivity.java +++ b/src/com/android/settings/SettingsActivity.java @@ -433,7 +433,8 @@ public class SettingsActivity extends SettingsBaseActivity final UserManager um = getSystemService(UserManager.class); final UserInfo userInfo = um.getUserInfo(getUser().getIdentifier()); if (userInfo.isManagedProfile()) { - trampolineIntent.putExtra(EXTRA_USER_HANDLE, getUser()); + trampolineIntent.setClass(this, DeepLinkHomepageActivityInternal.class) + .putExtra(EXTRA_USER_HANDLE, getUser()); startActivityAsUser(trampolineIntent, um.getPrimaryUser().getUserHandle()); } else { startActivity(trampolineIntent); diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java index c7bd3b5712e..3c72dd1c3b5 100644 --- a/src/com/android/settings/homepage/SettingsHomepageActivity.java +++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java @@ -27,9 +27,11 @@ import android.app.ActivityManager; import android.app.settings.SettingsEnums; import android.content.ComponentName; import android.content.Intent; +import android.content.pm.UserInfo; import android.content.res.Configuration; import android.os.Bundle; import android.os.UserHandle; +import android.os.UserManager; import android.text.TextUtils; import android.util.ArraySet; import android.util.FeatureFlagUtils; @@ -153,11 +155,6 @@ public class SettingsHomepageActivity extends FragmentActivity implements return mMainFragment; } - /** Whether the activity is showing in two-pane */ - public boolean isTwoPane() { - return mIsTwoPane; - } - @Override public CategoryMixin getCategoryMixin() { return mCategoryMixin; @@ -166,10 +163,26 @@ public class SettingsHomepageActivity extends FragmentActivity implements @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + mIsEmbeddingActivityEnabled = ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this); + if (mIsEmbeddingActivityEnabled) { + final UserManager um = getSystemService(UserManager.class); + final UserInfo userInfo = um.getUserInfo(getUser().getIdentifier()); + if (userInfo.isManagedProfile()) { + final Intent intent = new Intent(getIntent()) + .setClass(this, DeepLinkHomepageActivityInternal.class) + .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT) + .putExtra(EXTRA_USER_HANDLE, getUser()); + intent.removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivityAsUser(intent, um.getPrimaryUser().getUserHandle()); + finish(); + return; + } + } + setupEdgeToEdge(); setContentView(R.layout.settings_homepage_container); - mIsEmbeddingActivityEnabled = ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this); mSplitController = SplitController.getInstance(); mIsTwoPane = mSplitController.isActivityEmbedded(this); @@ -423,7 +436,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements // To prevent launchDeepLinkIntentToRight again for configuration change. intent.setAction(null); - targetIntent.setFlags(targetIntent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK); + targetIntent.removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK); targetIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); // Sender of intent may want to send intent extra data to the destination of targetIntent. From 7de5f9984711d5907ffcd412f0fd53dea6300b6b Mon Sep 17 00:00:00 2001 From: SongFerngWang Date: Fri, 20 May 2022 12:04:13 +0800 Subject: [PATCH 4/8] The settings crash when it add the RTL mark on MEP message The MEP message string is one line, so it did not need to add more RTL marks. hsv: https://hsv.googleplex.com/5694452275347456 Bug: 233082642 Test: manually test and take the hsv. Change-Id: Iad4d469eb6a74805f3c8d5fe5c7eacea1e7a25d3 --- .../network/telephony/ToggleSubscriptionDialogActivity.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java index d348b24e58f..f5f18b4b9ee 100644 --- a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java +++ b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java @@ -460,13 +460,10 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc switchDialogMsg.append( getString(R.string.sim_action_switch_sub_dialog_mep_text, displayName)); if (isRtlMode) { - /* There are two lines of message in the dialog, and the RTL symbols must be added - * before and after each sentence, so use the line break symbol to find the position. + /* The RTL symbols must be added before and after each sentence. * (Each message are all with two line break symbols) */ switchDialogMsg.insert(0, RTL_MARK) - .insert(switchDialogMsg.indexOf(LINE_BREAK) - LINE_BREAK_OFFSET_ONE, RTL_MARK) - .insert(switchDialogMsg.indexOf(LINE_BREAK) + LINE_BREAK_OFFSET_TWO, RTL_MARK) .insert(switchDialogMsg.length(), RTL_MARK); } ConfirmDialogFragment.show( From f8f2d17b66eaa23514287d61d43097bff1da5aaa Mon Sep 17 00:00:00 2001 From: Weng Su Date: Thu, 19 May 2022 05:03:58 +0800 Subject: [PATCH 5/8] Show mobile data icon with carrier Wi-Fi level in Settings - If carrier Wi-Fi is active then use carrier Wi-Fi level instead of mobile data level. - If carrier Wi-Fi level is less than min-level, use min-level instead. Bug: 222885558 Test: manual test atest -c SubscriptionsPreferenceControllerTest \ WifiPickerTrackerHelperTest Change-Id: I46ba5129357bae47e5e44dcbbb26e6673581fdc8 --- .../SubscriptionsPreferenceController.java | 19 +++-- .../wifi/WifiPickerTrackerHelper.java | 11 +++ ...SubscriptionsPreferenceControllerTest.java | 79 ++++++++++++++----- .../wifi/WifiPickerTrackerHelperTest.java | 49 ++++++++---- 4 files changed, 120 insertions(+), 38 deletions(-) diff --git a/src/com/android/settings/network/SubscriptionsPreferenceController.java b/src/com/android/settings/network/SubscriptionsPreferenceController.java index 44ad411c832..ea32de8b153 100644 --- a/src/com/android/settings/network/SubscriptionsPreferenceController.java +++ b/src/com/android/settings/network/SubscriptionsPreferenceController.java @@ -30,7 +30,6 @@ import android.content.IntentFilter; import android.graphics.drawable.Drawable; import android.net.wifi.WifiManager; import android.os.UserManager; -import android.provider.Settings; import android.telephony.AccessNetworkConstants; import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; @@ -65,6 +64,7 @@ import com.android.settingslib.mobile.MobileMappings; import com.android.settingslib.mobile.MobileMappings.Config; import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.net.SignalStrengthUtil; +import com.android.wifitrackerlib.WifiEntry; import java.util.Collections; import java.util.List; @@ -282,16 +282,18 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl return Html.fromHtml(result, Html.FROM_HTML_MODE_LEGACY); } - private Drawable getIcon(int subId) { + @VisibleForTesting + Drawable getIcon(int subId) { 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; boolean isCarrierNetworkActive = isCarrierNetworkActive(); - if (shouldInflateSignalStrength(subId) || isCarrierNetworkActive) { - level = isCarrierNetworkActive - ? SignalStrength.NUM_SIGNAL_STRENGTH_BINS - : (level + 1); + if (isCarrierNetworkActive) { + level = getCarrierNetworkLevel(); + numLevels = WifiEntry.WIFI_LEVEL_MAX + 1; + } else if (shouldInflateSignalStrength(subId)) { + level += 1; numLevels += 1; } @@ -484,6 +486,11 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl && mWifiPickerTrackerHelper.isCarrierNetworkActive(); } + private int getCarrierNetworkLevel() { + if (mWifiPickerTrackerHelper == null) return WifiEntry.WIFI_LEVEL_MIN; + return mWifiPickerTrackerHelper.getCarrierNetworkLevel(); + } + /** * To inject necessary data from each static api. */ diff --git a/src/com/android/settings/wifi/WifiPickerTrackerHelper.java b/src/com/android/settings/wifi/WifiPickerTrackerHelper.java index d4a7da8f5fd..1be71c8cc65 100644 --- a/src/com/android/settings/wifi/WifiPickerTrackerHelper.java +++ b/src/com/android/settings/wifi/WifiPickerTrackerHelper.java @@ -155,6 +155,17 @@ public class WifiPickerTrackerHelper implements LifecycleObserver { return mergedCarrierEntry.getSsid(); } + /** Return the carrier network level */ + public int getCarrierNetworkLevel() { + final MergedCarrierEntry mergedCarrierEntry = mWifiPickerTracker.getMergedCarrierEntry(); + if (mergedCarrierEntry == null) return WifiEntry.WIFI_LEVEL_MIN; + + int level = mergedCarrierEntry.getLevel(); + // To avoid icons not found with WIFI_LEVEL_UNREACHABLE(-1), use WIFI_LEVEL_MIN(0) instead. + if (level < WifiEntry.WIFI_LEVEL_MIN) level = WifiEntry.WIFI_LEVEL_MIN; + return level; + } + @VisibleForTesting void setWifiPickerTracker(@NonNull WifiPickerTracker wifiPickerTracker) { mWifiPickerTracker = wifiPickerTracker; diff --git a/tests/unit/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java index 3bcfcb48fa3..520d0c4ee98 100644 --- a/tests/unit/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java @@ -20,12 +20,14 @@ import static android.telephony.SignalStrength.SIGNAL_STRENGTH_GOOD; import static android.telephony.SignalStrength.SIGNAL_STRENGTH_GREAT; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; +import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MAX; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq;; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -55,7 +57,6 @@ import android.text.Html; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; -import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; @@ -78,12 +79,12 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; @RunWith(AndroidJUnit4.class) public class SubscriptionsPreferenceControllerTest { private static final String KEY = "preference_group"; + private static final int SUB_ID = 1; @Mock private UserManager mUserManager; @@ -105,6 +106,10 @@ public class SubscriptionsPreferenceControllerTest { private WifiPickerTrackerHelper mWifiPickerTrackerHelper; @Mock private WifiManager mWifiManager; + @Mock + private SignalStrength mSignalStrength; + @Mock + private ServiceState mServiceState; private LifecycleRegistry mLifecycleRegistry; private int mOnChildUpdatedCount; @@ -116,6 +121,7 @@ public class SubscriptionsPreferenceControllerTest { private NetworkCapabilities mNetworkCapabilities; private FakeSubscriptionsPreferenceController mController; private static SubsPrefCtrlInjector sInjector; + private NetworkRegistrationInfo mNetworkRegistrationInfo; @Before public void setUp() { @@ -131,6 +137,12 @@ public class SubscriptionsPreferenceControllerTest { when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager); when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager); + when(mSignalStrength.getLevel()).thenReturn(SIGNAL_STRENGTH_GREAT); + when(mTelephonyManager.getServiceState()).thenReturn(mServiceState); + mNetworkRegistrationInfo = createNetworkRegistrationInfo(false /* dateState */); + when(mServiceState.getNetworkRegistrationInfo(anyInt(), anyInt())) + .thenReturn(mNetworkRegistrationInfo); + when(mTelephonyManager.getSignalStrength()).thenReturn(mSignalStrength); when(mConnectivityManager.getActiveNetwork()).thenReturn(mActiveNetwork); when(mConnectivityManager.getNetworkCapabilities(mActiveNetwork)) .thenReturn(mNetworkCapabilities); @@ -153,6 +165,7 @@ public class SubscriptionsPreferenceControllerTest { mController = new FakeSubscriptionsPreferenceController(mContext, mLifecycle, mUpdateListener, KEY, 5); Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0); + mController.setWifiPickerTrackerHelper(mWifiPickerTrackerHelper); } @After @@ -284,7 +297,6 @@ public class SubscriptionsPreferenceControllerTest { doReturn(networkType) .when(sInjector).getNetworkType(any(), any(), any(), anyInt(), eq(true)); doReturn(true).when(mWifiPickerTrackerHelper).isCarrierNetworkActive(); - mController.setWifiPickerTrackerHelper(mWifiPickerTrackerHelper); mController.onResume(); mController.displayPreference(mPreferenceScreen); @@ -521,12 +533,7 @@ public class SubscriptionsPreferenceControllerTest { mController.displayPreference(mPreferenceScreen); Drawable actualIcon = mPreferenceCategory.getPreference(0).getIcon(); ServiceState ss = mock(ServiceState.class); - NetworkRegistrationInfo regInfo = new NetworkRegistrationInfo.Builder() - .setDomain(NetworkRegistrationInfo.DOMAIN_PS) - .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) - .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME) - .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE) - .build(); + NetworkRegistrationInfo regInfo = createNetworkRegistrationInfo(true /* dataState */); doReturn(ss).when(mTelephonyManagerForSub).getServiceState(); doReturn(regInfo).when(ss).getNetworkRegistrationInfo( NetworkRegistrationInfo.DOMAIN_PS, @@ -535,10 +542,43 @@ public class SubscriptionsPreferenceControllerTest { assertThat(icon).isEqualTo(actualIcon); } + @Test + @UiThreadTest + public void getIcon_carrierNetworkIsNotActive_useMobileDataLevel() { + // Fake mobile data active and level is SIGNAL_STRENGTH_GOOD(3) + mNetworkRegistrationInfo = createNetworkRegistrationInfo(true /* dateState */); + when(mServiceState.getNetworkRegistrationInfo(anyInt(), anyInt())) + .thenReturn(mNetworkRegistrationInfo); + when(mSignalStrength.getLevel()).thenReturn(SIGNAL_STRENGTH_GOOD); + // Fake carrier network not active and level is WIFI_LEVEL_MAX(4) + when(mWifiPickerTrackerHelper.isCarrierNetworkActive()).thenReturn(false); + when(mWifiPickerTrackerHelper.getCarrierNetworkLevel()).thenReturn(WIFI_LEVEL_MAX); + + mController.getIcon(SUB_ID); + + verify(sInjector).getIcon(any(), eq(SIGNAL_STRENGTH_GOOD), anyInt(), anyBoolean()); + } + + @Test + @UiThreadTest + public void getIcon_carrierNetworkIsActive_useCarrierNetworkLevel() { + // Fake mobile data not active and level is SIGNAL_STRENGTH_GOOD(3) + mNetworkRegistrationInfo = createNetworkRegistrationInfo(false /* dateState */); + when(mServiceState.getNetworkRegistrationInfo(anyInt(), anyInt())) + .thenReturn(mNetworkRegistrationInfo); + when(mSignalStrength.getLevel()).thenReturn(SIGNAL_STRENGTH_GOOD); + // Fake carrier network active and level is WIFI_LEVEL_MAX(4) + when(mWifiPickerTrackerHelper.isCarrierNetworkActive()).thenReturn(true); + when(mWifiPickerTrackerHelper.getCarrierNetworkLevel()).thenReturn(WIFI_LEVEL_MAX); + + mController.getIcon(SUB_ID); + + verify(sInjector).getIcon(any(), eq(WIFI_LEVEL_MAX), anyInt(), anyBoolean()); + } + @Test public void connectCarrierNetwork_isDataEnabled_helperConnect() { when(mTelephonyManager.isDataEnabled()).thenReturn(true); - mController.setWifiPickerTrackerHelper(mWifiPickerTrackerHelper); mController.connectCarrierNetwork(); @@ -548,7 +588,6 @@ public class SubscriptionsPreferenceControllerTest { @Test public void connectCarrierNetwork_isNotDataEnabled_helperNeverConnect() { when(mTelephonyManager.isDataEnabled()).thenReturn(false); - mController.setWifiPickerTrackerHelper(mWifiPickerTrackerHelper); mController.connectCarrierNetwork(); @@ -561,18 +600,22 @@ public class SubscriptionsPreferenceControllerTest { doReturn(isActiveCellularNetwork).when(sInjector).isActiveCellularNetwork(mContext); doReturn(isDataEnable).when(mTelephonyManagerForSub).isDataEnabled(); ServiceState ss = mock(ServiceState.class); - NetworkRegistrationInfo regInfo = new NetworkRegistrationInfo.Builder() + NetworkRegistrationInfo regInfo = createNetworkRegistrationInfo(dataState); + doReturn(ss).when(mTelephonyManagerForSub).getServiceState(); + doReturn(servicestate).when(ss).getState(); + doReturn(regInfo).when(ss).getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, + AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + } + + private NetworkRegistrationInfo createNetworkRegistrationInfo(boolean dataState) { + return new NetworkRegistrationInfo.Builder() .setDomain(NetworkRegistrationInfo.DOMAIN_PS) .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) .setRegistrationState(dataState ? NetworkRegistrationInfo.REGISTRATION_STATE_HOME : NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING) .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE) .build(); - doReturn(ss).when(mTelephonyManagerForSub).getServiceState(); - doReturn(servicestate).when(ss).getState(); - doReturn(regInfo).when(ss).getNetworkRegistrationInfo( - NetworkRegistrationInfo.DOMAIN_PS, - AccessNetworkConstants.TRANSPORT_TYPE_WWAN); } private List setupMockSubscriptions(int count) { diff --git a/tests/unit/src/com/android/settings/wifi/WifiPickerTrackerHelperTest.java b/tests/unit/src/com/android/settings/wifi/WifiPickerTrackerHelperTest.java index 52724fb3583..1c0cf0429cb 100644 --- a/tests/unit/src/com/android/settings/wifi/WifiPickerTrackerHelperTest.java +++ b/tests/unit/src/com/android/settings/wifi/WifiPickerTrackerHelperTest.java @@ -16,6 +16,10 @@ package com.android.settings.wifi; +import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MAX; +import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MIN; +import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -23,7 +27,6 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -48,6 +51,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -58,6 +62,8 @@ public class WifiPickerTrackerHelperTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Spy + Context mContext = ApplicationProvider.getApplicationContext(); @Mock public WifiManager mWifiManager; @Mock @@ -76,11 +82,10 @@ public class WifiPickerTrackerHelperTest { @Before public void setUp() { - final Context context = spy(ApplicationProvider.getApplicationContext()); - when(context.getSystemService(WifiManager.class)).thenReturn(mWifiManager); + when(mContext.getSystemService(WifiManager.class)).thenReturn(mWifiManager); mCarrierConfig = new PersistableBundle(); when(mCarrierConfigCache.getConfigForSubId(SUB_ID)).thenReturn(mCarrierConfig); - CarrierConfigCache.setTestInstance(context, mCarrierConfigCache); + CarrierConfigCache.setTestInstance(mContext, mCarrierConfigCache); mFeatureFactory = FakeFeatureFactory.setupForTest(); when(mFeatureFactory.wifiTrackerLibProvider @@ -88,7 +93,9 @@ public class WifiPickerTrackerHelperTest { any(), any(), any(), any(), any(), anyLong(), anyLong(), any())) .thenReturn(mWifiPickerTracker); mWifiPickerTrackerHelper = new WifiPickerTrackerHelper(mock(Lifecycle.class), - context, null); + mContext, null); + when(mWifiPickerTracker.getMergedCarrierEntry()).thenReturn(mMergedCarrierEntry); + mWifiPickerTrackerHelper.setWifiPickerTracker(mWifiPickerTracker); } @Test @@ -140,9 +147,6 @@ public class WifiPickerTrackerHelperTest { @Test public void setCarrierNetworkEnabled_shouldSetCorrect() { - mWifiPickerTrackerHelper.setWifiPickerTracker(mWifiPickerTracker); - when(mWifiPickerTracker.getMergedCarrierEntry()).thenReturn(mMergedCarrierEntry); - mWifiPickerTrackerHelper.setCarrierNetworkEnabled(true); verify(mMergedCarrierEntry).setEnabled(true); @@ -154,7 +158,6 @@ public class WifiPickerTrackerHelperTest { @Test public void setCarrierNetworkEnabled_mergedCarrierEntryIsNull_shouldNotSet() { - mWifiPickerTrackerHelper.setWifiPickerTracker(mWifiPickerTracker); when(mWifiPickerTracker.getMergedCarrierEntry()).thenReturn(null); mWifiPickerTrackerHelper.setCarrierNetworkEnabled(true); @@ -168,8 +171,6 @@ public class WifiPickerTrackerHelperTest { @Test public void connectCarrierNetwork_returnTrueAndConnect() { - mWifiPickerTrackerHelper.setWifiPickerTracker(mWifiPickerTracker); - when(mWifiPickerTracker.getMergedCarrierEntry()).thenReturn(mMergedCarrierEntry); when(mMergedCarrierEntry.canConnect()).thenReturn(true); assertThat(mWifiPickerTrackerHelper.connectCarrierNetwork(mConnectCallback)).isTrue(); @@ -178,7 +179,6 @@ public class WifiPickerTrackerHelperTest { @Test public void connectCarrierNetwork_mergedCarrierEntryIsNull_returnFalse() { - mWifiPickerTrackerHelper.setWifiPickerTracker(mWifiPickerTracker); when(mWifiPickerTracker.getMergedCarrierEntry()).thenReturn(null); assertThat(mWifiPickerTrackerHelper.connectCarrierNetwork(mConnectCallback)).isFalse(); @@ -186,11 +186,32 @@ public class WifiPickerTrackerHelperTest { @Test public void connectCarrierNetwork_canConnectIsFalse_returnFalseAndNeverConnect() { - mWifiPickerTrackerHelper.setWifiPickerTracker(mWifiPickerTracker); - when(mWifiPickerTracker.getMergedCarrierEntry()).thenReturn(mMergedCarrierEntry); when(mMergedCarrierEntry.canConnect()).thenReturn(false); assertThat(mWifiPickerTrackerHelper.connectCarrierNetwork(mConnectCallback)).isFalse(); verify(mMergedCarrierEntry, never()).connect(mConnectCallback); } + + @Test + public void getCarrierNetworkLevel_mergedCarrierEntryIsNull_returnMinLevel() { + when(mWifiPickerTracker.getMergedCarrierEntry()).thenReturn(null); + + assertThat(mWifiPickerTrackerHelper.getCarrierNetworkLevel()).isEqualTo(WIFI_LEVEL_MIN); + } + + @Test + public void getCarrierNetworkLevel_getUnreachableLevel_returnMinLevel() { + when(mMergedCarrierEntry.getLevel()).thenReturn(WIFI_LEVEL_UNREACHABLE); + + assertThat(mWifiPickerTrackerHelper.getCarrierNetworkLevel()).isEqualTo(WIFI_LEVEL_MIN); + } + + @Test + public void getCarrierNetworkLevel_getAvailableLevel_returnSameLevel() { + for (int level = WIFI_LEVEL_MIN; level <= WIFI_LEVEL_MAX; level++) { + when(mMergedCarrierEntry.getLevel()).thenReturn(level); + + assertThat(mWifiPickerTrackerHelper.getCarrierNetworkLevel()).isEqualTo(level); + } + } } From 9a0675cdc9d8921ba1f003dc7953aae082d1c705 Mon Sep 17 00:00:00 2001 From: Hank Sheng Date: Fri, 20 May 2022 09:02:19 +0000 Subject: [PATCH 6/8] Apply the glifv3 theme when the theme string in intentExtra is glifv4. Create the short-term solution for the issue in settings page. The issue is because when we set to use glifv4 theme, the theme util in settings do not have that version(glifv4) info in their util so that they will roll back to the glif v1 theme. Current solution is rollback to the glifv3 first. screenshot: https://hsv.googleplex.com/5599282846498816 https://hsv.googleplex.com/5354235030929408 Bug: 233032365 Bug: 233036258 Change-Id: I7cdd25d34eee6dd8593c550ac803de5852ad870c --- src/com/android/settings/SetupWizardUtils.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/com/android/settings/SetupWizardUtils.java b/src/com/android/settings/SetupWizardUtils.java index e0292ef5b9c..e0e9073a220 100644 --- a/src/com/android/settings/SetupWizardUtils.java +++ b/src/com/android/settings/SetupWizardUtils.java @@ -48,6 +48,9 @@ public class SetupWizardUtils { if (WizardManagerHelper.isAnySetupWizard(intent)) { if (ThemeHelper.isSetupWizardDayNightEnabled(context)) { switch (theme) { + // TODO(b/233032365): Create glif v4 theme for this case. + case ThemeHelper.THEME_GLIF_V4_LIGHT: + case ThemeHelper.THEME_GLIF_V4: case ThemeHelper.THEME_GLIF_V3_LIGHT: case ThemeHelper.THEME_GLIF_V3: return R.style.GlifV3Theme_DayNight; @@ -60,8 +63,11 @@ public class SetupWizardUtils { } } else { switch (theme) { + // TODO(b/233032365): Create glif v4 theme for this case. + case ThemeHelper.THEME_GLIF_V4_LIGHT: case ThemeHelper.THEME_GLIF_V3_LIGHT: return R.style.GlifV3Theme_Light; + case ThemeHelper.THEME_GLIF_V4: case ThemeHelper.THEME_GLIF_V3: return R.style.GlifV3Theme; case ThemeHelper.THEME_GLIF_V2_LIGHT: @@ -76,6 +82,9 @@ public class SetupWizardUtils { } } else { switch (theme) { + // TODO(b/233032365): Create glif v4 theme for this case. + case ThemeHelper.THEME_GLIF_V4_LIGHT: + case ThemeHelper.THEME_GLIF_V4: case ThemeHelper.THEME_GLIF_V3_LIGHT: case ThemeHelper.THEME_GLIF_V3: return R.style.GlifV3Theme; From e763ca1715a4444813d1ba8a3e52bf424c263e1a Mon Sep 17 00:00:00 2001 From: Yi-Ling Chuang Date: Fri, 20 May 2022 10:10:42 +0000 Subject: [PATCH 7/8] Revert "Show previous query upon configuration change" This reverts commit d9453bf8c612ee315bb8036698cec8c9a02b78fe. Reason for revert: This introduces a new flicker which doesn't seem good. Will have another solution once all things are ready. Fixes: 232355879 Change-Id: I0bbc4561ce4fbef36d59de15ae98705a6d81574c --- .../ManageApplications.java | 9 ---- .../ManageApplicationsTest.java | 50 ------------------- 2 files changed, 59 deletions(-) diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java index c081ef9af1e..062bfc576b2 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplications.java +++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java @@ -173,8 +173,6 @@ public class ManageApplications extends InstrumentedFragment private static final String EXTRA_HAS_BRIDGE = "hasBridge"; private static final String EXTRA_FILTER_TYPE = "filterType"; @VisibleForTesting - static final String EXTRA_SEARCH_QUERY = "search_query"; - @VisibleForTesting static final String EXTRA_EXPAND_SEARCH_VIEW = "expand_search_view"; // attributes used as keys when passing values to AppInfoDashboardFragment activity @@ -255,8 +253,6 @@ public class ManageApplications extends InstrumentedFragment // Whether or not search view is expanded. @VisibleForTesting boolean mExpandSearch; - @VisibleForTesting - CharSequence mPreQuery; private View mRootView; private Spinner mFilterSpinner; @@ -362,7 +358,6 @@ public class ManageApplications extends InstrumentedFragment mFilterType = savedInstanceState.getInt(EXTRA_FILTER_TYPE, AppFilterRegistry.FILTER_APPS_ALL); mExpandSearch = savedInstanceState.getBoolean(EXTRA_EXPAND_SEARCH_VIEW); - mPreQuery = savedInstanceState.getCharSequence(EXTRA_SEARCH_QUERY); } mInvalidSizeStr = activity.getText(R.string.invalid_size_value); @@ -549,7 +544,6 @@ public class ManageApplications extends InstrumentedFragment outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem); if (mSearchView != null) { outState.putBoolean(EXTRA_EXPAND_SEARCH_VIEW, !mSearchView.isIconified()); - outState.putCharSequence(EXTRA_SEARCH_QUERY, mSearchView.getQuery()); } if (mApplications != null) { outState.putBoolean(EXTRA_HAS_ENTRIES, mApplications.mHasReceivedLoadEntries); @@ -689,9 +683,6 @@ public class ManageApplications extends InstrumentedFragment if (mExpandSearch) { searchMenuItem.expandActionView(); } - if (!TextUtils.isEmpty(mPreQuery)) { - mSearchView.setQuery(mPreQuery, true); - } } updateOptionsMenu(); diff --git a/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java b/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java index ec4f8f2e8b6..da17f1143bb 100644 --- a/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java +++ b/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java @@ -188,24 +188,6 @@ public class ManageApplicationsTest { verify(searchMenu).expandActionView(); } - @Test - public void onCreateOptionsMenu_hasPreQuery_shouldSetQuery() { - final SearchView searchView = mock(SearchView.class); - final MenuItem searchMenu = mock(MenuItem.class); - final MenuItem helpMenu = mock(MenuItem.class); - when(searchMenu.getActionView()).thenReturn(searchView); - when(mMenu.findItem(R.id.search_app_list_menu)).thenReturn(searchMenu); - when(mMenu.add(anyInt() /* groupId */, anyInt() /* itemId */, anyInt() /* order */, - anyInt() /* titleRes */)).thenReturn(helpMenu); - doReturn("Test").when(mFragment).getText(anyInt() /* resId */); - doNothing().when(mFragment).updateOptionsMenu(); - - mFragment.mPreQuery = "test"; - mFragment.onCreateOptionsMenu(mMenu, mock(MenuInflater.class)); - - verify(searchView).setQuery("test", true); - } - @Test public void onQueryTextChange_shouldFilterSearchInApplicationsAdapter() { final ManageApplications.ApplicationsAdapter adapter = @@ -536,38 +518,6 @@ public class ManageApplicationsTest { assertThat(bundle.getBoolean(ManageApplications.EXTRA_EXPAND_SEARCH_VIEW)).isFalse(); } - @Test - public void onSaveInstanceState_noSearchView_shouldNotSaveQuery() { - final Bundle bundle = new Bundle(); - ReflectionHelpers.setField(mFragment, "mResetAppsHelper", mock(ResetAppsHelper.class)); - ReflectionHelpers.setField(mFragment, "mFilter", mock(AppFilterItem.class)); - ReflectionHelpers.setField(mFragment, "mApplications", - mock(ManageApplications.ApplicationsAdapter.class)); - - mFragment.onSaveInstanceState(bundle); - - assertThat(bundle.containsKey(ManageApplications.EXTRA_SEARCH_QUERY)).isFalse(); - } - - @Test - public void onSaveInstanceState_searchViewSet_shouldSaveQuery() { - final SearchView searchView = mock(SearchView.class); - final Bundle bundle = new Bundle(); - ReflectionHelpers.setField(mFragment, "mResetAppsHelper", mock(ResetAppsHelper.class)); - ReflectionHelpers.setField(mFragment, "mFilter", mock(AppFilterItem.class)); - ReflectionHelpers.setField(mFragment, "mApplications", - mock(ManageApplications.ApplicationsAdapter.class)); - ReflectionHelpers.setField(mFragment, "mSearchView", searchView); - when(searchView.isIconified()).thenReturn(true); - when(searchView.getQuery()).thenReturn("test"); - - mFragment.onSaveInstanceState(bundle); - - assertThat(bundle.containsKey(ManageApplications.EXTRA_SEARCH_QUERY)).isTrue(); - assertThat(bundle.getCharSequence(ManageApplications.EXTRA_SEARCH_QUERY)) - .isEqualTo("test"); - } - @Test public void createHeader_batteryListType_hasCorrectItems() { ReflectionHelpers.setField(mFragment, "mListType", ManageApplications.LIST_TYPE_HIGH_POWER); From e04dfaa29e88e281adc989035a8f12cd1e92735a Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Fri, 20 May 2022 22:55:53 +0800 Subject: [PATCH 8/8] Filter out the duplicate click event on menu page Whenever the highlighted item is clicked, Settings shouldn't start the second-layer page again even if the user goes to its child page. Ex. When the user goes to the Tethering page, clicking on the menu entry "Network & Internet" should not switch to the page. Exception: in the deep link case, allow the first click event on the highlighted item to launch the second-layer page when the page is not the same as the one on the right pane. Fix: 215267159 Test: manual, robotest Change-Id: I2315e0069facc4867cb157752b1a3144716b7d17 --- .../DashboardFeatureProviderImpl.java | 32 ++++++--- .../homepage/TopLevelHighlightMixin.java | 4 ++ .../settings/homepage/TopLevelSettings.java | 72 +++++++++++++++++++ 3 files changed, 100 insertions(+), 8 deletions(-) diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java index 2ae20570e9c..0f108896ead 100644 --- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java +++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java @@ -180,15 +180,17 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { } pref.setOnPreferenceClickListener(preference -> { TopLevelHighlightMixin highlightMixin = null; + boolean isDuplicateClick = false; if (fragment instanceof TopLevelSettings && ActivityEmbeddingUtils.isEmbeddingActivityEnabled(mContext)) { // Highlight the preference whenever it's clicked final TopLevelSettings topLevelSettings = (TopLevelSettings) fragment; topLevelSettings.setHighlightPreferenceKey(key); highlightMixin = topLevelSettings.getHighlightMixin(); + isDuplicateClick = topLevelSettings.isDuplicateClick(preference); } launchIntentOrSelectProfile(activity, tile, intent, sourceMetricsCategory, - highlightMixin); + highlightMixin, isDuplicateClick); return true; }); } @@ -221,7 +223,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { SettingsEnums.DASHBOARD_SUMMARY) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); launchIntentOrSelectProfile(activity, tile, intent, SettingsEnums.DASHBOARD_SUMMARY, - /* highlightMixin= */ null); + /* highlightMixin= */ null, /* isDuplicateClick= */ false); } private DynamicDataObserver createDynamicDataObserver(String method, Uri uri, Preference pref) { @@ -433,31 +435,45 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider { } private void launchIntentOrSelectProfile(FragmentActivity activity, Tile tile, Intent intent, - int sourceMetricCategory, TopLevelHighlightMixin highlightMixin) { + int sourceMetricCategory, TopLevelHighlightMixin highlightMixin, + boolean isDuplicateClick) { if (!isIntentResolvable(intent)) { Log.w(TAG, "Cannot resolve intent, skipping. " + intent); return; } ProfileSelectDialog.updateUserHandlesIfNeeded(mContext, tile); - mMetricsFeatureProvider.logStartedIntent(intent, sourceMetricCategory); if (tile.userHandle == null || tile.isPrimaryProfileOnly()) { - activity.startActivity(intent); + if (!isDuplicateClick) { + mMetricsFeatureProvider.logStartedIntent(intent, sourceMetricCategory); + activity.startActivity(intent); + } } else if (tile.userHandle.size() == 1) { - activity.startActivityAsUser(intent, tile.userHandle.get(0)); + if (!isDuplicateClick) { + mMetricsFeatureProvider.logStartedIntent(intent, sourceMetricCategory); + activity.startActivityAsUser(intent, tile.userHandle.get(0)); + } } else { final UserHandle userHandle = intent.getParcelableExtra(EXTRA_USER); if (userHandle != null && tile.userHandle.contains(userHandle)) { - activity.startActivityAsUser(intent, userHandle); + if (!isDuplicateClick) { + mMetricsFeatureProvider.logStartedIntent(intent, sourceMetricCategory); + activity.startActivityAsUser(intent, userHandle); + } return; } final List resolvableUsers = getResolvableUsers(intent, tile); if (resolvableUsers.size() == 1) { - activity.startActivityAsUser(intent, resolvableUsers.get(0)); + if (!isDuplicateClick) { + mMetricsFeatureProvider.logStartedIntent(intent, sourceMetricCategory); + activity.startActivityAsUser(intent, resolvableUsers.get(0)); + } return; } + // Show the profile select dialog regardless of the duplicate click. + mMetricsFeatureProvider.logStartedIntent(intent, sourceMetricCategory); ProfileSelectDialog.show(activity.getSupportFragmentManager(), tile, sourceMetricCategory, /* onShowListener= */ highlightMixin, /* onDismissListener= */ highlightMixin, diff --git a/src/com/android/settings/homepage/TopLevelHighlightMixin.java b/src/com/android/settings/homepage/TopLevelHighlightMixin.java index c473e87eeca..4718443f8a4 100644 --- a/src/com/android/settings/homepage/TopLevelHighlightMixin.java +++ b/src/com/android/settings/homepage/TopLevelHighlightMixin.java @@ -140,6 +140,10 @@ public class TopLevelHighlightMixin implements Parcelable, DialogInterface.OnSho } } + String getHighlightPreferenceKey() { + return mCurrentKey; + } + void highlightPreferenceIfNeeded() { if (mTopLevelAdapter != null) { mTopLevelAdapter.requestHighlight(); diff --git a/src/com/android/settings/homepage/TopLevelSettings.java b/src/com/android/settings/homepage/TopLevelSettings.java index 8623509ce9b..d221d253b14 100644 --- a/src/com/android/settings/homepage/TopLevelSettings.java +++ b/src/com/android/settings/homepage/TopLevelSettings.java @@ -16,12 +16,17 @@ package com.android.settings.homepage; +import static android.provider.Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI; + import static com.android.settings.search.actionbar.SearchMenuController.NEED_SEARCH_ICON_IN_ACTION_BAR; import static com.android.settingslib.search.SearchIndexable.MOBILE; import android.app.ActivityManager; import android.app.settings.SettingsEnums; import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -38,6 +43,7 @@ import androidx.recyclerview.widget.RecyclerView; import androidx.window.embedding.SplitController; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.activityembedding.ActivityEmbeddingRulesController; import com.android.settings.activityembedding.ActivityEmbeddingUtils; @@ -51,12 +57,15 @@ import com.android.settingslib.core.instrumentation.Instrumentable; import com.android.settingslib.drawer.Tile; import com.android.settingslib.search.SearchIndexable; +import java.net.URISyntaxException; + @SearchIndexable(forTarget = MOBILE) public class TopLevelSettings extends DashboardFragment implements SplitLayoutListener, PreferenceFragmentCompat.OnPreferenceStartFragmentCallback { private static final String TAG = "TopLevelSettings"; private static final String SAVED_HIGHLIGHT_MIXIN = "highlight_mixin"; + private static final String SAVED_FIRST_PREFERENCE_CLICK = "first_pref_click"; private static final String PREF_KEY_SUPPORT = "top_level_support"; private boolean mIsEmbeddingActivityEnabled; @@ -64,6 +73,7 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi private int mPaddingHorizontal; private boolean mScrollNeeded = true; private boolean mFirstStarted = true; + private boolean mFirstDuplicateClickCheck = true; public TopLevelSettings() { final Bundle args = new Bundle(); @@ -107,6 +117,10 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi @Override public boolean onPreferenceTreeClick(Preference preference) { + if (isDuplicateClick(preference)) { + return true; + } + // Register SplitPairRule for SubSettings. ActivityEmbeddingRulesController.registerSubSettingsPairRule(getContext(), true /* clearTop */); @@ -140,6 +154,7 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi boolean activityEmbedded = SplitController.getInstance().isActivityEmbedded(getActivity()); if (icicle != null) { + mFirstDuplicateClickCheck = icicle.getBoolean(SAVED_FIRST_PREFERENCE_CLICK); mHighlightMixin = icicle.getParcelable(SAVED_HIGHLIGHT_MIXIN); mScrollNeeded = !mHighlightMixin.isActivityEmbedded() && activityEmbedded; mHighlightMixin.setActivityEmbedded(activityEmbedded); @@ -173,6 +188,7 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); + outState.putBoolean(SAVED_FIRST_PREFERENCE_CLICK, mFirstDuplicateClickCheck); if (mHighlightMixin != null) { outState.putParcelable(SAVED_HIGHLIGHT_MIXIN, mHighlightMixin); } @@ -271,6 +287,41 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi } } + /** Returns whether clicking the specified preference is considered as a duplicate click. */ + public boolean isDuplicateClick(Preference pref) { + boolean firstCheck = mFirstDuplicateClickCheck; + mFirstDuplicateClickCheck = false; + /* + * Return false when + * 1. The device doesn't support activity embedding + * 2. The target preference is not highlighted + * 3. The current activity is not embedded + */ + if (mHighlightMixin == null + || !TextUtils.equals(pref.getKey(), mHighlightMixin.getHighlightPreferenceKey()) + || !SplitController.getInstance().isActivityEmbedded(getActivity())) { + return false; + } + + /* + * Return true when + * 1. This method has been called before + * 2. The preference doesn't have a target fragment, ex. Wallpaper and injections + */ + if (!firstCheck || TextUtils.isEmpty(pref.getFragment())) { + return true; + } + + /* + * Returns true when + * 1. The right pane fragment is not started by a deep link. + * 2. The target fragment equals the right pane fragment + */ + String intentUri = getIntent().getStringExtra(EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI); + return TextUtils.isEmpty(intentUri) + || TextUtils.equals(pref.getFragment(), getIntentTargetFragment(intentUri)); + } + /** Show/hide the highlight on the menu entry for the search page presence */ public void setMenuHighlightShowed(boolean show) { if (mHighlightMixin != null) { @@ -310,6 +361,27 @@ public class TopLevelSettings extends DashboardFragment implements SplitLayoutLi } } + private String getIntentTargetFragment(String intentUri) { + Intent targetIntent; + try { + targetIntent = Intent.parseUri(intentUri, Intent.URI_INTENT_SCHEME); + } catch (URISyntaxException e) { + return null; + } + + String fragment = targetIntent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT); + if (!TextUtils.isEmpty(fragment)) { + return fragment; + } + + ActivityInfo info = targetIntent.resolveActivityInfo(getPackageManager(), + PackageManager.GET_META_DATA); + if (info == null || info.metaData == null) { + return null; + } + return info.metaData.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS); + } + private void iteratePreferences(PreferenceJob job) { if (job == null || getPreferenceManager() == null) { return;