From 3ebfceafa6cd436c7ace62aab0427f9a81a73b01 Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Wed, 29 Mar 2023 10:48:23 -0700 Subject: [PATCH 01/12] settings(nfc): Remove reference to APM setting in NFC page APM mode no longer affects NFC stack as of Android Q. Bug: 275381319 Test: atest NfcEnablerTest NfcPreferenceControllerTest Test: Manual settings page verification Change-Id: I974a424332eb85e27d51816507183e1ca0def42c --- src/com/android/settings/nfc/NfcEnabler.java | 16 +----- .../settings/nfc/NfcPreferenceController.java | 13 ----- .../android/settings/nfc/NfcEnablerTest.java | 56 ++----------------- .../nfc/NfcPreferenceControllerTest.java | 39 ------------- 4 files changed, 7 insertions(+), 117 deletions(-) diff --git a/src/com/android/settings/nfc/NfcEnabler.java b/src/com/android/settings/nfc/NfcEnabler.java index 88482d59d8b..fe8762b8a90 100644 --- a/src/com/android/settings/nfc/NfcEnabler.java +++ b/src/com/android/settings/nfc/NfcEnabler.java @@ -18,9 +18,6 @@ package com.android.settings.nfc; import android.content.Context; import android.nfc.NfcAdapter; -import android.provider.Settings; - -import androidx.annotation.VisibleForTesting; import com.android.settingslib.widget.MainSwitchPreference; @@ -41,7 +38,7 @@ public class NfcEnabler extends BaseNfcEnabler { switch (newState) { case NfcAdapter.STATE_OFF: mPreference.updateStatus(false); - mPreference.setEnabled(isToggleable()); + mPreference.setEnabled(true); break; case NfcAdapter.STATE_ON: mPreference.updateStatus(true); @@ -57,15 +54,4 @@ public class NfcEnabler extends BaseNfcEnabler { break; } } - - @VisibleForTesting - boolean isToggleable() { - if (NfcPreferenceController.isToggleableInAirplaneMode(mContext) - || !NfcPreferenceController.shouldTurnOffNFCInAirplaneMode(mContext)) { - return true; - } - final int airplaneMode = Settings.Global.getInt( - mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0); - return airplaneMode != 1; - } } diff --git a/src/com/android/settings/nfc/NfcPreferenceController.java b/src/com/android/settings/nfc/NfcPreferenceController.java index 483fead807e..2ba00c6670a 100644 --- a/src/com/android/settings/nfc/NfcPreferenceController.java +++ b/src/com/android/settings/nfc/NfcPreferenceController.java @@ -21,7 +21,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.nfc.NfcAdapter; -import android.provider.Settings; import android.util.Log; import android.widget.Switch; @@ -128,18 +127,6 @@ public class NfcPreferenceController extends TogglePreferenceController } } - public static boolean shouldTurnOffNFCInAirplaneMode(Context context) { - final String airplaneModeRadios = Settings.Global.getString(context.getContentResolver(), - Settings.Global.AIRPLANE_MODE_RADIOS); - return airplaneModeRadios != null && airplaneModeRadios.contains(Settings.Global.RADIO_NFC); - } - - public static boolean isToggleableInAirplaneMode(Context context) { - final String toggleable = Settings.Global.getString(context.getContentResolver(), - Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS); - return toggleable != null && toggleable.contains(Settings.Global.RADIO_NFC); - } - /** * Listener for background changes to NFC. * diff --git a/tests/robotests/src/com/android/settings/nfc/NfcEnablerTest.java b/tests/robotests/src/com/android/settings/nfc/NfcEnablerTest.java index cc451906214..5299bbdb10c 100644 --- a/tests/robotests/src/com/android/settings/nfc/NfcEnablerTest.java +++ b/tests/robotests/src/com/android/settings/nfc/NfcEnablerTest.java @@ -16,15 +16,12 @@ package com.android.settings.nfc; -import static com.google.common.truth.Truth.assertThat; - import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import android.content.ContentResolver; import android.content.Context; import android.nfc.NfcAdapter; -import android.provider.Settings; import com.android.settingslib.widget.MainSwitchPreference; @@ -52,55 +49,14 @@ public class NfcEnablerTest { mNfcEnabler = spy(new NfcEnabler(mContext, mNfcPreference)); } - @Test - public void isToggleable_AirplaneModeOff_shouldReturnTrue() { - final ContentResolver contentResolver = mContext.getContentResolver(); - Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0); - Settings.Global.putString(contentResolver, - Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC); - Settings.Global.putString(contentResolver, - Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, Settings.Global.RADIO_NFC); - - assertThat(mNfcEnabler.isToggleable()).isTrue(); - } - - @Test - public void isToggleable_AirplaneModeOnNfcNotInAirplaneModeRadio_shouldReturnTrue() { - final ContentResolver contentResolver = mContext.getContentResolver(); - Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1); - Settings.Global.putString(contentResolver, Settings.Global.AIRPLANE_MODE_RADIOS, ""); - - assertThat(mNfcEnabler.isToggleable()).isTrue(); - } - - @Test - public void isToggleable_AirplaneModeOnNfcToggleable_shouldReturnTrue() { - final ContentResolver contentResolver = mContext.getContentResolver(); - Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1); - Settings.Global.putString(contentResolver, - Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC); - Settings.Global.putString(contentResolver, - Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, Settings.Global.RADIO_NFC); - - assertThat(mNfcEnabler.isToggleable()).isTrue(); - } - - @Test - public void isToggleable_AirplaneModeOnNfcNotToggleable_shouldReturnFalse() { - final ContentResolver contentResolver = mContext.getContentResolver(); - Settings.Global.putInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1); - Settings.Global.putString(contentResolver, - Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC); - Settings.Global.putString(contentResolver, - Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, ""); - - assertThat(mNfcEnabler.isToggleable()).isFalse(); - } - @Test public void handleNfcStateChanged_stateOff_shouldCheckIfPreferenceEnableState() { mNfcEnabler.handleNfcStateChanged(NfcAdapter.STATE_OFF); + verify(mNfcPreference).updateStatus(false); + verify(mNfcPreference).setEnabled(true); - verify(mNfcEnabler).isToggleable(); + mNfcEnabler.handleNfcStateChanged(NfcAdapter.STATE_ON); + verify(mNfcPreference).updateStatus(true); + verify(mNfcPreference, times(2)).setEnabled(true); } } diff --git a/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java index 3ca553bc3ba..4db51aab3b6 100644 --- a/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/nfc/NfcPreferenceControllerTest.java @@ -28,7 +28,6 @@ import android.content.Intent; import android.nfc.NfcAdapter; import android.nfc.NfcManager; import android.os.UserManager; -import android.provider.Settings; import androidx.preference.PreferenceScreen; @@ -188,44 +187,6 @@ public class NfcPreferenceControllerTest { assertThat(mNfcController.hasAsyncUpdate()).isTrue(); } - @Test - public void isToggleableInAirplaneMode_containNfc_shouldReturnTrue() { - Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, - Settings.Global.RADIO_NFC); - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_ON, 1); - - assertThat(NfcPreferenceController.isToggleableInAirplaneMode(mContext)).isTrue(); - } - - @Test - public void isToggleableInAirplaneMode_withoutNfc_shouldReturnFalse() { - Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, - "null"); - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_ON, 1); - - assertThat(NfcPreferenceController.isToggleableInAirplaneMode(mContext)).isFalse(); - } - - @Test - public void shouldTurnOffNFCInAirplaneMode_airplaneModeRadiosContainsNfc_shouldReturnTrue() { - Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_RADIOS, Settings.Global.RADIO_NFC); - - assertThat(NfcPreferenceController.shouldTurnOffNFCInAirplaneMode(mContext)).isTrue(); - } - - @Test - public void shouldTurnOffNFCInAirplaneMode_airplaneModeRadiosWithoutNfc_shouldReturnFalse() { - Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_RADIOS, ""); - - assertThat(NfcPreferenceController.shouldTurnOffNFCInAirplaneMode(mContext)).isFalse(); - } - @Test public void ncfSliceWorker_nfcBroadcast_noExtra_sliceDoesntUpdate() { final NfcSliceWorker worker = spy( From a86614db7494f5f2749fa1b4299afca6c4dafaac Mon Sep 17 00:00:00 2001 From: Darrell Shi Date: Thu, 30 Mar 2023 16:42:05 +0000 Subject: [PATCH 02/12] Hide timeout to user0 pref when switching blocked. Do not show the "timeout to admin user" settings preference when user switching is blocked on the current user, e.g. in demo mode. Test: atest SettingsRoboTests:TimeoutToDockUserPreferenceControllerTest Bug: 255277938 Change-Id: Ibd0944c69254000a453f477eb72a0ba5832e9793 --- ...TimeoutToDockUserPreferenceController.java | 10 ++++++++-- ...outToDockUserPreferenceControllerTest.java | 20 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/users/TimeoutToDockUserPreferenceController.java b/src/com/android/settings/users/TimeoutToDockUserPreferenceController.java index 57e8d402825..322a4abbeed 100644 --- a/src/com/android/settings/users/TimeoutToDockUserPreferenceController.java +++ b/src/com/android/settings/users/TimeoutToDockUserPreferenceController.java @@ -20,6 +20,7 @@ import static android.provider.Settings.Secure.TIMEOUT_TO_DOCK_USER; import android.content.Context; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import androidx.preference.PreferenceScreen; @@ -34,6 +35,8 @@ import java.util.Arrays; * automatically switch to the designated Dock User when the device is docked. */ public class TimeoutToDockUserPreferenceController extends BasePreferenceController { + private final UserManager mUserManager; + private final String[] mEntries; private final String[] mValues; @@ -41,6 +44,8 @@ public class TimeoutToDockUserPreferenceController extends BasePreferenceControl String preferenceKey) { super(context, preferenceKey); + mUserManager = context.getSystemService(UserManager.class); + mEntries = mContext.getResources().getStringArray( com.android.settings.R.array.switch_to_dock_user_when_docked_timeout_entries); mValues = mContext.getResources().getStringArray( @@ -62,9 +67,10 @@ public class TimeoutToDockUserPreferenceController extends BasePreferenceControl return UNSUPPORTED_ON_DEVICE; } - // Multi-user feature disabled by user. + // Multi-user feature disabled by user, or user switching blocked on the user. if (Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.USER_SWITCHER_ENABLED, 0) != 1) { + Settings.Global.USER_SWITCHER_ENABLED, 0) != 1 + || mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)) { return CONDITIONALLY_UNAVAILABLE; } diff --git a/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java index 2e7e2d797be..3d8e8930775 100644 --- a/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import androidx.test.core.app.ApplicationProvider; @@ -41,6 +42,8 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; @@ -51,6 +54,9 @@ public class TimeoutToDockUserPreferenceControllerTest { private Resources mResources; private TimeoutToDockUserPreferenceController mController; + @Mock + private UserManager mUserManager; + private static final String FAKE_PREFERENCE_KEY = "timeout_to_dock_user_preference"; private String[] mEntries; @@ -58,9 +64,12 @@ public class TimeoutToDockUserPreferenceControllerTest { @Before public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(ApplicationProvider.getApplicationContext()); mResources = spy(mContext.getResources()); doReturn(mResources).when(mContext).getResources(); + when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager); mEntries = mResources.getStringArray( R.array.switch_to_dock_user_when_docked_timeout_entries); @@ -78,6 +87,9 @@ public class TimeoutToDockUserPreferenceControllerTest { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.USER_SWITCHER_ENABLED, 1); + // User switching not blocked. + when(mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)).thenReturn(false); + // Set to user 1; ShadowUserHandle.setUid(1); } @@ -106,6 +118,14 @@ public class TimeoutToDockUserPreferenceControllerTest { BasePreferenceController.CONDITIONALLY_UNAVAILABLE); } + @Test + public void getAvailabilityStatus_userSwitchingBlocked_returnConditionallyUnavailable() { + when(mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)).thenReturn(true); + + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.CONDITIONALLY_UNAVAILABLE); + } + @Test public void getAvailabilityStatus_isCurrentlyMainUser_returnDisabledForUser() { when(Utils.canCurrentUserDream(mContext)).thenReturn(true); From a53a89d3701fbf3ba0de09516091a697bce1b34f Mon Sep 17 00:00:00 2001 From: Prabal Singh Date: Thu, 26 Jan 2023 16:29:17 +0000 Subject: [PATCH 03/12] Add "for work" in work profile biometrics string JUSTIFICATION: Around 8 users reporting this issue in b/271250248 RISK: This change is a cherrypick from a fix already made in Android U where no issue has been reported after a few months. Bug: 271250248 Test: manually tested Change-Id: Ib16b2d045f4695883276749fcce6ccce60688c09 (cherry picked from commit a9231555e425e2629113bc2a51ba14c04df1f106) --- res/values/strings.xml | 2 ++ res/xml/security_advanced_settings.xml | 4 ++-- res/xml/security_settings_combined_biometric_profile.xml | 6 +++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 35a4c9d0969..ccf4af7d432 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1199,6 +1199,8 @@ Face & Fingerprint Unlock + + Face & Fingerprint Unlock for work Tap to set up diff --git a/res/xml/security_advanced_settings.xml b/res/xml/security_advanced_settings.xml index b36fc73fdd2..8c57cac5557 100644 --- a/res/xml/security_advanced_settings.xml +++ b/res/xml/security_advanced_settings.xml @@ -137,12 +137,12 @@ diff --git a/res/xml/security_settings_combined_biometric_profile.xml b/res/xml/security_settings_combined_biometric_profile.xml index 1ba48ef0a80..22d19ac9264 100644 --- a/res/xml/security_settings_combined_biometric_profile.xml +++ b/res/xml/security_settings_combined_biometric_profile.xml @@ -17,7 +17,7 @@ + android:title="@string/security_settings_work_biometric_preference_title"> @@ -28,14 +28,14 @@ From 72534295d3cf82df8b94c702ce3cc2fc36df67b5 Mon Sep 17 00:00:00 2001 From: Ben Lin Date: Thu, 2 Mar 2023 11:37:39 -0800 Subject: [PATCH 04/12] Do isAvailable() checks when accessing AirplaneEnabler. This instance is only initialized if isAvailable() returns true in the first place, so we should check for it everytime we try to access it, or else we will result in a NPE. Bug: 271223463 Test: atest AirplaneModePreferenceControllerTest Change-Id: I43d35b91c86517201c8ec4f458f8c0328a9fb768 --- .../network/AirplaneModePreferenceController.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/com/android/settings/network/AirplaneModePreferenceController.java b/src/com/android/settings/network/AirplaneModePreferenceController.java index 720a33409c2..5b4ce1b00b9 100644 --- a/src/com/android/settings/network/AirplaneModePreferenceController.java +++ b/src/com/android/settings/network/AirplaneModePreferenceController.java @@ -79,7 +79,7 @@ public class AirplaneModePreferenceController extends TogglePreferenceController @Override public boolean handlePreferenceTreeClick(Preference preference) { - if (KEY_AIRPLANE_MODE.equals(preference.getKey()) + if (KEY_AIRPLANE_MODE.equals(preference.getKey()) && isAvailable() && mAirplaneModeEnabler.isInEcmMode()) { // In ECM mode launch ECM app dialog if (mFragment != null) { @@ -141,12 +141,14 @@ public class AirplaneModePreferenceController extends TogglePreferenceController @Override public void onDestroy() { - mAirplaneModeEnabler.close(); + if (isAvailable()) { + mAirplaneModeEnabler.close(); + } } public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == REQUEST_CODE_EXIT_ECM) { + if (requestCode == REQUEST_CODE_EXIT_ECM && isAvailable()) { final boolean isChoiceYes = (resultCode == Activity.RESULT_OK); // Set Airplane mode based on the return value and checkbox state mAirplaneModeEnabler.setAirplaneModeInECM(isChoiceYes, @@ -156,7 +158,7 @@ public class AirplaneModePreferenceController extends TogglePreferenceController @Override public boolean isChecked() { - return mAirplaneModeEnabler.isAirplaneModeOn(); + return isAvailable() && mAirplaneModeEnabler.isAirplaneModeOn(); } @Override @@ -164,7 +166,9 @@ public class AirplaneModePreferenceController extends TogglePreferenceController if (isChecked() == isChecked) { return false; } - mAirplaneModeEnabler.setAirplaneMode(isChecked); + if (isAvailable()) { + mAirplaneModeEnabler.setAirplaneMode(isChecked); + } return true; } From 2659074d0243a5a73f30bfeb92fba00b8a18f949 Mon Sep 17 00:00:00 2001 From: danielwbhuang Date: Fri, 31 Mar 2023 17:51:21 +0800 Subject: [PATCH 05/12] Handling multiple profiles for PK layout selection 1. Use new @hide IMM#getEnabledInputMethodSubtypeListAsUser() 2. Use ProfileSelectFragment Demo: https://screencast.googleplex.com/cast/NjMzNTA2NTA2NDczNDcyMHwxNTUyMjQ1ZS03YQ Bug: 275106096 Test: manual Change-Id: I51cfd16fc7162e2b24782017b9366b0aad36f915 --- .../ProfileFragmentBridge.java | 3 + ...ProfileSelectPhysicalKeyboardFragment.java | 72 +++++++++++++ ...wKeyboardLayoutEnabledLocalesFragment.java | 100 +++++++++++------- 3 files changed, 138 insertions(+), 37 deletions(-) create mode 100644 src/com/android/settings/dashboard/profileselector/ProfileSelectPhysicalKeyboardFragment.java diff --git a/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java b/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java index 2bcc02e2b04..1e5145acb6d 100644 --- a/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java +++ b/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java @@ -22,6 +22,7 @@ import com.android.settings.accounts.AccountDashboardFragment; import com.android.settings.applications.manageapplications.ManageApplications; import com.android.settings.deviceinfo.StorageDashboardFragment; import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment; +import com.android.settings.inputmethod.NewKeyboardLayoutEnabledLocalesFragment; import com.android.settings.location.LocationServices; import java.util.Map; @@ -49,5 +50,7 @@ public class ProfileFragmentBridge { ProfileSelectStorageFragment.class.getName()); FRAGMENT_MAP.put(AvailableVirtualKeyboardFragment.class.getName(), ProfileSelectKeyboardFragment.class.getName()); + FRAGMENT_MAP.put(NewKeyboardLayoutEnabledLocalesFragment.class.getName(), + ProfileSelectPhysicalKeyboardFragment.class.getName()); } } diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectPhysicalKeyboardFragment.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectPhysicalKeyboardFragment.java new file mode 100644 index 00000000000..3c1546e7d95 --- /dev/null +++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectPhysicalKeyboardFragment.java @@ -0,0 +1,72 @@ +/* + * 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.dashboard.profileselector; + +import android.hardware.input.InputDeviceIdentifier; +import android.os.Bundle; +import android.provider.Settings; + +import androidx.fragment.app.Fragment; + +import com.android.settings.R; +import com.android.settings.inputmethod.NewKeyboardLayoutEnabledLocalesFragment; + +/** + * When current user has work profile, this fragment used following fragments to represent the + * enabled IMEs keyboard layout settings page. + * + *

{@link NewKeyboardLayoutEnabledLocalesFragment} used to show both of personal/work user + * enabled IMEs and their physical keyboard layouts.

+ */ +public final class ProfileSelectPhysicalKeyboardFragment extends ProfileSelectFragment { + + private InputDeviceIdentifier mInputDeviceIdentifier; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + Bundle arguments = getArguments(); + mInputDeviceIdentifier = + arguments.getParcelable(Settings.EXTRA_INPUT_DEVICE_IDENTIFIER); + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.keyboard_settings_enabled_locales_list; + } + + @Override + public Fragment[] getFragments() { + final Bundle personalOnly = new Bundle(); + personalOnly.putInt(EXTRA_PROFILE, ProfileType.PERSONAL); + final Fragment personalFragment = new NewKeyboardLayoutEnabledLocalesFragment(); + personalOnly.putParcelable( + Settings.EXTRA_INPUT_DEVICE_IDENTIFIER, mInputDeviceIdentifier); + personalFragment.setArguments(personalOnly); + + final Bundle workOnly = new Bundle(); + workOnly.putInt(EXTRA_PROFILE, ProfileType.WORK); + final Fragment workFragment = new NewKeyboardLayoutEnabledLocalesFragment(); + workOnly.putParcelable(Settings.EXTRA_INPUT_DEVICE_IDENTIFIER, mInputDeviceIdentifier); + workFragment.setArguments(workOnly); + + return new Fragment[]{ + personalFragment, + workFragment + }; + } +} diff --git a/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java b/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java index 3344f4e99d0..054ce610944 100644 --- a/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java +++ b/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java @@ -23,6 +23,7 @@ import android.hardware.input.InputManager; import android.hardware.input.KeyboardLayout; import android.os.Bundle; import android.os.UserHandle; +import android.os.UserManager; import android.util.Log; import android.view.InputDevice; import android.view.inputmethod.InputMethodInfo; @@ -34,8 +35,10 @@ import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceScreen; import com.android.settings.R; +import com.android.settings.Utils; import com.android.settings.core.SubSettingLauncher; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.dashboard.profileselector.ProfileSelectFragment; import com.android.settings.inputmethod.NewKeyboardSettingsUtils.KeyboardInfo; import java.util.ArrayList; @@ -56,6 +59,39 @@ public class NewKeyboardLayoutEnabledLocalesFragment extends DashboardFragment private Context mContext; private ArrayList mKeyboardInfoList = new ArrayList<>(); + @Override + public void onAttach(Context context) { + super.onAttach(context); + + mContext = context; + final int profileType = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE); + final int currentUserId = UserHandle.myUserId(); + final int newUserId; + final UserManager userManager = mContext.getSystemService(UserManager.class); + + switch (profileType) { + case ProfileSelectFragment.ProfileType.WORK: { + // If the user is a managed profile user, use currentUserId directly. Or get the + // managed profile userId instead. + newUserId = userManager.isManagedProfile() + ? currentUserId : Utils.getManagedProfileId(userManager, currentUserId); + break; + } + case ProfileSelectFragment.ProfileType.PERSONAL: { + final UserHandle primaryUser = userManager.getPrimaryUser().getUserHandle(); + newUserId = primaryUser.getIdentifier(); + break; + } + default: + newUserId = currentUserId; + } + + mUserId = newUserId; + mIm = mContext.getSystemService(InputManager.class); + mImm = mContext.getSystemService(InputMethodManager.class); + mInputDeviceId = -1; + } + @Override public void onActivityCreated(final Bundle icicle) { super.onActivityCreated(icicle); @@ -74,13 +110,39 @@ public class NewKeyboardLayoutEnabledLocalesFragment extends DashboardFragment } final String title = inputDevice.getName(); getActivity().setTitle(title); + } + + @Override + public void onStart() { + super.onStart(); + mIm.registerInputDeviceListener(this, null); + InputDevice inputDevice = + NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier); + if (inputDevice == null) { + getActivity().finish(); + return; + } + mInputDeviceId = inputDevice.getId(); + } + + @Override + public void onResume() { + super.onResume(); updateCheckedState(); } + @Override + public void onStop() { + super.onStop(); + mIm.unregisterInputDeviceListener(this); + mInputDeviceId = -1; + } + private void updateCheckedState() { if (NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier) == null) { return; } + PreferenceScreen preferenceScreen = getPreferenceScreen(); preferenceScreen.removeAll(); List infoList = mImm.getEnabledInputMethodListAsUser(mUserId); @@ -95,7 +157,7 @@ public class NewKeyboardLayoutEnabledLocalesFragment extends DashboardFragment for (InputMethodInfo info : infoList) { mKeyboardInfoList.clear(); List subtypes = - mImm.getEnabledInputMethodSubtypeList(info, true); + mImm.getEnabledInputMethodSubtypeListAsUser(info.getId(), true, mUserId); for (InputMethodSubtype subtype : subtypes) { if (subtype.isSuitableForPhysicalKeyboardLayoutMapping()) { mapLanguageWithLayout(info, subtype); @@ -188,42 +250,6 @@ public class NewKeyboardLayoutEnabledLocalesFragment extends DashboardFragment } } - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mContext = getContext(); - mIm = mContext.getSystemService(InputManager.class); - mImm = mContext.getSystemService(InputMethodManager.class); - mInputDeviceId = -1; - mUserId = UserHandle.myUserId(); - } - - @Override - public void onStart() { - super.onStart(); - mIm.registerInputDeviceListener(this, null); - InputDevice inputDevice = - NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier); - if (inputDevice == null) { - getActivity().finish(); - return; - } - mInputDeviceId = inputDevice.getId(); - } - - @Override - public void onStop() { - super.onStop(); - mIm.unregisterInputDeviceListener(this); - mInputDeviceId = -1; - } - - @Override - public void onResume() { - super.onResume(); - updateCheckedState(); - } - @Override protected String getLogTag() { return TAG; From 37114bb2b0587536b965176fb0da91d76b00e995 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 5 Apr 2023 18:03:09 -0700 Subject: [PATCH 06/12] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I4b1f4ec2b736b8123de1ee218b367083163d737e --- res/values-bs/arrays.xml | 2 +- res/values-hy/arrays.xml | 2 +- res/values-ne/arrays.xml | 2 +- res/values-or/arrays.xml | 6 +++--- res/values-pa/arrays.xml | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/res/values-bs/arrays.xml b/res/values-bs/arrays.xml index 3bc21563ead..d66ed880c6e 100644 --- a/res/values-bs/arrays.xml +++ b/res/values-bs/arrays.xml @@ -331,7 +331,7 @@ "Jačina zvuka zvona" "Jačina zvuka medija" "Jačina zvuka alarma" - "Jačina zvuka za obavještenja" + "Jačina zvuka obavještenja" "Jačina zvuka za Bluetooth vezu" "Drži aktivnim" "Lokacija" diff --git a/res/values-hy/arrays.xml b/res/values-hy/arrays.xml index 6351b7b9f34..45a3a5a034c 100644 --- a/res/values-hy/arrays.xml +++ b/res/values-hy/arrays.xml @@ -331,7 +331,7 @@ "Զանգի բարձրություն" "Մուլտիմեդիայի ձայնը" "Զարթուցիչի ձայնի բարձրություն" - "Ծանուցման ձայնի բարձրություն" + "Ծանուցումների ձայնի ուժգնություն" "Bluetooth-ի ձայնի բարձրություն" "Արթուն պահել" "Տեղադրություն" diff --git a/res/values-ne/arrays.xml b/res/values-ne/arrays.xml index da316207db7..e609ae35837 100644 --- a/res/values-ne/arrays.xml +++ b/res/values-ne/arrays.xml @@ -264,7 +264,7 @@ "घन्टी मात्रा" "मिडियाको भोल्युम" "अलार्मको भोल्युम" - "सूचना मात्रा" + "सूचनाको भोल्युम" "ब्लुटुथ मात्रा" "जागा रहनुहोस्" "स्थानको निरीक्षण गर्नुहोस्" diff --git a/res/values-or/arrays.xml b/res/values-or/arrays.xml index 0322fbe0e00..b9b5c956db9 100644 --- a/res/values-or/arrays.xml +++ b/res/values-or/arrays.xml @@ -262,8 +262,8 @@ "ମାଷ୍ଟର୍ ଭଲ୍ୟୁମ୍" "ଭଏସ ଭଲ୍ୟୁମ" "ରିଙ୍ଗ ଭଲ୍ୟୁମ୍" - "ମିଡିଆ ଭଲ୍ୟୁମ୍‌" - "ଆଲାରାମର ଭଲ୍ୟୁମ୍‌" + "ମିଡିଆ ଭଲ୍ୟୁମ" + "ଆଲାରାମ ଭଲ୍ୟୁମ" "ବିଜ୍ଞପ୍ତି ଭଲ୍ୟୁମ୍" "ବ୍ଲୁଟୂଥ୍‍‌ ଭଲ୍ୟୁମ୍‌" "ଜାଗ୍ରତ ରଖନ୍ତୁ" @@ -329,7 +329,7 @@ "ମାଷ୍ଟର୍‌ ଭଲ୍ୟୁମ୍" "ଭଏସ୍ ଭଲ୍ୟୁମ୍" "ରିଙ୍ଗ ଭଲ୍ୟୁମ୍" - "ମିଡିଆ ଭଲ୍ୟୁମ୍‌" + "ମିଡିଆ ଭଲ୍ୟୁମ" "ଆଲାରାମ୍ ଭଲ୍ୟୁମ୍" "ବିଜ୍ଞପ୍ତି ଭଲ୍ୟୁମ୍‌" "ବ୍ଲୁଟୂଥ୍‍‌ ଭଲ୍ୟୁମ୍‌" diff --git a/res/values-pa/arrays.xml b/res/values-pa/arrays.xml index 2f68e89018e..d22a3dd3646 100644 --- a/res/values-pa/arrays.xml +++ b/res/values-pa/arrays.xml @@ -264,7 +264,7 @@ "ਰਿੰਗ ਦੀ ਅਵਾਜ਼" "ਮੀਡੀਆ ਦੀ ਅਵਾਜ਼" "ਅਲਾਰਮ ਦੀ ਅਵਾਜ਼" - "ਸੂਚਨਾ ਵੌਲਿਊਮ" + "ਸੂਚਨਾ ਦੀ ਅਵਾਜ਼" "bluetooth ਵੌਲਿਊਮ" "ਸਕਿਰਿਆ ਰੱਖੋ" "ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਦਾ ਨਿਰੀਖਣ ਕਰੋ" @@ -331,7 +331,7 @@ "ਰਿੰਗ ਦੀ ਅਵਾਜ਼" "ਮੀਡੀਆ ਦੀ ਅਵਾਜ਼" "ਅਲਾਰਮ ਦੀ ਅਵਾਜ਼" - "ਸੂਚਨਾ ਵੌਲਿਊਮ" + "ਸੂਚਨਾ ਦੀ ਅਵਾਜ਼" "Bluetooth ਵੌਲਿਊਮ" "ਸਕਿਰਿਆ ਰੱਖੋ" "ਟਿਕਾਣਾ" From a5ef604de7c58ba59977adac9edf3741ddf24bda Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 5 Apr 2023 18:05:09 -0700 Subject: [PATCH 07/12] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Ibda77f75cc4865437dd8dc5f52260b62b9d6f00b --- res/values-ar/strings.xml | 4 ++-- res/values-as/strings.xml | 2 +- res/values-b+sr+Latn/strings.xml | 2 +- res/values-bs/strings.xml | 2 +- res/values-ca/strings.xml | 2 +- res/values-de/strings.xml | 2 +- res/values-es/strings.xml | 2 +- res/values-fa/strings.xml | 12 ++++++------ res/values-fr/strings.xml | 12 ++++++------ res/values-hi/strings.xml | 6 +++--- res/values-hy/strings.xml | 4 ++-- res/values-iw/strings.xml | 2 +- res/values-ja/strings.xml | 10 +++++----- res/values-ka/strings.xml | 2 +- res/values-kk/strings.xml | 2 +- res/values-ko/strings.xml | 2 +- res/values-ky/strings.xml | 2 +- res/values-mk/strings.xml | 2 +- res/values-nb/strings.xml | 24 ++++++++++++------------ res/values-ne/strings.xml | 2 +- res/values-nl/strings.xml | 2 +- res/values-or/strings.xml | 4 ++-- res/values-pa/strings.xml | 6 +++--- res/values-pl/strings.xml | 2 +- res/values-pt-rPT/strings.xml | 4 ++-- res/values-ru/strings.xml | 4 ++-- res/values-si/strings.xml | 4 ++-- res/values-sk/strings.xml | 2 +- res/values-sr/strings.xml | 2 +- res/values-sv/strings.xml | 2 +- res/values-ta/strings.xml | 2 +- res/values-th/strings.xml | 8 ++++---- res/values-uz/strings.xml | 2 +- res/values-zh-rCN/strings.xml | 6 +++--- res/values-zh-rHK/strings.xml | 2 +- res/values-zh-rTW/strings.xml | 6 +++--- 36 files changed, 78 insertions(+), 78 deletions(-) diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index f0e34233e65..880e6655f1e 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -1573,7 +1573,7 @@ "لن يتم تفعيل الميزة تلقائيًا على الإطلاق." "سيتم التفعيل تلقائيًا في %1$s" "سيتم التفعيل تلقائيًا عند غروب الشمس." - "لن يتم الإيقاف تلقائيًا مطلقًا" + "لن يتم الإيقاف بشكل تلقائي مطلقًا" "سيتم الإيقاف تلقائيًا عند الساعة %1$s" "سيتم الإيقاف تلقائيًا عند شروق الشمس" "التفعيل الآن" @@ -1600,7 +1600,7 @@ "سيتم التفعيل تلقائيًا عند غروب الشمس." "سيتم تفعيل الإعداد تلقائيًا في %1$s." "سيتم تفعيله تلقائيًا عند حلول وقت النوم." - "لن يتم مطلقًا إيقاف الوضع تلقائيًا." + "لن يتم الإيقاف بشكل تلقائي مطلقًا" "سيتم إيقاف الوضع تلقائيًا عند شروق الشمس" "سيتم إيقاف الإعداد تلقائيًا في %1$s." "سيتم إيقافه تلقائيًا بعد انتهاء وقت النوم." diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml index c6ea2604ae2..46b18fea16c 100644 --- a/res/values-as/strings.xml +++ b/res/values-as/strings.xml @@ -3848,7 +3848,7 @@ "এলাৰ্মৰ ভলিউম" "ৰিং আৰু জাননীৰ ভলিউম" "ৰিঙৰ ভলিউম" - "জাননী-ধ্বনিৰ ভলিউম" + "জাননীৰ ভলিউম" "ৰিং মিউট কৰি থোৱাৰ বাবে উপলব্ধ নহয়" "ফ\'নৰ ৰিংট\'ন" "ডিফ\'ল্ট জাননী ধ্বনি" diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index f463d1e8295..0a9c41937a2 100644 --- a/res/values-b+sr+Latn/strings.xml +++ b/res/values-b+sr+Latn/strings.xml @@ -5749,7 +5749,7 @@ "Dozvolite aplikacijama da šalju sadržaj Android sistemu" "Snimi dinamički deo memorije sistema" "Restartuj uz MTE" - "Sistem će se restartovati i omogućiti eksperimentisanje sa Dodatkom za označavanje memorije (MTE). MTE može negativno da utiče na performanse i stabilnost sistema. Resetovanje će se obaviti pri sledećem restartovanju." + "Sistem će se restartovati i omogućiti eksperimentisanje sa Dodatkom za označavanje memorije (MTE). MTE može negativno da utiče na performanse i stabilnost sistema. Biće poništeno pri sledećem restartovanju." "Snimanje dinamičkog dela memorije sistema" "Snimanje dinamičkog dela memorije sistema nije uspelo" "Automatski snimaj dinamički deo memorije sistema" diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index 15b8429d74a..63e56b307f5 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -3891,7 +3891,7 @@ "Jačina zvuka alarma" "Jačina zvuka zvona i obavještenja" "Jačina zvuka zvona" - "Jačina zvuka za obavještenja" + "Jačina zvuka obavještenja" "Nedostupno zbog isključenog zvona" "Melodija zvona telefona" "Zadani zvuk obavještenja" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 47235cd9912..39899cb1da0 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -3849,7 +3849,7 @@ "Volum de sons i notificacions" "Volum del to" "Volum de notificació" - "No està disponible perquè el so s\'ha silenciat" + "No disponible perquè el so s\'ha silenciat" "So de trucada del telèfon" "So de notificació predeterminat" "So de l\'aplicació" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index dd6243fcf66..48669e740e3 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -2509,7 +2509,7 @@ "Lautstärketasten gedrückt halten" "Halte beide Lautstärketasten gedrückt" "Dreimal auf den Bildschirm tippen" - "Dreimal auf den Bildschirm tippen" + "dreimal auf den Bildschirm tippen" "Tippe {0,number,integer}-mal schnell auf das Display. Durch diesen Kurzbefehl kann dein Gerät langsamer werden." "Erweitert" "Die Schaltfläche für die Bedienungshilfen ist auf %1$s gestellt. Um die Vergrößerung nutzen zu können, tippe & halte die Schaltfläche für die Bedienungshilfen gedrückt und wähle anschließend \"Vergrößerung\" aus." diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 9707a7bb998..96597f69ccf 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -1407,7 +1407,7 @@ "Cuentas de perfil personal" "Cuenta de trabajo (%s)" "Cuenta personal (%s)" - "Búsqueda" + "Buscar" "Pantalla" "Girar pantalla automáticamente" "Desactivado" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 9c2d1256de1..df5081ec64c 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -5236,7 +5236,7 @@ "ارائه‌دهنده اعتبارتان می‌تواند تنظیمات این دستگاه را تغییر دهد و در آن نرم‌افزار نصب کند.\n\nاگر پرداختی را انجام ندهید، دستگاهتان قفل خواهد شد.\n\nبرای دریافت اطلاعات بیشتر، با ارائه‌دهنده اعتبارتان تماس بگیرید." "اگر دستگاهتان را اعتباری دریافت کرده‌اید نمی‌توانید:" "‏نصب برنامه از منابع خارج از «فروشگاه Play»" - "راه‌اندازی مجدد دستگاه در حالت ایمن" + "بازراه‌اندازی دستگاه در حالت ایمن" "افزودن چندین کاربر به دستگاه" "تغییر تاریخ، ساعت، و منطقه زمانی" "استفاده از گزینه‌های توسعه‌دهنده" @@ -5661,8 +5661,8 @@ "محتوای برنامه" "‏به برنامه‌ها برای پخش محتوا به سیستم Android اجازه داده می‌شود" "ثبت رونوشت حافظه آزاد سیستم" - "‏راه‌اندازی مجدد با MTE" - "‏سیستم راه‌اندازی مجدد می‌شود و پس‌از آن، آزمایش کردن با «افزونه‌های نشان‌گذاری حافظه» (MTE) را مجاز می‌کند. MTE می‌تواند بر عملکرد و پایداری سیستم اثر منفی بگذارد. در راه‌اندازی مجدد بعدی، بازنشانی می‌شود." + "‏بازراه‌اندازی با MTE" + "‏سیستم بازراه‌اندازی می‌شود و آزمایش کردن با «افزونه‌های نشان‌گذاری حافظه» (MTE) را مجاز می‌کند. MTE می‌تواند بر عملکرد و پایداری سیستم اثر منفی بگذارد. در بازراه‌اندازی بعدی، بازنشانی می‌شود." "ثبت رونوشت حافطه آزاد سیستم" "رونوشت حافظه آزاد سیستم ثبت نشد" "ثبت خودکار رونوشت‌های حافظه آزاد سیستم" @@ -5935,9 +5935,9 @@ "سفارشی کردن" "سفارشی کردن %1$s" "برای فعال کردن پشتیبانی قالب آزاد، راه‌اندازی مجدد الزامی است." - "برای رفتن به حالت میز کار در نمایشگرهای ثانویه، راه‌اندازی مجدد الزامی است." - "اکنون راه‌اندازی مجدد شود" - "بعداً راه‌اندازی مجدد شود" + "برای رفتن به حالت میز کار در نمایشگرهای ثانویه، بازراه‌اندازی الزامی است." + "اکنون بازراه‌اندازی شود" + "بعداً بازراه‌اندازی شود" "صدای فضایی" "صدای رسانه‌های سازگار فراگیرتر می‌شود" "ردیابی سر" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 836bd6e0323..73d489821d7 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -2163,7 +2163,7 @@ "Votre appareil et vos données personnelles sont très vulnérables aux attaques provenant d\'applications inconnues. En installant des applications provenant de cette source, vous acceptez d\'être le seul responsable de tout dommage causé à votre appareil ou de toute perte de données pouvant découler de l\'utilisation de telles applications." "Paramètres avancés" "Activer d\'autres paramètres" - "Infos sur les applis" + "Infos sur l\'appli" "Stockage" "Ouvrir par défaut" "Paramètres par défaut" @@ -3849,7 +3849,7 @@ "Volume des sonneries/notifications" "Volume de la sonnerie" "Volume des notifications" - "Indisponible, car le son de la sonnerie est coupé" + "Indisponible car la sonnerie est coupée" "Sonnerie du téléphone" "Son par défaut des notifications" "Son fourni par l\'application" @@ -4568,8 +4568,8 @@ %d appli inutilisée %d applis inutilisées - "Paramètres des applis inutilisées" - "Suspendre activité appli si inutilisée" + "Paramètres de l\'appli si inutilisée" + "Suspendre l\'activité si inutilisée" "Retirer les autorisations, supprimer les fichiers temporaires et arrêter les notifications" "Toutes les applis" "Applications installées" @@ -4968,8 +4968,8 @@ "Activer maintenant" "Désactiver maintenant" "Optimisation de la batterie non utilisée" - "Utilisation de la batterie par l\'appli" - "Définir utilisation de la batterie pour les applis" + "Utilisation de la batterie par les applis" + "Définir l\'utilisation de la batterie pour les applis" "Sans restriction" "Optimisée" "Restreinte" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index d2b1f447ab5..c4aa1d97e3a 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -1104,8 +1104,8 @@ "आपको वाई-फ़ाई नेटवर्क बदलने की अनुमति नहीं है." "ज़्यादा" "स्‍वचालित सेटअप (WPS)" - "वाई-फ़ाई स्कैन करना चालू करें?" - "पहले आपको वाई-फ़ाई स्कैन करना होगा, इसके बाद आपको अपने-आप वाई-फ़ाई चालू होने की सुविधा मिलेगी." + "वाई-फ़ाई स्कैनिंग चालू करनी है?" + "वाई-फ़ाई अपने-आप चालू होने की सुविधा पाने के लिए, पहले आपको वाई-फ़ाई स्कैनिंग चालू करनी होगी." "वाई-फ़ाई स्कैन करने की सुविधा से ऐप्लिकेशन और सेवाएं, किसी भी समय वाई-फ़ाई नेटवर्क स्कैन कर सकती हैं. वाई-फ़ाई की सुविधा बंद होने पर भी ऐसा किया जा सकता है. उदाहरण के लिए, इसका इस्तेमाल जगह की जानकारी से जुड़ी सुविधाओं और सेवाओं को बेहतर बनाने के लिए किया जा सकता है." "चालू करें" "वाई-फ़ाई स्कैन करना चालू हो गया है" @@ -3170,7 +3170,7 @@ "सुरक्षित नहीं है" "%d वीपीएन सुरक्षित नहीं है" "%d वीपीएन सुरक्षित नहीं हैं" - "Adaptive connectivity" + "अडैप्टिव कनेक्टिविटी" "अडैप्टिव कनेक्टिविटी का विकल्प चुनने पर, आपका इंटरनेट कनेक्शन अपने-आप मैनेज हो जाएगा. इससे बैटरी लाइफ़ बढ़ती है और डिवाइस की परफ़ॉर्मेंस बेहतर होती है" "चालू" "बंद करें" diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml index 5af62cc25bc..abe7cbdc08c 100644 --- a/res/values-hy/strings.xml +++ b/res/values-hy/strings.xml @@ -3848,8 +3848,8 @@ "Զարթուցիչ" "Զանգեր և ծանուցումներ" "Զանգի ուժգնություն" - "Ծանուցման ձայնի բարձրություն" - "Անհասանելի է, քանի որ զանգի ձայնն անջատված է" + "Ծանուցումներ" + "Անհասանելի է․ զանգի ձայնն անջատված է" "Հեռախոսի զանգերանգ" "Ծանուցման կանխադրված ձայնը" "Հավելվածի տրամադրված ձայնը" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index d3a32df38fb..da2df9134e5 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -3892,7 +3892,7 @@ "עוצמת קול של צלצול והתראות" "עוצמת הצלצול" "עוצמת קול של התראות" - "לא זמין כי הצלצול מושתק" + "האפשרות לא זמינה כי הצלצול מושתק" "הרינגטון של הטלפון" "צליל ברירת המחדל להתראות" "צליל שסיפקה האפליקציה" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index f956bf694fe..0f0f7ce4ad9 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -4506,15 +4506,15 @@ "リンクを追加" "リンクが安全かどうかをアプリが確認し、安全な場合はアプリ内で自動的に開きます。" - 選択済みのリンク: %d - 選択済みのリンク: %d + 確認済みのリンク: %d + 確認済みのリンク: %d 以前の選択内容により、以下のリンクはこのアプリで自動的に開きます。 以前の選択内容により、以下のリンクはこのアプリで自動的に開きます。 "OK" - "選択済みのリンク一覧を表示" + "確認済みのリンク一覧を表示" "他のサポートされているリンクを確認しています…" "キャンセル" @@ -4599,7 +4599,7 @@ "対応リンクを開くことをアプリに許可します" "その都度確認" - "リンクを開くことをアプリに許可しない" + "リンクを開くことをアプリに許可しません" %d 個のリンクをアプリで処理します %d 個のリンクをアプリで処理します @@ -4845,7 +4845,7 @@ "表示" "非表示" "アクセス ポイント有効" - "機内モードは ON です" + "機内モードが ON です" "ネットワークは利用できません" "サイレント モード ON" "スマートフォンをミュートに設定" diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml index 5a98597f6d5..cf07124c4ce 100644 --- a/res/values-ka/strings.xml +++ b/res/values-ka/strings.xml @@ -3849,7 +3849,7 @@ "ზარების/შეტყობინებების ხმა" "ზარის ხმა" "შეტყობინების ხმა" - "არ არის ხელმისაწვდომი, რადგან დარეკვა დადუმებულია" + "მიუწვდომელია, რადგან დარეკვა დადუმებულია" "ტელეფონის ზარი" "შეტყობინებების ნაგულისხმევი ხმა" "აპის მიერ უზრუნველყოფილი ხმა" diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml index 252f61cbf97..927756832dd 100644 --- a/res/values-kk/strings.xml +++ b/res/values-kk/strings.xml @@ -3849,7 +3849,7 @@ "Қоңырау мен хабарландыру дыбысы" "Қоңыраудың дыбыс деңгейі" "Хабарландырудың дыбыс деңгейі" - "Шылдырлату өшірулі болғандықтан, қолжетімді емес." + "Қоңырау дыбысы өшіп тұрғанда қолжетімсіз." "Телефон рингтоны" "Әдепкі хабарландыру дыбысы" "Қолданбамен берілген дыбыс" diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index bd5a57731e4..e612704eeb0 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -5019,7 +5019,7 @@ "더보기" "ShortcutManager 대역 제한 기능 재설정" "ShortcutManager 대역 제한 기능이 재설정되었습니다." - "잠금 화면에서 정보 제어" + "잠금 화면에 표시되는 정보 설정" "알림 내용 표시 또는 숨기기" "전체" "도움말 및 지원" diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml index 06c96071dd0..3cd476ddf4d 100644 --- a/res/values-ky/strings.xml +++ b/res/values-ky/strings.xml @@ -3848,7 +3848,7 @@ "Ойготкучтун катуулугу" "Шыңгырлар менен билдирмелердин үнүнүн катуулугу" "Шыңгырдын үнүнүн катуулугу" - "Эскертме үнүнүн катуулугу" + "Билдирменин үнүнүн катуулугу" "Үнсүз режимде жеткиликсиз" "Телефондун шыңгыры" "Билдирменин демейки үнү" diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index 8493012a89c..98757d5207d 100644 --- a/res/values-mk/strings.xml +++ b/res/values-mk/strings.xml @@ -3849,7 +3849,7 @@ "Ѕвонење и известувања" "Јачина на звук на ѕвонењето" "Јачина на звук на известување" - "Недостапно бидејќи тонот на ѕвонење е исклучен" + "Недостапно бидејќи звукот е исклучен" "Мелодија на телефон" "Стандарден звук за известување" "Звук што го дава апликацијата" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index cdb47f21920..502622d5bd4 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -677,7 +677,7 @@ "Konfigurering av fingeravtrykk er tidsavbrutt" "Prøv på nytt nå, eller konfigurer fingeravtrykket senere i innstillingene" "Registrering av fingeravtrykket mislyktes. Prøv på nytt, eller bruk en annen finger." - "Legg til ett til" + "Legg til et til" "Neste" "I tillegg til å låse opp telefonen din, kan du bruke fingeravtrykket ditt til å godkjenne kjøp og apptilgang. ""Finn ut mer" "Alternativet for skjermlås er slått av. For å finne ut mer, kontakt administratoren for organisasjonen din." @@ -1045,7 +1045,7 @@ "Android Beam" "Når denne funksjonen er slått på, kan du beame appinnhold til en annen enhet som støtter nærfeltskommunikasjon (NFC). Du gjør dette ved å holde enhetene tett sammen. Du kan for eksempel beame nettsider, YouTube-videoer, kontakter med mer.\n\nDu holder bare enhetene mot hverandre (vanligvis rygg mot rygg) og trykker på skjermen. Appen avgjør hva som beames." "Wifi" - "Slå på Wifi" + "Slå på wifi" "Wifi" "Bruk Wifi" "Innstillinger for wifi" @@ -1059,8 +1059,8 @@ "I flymodus" "Varsle om offentlige nettverk" "Varsle når et offentlig nettverk av høy kvalitet er tilgjengelig" - "Slå på Wi‑Fi automatisk" - "Wi‑Fi slås på igjen i nærheten av lagrede nettverk av høy kvalitet, for eksempel hjemmenettverket ditt" + "Slå på wifi automatisk" + "Wifi slås på igjen i nærheten av lagrede nettverk av høy kvalitet, for eksempel hjemmenettverket ditt" "Utilgjengelig fordi Posisjon er slått av. Slå på ""Posisjon""." "Utilgjengelig fordi Wi‑Fi-søking er slått av" "For å bruke funksjonen, velg en leverandør av nettverksvurdering" @@ -1087,8 +1087,8 @@ "Bruk mobildata når Wifi ikke har Internett-tilgang. Avgifter for databruk kan påløpe." "Legg til nettverk" "Wi‑Fi-innstillinger" - "Wi‑Fi slås på igjen automatisk" - "Wi‑Fi slås ikke på igjen automatisk" + "Wifi slås på igjen automatisk" + "Wifi slås ikke på igjen automatisk" "Wifi-nettverk" "Flere alternativer" "Wi-Fi Direct" @@ -1099,16 +1099,16 @@ "Husk nettverket" "Glem nettverket" "Endre nettverket" - "Slå på Wifi for å se tilgjengelige nettverk." + "Slå på wifi for å se tilgjengelige nettverk." "Søker etter nettverk …" "Du har ikke tillatelse til å endre Wifi-nettverk." "Se mer" "Automatisk konfig. (WPS)" - "Vil du slå på Wi‑Fi-skanning?" + "Vil du slå på wifi-skanning?" "For å slå på Wifi automatisk må du først slå på wifi-skanning." "Med wifi-skanning kan apper og tjenester søke etter enheter i nærheten når som helst, selv når Wifi er slått av. Dette kan for eksempel brukes til å forbedre posisjonsbaserte funksjoner og tjenester." "Slå på" - "Wi‑Fi-skanning er slått på" + "Wifi-skanning er slått på" "Avanserte alternativer" "Rullegardinliste med avanserte alternativer" "vis" @@ -3006,7 +3006,7 @@ "Batteri brukt av skjermen og baklyset" "Reduser lysstyrken og/eller tidsavbruddet for aktivering av skjermsparer" "Batteri brukt av Wifi" - "Slå av Wifi når du ikke bruker det, eller det ikke er tilgjengelig" + "Slå av wifi når du ikke bruker det, eller det ikke er tilgjengelig" "Batteri brukt av Bluetooth" "Slå av Bluetooth når den ikke er i bruk" "Prøv å koble til en annen Bluetooth-enhet" @@ -5769,8 +5769,8 @@ "Uten SIM-kort" "Nettverksinnstillinger" "nettverkstilkobling, internett, trådløs, data, wifi, wi-fi, wi fi, mobilnett, mobil, mobiloperatør, 4g, 3g, 2g, lte" - "Slå på Wifi" - "Slå av Wifi" + "Slå på wifi" + "Slå av wifi" "Vil du tilbakestille internett?" "Dette avslutter anropet" "Dette avslutter anropet" diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index d7227d69f0c..78f5574c48f 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml @@ -3848,7 +3848,7 @@ "अलार्मको भोल्युम" "घन्टी तथा सूचनाको भोल्युम" "घण्टीको भोल्युम" - "ध्वनी सूचना" + "सूचनाको भोल्युम" "सूचना आउँदा घन्टी बज्ने सुविधा म्युट गरिएकाले यो सुविधा उपलब्ध छैन" "फोनको रिङटोन" "सूचना आउँदा बज्ने डिफल्ट साउन्ड" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 95588143729..13d94788d67 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -2059,7 +2059,7 @@ "Geef je werkwachtwoord op" "Bevestig je patroon" "Geef je werkpatroon op" - "Geef je pincode opnieuw op" + "Voer je pincode opnieuw in" "Je werkpincode opnieuw invullen" "Geef je werkpincode op" "Wachtwoorden komen niet overeen" diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml index ff9b7196f9f..0be535f2d0e 100644 --- a/res/values-or/strings.xml +++ b/res/values-or/strings.xml @@ -3845,10 +3845,10 @@ "ମିଡିଆ ଭଲ୍ୟୁମ୍‌" "କାଷ୍ଟ ଭଲ୍ୟୁମ୍‌" "କଲ୍‍ ଭଲ୍ୟୁମ୍‍" - "ଆଲାର୍ମର ଭଲ୍ୟୁମ୍‌" + "ଆଲାରାମ ଭଲ୍ୟୁମ" "ରିଙ୍ଗ ଏବଂ ବିଜ୍ଞପ୍ତି ଭଲ୍ୟୁମ୍" "ରିଂ ଭଲ୍ୟୁମ" - "ସୂଚନା ଭଲ୍ୟୁମ" + "ବିଜ୍ଞପ୍ତି ଭଲ୍ୟୁମ" "ରିଂକୁ ମ୍ୟୁଟ କରାଯାଇଥିବା ଯୋଗୁଁ ଉପଲବ୍ଧ ନାହିଁ" "ଫୋନ ରିଂଟୋନ" "ଡିଫଲ୍ଟ ବିଜ୍ଞପ୍ତି ସାଉଣ୍ଡ" diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml index f17568faf23..9ffbfcf5f2a 100644 --- a/res/values-pa/strings.xml +++ b/res/values-pa/strings.xml @@ -3848,8 +3848,8 @@ "ਅਲਾਰਮ ਦੀ ਅਵਾਜ਼" "ਘੰਟੀ ਅਤੇ ਸੂਚਨਾ ਦੀ ਅਵਾਜ਼" "ਘੰਟੀ ਦੀ ਅਵਾਜ਼" - "ਸੂਚਨਾ ਵੌਲਿਊਮ" - "ਉਪਲਬਧ ਨਹੀਂ ਹੈ ਕਿਉਂਕਿ ਘੰਟੀ ਮਿਊਟ ਕੀਤੀ ਹੋਈ ਹੈ" + "ਸੂਚਨਾ ਦੀ ਅਵਾਜ਼" + "ਘੰਟੀ ਮਿਊਟ ਹੋਣ ਕਰਕੇ ਉਪਲਬਧ ਨਹੀਂ ਹੈ" "ਫ਼ੋਨ ਰਿੰਗਟੋਨ" "ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਸੂਚਨਾ ਧੁਨੀ" "ਐਪ ਵੱਲੋਂ ਮੁਹੱਈਆ ਕਰਵਾਈ ਧੁਨੀ" @@ -4161,7 +4161,7 @@ "ਕਦੇ ਵੀ ਨਹੀਂ" "ਡੀਵਾਈਸ ਅਤੇ ਐਪ ਸੂਚਨਾਵਾਂ" - "ਕੰਟਰੋਲ ਕਰੋ ਕਿ ਕਿਹੜੀਆਂ ਐਪਾਂ ਅਤੇ ਡੀਵਾਈਸਾਂ ਵੱਲੋਂ ਸੂਚਨਾਵਾਂ ਪੜ੍ਹੀਆਂ ਜਾ ਸਕਦੀਆਂ ਹਨ" + "ਕੰਟਰੋਲ ਕਰੋ ਕਿ ਕਿਹੜੀਆਂ ਐਪਾਂ ਅਤੇ ਡੀਵਾਈਸ ਸੂਚਨਾਵਾਂ ਪੜ੍ਹ ਸਕਦੇ ਹਨ" "ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਸੂਚਨਾਵਾਂ \'ਤੇ ਪਹੁੰਚ ਨੂੰ ਬਲਾਕ ਕੀਤਾ ਗਿਆ ਹੈ" "ਐਪ ਸੂਚਨਾਵਾਂ ਨਹੀਂ ਪੜ੍ਹ ਸਕਦੇ" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index b77c6de987b..7db89a38d4f 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -3935,7 +3935,7 @@ "Głośność dzwonka i powiadomień" "Głośność dzwonka" "Głośność powiadomień" - "Niedostępne, ponieważ dzwonek jest wyciszony" + "Niedostępne, bo dzwonek jest wyciszony" "Dzwonek telefonu" "Domyślny dźwięk powiadomienia" "Dźwięk z aplikacji" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index efce94497a0..7f0bfde6fe9 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -3849,7 +3849,7 @@ "Volume de notificações e toque" "Volume do toque" "Volume de notificações" - "Indisponível porque o som do toque está desativado" + "Indisponível com som do toque desativado" "Toque do telemóvel" "Som de notificação padrão" "Som fornecido pela aplicação" @@ -5662,7 +5662,7 @@ "Permitir que as apps enviem conteúdo para o sistema Android" "Fazer a captura da área dinâmica para dados do sistema" "Reiniciar com MTE" - "O sistema vai reiniciar e permitir experimentar com a Memory Tagging Extension (MTE). A MTE pode afetar negativamente o desempenho e a estabilidade do sistema. Será reposto no próximo reinício subsequente." + "O sistema vai reiniciar e permitir experimentar com a Memory Tagging Extension (MTE). A MTE pode afetar negativamente o desempenho e a estabilidade do sistema. A opção será reposta no próximo reinício." "A fazer a captura da área dinâmica para dados do sistema…" "Não foi possível fazer a captura da área dinâmica para dados do sistema." "Fazer automaticamente capturas da área dinâmica para dados do sistema" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 4c399cbd795..0f1dca4419a 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -3930,8 +3930,8 @@ "Громкость сигналов при входящих вызовах и уведомлениях: 80 %." "Музыка, видео, игры" "Громкость трансляции" - "Разговор" - "Будильник" + "Громкость разговора" + "Громкость будильника" "Вызовы и уведомления" "Громкость звонка" "Громкость уведомлений" diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml index 0b4cbb813ca..42517715b20 100644 --- a/res/values-si/strings.xml +++ b/res/values-si/strings.xml @@ -3844,11 +3844,11 @@ "නාද සහ දැනුම්දීම හඬ 80%" "මාධ්‍ය ශබ්දය" "විකාශන හඬ" - "ඇමතුම් ශබ්දය ත්‍රීවතාවය" + "ඇමතුම් ශබ්දය" "සීනුවේ ශබ්දය" "නාද සහ දැනුම්දීම හඬ" "නාද ශබ්දය" - "දැනුම්දීමේ ශබ්ද ත්‍රීවතාව" + "දැනුම්දීමේ ශබ්දය" "නාදය නිහඬ කර ඇති නිසා ලබා ගත නොහැක" "දුරකථන රිගින්ටෝනය" "පෙරනිමි දැනුම්දීම් හඬ" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index e3ed2ba6054..915cd48a148 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -3930,7 +3930,7 @@ "Hlasitosť zvonenia a upozornení je na 80 %" "Hlasitosť médií" "Hlasitosť prenášania" - "Hlasitosť hovoru" + "Hlasitosť hovorov" "Hlasitosť budíkov" "Hlasitosť zvonení a upozornení" "Hlasitosť zvonenia" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 1e3879de138..c68cba91a41 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -5749,7 +5749,7 @@ "Дозволите апликацијама да шаљу садржај Android систему" "Сними динамички део меморије система" "Рестартуј уз MTE" - "Систем ће се рестартовати и омогућити експериментисање са Додатком за означавање меморије (MTE). MTE може негативно да утиче на перформансе и стабилност система. Ресетовање ће се обавити при следећем рестартовању." + "Систем ће се рестартовати и омогућити експериментисање са Додатком за означавање меморије (MTE). MTE може негативно да утиче на перформансе и стабилност система. Биће поништено при следећем рестартовању." "Снимање динамичког дела меморије система" "Снимање динамичког дела меморије система није успело" "Аутоматски снимај динамички део меморије система" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index cdf7bc6b953..b6e3c1d6618 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -3849,7 +3849,7 @@ "Ring- och aviseringsvolym" "Ringvolym" "Aviseringsvolym" - "Ej tillgänglig eftersom ljudet är avstängt för ringsignalen" + "Ej tillgänglig – ringsignalen är avstängd" "Ringsignal" "Standardljud för aviseringar" "Ljud från appen" diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index 341e9351681..45044a5bb55 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -3849,7 +3849,7 @@ "ரிங் & அறிவிப்பு ஒலியளவு" "ரிங்டோன் ஒலியளவு" "அறிவிப்பின் ஒலியளவு" - "ஒலிக்கச் செய்தல் முடக்கப்பட்டுள்ளதால் கிடைக்கவில்லை" + "ஒலி முடக்கப்பட்டுள்ளதால் கிடைக்கவில்லை" "மொபைலின் ரிங்டோன்" "இயல்பு அறிவிப்பு ஒலி" "ஆப்ஸ் வழங்கும் ஒலி" diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 1040b048d18..7ca037f14a2 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -2410,7 +2410,7 @@ "คุณปรับแต่งอุปกรณ์นี้ให้ตรงกับความต้องการได้ และเปลี่ยนฟีเจอร์การช่วยเหลือพิเศษเหล่านี้ได้ภายหลังในการตั้งค่า" "เปลี่ยนขนาดแบบอักษร" "โปรแกรมอ่านหน้าจอ" - "คำบรรยายวิดีโอ" + "คำบรรยายแทนเสียง" "เสียง" "ทั่วไป" "การแสดงผล" @@ -2425,7 +2425,7 @@ "TalkBack" "โปรแกรมอ่านหน้าจอสำหรับผู้ที่ตาบอดหรือมีสายตาเลือนรางเป็นหลัก" "แตะรายการบนหน้าจอเพื่อฟังการอ่านออกเสียง" - "ค่ากำหนดคำบรรยายวิดีโอ" + "ค่ากำหนดคำบรรยายแทนเสียง" "เกี่ยวกับค่ากำหนดคำบรรยาย" "ดูข้อมูลเพิ่มเติมเกี่ยวกับค่ากำหนดคำบรรยาย" "การขยาย" @@ -2748,7 +2748,7 @@ "ใช้งานง่าย, เข้าถึงง่าย, ความช่วยเหลือ, การช่วยเหลือพิเศษ" "แว่นขยายหน้าจอ, ซูม, การขยาย, สายตาเลือนราง, ขยาย, ทำให้ใหญ่ขึ้น" - "คำบรรยายวิดีโอ, คำบรรภาพ, คำบรรยาย, Live Transcribe, มีปัญหาในการได้ยิน, สูญเสียการได้ยิน, CART, การแปลงคำพูดเป็นข้อความ, คำบรรยาย" + "คำบรรยายแทนเสียง, คำบรรภาพ, คำบรรยาย, Live Transcribe, มีปัญหาในการได้ยิน, สูญเสียการได้ยิน, CART, การแปลงคำพูดเป็นข้อความ" @@ -2761,7 +2761,7 @@ "การเคลื่อนไหว, เมาส์" "มีปัญหาในการได้ยิน, สูญเสียการได้ยิน" - "มีปัญหาในการได้ยิน, สูญเสียการได้ยิน, คำบรรยายวิดีโอ, โทรพิมพ์, tty" + "มีปัญหาในการได้ยิน, สูญเสียการได้ยิน, คำบรรยายแทนเสียง, โทรพิมพ์, tty" "การพิมพ์" "ปิด" diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml index 4446b7a5d95..ccf5619d7e4 100644 --- a/res/values-uz/strings.xml +++ b/res/values-uz/strings.xml @@ -3848,7 +3848,7 @@ "Signal tovushi" "Jiringlash va bildirishnoma tovushi" "Jiringlash tovushi" - "Bildirishnomalar ovozi" + "Bildirishnoma tovushi" "Jiringlash ovozsizligi uchun ishlamaydi" "Telefon ringtoni" "Standart bildirishnoma tovushi" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 4c207326dcd..f495893b866 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -2646,7 +2646,7 @@ "音频调整" "语音描述" - "听取有关当前屏幕画面内容的语音说明(仅限支持该设置的电影或电视节目)" + "以语音的形式描述当前画面呈现的内容(仅限支持该设置的电影或电视节目)" "语音描述, 语音, 描述, 弱视, audio description, audio, description, low vision," "快捷方式已开启" "已关闭" @@ -3849,7 +3849,7 @@ "铃声和通知音量" "铃声音量" "通知音量" - "该功能无法使用,因为铃声被静音" + "铃声已被静音,因此无法调整" "手机铃声" "默认通知提示音" "应用提供的提示音" @@ -5662,7 +5662,7 @@ "允许应用将内容发送到 Android 系统" "捕获系统堆转储数据" "重新启动并启用 MTE" - "系统将重新启动并允许尝试内存标记扩展 (MTE)。MTE 可能会对系统性能和稳定性产生负面影响。将在下一次重新启动时重置。" + "系统将重新启动并允许尝试内存标记扩展 (MTE)。MTE 可能会导致系统性能和稳定性下降。在系统下一次重新启动时,此设置会被重置。" "正在捕获系统堆转储数据" "无法捕获系统堆转储数据" "自动捕获堆转储数据" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 6057234e658..f796607bb53 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -3850,7 +3850,7 @@ "鈴聲和通知音量" "鈴聲音量" "通知音量" - "鈴聲已設定為靜音,因此無法使用" + "鈴聲已設為靜音,無法使用" "電話鈴聲" "預設通知音效" "應用程式提供的音效" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index bc8309529cc..a68cd01dfcf 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -1150,12 +1150,12 @@ "查看或變更訂閱" "已隨機化的 MAC" "新增裝置" - "將下面的視窗對準 QR 圖碼,即可將這個裝置新增至「%1$s」" + "將下面的視窗對準 QR code,即可將這個裝置新增至「%1$s」" "掃描 QR code" "將 QR 圖碼置於相機正下方即可連線到「%1$s」" "掃描 QR code 即可加入 Wi‑Fi" "分享 Wi-Fi" - "使用其他裝置掃描這個 QR 圖碼即可加入「%1$s」" + "使用其他裝置掃描這個 QR code 即可加入「%1$s」" "掃描這個 QR 圖碼即可連線至「%1$s」" "請再試一次,如果問題持續發生,請與裝置製造商聯絡" "發生錯誤" @@ -1183,7 +1183,7 @@ "自動連線" "允許裝置在進入有效範圍時連線至這個網路" "新增裝置" - "使用 QR 圖碼將裝置新增至這個網路" + "使用 QR code 將裝置新增至這個網路" "QR 圖碼格式無效" "重試" "與裝置的其他使用者共用" From 4f7205b2ea441d3efcb7b86bce744f3aa735ebe0 Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Thu, 23 Mar 2023 15:30:19 +0800 Subject: [PATCH 08/12] Refine permission check process of 2-pane deep link - Check the deep link activity instance before redirecting to the internal activity for the managed profile invocation, so the caller can't bypass the permission check. - Get the referrer as the caller so that onNewIntent can recognize the new caller and check if it has a permission to open the target page. Test: robotest & manual Bug: 268193384 Bug: 272437506 Change-Id: Ie69742983fb74ee2316b7aad16461db95ed927c2 Merged-In: Ie69742983fb74ee2316b7aad16461db95ed927c2 --- .../homepage/SettingsHomepageActivity.java | 95 +++++++++++++------ .../SettingsHomepageActivityTest.java | 89 ++++++++++++++--- 2 files changed, 142 insertions(+), 42 deletions(-) diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java index b27f5cc8b66..60252fa8b95 100644 --- a/src/com/android/settings/homepage/SettingsHomepageActivity.java +++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java @@ -29,11 +29,12 @@ import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.pm.UserInfo; import android.content.res.Configuration; +import android.net.Uri; import android.os.Bundle; import android.os.Process; -import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.text.TextUtils; @@ -57,7 +58,6 @@ import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import androidx.window.embedding.ActivityEmbeddingController; -import androidx.window.embedding.SplitController; import androidx.window.embedding.SplitRule; import com.android.settings.R; @@ -71,7 +71,6 @@ import com.android.settings.core.CategoryMixin; import com.android.settings.core.FeatureFlags; import com.android.settings.homepage.contextualcards.ContextualCardsFragment; import com.android.settings.overlay.FeatureFactory; -import com.android.settings.password.PasswordUtils; import com.android.settings.safetycenter.SafetyCenterManagerWrapper; import com.android.settingslib.Utils; import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin; @@ -94,6 +93,10 @@ public class SettingsHomepageActivity extends FragmentActivity implements public static final String EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_DATA = "settings_large_screen_deep_link_intent_data"; + // The referrer who fires the initial intent to start the homepage + @VisibleForTesting + static final String EXTRA_INITIAL_REFERRER = "initial_referrer"; + static final int DEFAULT_HIGHLIGHT_MENU_KEY = R.string.menu_key_network; private static final long HOMEPAGE_LOADING_TIMEOUT_MS = 300; @@ -175,12 +178,16 @@ public class SettingsHomepageActivity extends FragmentActivity implements mIsEmbeddingActivityEnabled = ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this); if (mIsEmbeddingActivityEnabled) { final UserManager um = getSystemService(UserManager.class); - final UserInfo userInfo = um.getUserInfo(getUser().getIdentifier()); + final UserInfo userInfo = um.getUserInfo(getUserId()); if (userInfo.isManagedProfile()) { final Intent intent = new Intent(getIntent()) - .setClass(this, DeepLinkHomepageActivityInternal.class) .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT) - .putExtra(EXTRA_USER_HANDLE, getUser()); + .putExtra(EXTRA_USER_HANDLE, getUser()) + .putExtra(EXTRA_INITIAL_REFERRER, getCurrentReferrer()); + if (TextUtils.equals(intent.getAction(), ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY) + && this instanceof DeepLinkHomepageActivity) { + intent.setClass(this, DeepLinkHomepageActivityInternal.class); + } intent.removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivityAsUser(intent, um.getPrimaryUser().getUserHandle()); finish(); @@ -451,7 +458,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements return; } - ActivityInfo targetActivityInfo = null; + ActivityInfo targetActivityInfo; try { targetActivityInfo = getPackageManager().getActivityInfo(targetComponentName, /* flags= */ 0); @@ -461,23 +468,29 @@ public class SettingsHomepageActivity extends FragmentActivity implements return; } - int callingUid = -1; - try { - callingUid = ActivityManager.getService().getLaunchedFromUid(getActivityToken()); - } catch (RemoteException re) { - Log.e(TAG, "Not able to get callingUid: " + re); - finish(); - return; + UserHandle user = intent.getParcelableExtra(EXTRA_USER_HANDLE, UserHandle.class); + String caller = getInitialReferrer(); + int callerUid = -1; + if (caller != null) { + try { + callerUid = getPackageManager().getApplicationInfoAsUser(caller, + ApplicationInfoFlags.of(/* flags= */ 0), + user != null ? user.getIdentifier() : getUserId()).uid; + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Not able to get callerUid: " + e); + finish(); + return; + } } - if (!hasPrivilegedAccess(callingUid, targetActivityInfo)) { + if (!hasPrivilegedAccess(caller, callerUid, targetActivityInfo.packageName)) { if (!targetActivityInfo.exported) { Log.e(TAG, "Target Activity is not exported"); finish(); return; } - if (!isCallingAppPermitted(targetActivityInfo.permission)) { + if (!isCallingAppPermitted(targetActivityInfo.permission, callerUid)) { Log.e(TAG, "Calling app must have the permission of deep link Activity"); finish(); return; @@ -507,7 +520,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); if (targetIntent.getData() != null && uriPermissionFlags != 0 - && checkUriPermission(targetIntent.getData(), /* pid= */ -1, callingUid, + && checkUriPermission(targetIntent.getData(), /* pid= */ -1, callerUid, uriPermissionFlags) == PackageManager.PERMISSION_DENIED) { Log.e(TAG, "Calling app must have the permission to access Uri and grant permission"); finish(); @@ -530,7 +543,6 @@ public class SettingsHomepageActivity extends FragmentActivity implements SplitRule.FinishBehavior.ALWAYS, true /* clearTop */); - final UserHandle user = intent.getParcelableExtra(EXTRA_USER_HANDLE, UserHandle.class); if (user != null) { startActivityAsUser(targetIntent, user); } else { @@ -538,31 +550,30 @@ public class SettingsHomepageActivity extends FragmentActivity implements } } - // Check if calling app has privileged access to launch Activity of activityInfo. - private boolean hasPrivilegedAccess(int callingUid, ActivityInfo activityInfo) { - if (TextUtils.equals(PasswordUtils.getCallingAppPackageName(getActivityToken()), - getPackageName())) { + // Check if the caller has privileged access to launch the target page. + private boolean hasPrivilegedAccess(String callerPkg, int callerUid, String targetPackage) { + if (TextUtils.equals(callerPkg, getPackageName())) { return true; } int targetUid = -1; try { - targetUid = getPackageManager().getApplicationInfo(activityInfo.packageName, - /* flags= */ 0).uid; - } catch (PackageManager.NameNotFoundException nnfe) { - Log.e(TAG, "Not able to get targetUid: " + nnfe); + targetUid = getPackageManager().getApplicationInfo(targetPackage, + ApplicationInfoFlags.of(/* flags= */ 0)).uid; + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Not able to get targetUid: " + e); return false; } // When activityInfo.exported is false, Activity still can be launched if applications have // the same user ID. - if (UserHandle.isSameApp(callingUid, targetUid)) { + if (UserHandle.isSameApp(callerUid, targetUid)) { return true; } // When activityInfo.exported is false, Activity still can be launched if calling app has // root or system privilege. - int callingAppId = UserHandle.getAppId(callingUid); + int callingAppId = UserHandle.getAppId(callerUid); if (callingAppId == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID) { return true; } @@ -571,9 +582,31 @@ public class SettingsHomepageActivity extends FragmentActivity implements } @VisibleForTesting - boolean isCallingAppPermitted(String permission) { - return TextUtils.isEmpty(permission) || PasswordUtils.isCallingAppPermitted( - this, getActivityToken(), permission); + String getInitialReferrer() { + String referrer = getCurrentReferrer(); + if (!TextUtils.equals(referrer, getPackageName())) { + return referrer; + } + + String initialReferrer = getIntent().getStringExtra(EXTRA_INITIAL_REFERRER); + return TextUtils.isEmpty(initialReferrer) ? referrer : initialReferrer; + } + + @VisibleForTesting + String getCurrentReferrer() { + Intent intent = getIntent(); + // Clear extras to get the real referrer + intent.removeExtra(Intent.EXTRA_REFERRER); + intent.removeExtra(Intent.EXTRA_REFERRER_NAME); + Uri referrer = getReferrer(); + return referrer != null ? referrer.getHost() : null; + } + + @VisibleForTesting + boolean isCallingAppPermitted(String permission, int callerUid) { + return TextUtils.isEmpty(permission) + || checkPermission(permission, /* pid= */ -1, callerUid) + == PackageManager.PERMISSION_GRANTED; } private String getHighlightMenuKey() { diff --git a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java index 337b659ddd0..b16333632fb 100644 --- a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java +++ b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java @@ -20,8 +20,13 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -31,6 +36,8 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Build; import android.view.View; import android.view.Window; @@ -218,29 +225,89 @@ public class SettingsHomepageActivityTest { } @Test - @Config(shadows = {ShadowPasswordUtils.class}) + public void getInitialReferrer_differentPackage_returnCurrentReferrer() { + SettingsHomepageActivity activity = + spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get()); + String referrer = "com.abc"; + doReturn(referrer).when(activity).getCurrentReferrer(); + + assertEquals(activity.getInitialReferrer(), referrer); + } + + @Test + public void getInitialReferrer_noReferrerExtra_returnCurrentReferrer() { + SettingsHomepageActivity activity = + spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get()); + String referrer = activity.getPackageName(); + doReturn(referrer).when(activity).getCurrentReferrer(); + + assertEquals(activity.getInitialReferrer(), referrer); + } + + @Test + public void getInitialReferrer_hasReferrerExtra_returnGivenReferrer() { + SettingsHomepageActivity activity = + spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get()); + doReturn(activity.getPackageName()).when(activity).getCurrentReferrer(); + String referrer = "com.abc"; + activity.setIntent(new Intent().putExtra(SettingsHomepageActivity.EXTRA_INITIAL_REFERRER, + referrer)); + + assertEquals(activity.getInitialReferrer(), referrer); + } + + @Test + public void getCurrentReferrer_hasReferrerExtra_shouldNotEqual() { + String referrer = "com.abc"; + Uri uri = new Uri.Builder().scheme("android-app").authority(referrer).build(); + SettingsHomepageActivity activity = + spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get()); + activity.setIntent(new Intent().putExtra(Intent.EXTRA_REFERRER, uri)); + + assertNotEquals(activity.getCurrentReferrer(), referrer); + } + + @Test + public void getCurrentReferrer_hasReferrerNameExtra_shouldNotEqual() { + String referrer = "com.abc"; + SettingsHomepageActivity activity = + spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get()); + activity.setIntent(new Intent().putExtra(Intent.EXTRA_REFERRER_NAME, referrer)); + + assertNotEquals(activity.getCurrentReferrer(), referrer); + } + + @Test public void isCallingAppPermitted_emptyPermission_returnTrue() { - SettingsHomepageActivity homepageActivity = spy(new SettingsHomepageActivity()); + SettingsHomepageActivity activity = + spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get()); + doReturn(PackageManager.PERMISSION_DENIED).when(activity) + .checkPermission(anyString(), anyInt(), anyInt()); - assertTrue(homepageActivity.isCallingAppPermitted("")); + assertTrue(activity.isCallingAppPermitted("", 1000)); } @Test - @Config(shadows = {ShadowPasswordUtils.class}) - public void isCallingAppPermitted_noGrantedPermission_returnFalse() { - SettingsHomepageActivity homepageActivity = spy(new SettingsHomepageActivity()); + public void isCallingAppPermitted_notGrantedPermission_returnFalse() { + SettingsHomepageActivity activity = + spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get()); + doReturn(PackageManager.PERMISSION_DENIED).when(activity) + .checkPermission(anyString(), anyInt(), anyInt()); - assertFalse(homepageActivity.isCallingAppPermitted("android.permission.TEST")); + assertFalse(activity.isCallingAppPermitted("android.permission.TEST", 1000)); } @Test - @Config(shadows = {ShadowPasswordUtils.class}) public void isCallingAppPermitted_grantedPermission_returnTrue() { - SettingsHomepageActivity homepageActivity = spy(new SettingsHomepageActivity()); + SettingsHomepageActivity activity = + spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get()); String permission = "android.permission.TEST"; - ShadowPasswordUtils.addGrantedPermission(permission); + doReturn(PackageManager.PERMISSION_DENIED).when(activity) + .checkPermission(anyString(), anyInt(), anyInt()); + doReturn(PackageManager.PERMISSION_GRANTED).when(activity) + .checkPermission(eq(permission), anyInt(), eq(1000)); - assertTrue(homepageActivity.isCallingAppPermitted(permission)); + assertTrue(activity.isCallingAppPermitted(permission, 1000)); } @Implements(SuggestionFeatureProviderImpl.class) From 0f13f70655099543ba34eb8aeaa74b34a3993a3b Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Thu, 23 Mar 2023 15:30:19 +0800 Subject: [PATCH 09/12] Refine permission check process of 2-pane deep link - Check the deep link activity instance before redirecting to the internal activity for the managed profile invocation, so the caller can't bypass the permission check. - Get the referrer as the caller so that onNewIntent can recognize the new caller and check if it has a permission to open the target page. Test: robotest & manual Bug: 268193384 Change-Id: Ie69742983fb74ee2316b7aad16461db95ed927c2 Merged-In: Ie69742983fb74ee2316b7aad16461db95ed927c2 --- .../homepage/SettingsHomepageActivity.java | 94 +++++++++++++------ .../SettingsHomepageActivityTest.java | 92 +++++++++++++++--- 2 files changed, 145 insertions(+), 41 deletions(-) diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java index a23b743aade..b3f84d68e00 100644 --- a/src/com/android/settings/homepage/SettingsHomepageActivity.java +++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java @@ -29,11 +29,12 @@ import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.pm.UserInfo; import android.content.res.Configuration; +import android.net.Uri; import android.os.Bundle; import android.os.Process; -import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.text.TextUtils; @@ -70,7 +71,6 @@ import com.android.settings.core.CategoryMixin; import com.android.settings.core.FeatureFlags; import com.android.settings.homepage.contextualcards.ContextualCardsFragment; import com.android.settings.overlay.FeatureFactory; -import com.android.settings.password.PasswordUtils; import com.android.settingslib.Utils; import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin; @@ -92,6 +92,10 @@ public class SettingsHomepageActivity extends FragmentActivity implements public static final String EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_DATA = "settings_large_screen_deep_link_intent_data"; + // The referrer who fires the initial intent to start the homepage + @VisibleForTesting + static final String EXTRA_INITIAL_REFERRER = "initial_referrer"; + static final int DEFAULT_HIGHLIGHT_MENU_KEY = R.string.menu_key_network; private static final long HOMEPAGE_LOADING_TIMEOUT_MS = 300; @@ -173,12 +177,16 @@ public class SettingsHomepageActivity extends FragmentActivity implements mIsEmbeddingActivityEnabled = ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this); if (mIsEmbeddingActivityEnabled) { final UserManager um = getSystemService(UserManager.class); - final UserInfo userInfo = um.getUserInfo(getUser().getIdentifier()); + final UserInfo userInfo = um.getUserInfo(getUserId()); if (userInfo.isManagedProfile()) { final Intent intent = new Intent(getIntent()) - .setClass(this, DeepLinkHomepageActivityInternal.class) .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT) - .putExtra(EXTRA_USER_HANDLE, getUser()); + .putExtra(EXTRA_USER_HANDLE, getUser()) + .putExtra(EXTRA_INITIAL_REFERRER, getCurrentReferrer()); + if (TextUtils.equals(intent.getAction(), ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY) + && this instanceof DeepLinkHomepageActivity) { + intent.setClass(this, DeepLinkHomepageActivityInternal.class); + } intent.removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivityAsUser(intent, um.getPrimaryUser().getUserHandle()); finish(); @@ -438,7 +446,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements return; } - ActivityInfo targetActivityInfo = null; + ActivityInfo targetActivityInfo; try { targetActivityInfo = getPackageManager().getActivityInfo(targetComponentName, /* flags= */ 0); @@ -448,23 +456,29 @@ public class SettingsHomepageActivity extends FragmentActivity implements return; } - int callingUid = -1; - try { - callingUid = ActivityManager.getService().getLaunchedFromUid(getActivityToken()); - } catch (RemoteException re) { - Log.e(TAG, "Not able to get callingUid: " + re); - finish(); - return; + UserHandle user = intent.getParcelableExtra(EXTRA_USER_HANDLE, UserHandle.class); + String caller = getInitialReferrer(); + int callerUid = -1; + if (caller != null) { + try { + callerUid = getPackageManager().getApplicationInfoAsUser(caller, + ApplicationInfoFlags.of(/* flags= */ 0), + user != null ? user.getIdentifier() : getUserId()).uid; + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Not able to get callerUid: " + e); + finish(); + return; + } } - if (!hasPrivilegedAccess(callingUid, targetActivityInfo)) { + if (!hasPrivilegedAccess(caller, callerUid, targetActivityInfo.packageName)) { if (!targetActivityInfo.exported) { Log.e(TAG, "Target Activity is not exported"); finish(); return; } - if (!isCallingAppPermitted(targetActivityInfo.permission)) { + if (!isCallingAppPermitted(targetActivityInfo.permission, callerUid)) { Log.e(TAG, "Calling app must have the permission of deep link Activity"); finish(); return; @@ -494,7 +508,7 @@ public class SettingsHomepageActivity extends FragmentActivity implements & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); if (targetIntent.getData() != null && uriPermissionFlags != 0 - && checkUriPermission(targetIntent.getData(), /* pid= */ -1, callingUid, + && checkUriPermission(targetIntent.getData(), /* pid= */ -1, callerUid, uriPermissionFlags) == PackageManager.PERMISSION_DENIED) { Log.e(TAG, "Calling app must have the permission to access Uri and grant permission"); finish(); @@ -517,7 +531,6 @@ public class SettingsHomepageActivity extends FragmentActivity implements SplitRule.FINISH_ALWAYS, true /* clearTop */); - final UserHandle user = intent.getParcelableExtra(EXTRA_USER_HANDLE, UserHandle.class); if (user != null) { startActivityAsUser(targetIntent, user); } else { @@ -525,31 +538,30 @@ public class SettingsHomepageActivity extends FragmentActivity implements } } - // Check if calling app has privileged access to launch Activity of activityInfo. - private boolean hasPrivilegedAccess(int callingUid, ActivityInfo activityInfo) { - if (TextUtils.equals(PasswordUtils.getCallingAppPackageName(getActivityToken()), - getPackageName())) { + // Check if the caller has privileged access to launch the target page. + private boolean hasPrivilegedAccess(String callerPkg, int callerUid, String targetPackage) { + if (TextUtils.equals(callerPkg, getPackageName())) { return true; } int targetUid = -1; try { - targetUid = getPackageManager().getApplicationInfo(activityInfo.packageName, - /* flags= */ 0).uid; - } catch (PackageManager.NameNotFoundException nnfe) { - Log.e(TAG, "Not able to get targetUid: " + nnfe); + targetUid = getPackageManager().getApplicationInfo(targetPackage, + ApplicationInfoFlags.of(/* flags= */ 0)).uid; + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Not able to get targetUid: " + e); return false; } // When activityInfo.exported is false, Activity still can be launched if applications have // the same user ID. - if (UserHandle.isSameApp(callingUid, targetUid)) { + if (UserHandle.isSameApp(callerUid, targetUid)) { return true; } // When activityInfo.exported is false, Activity still can be launched if calling app has // root or system privilege. - int callingAppId = UserHandle.getAppId(callingUid); + int callingAppId = UserHandle.getAppId(callerUid); if (callingAppId == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID) { return true; } @@ -558,9 +570,31 @@ public class SettingsHomepageActivity extends FragmentActivity implements } @VisibleForTesting - boolean isCallingAppPermitted(String permission) { - return TextUtils.isEmpty(permission) || PasswordUtils.isCallingAppPermitted( - this, getActivityToken(), permission); + String getInitialReferrer() { + String referrer = getCurrentReferrer(); + if (!TextUtils.equals(referrer, getPackageName())) { + return referrer; + } + + String initialReferrer = getIntent().getStringExtra(EXTRA_INITIAL_REFERRER); + return TextUtils.isEmpty(initialReferrer) ? referrer : initialReferrer; + } + + @VisibleForTesting + String getCurrentReferrer() { + Intent intent = getIntent(); + // Clear extras to get the real referrer + intent.removeExtra(Intent.EXTRA_REFERRER); + intent.removeExtra(Intent.EXTRA_REFERRER_NAME); + Uri referrer = getReferrer(); + return referrer != null ? referrer.getHost() : null; + } + + @VisibleForTesting + boolean isCallingAppPermitted(String permission, int callerUid) { + return TextUtils.isEmpty(permission) + || checkPermission(permission, /* pid= */ -1, callerUid) + == PackageManager.PERMISSION_GRANTED; } private String getHighlightMenuKey() { diff --git a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java index 4de8b005c3c..bf109c46c1a 100644 --- a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java +++ b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java @@ -20,14 +20,24 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Build; import android.view.View; import android.view.Window; @@ -205,29 +215,89 @@ public class SettingsHomepageActivityTest { } @Test - @Config(shadows = {ShadowPasswordUtils.class}) + public void getInitialReferrer_differentPackage_returnCurrentReferrer() { + SettingsHomepageActivity activity = + spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get()); + String referrer = "com.abc"; + doReturn(referrer).when(activity).getCurrentReferrer(); + + assertEquals(activity.getInitialReferrer(), referrer); + } + + @Test + public void getInitialReferrer_noReferrerExtra_returnCurrentReferrer() { + SettingsHomepageActivity activity = + spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get()); + String referrer = activity.getPackageName(); + doReturn(referrer).when(activity).getCurrentReferrer(); + + assertEquals(activity.getInitialReferrer(), referrer); + } + + @Test + public void getInitialReferrer_hasReferrerExtra_returnGivenReferrer() { + SettingsHomepageActivity activity = + spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get()); + doReturn(activity.getPackageName()).when(activity).getCurrentReferrer(); + String referrer = "com.abc"; + activity.setIntent(new Intent().putExtra(SettingsHomepageActivity.EXTRA_INITIAL_REFERRER, + referrer)); + + assertEquals(activity.getInitialReferrer(), referrer); + } + + @Test + public void getCurrentReferrer_hasReferrerExtra_shouldNotEqual() { + String referrer = "com.abc"; + Uri uri = new Uri.Builder().scheme("android-app").authority(referrer).build(); + SettingsHomepageActivity activity = + spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get()); + activity.setIntent(new Intent().putExtra(Intent.EXTRA_REFERRER, uri)); + + assertNotEquals(activity.getCurrentReferrer(), referrer); + } + + @Test + public void getCurrentReferrer_hasReferrerNameExtra_shouldNotEqual() { + String referrer = "com.abc"; + SettingsHomepageActivity activity = + spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get()); + activity.setIntent(new Intent().putExtra(Intent.EXTRA_REFERRER_NAME, referrer)); + + assertNotEquals(activity.getCurrentReferrer(), referrer); + } + + @Test public void isCallingAppPermitted_emptyPermission_returnTrue() { - SettingsHomepageActivity homepageActivity = spy(new SettingsHomepageActivity()); + SettingsHomepageActivity activity = + spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get()); + doReturn(PackageManager.PERMISSION_DENIED).when(activity) + .checkPermission(anyString(), anyInt(), anyInt()); - assertTrue(homepageActivity.isCallingAppPermitted("")); + assertTrue(activity.isCallingAppPermitted("", 1000)); } @Test - @Config(shadows = {ShadowPasswordUtils.class}) - public void isCallingAppPermitted_noGrantedPermission_returnFalse() { - SettingsHomepageActivity homepageActivity = spy(new SettingsHomepageActivity()); + public void isCallingAppPermitted_notGrantedPermission_returnFalse() { + SettingsHomepageActivity activity = + spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get()); + doReturn(PackageManager.PERMISSION_DENIED).when(activity) + .checkPermission(anyString(), anyInt(), anyInt()); - assertFalse(homepageActivity.isCallingAppPermitted("android.permission.TEST")); + assertFalse(activity.isCallingAppPermitted("android.permission.TEST", 1000)); } @Test - @Config(shadows = {ShadowPasswordUtils.class}) public void isCallingAppPermitted_grantedPermission_returnTrue() { - SettingsHomepageActivity homepageActivity = spy(new SettingsHomepageActivity()); + SettingsHomepageActivity activity = + spy(Robolectric.buildActivity(SettingsHomepageActivity.class).get()); String permission = "android.permission.TEST"; - ShadowPasswordUtils.addGrantedPermission(permission); + doReturn(PackageManager.PERMISSION_DENIED).when(activity) + .checkPermission(anyString(), anyInt(), anyInt()); + doReturn(PackageManager.PERMISSION_GRANTED).when(activity) + .checkPermission(eq(permission), anyInt(), eq(1000)); - assertTrue(homepageActivity.isCallingAppPermitted(permission)); + assertTrue(activity.isCallingAppPermitted(permission, 1000)); } @Implements(SuggestionFeatureProviderImpl.class) From 4140b8488195f9fc65570a48297a1017183e0d4d Mon Sep 17 00:00:00 2001 From: Milton Wu Date: Thu, 30 Mar 2023 14:51:26 +0800 Subject: [PATCH 10/12] [BiometricsV2] Fix 2nd touch not work for enroll Use MessageDisplayController only when enroll reason is ENROLL_ENROLL and R.bool.enrollment_message_display_controller_flag is true. And always allocate a new MessageDisplayController for each new enroll to avoid the possibility of events being ignored by MessageDisplayController. Bug: 275510856 Test: atest FingerprintEnrollProgressViewModelTest Test: manually test sfps/udfps enrollment for biometricsV2 Change-Id: Ifc8b91916a3d76bed68dc523a90dc6ba422e3923 --- .../FingerprintEnrollProgressViewModel.java | 41 +++--- ...ingerprintEnrollProgressViewModelTest.java | 125 +++++++++++++++++- 2 files changed, 144 insertions(+), 22 deletions(-) diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java index b1b420db9a3..695ea0d0c0f 100644 --- a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java +++ b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModel.java @@ -16,6 +16,8 @@ package com.android.settings.biometrics2.ui.viewmodel; +import static android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL; + import static com.android.settings.biometrics2.ui.model.EnrollmentProgress.INITIAL_REMAINING; import static com.android.settings.biometrics2.ui.model.EnrollmentProgress.INITIAL_STEPS; @@ -63,7 +65,6 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel { private final int mUserId; private final FingerprintUpdater mFingerprintUpdater; - private final MessageDisplayController mMessageDisplayController; @Nullable private CancellationSignal mCancellationSignal = null; private final EnrollmentCallback mEnrollmentCallback = new EnrollmentCallback() { @@ -81,6 +82,9 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel { @Override public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { + if (DEBUG) { + Log.d(TAG, "onEnrollmentHelp(" + helpMsgId + ", " + helpString + ")"); + } mHelpMessageLiveData.postValue(new EnrollmentStatusMessage(helpMsgId, helpString)); } @@ -113,20 +117,6 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel { super(application); mFingerprintUpdater = fingerprintUpdater; mUserId = userId; - - final Resources res = application.getResources(); - mMessageDisplayController = - res.getBoolean(R.bool.enrollment_message_display_controller_flag) - ? new MessageDisplayController( - application.getMainThreadHandler(), - mEnrollmentCallback, - SystemClock.elapsedRealtimeClock(), - res.getInteger(R.integer.enrollment_help_minimum_time_display), - res.getInteger(R.integer.enrollment_progress_minimum_time_display), - res.getBoolean(R.bool.enrollment_progress_priority_over_help), - res.getBoolean(R.bool.enrollment_prioritize_acquire_messages), - res.getInteger(R.integer.enrollment_collect_time)) - : null; } public void setToken(byte[] token) { @@ -195,9 +185,24 @@ public class FingerprintEnrollProgressViewModel extends AndroidViewModel { mErrorMessageLiveData.setValue(null); mCancellationSignal = new CancellationSignal(); - mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, - mMessageDisplayController != null ? mMessageDisplayController : mEnrollmentCallback, - reason); + + final Resources res = getApplication().getResources(); + if (reason == ENROLL_ENROLL + && res.getBoolean(R.bool.enrollment_message_display_controller_flag)) { + final EnrollmentCallback callback = new MessageDisplayController( + getApplication().getMainThreadHandler(), + mEnrollmentCallback, + SystemClock.elapsedRealtimeClock(), + res.getInteger(R.integer.enrollment_help_minimum_time_display), + res.getInteger(R.integer.enrollment_progress_minimum_time_display), + res.getBoolean(R.bool.enrollment_progress_priority_over_help), + res.getBoolean(R.bool.enrollment_prioritize_acquire_messages), + res.getInteger(R.integer.enrollment_collect_time)); + mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, callback, reason); + } else { + mFingerprintUpdater.enroll(mToken, mCancellationSignal, mUserId, mEnrollmentCallback, + reason); + } return true; } diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java index 323618a1d63..6190c5e3f8f 100644 --- a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java +++ b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollProgressViewModelTest.java @@ -21,6 +21,8 @@ import static android.hardware.fingerprint.FingerprintManager.ENROLL_FIND_SENSOR import static android.hardware.fingerprint.FingerprintManager.EnrollReason; import static android.hardware.fingerprint.FingerprintManager.EnrollmentCallback; +import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyInt; @@ -29,18 +31,24 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.only; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Application; import android.content.res.Resources; import android.os.CancellationSignal; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import androidx.annotation.NonNull; import androidx.lifecycle.LiveData; +import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.android.settings.R; import com.android.settings.biometrics.fingerprint.FingerprintUpdater; +import com.android.settings.biometrics.fingerprint.MessageDisplayController; import com.android.settings.biometrics2.ui.model.EnrollmentProgress; import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage; import com.android.settings.testutils.InstantTaskExecutorRule; @@ -68,12 +76,18 @@ public class FingerprintEnrollProgressViewModelTest { private FingerprintEnrollProgressViewModel mViewModel; private final TestWrapper mCancellationSignalWrapper = new TestWrapper<>(); private final TestWrapper mCallbackWrapper = new TestWrapper<>(); + private int mEnrollmentMessageDisplayControllerFlagResId; @Before public void setUp() { + mEnrollmentMessageDisplayControllerFlagResId = ApplicationProvider.getApplicationContext() + .getResources().getIdentifier("enrollment_message_display_controller_flag", "bool", + SETTINGS_PACKAGE_NAME); + when(mApplication.getResources()).thenReturn(mResources); - when(mResources.getBoolean(R.bool.enrollment_message_display_controller_flag)) - .thenReturn(false); + + // Not use MessageDisplayController by default + when(mResources.getBoolean(mEnrollmentMessageDisplayControllerFlagResId)).thenReturn(false); mViewModel = new FingerprintEnrollProgressViewModel(mApplication, mFingerprintUpdater, TEST_USER_ID); @@ -88,7 +102,7 @@ public class FingerprintEnrollProgressViewModelTest { } @Test - public void testStartEnrollment() { + public void testStartFindSensor() { @EnrollReason final int enrollReason = ENROLL_FIND_SENSOR; final byte[] token = new byte[] { 1, 2, 3 }; mViewModel.setToken(token); @@ -99,6 +113,54 @@ public class FingerprintEnrollProgressViewModelTest { assertThat(ret).isTrue(); verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class), eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason)); + assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isFalse(); + } + + @Test + public void testStartEnrolling() { + @EnrollReason final int enrollReason = ENROLL_ENROLL; + final byte[] token = new byte[] { 1, 2, 3 }; + mViewModel.setToken(token); + + // Start enrollment + final boolean ret = mViewModel.startEnrollment(enrollReason); + + assertThat(ret).isTrue(); + verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class), + eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason)); + assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isFalse(); + } + + @Test + public void testStartEnrollingWithMessageDisplayController() { + // Enable MessageDisplayController and mock handler for it + when(mResources.getBoolean(mEnrollmentMessageDisplayControllerFlagResId)).thenReturn(true); + when(mApplication.getMainThreadHandler()).thenReturn(new TestHandler()); + + @EnrollReason final int enrollReason = ENROLL_ENROLL; + final byte[] token = new byte[] { 1, 2, 3 }; + mViewModel.setToken(token); + + // Start enrollment + final boolean ret = mViewModel.startEnrollment(enrollReason); + + assertThat(ret).isTrue(); + verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class), + eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason)); + assertThat(mCallbackWrapper.mValue).isNotNull(); + + assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isTrue(); + final EnrollmentCallback callback1 = mCallbackWrapper.mValue; + + // Cancel and start again + mViewModel.cancelEnrollment(); + mViewModel.startEnrollment(enrollReason); + + // Shall not use the same MessageDisplayController + verify(mFingerprintUpdater, times(2)).enroll(eq(token), any(CancellationSignal.class), + eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason)); + assertThat(mCallbackWrapper.mValue).isNotNull(); + assertThat(callback1).isNotEqualTo(mCallbackWrapper.mValue); } @Test @@ -162,6 +224,48 @@ public class FingerprintEnrollProgressViewModelTest { assertThat(progress.getRemaining()).isEqualTo(0); } + @Test + public void testProgressUpdateWithMessageDisplayController() { + // Enable MessageDisplayController and mock handler for it + when(mResources.getBoolean(mEnrollmentMessageDisplayControllerFlagResId)).thenReturn(true); + when(mApplication.getMainThreadHandler()).thenReturn(new TestHandler()); + + mViewModel.setToken(new byte[] { 1, 2, 3 }); + + // Start enrollment + final boolean ret = mViewModel.startEnrollment(ENROLL_ENROLL); + assertThat(ret).isTrue(); + assertThat(mCallbackWrapper.mValue).isNotNull(); + + // Test default value + final LiveData progressLiveData = mViewModel.getProgressLiveData(); + EnrollmentProgress progress = progressLiveData.getValue(); + assertThat(progress).isNotNull(); + assertThat(progress.getSteps()).isEqualTo(-1); + assertThat(progress.getRemaining()).isEqualTo(0); + + // Update first progress + mCallbackWrapper.mValue.onEnrollmentProgress(25); + progress = progressLiveData.getValue(); + assertThat(progress).isNotNull(); + assertThat(progress.getSteps()).isEqualTo(25); + assertThat(progress.getRemaining()).isEqualTo(25); + + // Update second progress + mCallbackWrapper.mValue.onEnrollmentProgress(20); + progress = progressLiveData.getValue(); + assertThat(progress).isNotNull(); + assertThat(progress.getSteps()).isEqualTo(25); + assertThat(progress.getRemaining()).isEqualTo(20); + + // Update latest progress + mCallbackWrapper.mValue.onEnrollmentProgress(0); + progress = progressLiveData.getValue(); + assertThat(progress).isNotNull(); + assertThat(progress.getSteps()).isEqualTo(25); + assertThat(progress.getRemaining()).isEqualTo(0); + } + @Test public void testGetErrorMessageLiveData() { // Start enrollment @@ -262,4 +366,17 @@ public class FingerprintEnrollProgressViewModelTest { private static class TestWrapper { T mValue; } + + private static class TestHandler extends Handler { + + TestHandler() { + super(Looper.getMainLooper()); + } + + @Override + public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) { + msg.getCallback().run(); + return true; + } + } } From fd531d6a07e71b77322258307250d7f0ef8eda99 Mon Sep 17 00:00:00 2001 From: SongFerngWang Date: Thu, 6 Apr 2023 18:32:28 +0800 Subject: [PATCH 11/12] Unregister MobileNetworkRepository during onPause() - Move MobileNetworkRepository unregistration from onDestory() to onPause() for corresponding to its registration by onResume. Bug: 275456375 Test: built pass and verified in bug Change-Id: I4cd2f23501485d4f3cfcf867cdb8d81e0385794a --- .../settings/network/telephony/MobileNetworkSettings.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java index ce8305308c0..83d2117af1b 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java +++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java @@ -350,10 +350,15 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme } } + @Override + public void onPause() { + mMobileNetworkRepository.removeRegister(this); + super.onPause(); + } + @Override public void onDestroy() { super.onDestroy(); - mMobileNetworkRepository.removeRegister(this); } @VisibleForTesting From c616a6514461d1535f35c8a9025c6c7b262ebd8e Mon Sep 17 00:00:00 2001 From: Zaiyue Xue Date: Wed, 5 Apr 2023 16:40:20 +0800 Subject: [PATCH 12/12] Fix b/276423111: Keep unchanged perferences in the app list instead of clear all. Bug: 276423111 Bug: 269203672 Fix: 276423111 Fix: 269203672 Test: manual Change-Id: Ie2cb668ae4c46f5c40eed555510be034be45d713 --- .../BatteryUsageBreakdownController.java | 43 +++++++++++++------ .../BatteryUsageBreakdownControllerTest.java | 27 +++++++++--- 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java index 0be6c988ec7..2121c60218b 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java @@ -23,6 +23,7 @@ import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.text.TextUtils; +import android.util.ArraySet; import android.util.Log; import android.view.View; import android.widget.AdapterView; @@ -48,9 +49,11 @@ import com.android.settingslib.core.lifecycle.events.OnDestroy; import com.android.settingslib.core.lifecycle.events.OnResume; import com.android.settingslib.widget.FooterPreference; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; /** Controller for battery usage breakdown preference group. */ public class BatteryUsageBreakdownController extends BasePreferenceController @@ -61,6 +64,7 @@ public class BatteryUsageBreakdownController extends BasePreferenceController private static final String SPINNER_PREFERENCE_KEY = "battery_usage_spinner"; private static final String APP_LIST_PREFERENCE_KEY = "app_list"; private static final String PACKAGE_NAME_NONE = "none"; + private static final List EMPTY_ENTRY_LIST = new ArrayList<>(); private static int sUiMode = Configuration.UI_MODE_NIGHT_UNDEFINED; @@ -183,7 +187,7 @@ public class BatteryUsageBreakdownController extends BasePreferenceController if (mSpinnerPosition != position) { mSpinnerPosition = position; mHandler.post(() -> { - removeAndCacheAllPreferences(); + removeAndCacheAllUnusedPreferences(); addAllPreferences(); mMetricsFeatureProvider.action( mPrefContext, @@ -238,27 +242,34 @@ public class BatteryUsageBreakdownController extends BasePreferenceController private void showSpinnerAndAppList() { if (mBatteryDiffData == null) { mHandler.post(() -> { - removeAndCacheAllPreferences(); + removeAndCacheAllUnusedPreferences(); }); return; } mSpinnerPreference.setVisible(true); mAppListPreferenceGroup.setVisible(true); mHandler.post(() -> { - removeAndCacheAllPreferences(); + removeAndCacheAllUnusedPreferences(); addAllPreferences(); }); } + private List getBatteryDiffEntries() { + if (mBatteryDiffData == null) { + return EMPTY_ENTRY_LIST; + } + return mSpinnerPosition == 0 + ? mBatteryDiffData.getAppDiffEntryList() + : mBatteryDiffData.getSystemDiffEntryList(); + } + @VisibleForTesting void addAllPreferences() { if (mBatteryDiffData == null) { return; } final long start = System.currentTimeMillis(); - final List entries = mSpinnerPosition == 0 - ? mBatteryDiffData.getAppDiffEntryList() - : mBatteryDiffData.getSystemDiffEntryList(); + final List entries = getBatteryDiffEntries(); int prefIndex = mAppListPreferenceGroup.getPreferenceCount(); for (BatteryDiffEntry entry : entries) { boolean isAdded = false; @@ -272,7 +283,6 @@ public class BatteryUsageBreakdownController extends BasePreferenceController PowerGaugePreference pref = mAppListPreferenceGroup.findPreference(prefKey); if (pref != null) { isAdded = true; - Log.w(TAG, "preference should be removed for:" + entry.getPackageName()); } else { pref = (PowerGaugePreference) mPreferenceCache.get(prefKey); } @@ -301,16 +311,25 @@ public class BatteryUsageBreakdownController extends BasePreferenceController } @VisibleForTesting - void removeAndCacheAllPreferences() { + void removeAndCacheAllUnusedPreferences() { + List entries = getBatteryDiffEntries(); + Set entryKeySet = new ArraySet<>(); + for (BatteryDiffEntry entry : entries) { + entryKeySet.add(entry.getKey()); + } + final int prefsCount = mAppListPreferenceGroup.getPreferenceCount(); - for (int index = 0; index < prefsCount; index++) { + for (int index = prefsCount - 1; index >= 0; index--) { final Preference pref = mAppListPreferenceGroup.getPreference(index); - if (TextUtils.isEmpty(pref.getKey())) { + if (entryKeySet.contains(pref.getKey())) { + // The pref is still used, don't remove. continue; } - mPreferenceCache.put(pref.getKey(), pref); + if (!TextUtils.isEmpty(pref.getKey())) { + mPreferenceCache.put(pref.getKey(), pref); + } + mAppListPreferenceGroup.removePreference(pref); } - mAppListPreferenceGroup.removeAll(); } @VisibleForTesting diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java index 37f05bc0d8a..16fec0909e0 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java @@ -55,6 +55,7 @@ import java.util.TimeZone; @RunWith(RobolectricTestRunner.class) public final class BatteryUsageBreakdownControllerTest { private static final String PREF_KEY = "pref_key"; + private static final String PREF_KEY2 = "pref_key2"; private static final String PREF_SUMMARY = "fake preference summary"; @Mock @@ -175,7 +176,24 @@ public final class BatteryUsageBreakdownControllerTest { } @Test - public void removeAndCacheAllPreferences_buildCacheAndRemoveAllPreference() { + public void removeAndCacheAllUnusedPreferences_removePerf_buildCacheAndRemoveAllPreference() { + doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount(); + doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).getPreference(0); + doReturn(PREF_KEY2).when(mBatteryHistEntry).getKey(); + doReturn(PREF_KEY).when(mPowerGaugePreference).getKey(); + doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY); + // Ensures the testing data is correct. + assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty(); + + mBatteryUsageBreakdownController.removeAndCacheAllUnusedPreferences(); + + assertThat(mBatteryUsageBreakdownController.mPreferenceCache.get(PREF_KEY)) + .isEqualTo(mPowerGaugePreference); + verify(mAppListPreferenceGroup).removePreference(mPowerGaugePreference); + } + + @Test + public void removeAndCacheAllUnusedPreferences_keepPerf_KeepAllPreference() { doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount(); doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).getPreference(0); doReturn(PREF_KEY).when(mBatteryHistEntry).getKey(); @@ -184,11 +202,10 @@ public final class BatteryUsageBreakdownControllerTest { // Ensures the testing data is correct. assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty(); - mBatteryUsageBreakdownController.removeAndCacheAllPreferences(); + mBatteryUsageBreakdownController.removeAndCacheAllUnusedPreferences(); - assertThat(mBatteryUsageBreakdownController.mPreferenceCache.get(PREF_KEY)) - .isEqualTo(mPowerGaugePreference); - verify(mAppListPreferenceGroup).removeAll(); + verify(mAppListPreferenceGroup, never()).removePreference(any()); + assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty(); } @Test