From db40deb26fa7e94c4feec8e076bc0dc1785e6178 Mon Sep 17 00:00:00 2001 From: Jan Tomljanovic Date: Thu, 21 Dec 2023 18:16:33 +0000 Subject: [PATCH 01/10] Require authentication for changing USB preferences. Test: atest SettingsRoboTests Test: also tested manually Bug: 317367746 Change-Id: I17daaad7c7772664cae6d12cf2322991827f33bb --- ..._connecteddevice_flag_declarations.aconfig | 7 ++ .../usb/UsbDetailsController.java | 14 +++ .../usb/UsbDetailsDataRoleController.java | 22 ++-- .../usb/UsbDetailsFragment.java | 15 +++ .../usb/UsbDetailsFunctionsController.java | 58 ++++----- .../usb/UsbDetailsTranscodeMtpController.java | 14 ++- .../usb/UsbDetailsControllerTest.java | 113 ++++++++++++++++++ .../usb/UsbDetailsDataRoleControllerTest.java | 39 +++--- .../UsbDetailsFunctionsControllerTest.java | 26 +++- .../UsbDetailsTranscodeMtpControllerTest.java | 34 ++++-- 10 files changed, 271 insertions(+), 71 deletions(-) create mode 100644 tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsControllerTest.java diff --git a/aconfig/settings_connecteddevice_flag_declarations.aconfig b/aconfig/settings_connecteddevice_flag_declarations.aconfig index 07aaecc93b0..1a3afed0a1e 100644 --- a/aconfig/settings_connecteddevice_flag_declarations.aconfig +++ b/aconfig/settings_connecteddevice_flag_declarations.aconfig @@ -20,3 +20,10 @@ flag { description: "Gates whether to enable LE audio private broadcast sharing via QR code" bug: "308368124" } + +flag { + name: "enable_auth_challenge_for_usb_preferences" + namespace: "safety_center" + description: "Gates whether to require an auth challenge for changing USB preferences" + bug: "317367746" +} diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsController.java index 1219211a3ab..488de199d98 100644 --- a/src/com/android/settings/connecteddevice/usb/UsbDetailsController.java +++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsController.java @@ -23,6 +23,8 @@ import androidx.annotation.UiThread; import androidx.annotation.VisibleForTesting; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.flags.Flags; +import com.android.settings.wifi.dpp.WifiDppUtils; import com.android.settingslib.core.AbstractPreferenceController; /** @@ -61,4 +63,16 @@ public abstract class UsbDetailsController extends AbstractPreferenceController */ @UiThread protected abstract void refresh(boolean connected, long functions, int powerRole, int dataRole); + + /** Protects given action with an auth challenge. */ + protected final void requireAuthAndExecute(Runnable action) { + if (Flags.enableAuthChallengeForUsbPreferences() && !mFragment.isUserAuthenticated()) { + WifiDppUtils.showLockScreen(mContext, () -> { + mFragment.setUserAuthenticated(true); + action.run(); + }); + } else { + action.run(); + } + } } diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsDataRoleController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsDataRoleController.java index 6d455a69ebd..8782c796140 100644 --- a/src/com/android/settings/connecteddevice/usb/UsbDetailsDataRoleController.java +++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsDataRoleController.java @@ -98,17 +98,19 @@ public class UsbDetailsDataRoleController extends UsbDetailsController @Override public void onRadioButtonClicked(SelectorWithWidgetPreference preference) { - int role = UsbBackend.dataRoleFromString(preference.getKey()); - if (role != mUsbBackend.getDataRole() && mNextRolePref == null - && !Utils.isMonkeyRunning()) { - mUsbBackend.setDataRole(role); - mNextRolePref = preference; - preference.setSummary(R.string.usb_switching); + requireAuthAndExecute(() -> { + int role = UsbBackend.dataRoleFromString(preference.getKey()); + if (role != mUsbBackend.getDataRole() && mNextRolePref == null + && !Utils.isMonkeyRunning()) { + mUsbBackend.setDataRole(role); + mNextRolePref = preference; + preference.setSummary(R.string.usb_switching); - mHandler.postDelayed(mFailureCallback, - mUsbBackend.areAllRolesSupported() ? UsbBackend.PD_ROLE_SWAP_TIMEOUT_MS - : UsbBackend.NONPD_ROLE_SWAP_TIMEOUT_MS); - } + mHandler.postDelayed(mFailureCallback, + mUsbBackend.areAllRolesSupported() ? UsbBackend.PD_ROLE_SWAP_TIMEOUT_MS + : UsbBackend.NONPD_ROLE_SWAP_TIMEOUT_MS); + } + }); } @Override diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java index 0c94d192f61..f8cabbc08fb 100644 --- a/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java +++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsFragment.java @@ -45,6 +45,7 @@ public class UsbDetailsFragment extends DashboardFragment { private List mControllers; private UsbBackend mUsbBackend; + private boolean mUserAuthenticated = false; @VisibleForTesting UsbConnectionBroadcastReceiver mUsbReceiver; @@ -56,6 +57,20 @@ public class UsbDetailsFragment extends DashboardFragment { } }; + boolean isUserAuthenticated() { + return mUserAuthenticated; + } + + void setUserAuthenticated(boolean userAuthenticated) { + mUserAuthenticated = userAuthenticated; + } + + @Override + public void onStart() { + super.onStart(); + mUserAuthenticated = false; + } + @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java index 88e20b60dd0..04fab7d7d57 100644 --- a/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java +++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsController.java @@ -130,37 +130,39 @@ public class UsbDetailsFunctionsController extends UsbDetailsController @Override public void onRadioButtonClicked(SelectorWithWidgetPreference preference) { - final long function = UsbBackend.usbFunctionsFromString(preference.getKey()); - final long previousFunction = mUsbBackend.getCurrentFunctions(); - if (DEBUG) { - Log.d(TAG, "onRadioButtonClicked() function : " + function + ", toString() : " - + UsbManager.usbFunctionsToString(function) + ", previousFunction : " - + previousFunction + ", toString() : " - + UsbManager.usbFunctionsToString(previousFunction)); - } - if (function != previousFunction && !Utils.isMonkeyRunning() - && !isClickEventIgnored(function, previousFunction)) { - mPreviousFunction = previousFunction; - - //Update the UI in advance to make it looks smooth - final SelectorWithWidgetPreference prevPref = - (SelectorWithWidgetPreference) mProfilesContainer.findPreference( - UsbBackend.usbFunctionsToString(mPreviousFunction)); - if (prevPref != null) { - prevPref.setChecked(false); - preference.setChecked(true); + requireAuthAndExecute(() -> { + final long function = UsbBackend.usbFunctionsFromString(preference.getKey()); + final long previousFunction = mUsbBackend.getCurrentFunctions(); + if (DEBUG) { + Log.d(TAG, "onRadioButtonClicked() function : " + function + ", toString() : " + + UsbManager.usbFunctionsToString(function) + ", previousFunction : " + + previousFunction + ", toString() : " + + UsbManager.usbFunctionsToString(previousFunction)); } + if (function != previousFunction && !Utils.isMonkeyRunning() + && !isClickEventIgnored(function, previousFunction)) { + mPreviousFunction = previousFunction; - if (function == UsbManager.FUNCTION_RNDIS || function == UsbManager.FUNCTION_NCM) { - // We need to have entitlement check for usb tethering, so use API in - // TetheringManager. - mTetheringManager.startTethering( - TetheringManager.TETHERING_USB, new HandlerExecutor(mHandler), - mOnStartTetheringCallback); - } else { - mUsbBackend.setCurrentFunctions(function); + //Update the UI in advance to make it looks smooth + final SelectorWithWidgetPreference prevPref = + (SelectorWithWidgetPreference) mProfilesContainer.findPreference( + UsbBackend.usbFunctionsToString(mPreviousFunction)); + if (prevPref != null) { + prevPref.setChecked(false); + preference.setChecked(true); + } + + if (function == UsbManager.FUNCTION_RNDIS || function == UsbManager.FUNCTION_NCM) { + // We need to have entitlement check for usb tethering, so use API in + // TetheringManager. + mTetheringManager.startTethering( + TetheringManager.TETHERING_USB, new HandlerExecutor(mHandler), + mOnStartTetheringCallback); + } else { + mUsbBackend.setCurrentFunctions(function); + } } - } + }); } private boolean isClickEventIgnored(long function, long previousFunction) { diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsTranscodeMtpController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsTranscodeMtpController.java index 192ae742f23..a0231712e34 100644 --- a/src/com/android/settings/connecteddevice/usb/UsbDetailsTranscodeMtpController.java +++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsTranscodeMtpController.java @@ -78,13 +78,15 @@ public class UsbDetailsTranscodeMtpController extends UsbDetailsController @Override public boolean onPreferenceClick(Preference preference) { - SystemProperties.set(TRANSCODE_MTP_SYS_PROP_KEY, - Boolean.toString(mSwitchPreference.isChecked())); + requireAuthAndExecute(() -> { + SystemProperties.set(TRANSCODE_MTP_SYS_PROP_KEY, + Boolean.toString(mSwitchPreference.isChecked())); - final long previousFunctions = mUsbBackend.getCurrentFunctions(); - // Toggle the MTP connection to reload file sizes for files shared via MTP clients - mUsbBackend.setCurrentFunctions(previousFunctions & ~UsbManager.FUNCTION_MTP); - mUsbBackend.setCurrentFunctions(previousFunctions); + final long previousFunctions = mUsbBackend.getCurrentFunctions(); + // Toggle the MTP connection to reload file sizes for files shared via MTP clients + mUsbBackend.setCurrentFunctions(previousFunctions & ~UsbManager.FUNCTION_MTP); + mUsbBackend.setCurrentFunctions(previousFunctions); + }); return true; } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsControllerTest.java new file mode 100644 index 00000000000..b8d31ed9acf --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsControllerTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.connecteddevice.usb; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.verify; + +import android.app.KeyguardManager; +import android.content.Context; +import android.hardware.usb.UsbPortStatus; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; + +import com.android.settings.flags.Flags; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.Shadows; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowKeyguardManager; + +@RunWith(RobolectricTestRunner.class) +@Config(shadows = { + ShadowKeyguardManager.class +}) +public class UsbDetailsControllerTest { + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + @Mock + private UsbBackend mUsbBackend; + + private Context mContext; + private UsbDetailsController mUsbDetailsController; + private UsbDetailsFragment mUsbDetailsFragment; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = getApplicationContext(); + mUsbDetailsFragment = new UsbDetailsFragment(); + mUsbDetailsController = new UsbDetailsController( + mContext, mUsbDetailsFragment, mUsbBackend) { + @Override + protected void refresh(boolean connected, long functions, int powerRole, int dataRole) { + } + + @Override + public String getPreferenceKey() { + return null; + } + }; + } + + @Test + public void isAvailable_returnsTrue() { + assertThat(mUsbDetailsController.isAvailable()).isTrue(); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_AUTH_CHALLENGE_FOR_USB_PREFERENCES) + public void requireAuthAndExecute_whenAlreadyAuthenticated_executes() { + mUsbDetailsFragment.setUserAuthenticated(true); + Runnable action = () -> mUsbBackend.setDataRole(UsbPortStatus.DATA_ROLE_HOST); + + mUsbDetailsController.requireAuthAndExecute(action); + + verify(mUsbBackend).setDataRole(anyInt()); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_AUTH_CHALLENGE_FOR_USB_PREFERENCES) + public void requireAuthAndExecute_authenticatesAndExecutes() { + mUsbDetailsFragment.setUserAuthenticated(false); + setAuthPassesAutomatically(); + Runnable action = () -> mUsbBackend.setDataRole(UsbPortStatus.DATA_ROLE_HOST); + + mUsbDetailsController.requireAuthAndExecute(action); + + assertThat(mUsbDetailsFragment.isUserAuthenticated()).isTrue(); + verify(mUsbBackend).setDataRole(anyInt()); + } + + private void setAuthPassesAutomatically() { + Shadows.shadowOf(mContext.getSystemService(KeyguardManager.class)) + .setIsKeyguardSecure(false); + } +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsDataRoleControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsDataRoleControllerTest.java index 9e24387c3d9..3324843f582 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsDataRoleControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsDataRoleControllerTest.java @@ -25,12 +25,15 @@ import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.KeyguardManager; import android.content.Context; import android.hardware.usb.UsbManager; import android.os.Handler; +import android.platform.test.annotations.RequiresFlagsEnabled; import androidx.fragment.app.FragmentActivity; import androidx.preference.PreferenceCategory; @@ -38,6 +41,7 @@ import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; import com.android.settings.R; +import com.android.settings.flags.Flags; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.widget.SelectorWithWidgetPreference; @@ -49,6 +53,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) @@ -63,12 +68,11 @@ public class UsbDetailsDataRoleControllerTest { private PreferenceCategory mPreference; private PreferenceManager mPreferenceManager; private PreferenceScreen mScreen; + private UsbDetailsFragment mFragment; @Mock private UsbBackend mUsbBackend; @Mock - private UsbDetailsFragment mFragment; - @Mock private FragmentActivity mActivity; @Mock private Handler mHandler; @@ -76,7 +80,7 @@ public class UsbDetailsDataRoleControllerTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - + mFragment = spy(new UsbDetailsFragment()); mContext = RuntimeEnvironment.application; mLifecycle = new Lifecycle(() -> mLifecycle); mPreferenceManager = new PreferenceManager(mContext); @@ -95,12 +99,12 @@ public class UsbDetailsDataRoleControllerTest { mScreen.addPreference(mPreference); mDetailsDataRoleController.mHandler = mHandler; + + mDetailsDataRoleController.displayPreference(mScreen); } @Test public void displayRefresh_deviceRole_shouldCheckDevice() { - mDetailsDataRoleController.displayPreference(mScreen); - mDetailsDataRoleController.refresh(true, UsbManager.FUNCTION_NONE, POWER_ROLE_SINK, DATA_ROLE_DEVICE); @@ -112,8 +116,6 @@ public class UsbDetailsDataRoleControllerTest { @Test public void displayRefresh_hostRole_shouldCheckHost() { - mDetailsDataRoleController.displayPreference(mScreen); - mDetailsDataRoleController.refresh(true, UsbManager.FUNCTION_NONE, POWER_ROLE_SINK, DATA_ROLE_HOST); @@ -125,8 +127,6 @@ public class UsbDetailsDataRoleControllerTest { @Test public void displayRefresh_disconnected_shouldDisable() { - mDetailsDataRoleController.displayPreference(mScreen); - mDetailsDataRoleController.refresh(false, UsbManager.FUNCTION_NONE, POWER_ROLE_SINK, DATA_ROLE_DEVICE); @@ -135,7 +135,6 @@ public class UsbDetailsDataRoleControllerTest { @Test public void onClickDevice_hostEnabled_shouldSetDevice() { - mDetailsDataRoleController.displayPreference(mScreen); when(mUsbBackend.getDataRole()).thenReturn(DATA_ROLE_HOST); final SelectorWithWidgetPreference devicePref = getRadioPreference(DATA_ROLE_DEVICE); @@ -148,7 +147,6 @@ public class UsbDetailsDataRoleControllerTest { @Test public void onClickDeviceTwice_hostEnabled_shouldSetDeviceOnce() { - mDetailsDataRoleController.displayPreference(mScreen); when(mUsbBackend.getDataRole()).thenReturn(DATA_ROLE_HOST); final SelectorWithWidgetPreference devicePref = getRadioPreference(DATA_ROLE_DEVICE); @@ -162,7 +160,6 @@ public class UsbDetailsDataRoleControllerTest { @Test public void onClickDeviceAndRefresh_success_shouldClearSubtext() { - mDetailsDataRoleController.displayPreference(mScreen); when(mUsbBackend.getDataRole()).thenReturn(DATA_ROLE_HOST); final SelectorWithWidgetPreference devicePref = getRadioPreference(DATA_ROLE_DEVICE); @@ -180,7 +177,6 @@ public class UsbDetailsDataRoleControllerTest { @Test public void onClickDeviceAndRefresh_failed_shouldShowFailureText() { - mDetailsDataRoleController.displayPreference(mScreen); when(mUsbBackend.getDataRole()).thenReturn(DATA_ROLE_HOST); final SelectorWithWidgetPreference devicePref = getRadioPreference(DATA_ROLE_DEVICE); @@ -199,7 +195,6 @@ public class UsbDetailsDataRoleControllerTest { @Test public void onClickDevice_timedOut_shouldShowFailureText() { - mDetailsDataRoleController.displayPreference(mScreen); when(mUsbBackend.getDataRole()).thenReturn(DATA_ROLE_HOST); final SelectorWithWidgetPreference devicePref = getRadioPreference(DATA_ROLE_DEVICE); @@ -218,6 +213,22 @@ public class UsbDetailsDataRoleControllerTest { .isEqualTo(mContext.getString(R.string.usb_switching_failed)); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_AUTH_CHALLENGE_FOR_USB_PREFERENCES) + public void onRadioButtonClicked_userAuthenticated() { + SelectorWithWidgetPreference preference = getRadioPreference(DATA_ROLE_DEVICE); + setAuthPassesAutomatically(); + + mDetailsDataRoleController.onRadioButtonClicked(preference); + + assertThat(mFragment.isUserAuthenticated()).isTrue(); + } + + private void setAuthPassesAutomatically() { + Shadows.shadowOf(mContext.getSystemService(KeyguardManager.class)) + .setIsKeyguardSecure(false); + } + private SelectorWithWidgetPreference getRadioPreference(int role) { return (SelectorWithWidgetPreference) mPreference.findPreference(UsbBackend.dataRoleToString(role)); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsControllerTest.java index 091abde3748..eea4f52388b 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsFunctionsControllerTest.java @@ -30,15 +30,18 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.KeyguardManager; import android.content.Context; import android.hardware.usb.UsbManager; import android.net.TetheringManager; +import android.platform.test.annotations.RequiresFlagsEnabled; import androidx.fragment.app.FragmentActivity; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; +import com.android.settings.flags.Flags; import com.android.settings.testutils.shadow.ShadowUtils; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.widget.SelectorWithWidgetPreference; @@ -51,6 +54,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; import org.robolectric.annotation.Config; import java.util.ArrayList; @@ -70,12 +74,11 @@ public class UsbDetailsFunctionsControllerTest { private PreferenceManager mPreferenceManager; private PreferenceScreen mScreen; private SelectorWithWidgetPreference mRadioButtonPreference; + private UsbDetailsFragment mFragment; @Mock private UsbBackend mUsbBackend; @Mock - private UsbDetailsFragment mFragment; - @Mock private FragmentActivity mActivity; @Mock private TetheringManager mTetheringManager; @@ -83,7 +86,7 @@ public class UsbDetailsFunctionsControllerTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - + mFragment = spy(new UsbDetailsFragment()); mContext = spy(RuntimeEnvironment.application); mLifecycle = new Lifecycle(() -> mLifecycle); mPreferenceManager = new PreferenceManager(mContext); @@ -334,6 +337,23 @@ public class UsbDetailsFunctionsControllerTest { eq(mDetailsFunctionsController.mOnStartTetheringCallback)); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_AUTH_CHALLENGE_FOR_USB_PREFERENCES) + public void onRadioButtonClicked_userAuthenticated() { + mRadioButtonPreference.setKey(UsbBackend.usbFunctionsToString(UsbManager.FUNCTION_PTP)); + doReturn(UsbManager.FUNCTION_MTP).when(mUsbBackend).getCurrentFunctions(); + setAuthPassesAutomatically(); + + mDetailsFunctionsController.onRadioButtonClicked(mRadioButtonPreference); + + assertThat(mFragment.isUserAuthenticated()).isTrue(); + } + + private void setAuthPassesAutomatically() { + Shadows.shadowOf(mContext.getSystemService(KeyguardManager.class)) + .setIsKeyguardSecure(false); + } + @Test public void onTetheringFailed_resetPreviousFunctions() { mDetailsFunctionsController.mPreviousFunction = UsbManager.FUNCTION_PTP; diff --git a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsTranscodeMtpControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsTranscodeMtpControllerTest.java index 51d2c1664f2..07d14ec9935 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsTranscodeMtpControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbDetailsTranscodeMtpControllerTest.java @@ -21,11 +21,14 @@ import static android.hardware.usb.UsbPortStatus.POWER_ROLE_NONE; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import android.app.KeyguardManager; import android.content.Context; import android.hardware.usb.UsbManager; import android.os.SystemProperties; +import android.platform.test.annotations.RequiresFlagsEnabled; import androidx.fragment.app.FragmentActivity; import androidx.preference.PreferenceCategory; @@ -33,6 +36,7 @@ import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreference; +import com.android.settings.flags.Flags; import com.android.settings.testutils.shadow.ShadowUtils; import org.junit.Before; @@ -43,6 +47,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) @@ -57,18 +62,18 @@ public class UsbDetailsTranscodeMtpControllerTest { private PreferenceManager mPreferenceManager; private PreferenceScreen mScreen; private UsbDetailsTranscodeMtpController mUnderTest; + private UsbDetailsFragment mFragment; @Mock private UsbBackend mUsbBackend; @Mock - private UsbDetailsFragment mFragment; - @Mock private FragmentActivity mActivity; @Before public void setUp() { MockitoAnnotations.initMocks(this); + mFragment = spy(new UsbDetailsFragment()); mContext = RuntimeEnvironment.application; mPreferenceManager = new PreferenceManager(mContext); mScreen = mPreferenceManager.createPreferenceScreen(mContext); @@ -84,11 +89,12 @@ public class UsbDetailsTranscodeMtpControllerTest { mPreference = new PreferenceCategory(mContext); mPreference.setKey(mUnderTest.getPreferenceKey()); mScreen.addPreference(mPreference); + + mUnderTest.displayPreference(mScreen); } @Test public void displayRefresh_noUsbConnection_shouldDisablePrefCategory() { - mUnderTest.displayPreference(mScreen); when(mUsbBackend.areAllRolesSupported()).thenReturn(true); mUnderTest.refresh(false /* connected */, UsbManager.FUNCTION_MTP, POWER_ROLE_NONE, @@ -99,7 +105,6 @@ public class UsbDetailsTranscodeMtpControllerTest { @Test public void displayRefresh_noDataTransfer_shouldDisablePrefCategory() { - mUnderTest.displayPreference(mScreen); when(mUsbBackend.areAllRolesSupported()).thenReturn(true); mUnderTest.refresh(true /* connected */, UsbManager.FUNCTION_NONE, POWER_ROLE_NONE, @@ -110,7 +115,6 @@ public class UsbDetailsTranscodeMtpControllerTest { @Test public void displayRefresh_noDataRole_shouldDisablePrefCategory() throws InterruptedException { - mUnderTest.displayPreference(mScreen); when(mUsbBackend.areAllRolesSupported()).thenReturn(true); mUnderTest.refresh(true /* connected */, UsbManager.FUNCTION_MTP, POWER_ROLE_NONE, @@ -122,7 +126,6 @@ public class UsbDetailsTranscodeMtpControllerTest { @Ignore("b/313362757") @Test public void displayRefresh_fileTransfer_withAbsentProp_shouldCheck() { - mUnderTest.displayPreference(mScreen); when(mUsbBackend.areAllRolesSupported()).thenReturn(true); mUnderTest.refresh(true /* connected */, UsbManager.FUNCTION_MTP, POWER_ROLE_NONE, @@ -134,7 +137,6 @@ public class UsbDetailsTranscodeMtpControllerTest { @Ignore("b/313362757") @Test public void displayRefresh_fileTransfer_withUnsetProp_shouldUncheck() { - mUnderTest.displayPreference(mScreen); SystemProperties.set(TRANSCODE_MTP_SYS_PROP_KEY, Boolean.toString(false)); when(mUsbBackend.areAllRolesSupported()).thenReturn(true); @@ -147,7 +149,6 @@ public class UsbDetailsTranscodeMtpControllerTest { @Ignore("b/313362757") @Test public void displayRefresh_fileTransfer_withSetProp_shouldCheck() { - mUnderTest.displayPreference(mScreen); SystemProperties.set(TRANSCODE_MTP_SYS_PROP_KEY, Boolean.toString(true)); when(mUsbBackend.areAllRolesSupported()).thenReturn(true); @@ -160,7 +161,6 @@ public class UsbDetailsTranscodeMtpControllerTest { @Ignore("b/313362757") @Test public void click_checked_shouldSetSystemProperty() { - mUnderTest.displayPreference(mScreen); getSwitchPreference().performClick(); assertThat(SystemProperties.getBoolean(TRANSCODE_MTP_SYS_PROP_KEY, false)).isTrue(); } @@ -168,7 +168,6 @@ public class UsbDetailsTranscodeMtpControllerTest { @Ignore("b/313362757") @Test public void click_unChecked_shouldUnsetSystemProperty() { - mUnderTest.displayPreference(mScreen); getSwitchPreference().performClick(); getSwitchPreference().performClick(); assertThat(SystemProperties.getBoolean(TRANSCODE_MTP_SYS_PROP_KEY, true)).isFalse(); @@ -181,6 +180,21 @@ public class UsbDetailsTranscodeMtpControllerTest { assertThat(mUnderTest.isAvailable()).isFalse(); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_AUTH_CHALLENGE_FOR_USB_PREFERENCES) + public void onClick_userAuthenticated() { + setAuthPassesAutomatically(); + + mUnderTest.onPreferenceClick(null); + + assertThat(mFragment.isUserAuthenticated()).isTrue(); + } + + private void setAuthPassesAutomatically() { + Shadows.shadowOf(mContext.getSystemService(KeyguardManager.class)) + .setIsKeyguardSecure(false); + } + private SwitchPreference getSwitchPreference() { return (SwitchPreference) mPreference.getPreference(0); } From 20e48424d947cb56a6b99b93f7ac9445832b484c Mon Sep 17 00:00:00 2001 From: Beverly Date: Thu, 28 Dec 2023 16:59:26 +0000 Subject: [PATCH 02/10] Add new folding grace period settings strings Bug: 315219497 Flag: ACONFIG com.android.internal.foldables.flags.fold_grace_period_enabled DEVELOPMENT Test: manually enable and see new strings (adb root && adb shell setprop persist.fold_grace_period_enabled true && adb reboot) Change-Id: Ie278b45e3928e1154f78066e71551fb5cac8a176 --- res/values/strings.xml | 5 +++++ .../FoldLockBehaviorPreferenceController.java | 10 ++++++++-- .../display/FoldLockBehaviorSettings.java | 17 +++++++++++++---- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 88bd4d730bc..7eb5bdacdf6 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -88,6 +88,11 @@ Only games, videos, and more Front display turns on for apps that stop your screen going idle + + Swipe up to continue + + Fold your phone and swipe up on the front display to continue using the app, or wait a few seconds for the screen to lock + Never diff --git a/src/com/android/settings/display/FoldLockBehaviorPreferenceController.java b/src/com/android/settings/display/FoldLockBehaviorPreferenceController.java index 661eb99c4a2..bee3a22e9ed 100644 --- a/src/com/android/settings/display/FoldLockBehaviorPreferenceController.java +++ b/src/com/android/settings/display/FoldLockBehaviorPreferenceController.java @@ -29,6 +29,7 @@ import android.provider.Settings; import androidx.preference.Preference; +import com.android.internal.foldables.FoldGracePeriodProvider; import com.android.internal.foldables.FoldLockSettingAvailabilityProvider; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; @@ -58,8 +59,13 @@ public class FoldLockBehaviorPreferenceController extends BasePreferenceControll mFoldLockSettingAvailabilityProvider = foldLockSettingAvailabilityProvider; KEY_TO_TEXT.put(SETTING_VALUE_STAY_AWAKE_ON_FOLD, resourceToString(R.string.stay_awake_on_fold_title)); - KEY_TO_TEXT.put(SETTING_VALUE_SELECTIVE_STAY_AWAKE, - resourceToString(R.string.selective_stay_awake_title)); + if (new FoldGracePeriodProvider().isEnabled()) { + KEY_TO_TEXT.put(SETTING_VALUE_SELECTIVE_STAY_AWAKE, + resourceToString(R.string.stay_awake_on_lockscreen_title)); + } else { + KEY_TO_TEXT.put(SETTING_VALUE_SELECTIVE_STAY_AWAKE, + resourceToString(R.string.selective_stay_awake_title)); + } KEY_TO_TEXT.put(SETTING_VALUE_SLEEP_ON_FOLD, resourceToString(R.string.sleep_on_fold_title)); } diff --git a/src/com/android/settings/display/FoldLockBehaviorSettings.java b/src/com/android/settings/display/FoldLockBehaviorSettings.java index e94b17ecc7b..432c2304acf 100644 --- a/src/com/android/settings/display/FoldLockBehaviorSettings.java +++ b/src/com/android/settings/display/FoldLockBehaviorSettings.java @@ -24,6 +24,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.Log; +import com.android.internal.foldables.FoldGracePeriodProvider; import com.android.settings.R; import com.android.settings.support.actionbar.HelpResourceProvider; import com.android.settings.utils.CandidateInfoExtra; @@ -54,6 +55,7 @@ public class FoldLockBehaviorSettings extends RadioButtonPickerFragment implemen SETTING_VALUE_SLEEP_ON_FOLD)); private static final String SETTING_VALUE_DEFAULT = SETTING_VALUE_SELECTIVE_STAY_AWAKE; private Context mContext; + private final FoldGracePeriodProvider mFoldGracePeriodProvider = new FoldGracePeriodProvider(); @Override public void onAttach(Context context) { @@ -69,10 +71,17 @@ public class FoldLockBehaviorSettings extends RadioButtonPickerFragment implemen resourceToString(R.string.stay_awake_on_fold_title), resourceToString(R.string.stay_awake_on_fold_summary), SETTING_VALUE_STAY_AWAKE_ON_FOLD, /* enabled */ true)); - candidates.add(new CandidateInfoExtra( - resourceToString(R.string.selective_stay_awake_title), - resourceToString(R.string.selective_stay_awake_summary), - SETTING_VALUE_SELECTIVE_STAY_AWAKE, /* enabled */ true)); + if (mFoldGracePeriodProvider.isEnabled()) { + candidates.add(new CandidateInfoExtra( + resourceToString(R.string.stay_awake_on_lockscreen_title), + resourceToString(R.string.stay_awake_on_lockscreen_summary), + SETTING_VALUE_SELECTIVE_STAY_AWAKE, /* enabled */ true)); + } else { + candidates.add(new CandidateInfoExtra( + resourceToString(R.string.selective_stay_awake_title), + resourceToString(R.string.selective_stay_awake_summary), + SETTING_VALUE_SELECTIVE_STAY_AWAKE, /* enabled */ true)); + } candidates.add(new CandidateInfoExtra( resourceToString(R.string.sleep_on_fold_title), resourceToString(R.string.sleep_on_fold_summary), From 2f9787a0c0645420af4f4f57e23bcf27bbd0e7f1 Mon Sep 17 00:00:00 2001 From: Kevin Jeon Date: Fri, 29 Dec 2023 15:58:04 -0500 Subject: [PATCH 03/10] Increase character limit on PSS toggle translation This change increase the character limit on the toggle for enabling/disabling memory usage profiling. This toggle is gated behind developer settings and changes some device behaviors; a higher character limit will allow for more accurate translations. Test: N/A Bug: 296454553 Change-Id: I129a22a4c2ebd10f09b3d3a695fdf669da00fe33 --- 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 88bd4d730bc..dedebc794b0 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9484,7 +9484,7 @@ other {# apps used memory in the last {time}} } - + Enable memory usage profiling Memory usage profiling requires additional system resources. From 484f82e77635582c75cc34c21dfb9094022b32c3 Mon Sep 17 00:00:00 2001 From: Pajace Chen Date: Fri, 29 Dec 2023 13:48:34 +0000 Subject: [PATCH 04/10] [Shadow] Switch the button for defend battery tips Align the button position of defender settings tips with new design Bug: 299403437 Test: Manual Test Flag: None Change-Id: I897d7291bfc21da69b661893368ddf4d6af7b06b --- .../batterytip/tips/BatteryDefenderTip.java | 24 +++++++++---------- .../tips/BatteryDefenderTipTest.java | 16 ++++++------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java index b5ec522b707..047bf135741 100644 --- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java +++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java @@ -83,17 +83,8 @@ public class BatteryDefenderTip extends BatteryTip { } cardPreference.setSelectable(false); - cardPreference.setPrimaryButtonText( - context.getString(R.string.battery_tip_charge_to_full_button)); + cardPreference.setPrimaryButtonText(context.getString(R.string.learn_more)); cardPreference.setPrimaryButtonClickListener( - unused -> { - resumeCharging(context); - preference.setVisible(false); - }); - cardPreference.setPrimaryButtonVisible(mIsPluggedIn); - - cardPreference.setSecondaryButtonText(context.getString(R.string.learn_more)); - cardPreference.setSecondaryButtonClickListener( button -> button.startActivityForResult( HelpUtils.getHelpIntent( @@ -101,10 +92,19 @@ public class BatteryDefenderTip extends BatteryTip { context.getString(R.string.help_url_battery_defender), /* backupContext */ ""), /* requestCode */ 0)); - cardPreference.setSecondaryButtonVisible(true); - cardPreference.setSecondaryButtonContentDescription( + cardPreference.setPrimaryButtonVisible(true); + cardPreference.setPrimaryButtonContentDescription( context.getString( R.string.battery_tip_limited_temporarily_sec_button_content_description)); + + cardPreference.setSecondaryButtonText( + context.getString(R.string.battery_tip_charge_to_full_button)); + cardPreference.setSecondaryButtonClickListener( + unused -> { + resumeCharging(context); + preference.setVisible(false); + }); + cardPreference.setSecondaryButtonVisible(mIsPluggedIn); } private void resumeCharging(Context context) { diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java index f8a4f28d79d..296306dea3e 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java @@ -104,7 +104,7 @@ public class BatteryDefenderTipTest { @Test public void updatePreference_shouldSetPrimaryButtonText() { - String expectedText = mContext.getString(R.string.battery_tip_charge_to_full_button); + String expectedText = mContext.getString(R.string.learn_more); mBatteryDefenderTip.updatePreference(mCardPreference); @@ -113,7 +113,7 @@ public class BatteryDefenderTipTest { @Test public void updatePreference_shouldSetSecondaryButtonText() { - String expected = mContext.getString(R.string.learn_more); + String expected = mContext.getString(R.string.battery_tip_charge_to_full_button); mBatteryDefenderTip.updatePreference(mCardPreference); @@ -121,10 +121,10 @@ public class BatteryDefenderTipTest { } @Test - public void updatePreference_shouldSetSecondaryButtonVisible() { + public void updatePreference_shouldSetPrimaryButtonVisible() { mBatteryDefenderTip.updatePreference(mCardPreference); - verify(mCardPreference).setSecondaryButtonVisible(true); + verify(mCardPreference).setPrimaryButtonVisible(true); } @Test @@ -138,19 +138,19 @@ public class BatteryDefenderTipTest { } @Test - public void updatePreference_whenNotCharging_setPrimaryButtonVisibleToBeFalse() { + public void updatePreference_whenNotCharging_setSecondaryButtonVisibleToBeFalse() { mBatteryDefenderTip.updatePreference(mCardPreference); - verify(mCardPreference).setPrimaryButtonVisible(false); + verify(mCardPreference).setSecondaryButtonVisible(false); } @Test - public void updatePreference_whenGetChargingStatusFailed_setPrimaryButtonVisibleToBeFalse() { + public void updatePreference_whenGetChargingStatusFailed_setSecondaryButtonVisibleToBeFalse() { fakeGetChargingStatusFailed(); mBatteryDefenderTip.updatePreference(mCardPreference); - verify(mCardPreference).setPrimaryButtonVisible(false); + verify(mCardPreference).setSecondaryButtonVisible(false); } private void fakeGetChargingStatusFailed() { From 072fc7f1759e7583a73b483af2eceb72d6a463e2 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 2 Jan 2024 14:29:49 -0800 Subject: [PATCH 05/10] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Ib6fb378d4b39375c6d0356cf33ee16fce603afab --- res/values-as/strings.xml | 2 +- res/values-ca/strings.xml | 4 ++-- res/values-da/strings.xml | 6 +++--- res/values-fa/strings.xml | 8 ++++---- res/values-fi/strings.xml | 2 +- res/values-fr/strings.xml | 8 ++++---- res/values-kn/strings.xml | 8 ++++---- res/values-nl/strings.xml | 2 +- res/values-or/strings.xml | 4 ++-- res/values-sw/strings.xml | 24 ++++++++++++------------ res/values-ta/strings.xml | 2 +- 11 files changed, 35 insertions(+), 35 deletions(-) diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml index 909348feddd..faa0478614f 100644 --- a/res/values-as/strings.xml +++ b/res/values-as/strings.xml @@ -3455,7 +3455,7 @@ "{count,plural, =1{{time} পৰ্যন্ত কেৱল এক মিনিটৰ বাবে এলাৰ্মলৈ সলনি কৰক}one{কেৱল # মিনিটৰ বাবে এলাৰ্মলৈ সলনি কৰক ({time} পৰ্যন্ত)}other{কেৱল # মিনিটৰ বাবে এলাৰ্মলৈ সলনি কৰক ({time} পৰ্যন্ত)}}" "{count,plural, =1{{time} পৰ্যন্ত কেৱল এঘণ্টাৰ বাবে এলাৰ্মলৈ সলনি কৰক}one{{time} পৰ্যন্ত কেৱল # ঘণ্টাৰ বাবে এলাৰ্মলৈ সলনি কৰক}other{{time} পৰ্যন্ত কেৱল # ঘণ্টাৰ বাবে এলাৰ্মলৈ সলনি কৰক}}" "কেৱল %1$s পৰ্যন্ত এলাৰ্ম সলনি কৰক" - "সদায় ব্যাঘাত জন্মোৱা ছেটিংলৈ সলনি কৰক" + "সদায় ব্যাঘাত জন্মোৱা ছেটিঙলৈ সলনি কৰক" "সকীয়নি" "বন্ধ কৰক" "ডিভাইচটো ব্যৱহাৰ কৰি কেনে লাগিছে জনাওক" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 84f11cfea43..ec61cf16d17 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -674,7 +674,7 @@ "El registre detallat de l\'NFC només està indicat per a activitats de desenvolupament. Les dades de l\'NFC addicionals estan incloses als informes d\'errors, que poden contenir informació privada. Reinicia el dispositiu per canviar aquesta configuració." "Reinicia" "Emet" - "projectar" + "duplicar" "Activa pantalla sense fil" "No s\'ha trobat cap dispositiu a prop." "S\'està connectant" @@ -2881,7 +2881,7 @@ "pantalla atenuada, nit, color, canvi nocturn, brillantor, color de la pantalla, colors" "fons de pantalla, personalitzar, personalitzar la pantalla" "mida del text" - "projectar, emetre, projectar en pantalla, ús compartit de la pantalla, projecció, compartir pantalla, emetre en pantalla" + "duplicar, projectar, emetre, projectar en pantalla, ús compartit de la pantalla, projecció, duplicació, compartir pantalla, emetre en pantalla" "espai, disc, unitat de disc dur, ús del dispositiu" "consum de la bateria, càrrega" "mostra l\'ús de la bateria, ús de la bateria, ús d\'energia" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index df63efd4e08..8cbe10c15eb 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -2411,7 +2411,7 @@ "VPN- og appbrugercertifikat" "Wi‑Fi-certifikat" "Dine data er ikke private" - "CA-certifikater bruges af websites, apps og VPN-netværk til kryptering. Du bør kun installere CA-certifikater fra organisationer, du har tillid til. \n\nHvis du installerer et CA-certifikat, kan certifikatejeren få adgang til dine data, f.eks. adgangskoder eller betalingskortoplysninger fra websites, du besøger, eller apps, du bruger – selv hvis dine data er krypterede." + "CA-certifikater bruges af websites, apps og VPN-netværk til kryptering. Du bør kun installere CA-certifikater fra organisationer, du har tillid til. \n\nHvis du installerer et CA-certifikat, kan certifikatejeren få adgang til dine data, f.eks. adgangskoder eller kreditkortoplysninger fra websites, du besøger, eller apps, du bruger – selv hvis dine data er krypterede." "Installer ikke" "Installer alligevel" "Certifikatet er ikke installeret" @@ -2664,7 +2664,7 @@ "{count,plural, =1{Tillad eller fjern certifikat}one{Tillad eller fjern certifikat}other{Tillad eller fjern certifikater}}" "{numberOfCertificates,plural, =1{{orgName} har installeret en certifikatautoritet på din enhed, hvilket giver vedkommende mulighed for at overvåge netværksaktivitet på din enhed, herunder mails, apps og sikre websites.\n\nKontakt din administrator for at få flere oplysninger om dette certifikat.}one{{orgName} har installeret en certifikatautoritet på din enhed, hvilket giver vedkommende mulighed for at overvåge netværksaktivitet på din enhed, herunder mails, apps og sikre websites.\n\nKontakt din administrator for at få flere oplysninger om dette certifikat.}other{{orgName} har installeret certifikatautoriteter på din enhed, hvilket giver vedkommende mulighed for at overvåge netværksaktivitet på din enhed, herunder mails, apps og sikre websites.\n\nKontakt din administrator for at få flere oplysninger om disse certifikater.}}" "{numberOfCertificates,plural, =1{{orgName} har installeret en certifikatautoritet for din arbejdsprofil, hvilket giver vedkommende mulighed for at overvåge aktivitet på dit arbejdsnetværk, herunder mails, apps og sikre websites.\n\nKontakt din administrator for at få flere oplysninger om dette certifikat.}one{{orgName} har installeret en certifikatautoritet for din arbejdsprofil, hvilket giver vedkommende mulighed for at overvåge aktivitet på dit arbejdsnetværk, herunder mails, apps og sikre websites.\n\nKontakt din administrator for at få flere oplysninger om dette certifikat.}other{{orgName} har installeret certifikatautoriteter for din arbejdsprofil, hvilket giver vedkommende mulighed for at overvåge aktivitet på dit arbejdsnetværk, herunder mails, apps og sikre websites\n\nKontakt din administrator for at få flere oplysninger om disse certifikater.}}" - "En tredjepart kan overvåge din netværksaktivitet, herunder e-mails, apps og sikre websites.\n\nPålidelige loginoplysninger, der er installeret på din enhed, gør dette muligt." + "En tredjepart kan overvåge din netværksaktivitet, herunder mails, apps og sikre websites.\n\nPålidelige loginoplysninger, der er installeret på din enhed, gør dette muligt." "{count,plural, =1{Tjek certifikatet}one{Tjek certifikatet}other{Tjek certifikaterne}}" "Flere brugere" "Brugere og profiler" @@ -3913,7 +3913,7 @@ "Gå til startskærmen ved at stryge opad fra bunden af skærmen. Skift mellem apps ved at stryge opad fra bunden af skærmen, holde fingeren nede og derefter give slip. Gå tilbage ved at stryge fra enten venstre eller højre kant." "Navigering med tre knapper" "Gå tilbage, gå til startskærmen og skift mellem apps ved hjælp af knapperne nederst på din skærm." - "systemnavigering, navigering med to knapper, navigering med tre knapper, navigering med bevægelser, stryg" + "systemnavigation, navigering med to knapper, navigering med tre knapper, navigering med bevægelser, stryg" "Digital assistent" "Stryg for at aktivere assistenten" "Stryg opad fra et af de nederste hjørner for at aktivere appen for din digitale assistent." diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index e0591adddfe..a3ec6fa7bd7 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -475,10 +475,10 @@ "انگشت را بلند کنید، سپس دوباره حسگر را لمس کنید" "امکان استفاده از حسگر اثر انگشت وجود ندارد" "به ارائه‌دهنده خدمات تعمیر مراجعه کنید." - "تنظیمات امنیتی بیشتر" + "تنظیمات ایمنی بیشتر" "قفل نمایه کاری، رمزگذاری، و غیره" "رمزگذاری، اطلاعات اعتباری، و غیره" - "امنیت، تنظیمات امنیتی بیشتر، تنظیمات بیشتر، تنظیمات امنیتی پیشرفته" + "ایمنی، تنظیمات ایمنی بیشتر، تنظیمات بیشتر، تنظیمات ایمنی پیشرفته" "تنظیمات حریم خصوصی بیشتر" "امنیت و حریم خصوصی بیشتر" "امنیت" @@ -912,9 +912,9 @@ "۶ گیگاهرتز" "سریع‌ترین سرعت‌ها. با دستگاه‌های کمی سازگار است." "در کشور یا منطقه شما دردسترس نیست" - "اگر بسامد ترجیحی شما دردسترس نباشد، ممکن است نقطه اتصال از بسامد دیگری استفاده کند. اگر بسامد را تغییر دهید، ممکن است تنظیمات امنیتی نقطه اتصال تغییر کند." + "اگر بسامد ترجیحی شما دردسترس نباشد، ممکن است نقطه اتصال از بسامد دیگری استفاده کند. اگر بسامد را تغییر دهید، ممکن است تنظیمات ایمنی نقطه اتصال تغییر کند." "با ۶ گیگاهرتز دردسترس نیست" - "اگر فرکانس نقطه اتصال را تغییر دهید، ممکن است تنظیمات امنیتی تغییر کند" + "اگر فرکانس نقطه اتصال را تغییر دهید، ممکن است تنظیمات ایمنی تغییر کند" "در حال روشن کردن نقطه اتصال…" "در حال خاموش کردن نقطه اتصال…" "اشتراک‌گذاری اینترنت دردسترس نیست" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index 634358904ca..44f1f7a680a 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -89,7 +89,7 @@ "%1$s haluaa poistaa Bluetoothin käytöstä." "Sovellus haluaa ottaa Bluetoothin käyttöön." "Sovellus haluaa poistaa Bluetoothin käytöstä." - "Bluetooth otetaan käyttöön…" + "Bluetooth laitetaan päälle..." "Bluetooth poistetaan käytöstä..." "Bluetooth-yhteyspyyntö" "PBAP-pyyntö" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 3795ed8e1c2..6d019e7b3d2 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -1828,7 +1828,7 @@ "Agrandir avec un raccourci" "Agrandir avec un raccourci et trois tapotements" "À propos de %1$s" - "Taille d\'affichage et texte" + "Taille de l\'affichage et du texte" "Modifier l\'affichage du texte" "Objet : modèles de montgolfières" "De : Bill" @@ -1952,7 +1952,7 @@ "Plus court" "Plus long" "Délai du clic automatique" - "Vibreur et retour haptique" + "Vibreur et retour tactile" "Contrôler la force des vibrations pour différentes utilisations" "Activé" "Désactivés" @@ -2043,8 +2043,8 @@ "Rouge faible, protanomalie" "Tritanomalie" "Encore moins lumineux" - "Rendre l\'écran encore moins lumineux" - "Raccourci vers Encore moins lumineux" + "Luminosité ultra-réduite" + "Raccourci vers Luminosité ultra-réduite" "À propos d\'encore moins lumineux" "Réduisez encore plus la luminosité de l\'écran pour faciliter la lecture" "Intensité" diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index 36fc336af7e..ec3277b24b7 100644 --- a/res/values-kn/strings.xml +++ b/res/values-kn/strings.xml @@ -1955,7 +1955,7 @@ "ವೈಬ್ರೇಷನ್‌ ಮತ್ತು ಹ್ಯಾಪ್ಟಿಕ್ಸ್" "ವಿವಿಧ ಬಳಕೆಗಾಗಿ ವೈಬ್ರೇಷನ್‌ನ ಶಕ್ತಿಯನ್ನು ನಿಯಂತ್ರಿಸಿ" "ಆನ್ ಮಾಡಿ" - "ಆಫ್ ಮಾಡಿ" + "ಆಫ್" "ಸಾಧನವನ್ನು ನಿಶ್ಯಬ್ಧಕ್ಕೆ ಸೆಟ್ ಮಾಡಿರುವ ಕಾರಣ ಸೆಟ್ಟಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ" "ಕರೆಗಳು" "ಅಧಿಸೂಚನೆಗಳು ಮತ್ತು ಎಚ್ಚರಿಕೆಗಳು" @@ -2388,7 +2388,7 @@ "ಅಡಾಪ್ಟಿವ್ ಕನೆಕ್ಟಿವಿಟಿ" "ನಿಮ್ಮ ನೆಟ್‌ವರ್ಕ್ ಕನೆಕ್ಷನ್‌ಗಳನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ನಿರ್ವಹಿಸುವ ಮೂಲಕ ಬ್ಯಾಟರಿ ಅವಧಿಯನ್ನು ವಿಸ್ತರಿಸುತ್ತದೆ ಮತ್ತು ಸಾಧನದ ಕಾರ್ಯಕ್ಷಮತೆಯನ್ನು ಸುಧಾರಿಸುತ್ತದೆ" "ಆನ್" - "ಆಫ್ ಮಾಡಿ" + "ಆಫ್" "Adaptive Connectivity ಬಳಸಿ" "ರುಜುವಾತು ಸಂಗ್ರಹಣೆ" "ಪ್ರಮಾಣಪತ್ರ ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿ" @@ -3005,7 +3005,7 @@ "ಫೋನ್ ಸ್ಪೀಕರ್" "ವೈರ್ ಕನೆಕ್ಷನ್ ಹೊಂದಿರುವ ಹೆಡ್‌ಫೋನ್‌ಗಳು" "ಹೊಂದಾಣಿಕೆಯಾಗುವ ಮಾಧ್ಯಮಗಳ ಆಡಿಯೋ ಇನ್ನಷ್ಟು ತಲ್ಲೀನವಾಗಿ ಕೇಳಿಸುತ್ತದೆ" - "ಆಫ್ ಮಾಡಿ" + "ಆಫ್" "ಆನ್ / %1$s" "ಆನ್ / %1$s ಮತ್ತು %2$s" "ನೀವು ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳಿಗಾಗಿ ಸ್ಪೇಷಿಯಲ್ ಆಡಿಯೋ ಅನ್ನು ಸಹ ಆನ್ ಮಾಡಬಹುದು." @@ -3463,7 +3463,7 @@ "ಆನ್" "ಆಫ್" "ಆನ್ ಮಾಡಿ" - "ಆಫ್ ಮಾಡಿ" + "ಆಫ್" "ಆನ್ ಆಗಿದೆ" "ಆಫ್" "ಆ್ಯಪ್ ಪಿನ್ನಿಂಗ್" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 9a122815fdf..3852f39eac1 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -4340,7 +4340,7 @@ "Kan simkaart niet wissen" "Door een fout kan deze simkaart niet worden gewist.\n\nStart je apparaat opnieuw op en probeer het nogmaals." "Verbinding maken met apparaat" - "De app %1$s wil een tijdelijk wifi-netwerk gebruiken om verbinding te maken met je apparaat." + "De %1$s-app wil een tijdelijk wifi-netwerk gebruiken om verbinding te maken met je apparaat." "Geen apparaten gevonden. Zorg dat de apparaten aanstaan en verbinding kunnen maken." "Opnieuw proberen" "Er is een probleem opgetreden. De app heeft het verzoek om een apparaat te kiezen geannuleerd." diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml index e56fe273774..e40f8069a75 100644 --- a/res/values-or/strings.xml +++ b/res/values-or/strings.xml @@ -4071,8 +4071,8 @@ "ଖୋଲନ୍ତୁ" "ଗେମ୍" "ବ୍ୟବହାର କରାଯାଇଥିବା ଜାଗା" - "(%sକୁ ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ପାଇଁ ଅନ୍‌ଇନ୍‌ଷ୍ଟଲ୍‌ କରାଯାଇଛି)" - "(ବ୍ୟବହାରକର୍ତ୍ତା %sଙ୍କ ପାଇଁ ଅକ୍ଷମ କରାଯାଇଛି)" + "(ୟୁଜର %s ପାଇଁ ଅନଇନଷ୍ଟଲ ହୋଇଛି)" + "(ୟୁଜର %s ପାଇଁ ଅକ୍ଷମ କରାଯାଇଛି)" "ସ୍ୱତଃପୂରଣ ସେବା" "ଡିଫଲ୍ଟ ଅଟୋଫିଲ ସେବା" "ପାସୱାର୍ଡଗୁଡ଼ିକ" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index f9d5f27ed61..9af1ad0981f 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -54,15 +54,15 @@ "Badilisha jina la kifaa hiki" "Hifadhi jina jipya" "Ungependa kuondoa kifaa?" - "Oanisha kifaa kipya" + "Unganisha kifaa kipya" "bluetooth" - "Oanisha kisaidizi cha kulia" - "Oanisha kisaidizi cha kushoto" - "Oanisha kisaidizi cha sikio lako lingine" + "Unganisha kisaidizi cha kulia" + "Unganisha kisaidizi cha kushoto" + "Unganisha kisaidizi cha sikio jingine" "Kifaa chako cha kusikilizia cha kushoto kimeunganishwa.\n\nIli uoanishe cha kulia, hakikisha kuwa kimewashwa na kipo tayari kuoanishwa." "Kifaa chako cha kusikilizia cha kulia kimeunganishwa.\n\nIli uoanishe cha kushoto, hakikisha kuwa kimewashwa na kipo tayari kuoanishwa." - "Oanisha kisaidizi cha sikio la kulia" - "Oanisha kisaidizi cha sikio la kushoto" + "Unganisha kisaidizi cha kulia" + "Unganisha kisaidizi cha kushoto" "Kwa vifaa vyote vya kusikia vinavyopatikana" "Mipangilio ya vifaa vya kusikilizia" "Njia ya mkato, uoanifu wa kifaa cha kusaidia kusikia" @@ -97,7 +97,7 @@ "Inaonekana kama \'^1\' kwenye vifaa vingine" "Washa Bluetooth ili uunganishe kwenye vifaa vingine." "Vifaa vyako" - "Oanisha kifaa kipya" + "Unganisha kifaa kipya" "Zima upakiaji wa maunzi kwa Bluetooth A2DP" "Zima upakiaji wa maunzi kwa kipengele cha Bluetooth LE audio" "Ungependa Kuzima kisha Uwashe Kifaa?" @@ -647,7 +647,7 @@ "Vifaa vinavyopatikana" "Unganisha" "Tenganisha" - "Oanisha kisha unganisha" + "Oanisha kisha uunganishe" "Wakati Bluetooth imewashwa, kifaa chako kinaweza kuwasiliana na vifaa vingine vyenye Bluetooth vilivyo karibu." "Wakati Bluetooth imewashwa, kifaa chako kinaweza kuwasiliana na vifaa vingine vyenye Bluetooth vilivyo karibu.\n\nIli kuboresha hali ya matumizi ya kifaa, programu na huduma bado zinaweza kutafuta vifaa vilivyo karibu wakati wowote, hata wakati umezima Bluetooth. Hali hii inaweza kutumika, kwa mfano, kuboresha huduma na vipengele vinavyohusiana na mahali. Unaweza kubadilisha mipangilio hii katika mipangilio ya kutafuta Bluetooth." "Badilisha" @@ -2002,14 +2002,14 @@ "Unaweza kutumia simu yako kwa pamoja na visaidizi vya kusikia, vifaa vinavyosaidia kusikia vinavyopachikwa ndani ya sikio na vifaa vingine vya kukuza sauti" "Hujaunganisha vifaa vyovyote vya kusikilizia" "Ongeza vifaa vya kusaidia kusikia" - "Oanisha visaidizi vya kusikia" + "Unganisha visaidizi vya kusikia" "Kwenye skrini inayofuata, gusa visaidizi vyako vya kusikia. Huenda ukahitaji kuoanisha kisaidizi cha sikio la kushoto na cha kulia kando kando.\n\nHakikisha visaidizi vyako vya kusikia vimewashwa na vipo tayari kuoanishwa." "%1$s inatumika" "%1$s, upande wa kushoto pekee" "%1$s, upande wa kulia pekee" "%1$s, kushoto na kulia" "%1$s zaidi ya 1" - "Oanisha kifaa kipya" + "Unganisha kifaa kipya" "Kuhusu Kuoanisha kifaa kipya" "Vifaa vya kusaidia kusikia" "Vifaa ulivyohifadhi" @@ -2019,7 +2019,7 @@ "Huboresha uoanifu na zana za kudukiza umeme katika visaidizi vya kusikia na kupunguza kelele zisizotakiwa" "Kuhusu vifaa vya kusikiliza" "Hakikisha kifaa chako cha kusikizia kimewashwa na kiko tayari kuoanishwa" - "Oanisha vifaa vya kusaidia kusikia" + "Unganisha vifaa vya kusaidia kusikia" "Vifaa vya kusaidia kusikia vinavyopatikana" "Je, huoni vifaa vyako vya kusikia?" "Angalia vifaa zaidi" @@ -2944,7 +2944,7 @@ "dnd, ratiba, arifa, zuia, nyamazisha, tetema, lala, kazini, angazia, sauti, zima sauti, siku, siku ya kazi, wikiendi, usiku wa siku za wiki, tukio" "skrini, wakati wa kufunga, muda wa skrini kujizima, kifunga skrini" "hifadhi, akiba, data, futa, futa data yote, nafasi iliyosalia, nafasi" - "imeunganishwa, kifaa, vipokea sauti vinavyobanwa kichwani, vifaa vya sauti, spika, pasiwaya, oanisha, vifaa vya sauti vya masikioni, muziki, maudhui" + "imeunganishwa, kifaa, vipokea sauti vinavyobanwa kichwani, vifaa vya sauti, spika, pasiwaya, unganisha, vifaa vya sauti vya masikioni, muziki, maudhui" "mandharinyuma, mandhari, gridi, weka mapendeleo, weka mapendeleo" "aikoni, msisitizo, rangi, skrini ya kwanza, kufunga skrini, njia ya mkato, ukubwa wa saa" "chaguomsingi, mratibu" diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index 2df079172df..92be37465fb 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -2111,7 +2111,7 @@ "எளிதாகப் பயன்படுத்தக்கூடியது, எளிதாக அணுகக்கூடியது, உதவி, உதவிகரமானது" "சாளரப் பெரிதாக்கி, அளவை மாற்றுதல், பெரிதாக்கல், குறைந்த பார்வைத் திறன், பெரிதாக்கல், பெரிதாக்கு" - "வசனங்கள், விவரிப்பு சப்டைட்டில்கள், CC, உடனடி உரையாக்கம், கேட்பதில் சிரமம், கேட்கும் திறன் இன்மை, CART, பேச்சிலிருந்து உரை, சப்டைட்டில்" + "வசனங்கள், விவரிப்பு சப்டைட்டில்கள், CC, Live Transcribe, கேட்பதில் சிரமம், கேட்கும் திறன் இன்மை, CART, பேச்சிலிருந்து உரை, சப்டைட்டில்" From e3b527a2bf7d025b56aad38f3eda70c54a259e9c Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Wed, 3 Jan 2024 12:42:30 +0800 Subject: [PATCH 06/10] Refactor CellInfoUtil Unify and simplify the logic, and add unit test. Bug: 293845605 Test: manual - on Mobile Settings Test: unit test Change-Id: I5467b92baa8e47fbd400c7a21fd39fd5ec90ed3a --- .../network/telephony/CellInfoUtil.java | 219 ------------------ .../network/telephony/CellInfoUtil.kt | 113 +++++++++ .../telephony/NetworkOperatorPreference.java | 49 +--- .../telephony/NetworkSelectSettings.java | 6 +- .../network/telephony/CellInfoUtilTest.kt | 173 ++++++++++++++ 5 files changed, 297 insertions(+), 263 deletions(-) delete mode 100644 src/com/android/settings/network/telephony/CellInfoUtil.java create mode 100644 src/com/android/settings/network/telephony/CellInfoUtil.kt create mode 100644 tests/unit/src/com/android/settings/network/telephony/CellInfoUtilTest.kt diff --git a/src/com/android/settings/network/telephony/CellInfoUtil.java b/src/com/android/settings/network/telephony/CellInfoUtil.java deleted file mode 100644 index 8889586edd3..00000000000 --- a/src/com/android/settings/network/telephony/CellInfoUtil.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.network.telephony; - -import android.telephony.CellIdentity; -import android.telephony.CellIdentityGsm; -import android.telephony.CellIdentityLte; -import android.telephony.CellIdentityNr; -import android.telephony.CellIdentityTdscdma; -import android.telephony.CellIdentityWcdma; -import android.telephony.CellInfo; -import android.telephony.CellInfoCdma; -import android.telephony.CellInfoGsm; -import android.telephony.CellInfoLte; -import android.telephony.CellInfoNr; -import android.telephony.CellInfoTdscdma; -import android.telephony.CellInfoWcdma; -import android.text.BidiFormatter; -import android.text.TextDirectionHeuristics; -import android.text.TextUtils; - -import com.android.internal.telephony.OperatorInfo; - -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -/** - * Add static Utility functions to get information from the CellInfo object. - * TODO: Modify {@link CellInfo} for simplify those functions - */ -public final class CellInfoUtil { - private static final String TAG = "NetworkSelectSetting"; - - private CellInfoUtil() { - } - - /** - * Returns the title of the network obtained in the manual search. - * - * @param cellId contains the identity of the network. - * @param networkMccMnc contains the MCCMNC string of the network - * @return Long Name if not null/empty, otherwise Short Name if not null/empty, - * else MCCMNC string. - */ - public static String getNetworkTitle(CellIdentity cellId, String networkMccMnc) { - if (cellId != null) { - String title = Objects.toString(cellId.getOperatorAlphaLong(), ""); - if (TextUtils.isEmpty(title)) { - title = Objects.toString(cellId.getOperatorAlphaShort(), ""); - } - if (!TextUtils.isEmpty(title)) { - return title; - } - } - if (TextUtils.isEmpty(networkMccMnc)) { - return ""; - } - final BidiFormatter bidiFormatter = BidiFormatter.getInstance(); - return bidiFormatter.unicodeWrap(networkMccMnc, TextDirectionHeuristics.LTR); - } - - /** - * Returns the CellIdentity from CellInfo - * - * @param cellInfo contains the information of the network. - * @return CellIdentity within CellInfo - */ - public static CellIdentity getCellIdentity(CellInfo cellInfo) { - if (cellInfo == null) { - return null; - } - CellIdentity cellId = null; - if (cellInfo instanceof CellInfoGsm) { - cellId = ((CellInfoGsm) cellInfo).getCellIdentity(); - } else if (cellInfo instanceof CellInfoCdma) { - cellId = ((CellInfoCdma) cellInfo).getCellIdentity(); - } else if (cellInfo instanceof CellInfoWcdma) { - cellId = ((CellInfoWcdma) cellInfo).getCellIdentity(); - } else if (cellInfo instanceof CellInfoTdscdma) { - cellId = ((CellInfoTdscdma) cellInfo).getCellIdentity(); - } else if (cellInfo instanceof CellInfoLte) { - cellId = ((CellInfoLte) cellInfo).getCellIdentity(); - } else if (cellInfo instanceof CellInfoNr) { - cellId = ((CellInfoNr) cellInfo).getCellIdentity(); - } - return cellId; - } - - /** - * Creates a CellInfo object from OperatorInfo. GsmCellInfo is used here only because - * operatorInfo does not contain technology type while CellInfo is an abstract object that - * requires to specify technology type. It doesn't matter which CellInfo type to use here, since - * we only want to wrap the operator info and PLMN to a CellInfo object. - */ - public static CellInfo convertOperatorInfoToCellInfo(OperatorInfo operatorInfo) { - final String operatorNumeric = operatorInfo.getOperatorNumeric(); - String mcc = null; - String mnc = null; - if (operatorNumeric != null && operatorNumeric.matches("^[0-9]{5,6}$")) { - mcc = operatorNumeric.substring(0, 3); - mnc = operatorNumeric.substring(3); - } - final CellIdentityGsm cig = new CellIdentityGsm( - Integer.MAX_VALUE /* lac */, - Integer.MAX_VALUE /* cid */, - Integer.MAX_VALUE /* arfcn */, - Integer.MAX_VALUE /* bsic */, - mcc, - mnc, - operatorInfo.getOperatorAlphaLong(), - operatorInfo.getOperatorAlphaShort(), - Collections.emptyList()); - - final CellInfoGsm ci = new CellInfoGsm(); - ci.setCellIdentity(cig); - return ci; - } - - /** Convert a list of cellInfos to readable string without sensitive info. */ - public static String cellInfoListToString(List cellInfos) { - return cellInfos.stream() - .map(cellInfo -> cellInfoToString(cellInfo)) - .collect(Collectors.joining(", ")); - } - - /** Convert {@code cellInfo} to a readable string without sensitive info. */ - public static String cellInfoToString(CellInfo cellInfo) { - final String cellType = cellInfo.getClass().getSimpleName(); - final CellIdentity cid = getCellIdentity(cellInfo); - String mcc = getCellIdentityMcc(cid); - String mnc = getCellIdentityMnc(cid); - CharSequence alphaLong = null; - CharSequence alphaShort = null; - if (cid != null) { - alphaLong = cid.getOperatorAlphaLong(); - alphaShort = cid.getOperatorAlphaShort(); - } - return String.format( - "{CellType = %s, isRegistered = %b, mcc = %s, mnc = %s, alphaL = %s, alphaS = %s}", - cellType, cellInfo.isRegistered(), mcc, mnc, - alphaLong, alphaShort); - } - - /** - * Returns the MccMnc. - * - * @param cid contains the identity of the network. - * @return MccMnc string. - */ - public static String getCellIdentityMccMnc(CellIdentity cid) { - String mcc = getCellIdentityMcc(cid); - String mnc = getCellIdentityMnc(cid); - return (mcc == null || mnc == null) ? null : mcc + mnc; - } - - /** - * Returns the Mcc. - * - * @param cid contains the identity of the network. - * @return Mcc string. - */ - public static String getCellIdentityMcc(CellIdentity cid) { - String mcc = null; - if (cid != null) { - if (cid instanceof CellIdentityGsm) { - mcc = ((CellIdentityGsm) cid).getMccString(); - } else if (cid instanceof CellIdentityWcdma) { - mcc = ((CellIdentityWcdma) cid).getMccString(); - } else if (cid instanceof CellIdentityTdscdma) { - mcc = ((CellIdentityTdscdma) cid).getMccString(); - } else if (cid instanceof CellIdentityLte) { - mcc = ((CellIdentityLte) cid).getMccString(); - } else if (cid instanceof CellIdentityNr) { - mcc = ((CellIdentityNr) cid).getMccString(); - } - } - return (mcc == null) ? null : mcc; - } - - /** - * Returns the Mnc. - * - * @param cid contains the identity of the network. - * @return Mcc string. - */ - public static String getCellIdentityMnc(CellIdentity cid) { - String mnc = null; - if (cid != null) { - if (cid instanceof CellIdentityGsm) { - mnc = ((CellIdentityGsm) cid).getMncString(); - } else if (cid instanceof CellIdentityWcdma) { - mnc = ((CellIdentityWcdma) cid).getMncString(); - } else if (cid instanceof CellIdentityTdscdma) { - mnc = ((CellIdentityTdscdma) cid).getMncString(); - } else if (cid instanceof CellIdentityLte) { - mnc = ((CellIdentityLte) cid).getMncString(); - } else if (cid instanceof CellIdentityNr) { - mnc = ((CellIdentityNr) cid).getMncString(); - } - } - return (mnc == null) ? null : mnc; - } -} diff --git a/src/com/android/settings/network/telephony/CellInfoUtil.kt b/src/com/android/settings/network/telephony/CellInfoUtil.kt new file mode 100644 index 00000000000..c7b6b24b521 --- /dev/null +++ b/src/com/android/settings/network/telephony/CellInfoUtil.kt @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.network.telephony + +import android.telephony.CellIdentity +import android.telephony.CellIdentityGsm +import android.telephony.CellInfo +import android.telephony.CellInfoGsm +import android.text.BidiFormatter +import android.text.TextDirectionHeuristics +import com.android.internal.telephony.OperatorInfo + +/** + * Add static Utility functions to get information from the CellInfo object. + * TODO: Modify [CellInfo] for simplify those functions + */ +object CellInfoUtil { + + /** + * Returns the title of the network obtained in the manual search. + * + * By the following order, + * 1. Long Name if not null/empty + * 2. Short Name if not null/empty + * 3. OperatorNumeric (MCCMNC) string + */ + @JvmStatic + fun CellIdentity.getNetworkTitle(): String? { + operatorAlphaLong?.takeIf { it.isNotBlank() }?.let { return it.toString() } + operatorAlphaShort?.takeIf { it.isNotBlank() }?.let { return it.toString() } + val operatorNumeric = getOperatorNumeric() ?: return null + val bidiFormatter = BidiFormatter.getInstance() + return bidiFormatter.unicodeWrap(operatorNumeric, TextDirectionHeuristics.LTR) + } + + /** + * Creates a CellInfo object from OperatorInfo. GsmCellInfo is used here only because + * operatorInfo does not contain technology type while CellInfo is an abstract object that + * requires to specify technology type. It doesn't matter which CellInfo type to use here, since + * we only want to wrap the operator info and PLMN to a CellInfo object. + */ + @JvmStatic + fun convertOperatorInfoToCellInfo(operatorInfo: OperatorInfo): CellInfo { + val operatorNumeric = operatorInfo.operatorNumeric + var mcc: String? = null + var mnc: String? = null + if (operatorNumeric?.matches("^[0-9]{5,6}$".toRegex()) == true) { + mcc = operatorNumeric.substring(0, 3) + mnc = operatorNumeric.substring(3) + } + return CellInfoGsm().apply { + cellIdentity = CellIdentityGsm( + /* lac = */ Int.MAX_VALUE, + /* cid = */ Int.MAX_VALUE, + /* arfcn = */ Int.MAX_VALUE, + /* bsic = */ Int.MAX_VALUE, + /* mccStr = */ mcc, + /* mncStr = */ mnc, + /* alphal = */ operatorInfo.operatorAlphaLong, + /* alphas = */ operatorInfo.operatorAlphaShort, + /* additionalPlmns = */ emptyList(), + ) + } + } + + /** + * Convert a list of cellInfos to readable string without sensitive info. + */ + @JvmStatic + fun cellInfoListToString(cellInfos: List): String = + cellInfos.joinToString { cellInfo -> cellInfo.readableString() } + + /** + * Convert [CellInfo] to a readable string without sensitive info. + */ + private fun CellInfo.readableString(): String = buildString { + append("{CellType = ${this@readableString::class.simpleName}, ") + append("isRegistered = $isRegistered, ") + append(cellIdentity.readableString()) + append("}") + } + + private fun CellIdentity.readableString(): String = buildString { + append("mcc = $mccString, ") + append("mnc = $mncString, ") + append("alphaL = $operatorAlphaLong, ") + append("alphaS = $operatorAlphaShort") + } + + /** + * Returns the MccMnc. + */ + @JvmStatic + fun CellIdentity.getOperatorNumeric(): String? { + val mcc = mccString + val mnc = mncString + return if (mcc == null || mnc == null) null else mcc + mnc + } +} diff --git a/src/com/android/settings/network/telephony/NetworkOperatorPreference.java b/src/com/android/settings/network/telephony/NetworkOperatorPreference.java index 7404aa47353..7a584330425 100644 --- a/src/com/android/settings/network/telephony/NetworkOperatorPreference.java +++ b/src/com/android/settings/network/telephony/NetworkOperatorPreference.java @@ -18,14 +18,11 @@ package com.android.settings.network.telephony; import static android.telephony.SignalStrength.NUM_SIGNAL_STRENGTH_BINS; +import static com.android.settings.network.telephony.CellInfoUtil.getOperatorNumeric; + import android.content.Context; import android.telephony.AccessNetworkConstants.AccessNetworkType; import android.telephony.CellIdentity; -import android.telephony.CellIdentityGsm; -import android.telephony.CellIdentityLte; -import android.telephony.CellIdentityNr; -import android.telephony.CellIdentityTdscdma; -import android.telephony.CellIdentityWcdma; import android.telephony.CellInfo; import android.telephony.CellInfoCdma; import android.telephony.CellInfoGsm; @@ -36,6 +33,7 @@ import android.telephony.CellInfoWcdma; import android.telephony.CellSignalStrength; import android.util.Log; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; @@ -87,7 +85,7 @@ public class NetworkOperatorPreference extends Preference { * Change cell information */ public void updateCell(CellInfo cellinfo) { - updateCell(cellinfo, CellInfoUtil.getCellIdentity(cellinfo)); + updateCell(cellinfo, cellinfo.getCellIdentity()); } @VisibleForTesting @@ -104,14 +102,14 @@ public class NetworkOperatorPreference extends Preference { if (cellinfo == null) { return false; } - return mCellId.equals(CellInfoUtil.getCellIdentity(cellinfo)); + return mCellId.equals(cellinfo.getCellIdentity()); } /** * Return true when this preference is for forbidden network */ public boolean isForbiddenNetwork() { - return ((mForbiddenPlmns != null) && mForbiddenPlmns.contains(getOperatorNumeric())); + return ((mForbiddenPlmns != null) && mForbiddenPlmns.contains(getOperatorNumeric(mCellId))); } /** @@ -147,41 +145,12 @@ public class NetworkOperatorPreference extends Preference { updateIcon(level); } - /** - * Operator numeric of this cell - */ - public String getOperatorNumeric() { - final CellIdentity cellId = mCellId; - if (cellId == null) { - return null; - } - if (cellId instanceof CellIdentityGsm) { - return ((CellIdentityGsm) cellId).getMobileNetworkOperator(); - } - if (cellId instanceof CellIdentityWcdma) { - return ((CellIdentityWcdma) cellId).getMobileNetworkOperator(); - } - if (cellId instanceof CellIdentityTdscdma) { - return ((CellIdentityTdscdma) cellId).getMobileNetworkOperator(); - } - if (cellId instanceof CellIdentityLte) { - return ((CellIdentityLte) cellId).getMobileNetworkOperator(); - } - if (cellId instanceof CellIdentityNr) { - final String mcc = ((CellIdentityNr) cellId).getMccString(); - if (mcc == null) { - return null; - } - return mcc.concat(((CellIdentityNr) cellId).getMncString()); - } - return null; - } - /** * Operator name of this cell */ + @Nullable public String getOperatorName() { - return CellInfoUtil.getNetworkTitle(mCellId, getOperatorNumeric()); + return CellInfoUtil.getNetworkTitle(mCellId); } /** @@ -190,7 +159,7 @@ public class NetworkOperatorPreference extends Preference { public OperatorInfo getOperatorInfo() { return new OperatorInfo(Objects.toString(mCellId.getOperatorAlphaLong(), ""), Objects.toString(mCellId.getOperatorAlphaShort(), ""), - getOperatorNumeric(), getAccessNetworkTypeFromCellInfo(mCellInfo)); + getOperatorNumeric(mCellId), getAccessNetworkTypeFromCellInfo(mCellInfo)); } private int getIconIdForCell(CellInfo ci) { diff --git a/src/com/android/settings/network/telephony/NetworkSelectSettings.java b/src/com/android/settings/network/telephony/NetworkSelectSettings.java index 1cfa043ce6e..243b629ecb1 100644 --- a/src/com/android/settings/network/telephony/NetworkSelectSettings.java +++ b/src/com/android/settings/network/telephony/NetworkSelectSettings.java @@ -365,14 +365,12 @@ public class NetworkSelectSettings extends DashboardFragment { } ArrayList aggregatedList = new ArrayList<>(); for (CellInfo cellInfo : cellInfoListInput) { - String plmn = CellInfoUtil.getNetworkTitle(cellInfo.getCellIdentity(), - CellInfoUtil.getCellIdentityMccMnc(cellInfo.getCellIdentity())); + String plmn = CellInfoUtil.getNetworkTitle(cellInfo.getCellIdentity()); Class className = cellInfo.getClass(); Optional itemInTheList = aggregatedList.stream().filter( item -> { - String itemPlmn = CellInfoUtil.getNetworkTitle(item.getCellIdentity(), - CellInfoUtil.getCellIdentityMccMnc(item.getCellIdentity())); + String itemPlmn = CellInfoUtil.getNetworkTitle(item.getCellIdentity()); return itemPlmn.equals(plmn) && item.getClass().equals(className); }) .findFirst(); diff --git a/tests/unit/src/com/android/settings/network/telephony/CellInfoUtilTest.kt b/tests/unit/src/com/android/settings/network/telephony/CellInfoUtilTest.kt new file mode 100644 index 00000000000..c3c61888d9b --- /dev/null +++ b/tests/unit/src/com/android/settings/network/telephony/CellInfoUtilTest.kt @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.network.telephony + +import android.telephony.CellIdentityCdma +import android.telephony.CellIdentityGsm +import android.telephony.CellInfoCdma +import android.telephony.CellInfoGsm +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.internal.telephony.OperatorInfo +import com.android.settings.network.telephony.CellInfoUtil.getNetworkTitle +import com.android.settings.network.telephony.CellInfoUtil.getOperatorNumeric +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class CellInfoUtilTest { + + @Test + fun getNetworkTitle_alphaLong() { + val networkTitle = CELL_IDENTITY_GSM.getNetworkTitle() + + assertThat(networkTitle).isEqualTo(LONG) + } + + @Test + fun getNetworkTitle_alphaShort() { + val cellIdentity = CellIdentityGsm( + /* lac = */ 1, + /* cid = */ 2, + /* arfcn = */ 3, + /* bsic = */ 4, + /* mccStr = */ "123", + /* mncStr = */ "01", + /* alphal = */ "", + /* alphas = */ SHORT, + /* additionalPlmns = */ emptyList(), + ) + + val networkTitle = cellIdentity.getNetworkTitle() + + assertThat(networkTitle).isEqualTo(SHORT) + } + + @Test + fun getNetworkTitle_operatorNumeric() { + val cellIdentity = CellIdentityGsm( + /* lac = */ 1, + /* cid = */ 2, + /* arfcn = */ 3, + /* bsic = */ 4, + /* mccStr = */ "123", + /* mncStr = */ "01", + /* alphal = */ "", + /* alphas = */ "", + /* additionalPlmns = */ emptyList(), + ) + + val networkTitle = cellIdentity.getNetworkTitle() + + assertThat(networkTitle).isEqualTo("12301") + } + + @Test + fun getNetworkTitle_null() { + val cellIdentity = CellIdentityGsm( + /* lac = */ 1, + /* cid = */ 2, + /* arfcn = */ 3, + /* bsic = */ 4, + /* mccStr = */ null, + /* mncStr = */ null, + /* alphal = */ null, + /* alphas = */ null, + /* additionalPlmns = */ emptyList(), + ) + + val networkTitle = cellIdentity.getNetworkTitle() + + assertThat(networkTitle).isNull() + } + + @Test + fun convertOperatorInfoToCellInfo() { + val operatorInfo = OperatorInfo(LONG, SHORT, "12301") + + val cellInfo = CellInfoUtil.convertOperatorInfoToCellInfo(operatorInfo) + + assertThat(cellInfo.cellIdentity.mccString).isEqualTo("123") + assertThat(cellInfo.cellIdentity.mncString).isEqualTo("01") + assertThat(cellInfo.cellIdentity.operatorAlphaLong).isEqualTo(LONG) + assertThat(cellInfo.cellIdentity.operatorAlphaShort).isEqualTo(SHORT) + } + + @Test + fun cellInfoListToString() { + val cellInfoList = + listOf( + CellInfoCdma().apply { + cellIdentity = CELL_IDENTITY_CDMA + }, + CellInfoGsm().apply { + isRegistered = true + cellIdentity = CELL_IDENTITY_GSM + }, + ) + + val string = CellInfoUtil.cellInfoListToString(cellInfoList) + + assertThat(string).isEqualTo( + "{CellType = CellInfoCdma, isRegistered = false, " + + "mcc = null, mnc = null, alphaL = Long, alphaS = Short}, " + + "{CellType = CellInfoGsm, isRegistered = true, " + + "mcc = 123, mnc = 01, alphaL = Long, alphaS = Short}" + ) + } + + @Test + fun getOperatorNumeric_cdma() { + val operatorNumeric = CELL_IDENTITY_CDMA.getOperatorNumeric() + + assertThat(operatorNumeric).isNull() + } + + @Test + fun getOperatorNumeric_gsm() { + val operatorNumeric = CELL_IDENTITY_GSM.getOperatorNumeric() + + assertThat(operatorNumeric).isEqualTo("12301") + } + + private companion object { + const val LONG = "Long" + const val SHORT = "Short" + + val CELL_IDENTITY_GSM = CellIdentityGsm( + /* lac = */ 1, + /* cid = */ 2, + /* arfcn = */ 3, + /* bsic = */ 4, + /* mccStr = */ "123", + /* mncStr = */ "01", + /* alphal = */ LONG, + /* alphas = */ SHORT, + /* additionalPlmns = */ emptyList(), + ) + + val CELL_IDENTITY_CDMA = CellIdentityCdma( + /* nid = */ 1, + /* sid = */ 2, + /* bid = */ 3, + /* lon = */ 4, + /* lat = */ 5, + /* alphal = */ LONG, + /* alphas = */ SHORT, + ) + } +} From d8695cbf9bbb71218eb20b5c72a7404b842e0e41 Mon Sep 17 00:00:00 2001 From: Allen Su Date: Tue, 26 Dec 2023 08:58:11 +0000 Subject: [PATCH 07/10] Remove the xml entry when the system language is removed Bug: 301395074 Test: atest SettingsRoboTests:com.android.settings.localepicker Change-Id: Ic771cd5146ab954c3da65748bf87cd14d30770b4 --- .../localepicker/AppLocalePickerActivity.java | 5 +-- .../LocaleDragAndDropAdapter.java | 2 ++ .../LocaleNotificationDataManager.java | 11 ++++++ .../localepicker/NotificationController.java | 35 ++++++++++++------- .../AppLocalePickerActivityTest.java | 6 ++-- .../LocaleNotificationDataManagerTest.java | 12 +++++++ .../NotificationControllerTest.java | 13 +++++++ 7 files changed, 66 insertions(+), 18 deletions(-) diff --git a/src/com/android/settings/localepicker/AppLocalePickerActivity.java b/src/com/android/settings/localepicker/AppLocalePickerActivity.java index c0f3adcc7ed..b284c8d7c40 100644 --- a/src/com/android/settings/localepicker/AppLocalePickerActivity.java +++ b/src/com/android/settings/localepicker/AppLocalePickerActivity.java @@ -158,13 +158,14 @@ public class AppLocalePickerActivity extends SettingsBaseActivity private void broadcastAppLocaleChange(LocaleStore.LocaleInfo localeInfo) { if (!localeNotificationEnabled()) { + Log.w(TAG, "Locale notification is not enabled"); return; } - String localeTag = localeInfo.getLocale().toLanguageTag(); - if (LocaleUtils.isInSystemLocale(localeTag) || localeInfo.isAppCurrentLocale()) { + if (localeInfo.isAppCurrentLocale()) { return; } try { + String localeTag = localeInfo.getLocale().toLanguageTag(); int uid = getPackageManager().getApplicationInfo(mPackageName, PackageManager.GET_META_DATA).uid; boolean launchNotification = mNotificationController.shouldTriggerNotification( diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java index 1b3a4f20af5..24d992756ee 100644 --- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java +++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java @@ -270,12 +270,14 @@ class LocaleDragAndDropAdapter void removeChecked() { int itemCount = mFeedItemList.size(); LocaleStore.LocaleInfo localeInfo; + NotificationController controller = NotificationController.getInstance(mContext); for (int i = itemCount - 1; i >= 0; i--) { localeInfo = mFeedItemList.get(i); if (localeInfo.getChecked()) { FeatureFactory.getFeatureFactory().getMetricsFeatureProvider() .action(mContext, SettingsEnums.ACTION_REMOVE_LANGUAGE); mFeedItemList.remove(i); + controller.removeNotificationInfo(localeInfo.getLocale().toLanguageTag()); } } notifyDataSetChanged(); diff --git a/src/com/android/settings/localepicker/LocaleNotificationDataManager.java b/src/com/android/settings/localepicker/LocaleNotificationDataManager.java index 4d948f15ced..0e89366fbcc 100644 --- a/src/com/android/settings/localepicker/LocaleNotificationDataManager.java +++ b/src/com/android/settings/localepicker/LocaleNotificationDataManager.java @@ -62,6 +62,17 @@ public class LocaleNotificationDataManager { editor.apply(); } + /** + * Removes one entry with the corresponding locale from the {@link SharedPreferences}. + * + * @param locale A locale which the application sets to + */ + public void removeNotificationInfo(String locale) { + SharedPreferences.Editor editor = getSharedPreferences(mContext).edit(); + editor.remove(locale); + editor.apply(); + } + /** * Gets the {@link NotificationInfo} with the associated locale from the * {@link SharedPreferences}. diff --git a/src/com/android/settings/localepicker/NotificationController.java b/src/com/android/settings/localepicker/NotificationController.java index 2d36189132a..40967059452 100644 --- a/src/com/android/settings/localepicker/NotificationController.java +++ b/src/com/android/settings/localepicker/NotificationController.java @@ -110,6 +110,15 @@ public class NotificationController { return (info != null) ? info.getNotificationId() : -1; } + /** + * Remove the {@link NotificationInfo} with the corresponding locale + * + * @param locale The locale which the application sets to + */ + public void removeNotificationInfo(@NonNull String locale) { + mDataManager.removeNotificationInfo(locale); + } + private boolean updateLocaleNotificationInfo(int uid, String locale) { NotificationInfo info = mDataManager.getNotificationInfo(locale); if (info == null) { @@ -135,20 +144,20 @@ public class NotificationController { int notificationCount = info.getNotificationCount(); long lastNotificationTime = info.getLastNotificationTimeMs(); int notificationId = info.getNotificationId(); - - // Add the uid into the locale's uid list - uidSet.add(uid); if (dismissCount < DISMISS_COUNT_THRESHOLD - && notificationCount < NOTIFICATION_COUNT_THRESHOLD - // Notification should fire on multiples of 2 apps using the locale. - && uidSet.size() % MULTIPLE_BASE == 0 - && !isNotificationFrequent(lastNotificationTime)) { - // Increment the count because the notification can be triggered. - notificationCount = info.getNotificationCount() + 1; - lastNotificationTime = Calendar.getInstance().getTimeInMillis(); - Log.i(TAG, "notificationCount:" + notificationCount); - if (notificationCount == 1) { - notificationId = (int) SystemClock.uptimeMillis(); + && notificationCount < NOTIFICATION_COUNT_THRESHOLD) { + // Add the uid into the locale's uid list + uidSet.add(uid); + // Notification should fire on multiples of 2 apps using the locale. + if (uidSet.size() % MULTIPLE_BASE == 0 + && !isNotificationFrequent(lastNotificationTime)) { + // Increment the count because the notification can be triggered. + notificationCount = info.getNotificationCount() + 1; + lastNotificationTime = Calendar.getInstance().getTimeInMillis(); + Log.i(TAG, "notificationCount:" + notificationCount); + if (notificationCount == 1) { + notificationId = (int) SystemClock.uptimeMillis(); + } } } return new NotificationInfo(uidSet, notificationCount, dismissCount, lastNotificationTime, diff --git a/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java b/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java index e91c3886cc9..2989324701f 100644 --- a/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java +++ b/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java @@ -369,7 +369,7 @@ public class AppLocalePickerActivityTest { // In the proto file, en-US's uid list contains 103, the notificationCount equals 1, and // LastNotificationTime > 0. NotificationInfo info = mDataManager.getNotificationInfo(EN_US); - assertThat(info.getUidCollection().contains(sUid)).isTrue(); + assertThat(info.getUidCollection()).contains(sUid); assertThat(info.getNotificationCount()).isEqualTo(1); assertThat(info.getDismissCount()).isEqualTo(0); assertThat(info.getLastNotificationTimeMs()).isNotEqualTo(0); @@ -440,7 +440,7 @@ public class AppLocalePickerActivityTest { @Test @RequiresFlagsEnabled(Flags.FLAG_LOCALE_NOTIFICATION_ENABLED) - public void testEvaluateLocaleNotification_localeUpdateReachThreshold_uidAddedNoNotification() + public void testEvaluateLocaleNotification_localeUpdateReachThreshold_noUidNorNotification() throws Exception { // App with uid 106 changed its locale from System to en-US. sUid = 106; @@ -460,7 +460,7 @@ public class AppLocalePickerActivityTest { // In the proto file, en-US's uid list contains 106, the notificationCount equals 2, and // LastNotificationTime > 0. NotificationInfo info = mDataManager.getNotificationInfo(EN_US); - assertThat(info.getUidCollection()).contains(sUid); + assertThat(info.getUidCollection().contains(sUid)).isFalse(); assertThat(info.getNotificationCount()).isEqualTo(2); assertThat(info.getDismissCount()).isEqualTo(0); assertThat(info.getLastNotificationTimeMs()).isEqualTo(lastNotificationTime); diff --git a/tests/robotests/src/com/android/settings/localepicker/LocaleNotificationDataManagerTest.java b/tests/robotests/src/com/android/settings/localepicker/LocaleNotificationDataManagerTest.java index 99541b695f3..443c26d5a9b 100644 --- a/tests/robotests/src/com/android/settings/localepicker/LocaleNotificationDataManagerTest.java +++ b/tests/robotests/src/com/android/settings/localepicker/LocaleNotificationDataManagerTest.java @@ -68,6 +68,18 @@ public class LocaleNotificationDataManagerTest { info.getLastNotificationTimeMs()); } + @Test + public void testRemoveNotificationInfo() { + String locale = "en-US"; + Set uidSet = Set.of(101); + NotificationInfo info = new NotificationInfo(uidSet, 1, 1, 100L, 1000); + + mDataManager.putNotificationInfo(locale, info); + assertThat(mDataManager.getNotificationInfo(locale)).isEqualTo(info); + mDataManager.removeNotificationInfo(locale); + assertThat(mDataManager.getNotificationInfo(locale)).isNull(); + } + @Test public void testGetNotificationMap() { String enUS = "en-US"; diff --git a/tests/robotests/src/com/android/settings/localepicker/NotificationControllerTest.java b/tests/robotests/src/com/android/settings/localepicker/NotificationControllerTest.java index 3e31c0c7967..1e37f9b2f74 100644 --- a/tests/robotests/src/com/android/settings/localepicker/NotificationControllerTest.java +++ b/tests/robotests/src/com/android/settings/localepicker/NotificationControllerTest.java @@ -71,6 +71,19 @@ public class NotificationControllerTest { assertThat(result.getNotificationId()).isEqualTo(id); } + @Test + public void testRemoveNotificationInfo_removed() throws Exception { + String enUS = "en-US"; + Set uidSet = Set.of(100, 101); + long lastNotificationTime = Calendar.getInstance().getTimeInMillis(); + int id = (int) SystemClock.uptimeMillis(); + initSharedPreference(enUS, uidSet, 0, 1, lastNotificationTime, id); + + mNotificationController.removeNotificationInfo(enUS); + + assertThat(mDataManager.getNotificationInfo(enUS)).isNull(); + } + @Test public void testShouldTriggerNotification_inSystemLocale_returnFalse() throws Exception { int uid = 102; From 5ef828930d4a02136306792506786308dd7f28f4 Mon Sep 17 00:00:00 2001 From: mxyyiyi Date: Wed, 3 Jan 2024 15:11:58 +0800 Subject: [PATCH 08/10] Reset the delay time to refresh the periodic job. Bug: 315225261 Test: manual Change-Id: I5a42a9d2ac11ff5f036eaa85e37682438fceeac2 --- .../batteryusage/BootBroadcastReceiver.java | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java index dd4848393fa..4031eff7bdc 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java @@ -26,7 +26,6 @@ import android.util.Log; import com.android.settings.core.instrumentation.ElapsedTimeUtils; import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action; import com.android.settings.fuelgauge.batteryusage.bugreport.BatteryUsageLogUtils; -import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.fuelgauge.BatteryUtils; import java.time.Duration; @@ -34,9 +33,7 @@ import java.time.Duration; /** Receives broadcasts to start or stop the periodic fetching job. */ public final class BootBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "BootBroadcastReceiver"; - private static final long RESCHEDULE_FOR_BOOT_ACTION_WITH_DELAY = - Duration.ofMinutes(40).toMillis(); - private static final long RESCHEDULE_FOR_BOOT_ACTION_WITHOUT_DELAY = + private static final long RESCHEDULE_FOR_BOOT_ACTION_DELAY_MILLIS = Duration.ofSeconds(6).toMillis(); private final Handler mHandler = new Handler(Looper.getMainLooper()); @@ -81,7 +78,7 @@ public final class BootBroadcastReceiver extends BroadcastReceiver { if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { final Intent recheckIntent = new Intent(ACTION_PERIODIC_JOB_RECHECK); recheckIntent.setClass(context, BootBroadcastReceiver.class); - final long delayedTime = getRescheduleTimeForBootAction(context); + final long delayedTime = RESCHEDULE_FOR_BOOT_ACTION_DELAY_MILLIS; mHandler.postDelayed(() -> context.sendBroadcast(recheckIntent), delayedTime); // Refreshes the usage source from UsageStatsManager when booting. @@ -93,16 +90,6 @@ public final class BootBroadcastReceiver extends BroadcastReceiver { } } - private long getRescheduleTimeForBootAction(Context context) { - final boolean delayHourlyJobWhenBooting = - FeatureFactory.getFeatureFactory() - .getPowerUsageFeatureProvider() - .delayHourlyJobWhenBooting(); - return delayHourlyJobWhenBooting - ? RESCHEDULE_FOR_BOOT_ACTION_WITH_DELAY - : RESCHEDULE_FOR_BOOT_ACTION_WITHOUT_DELAY; - } - private static void refreshJobs(Context context) { PeriodicJobManager.getInstance(context).refreshJob(/* fromBoot= */ true); } From e6c3d18648c46a1b6025689fa1e5189ff4764e4a Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Tue, 2 Jan 2024 15:24:59 -0500 Subject: [PATCH 09/10] Update content description Test: manual: use TalkBack on conversations screen Fixes: 317316662 Change-Id: I7c9b0746bf610168e8a3c2bc0d2932082da221a8 --- res/values/strings.xml | 3 +++ .../notification/app/RecentConversationPreference.java | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 6f389ba7713..acf4321aebd 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8212,6 +8212,9 @@ Clear + + Clear %1$s + Priority and modified conversations will appear here diff --git a/src/com/android/settings/notification/app/RecentConversationPreference.java b/src/com/android/settings/notification/app/RecentConversationPreference.java index 167fdd61650..b60810af324 100644 --- a/src/com/android/settings/notification/app/RecentConversationPreference.java +++ b/src/com/android/settings/notification/app/RecentConversationPreference.java @@ -28,7 +28,7 @@ import com.google.common.annotations.VisibleForTesting; public class RecentConversationPreference extends TwoTargetPreference { private OnClearClickListener mOnClearClickListener; - + private final Context mContext; private View mClearView; public interface OnClearClickListener { @@ -37,6 +37,7 @@ public class RecentConversationPreference extends TwoTargetPreference { public RecentConversationPreference(Context context) { super(context); + mContext = context; } public void setOnClearClickListener( @@ -70,6 +71,9 @@ public class RecentConversationPreference extends TwoTargetPreference { final View widgetFrame = view.findViewById(android.R.id.widget_frame); widgetFrame.setVisibility(mOnClearClickListener != null ? View.VISIBLE : View.GONE); mClearView = view.findViewById(getClearId()); + mClearView.setContentDescription( + mContext.getString(R.string.clear_conversation, getTitle())); + mClearView.setOnClickListener(v -> { if (mOnClearClickListener != null) { mOnClearClickListener.onClear(); From 6f19be863084a31b010b6205dcbd4d3ce404c039 Mon Sep 17 00:00:00 2001 From: Andy Yu Date: Wed, 3 Jan 2024 10:04:30 -0800 Subject: [PATCH 10/10] Update game default frame description char limit To accomodate the highest number of characters for the localication request, updating the limit to 65. Bug: 306266471 Test: m; flash; boot; Change-Id: Ie3f6fd420ae3466b62da0d830a477039351a57f8 --- 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 22a1cbce4ab..b5df923b606 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2659,7 +2659,7 @@ Continue using apps on fold - + Disable default frame rate for games Disable limiting the maximum frame rate for games at %1$d Hz.