diff --git a/res/values/strings.xml b/res/values/strings.xml index c77edb41a48..eab76b361b0 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -258,10 +258,10 @@ Stylus - + Tail button press - - %s (Work profile) + + %s (Work profile) Write in text fields @@ -1446,6 +1446,9 @@ Pair with %1$s? + + Add new member to the existing coordinated set + Bluetooth pairing code diff --git a/res/xml/keyboard_settings.xml b/res/xml/keyboard_settings.xml index bb1c3dad3dd..b18338bef8f 100644 --- a/res/xml/keyboard_settings.xml +++ b/res/xml/keyboard_settings.xml @@ -33,21 +33,6 @@ android:summary="@string/summary_placeholder"/> - - - - - - diff --git a/res/xml/language_and_input.xml b/res/xml/language_and_input.xml index 601ba8e3537..a5f6f63855d 100644 --- a/res/xml/language_and_input.xml +++ b/res/xml/language_and_input.xml @@ -73,13 +73,6 @@ android:title="@string/voice_input_settings_title" android:fragment="com.android.settings.language.DefaultVoiceInputPicker" /> - - + + + + + + + + \ No newline at end of file diff --git a/src/com/android/settings/applications/AppStateClonedAppsBridge.java b/src/com/android/settings/applications/AppStateClonedAppsBridge.java index 3348079b294..719023d10e2 100644 --- a/src/com/android/settings/applications/AppStateClonedAppsBridge.java +++ b/src/com/android/settings/applications/AppStateClonedAppsBridge.java @@ -58,6 +58,11 @@ public class AppStateClonedAppsBridge extends AppStateBaseBridge{ mCloneProfileApps = mContext.getPackageManager() .getInstalledPackagesAsUser(GET_ACTIVITIES, mCloneUserId).stream().map(x -> x.packageName).toList(); + } else if (!mCloneProfileApps.isEmpty()) { + // In case we remove clone profile (mCloneUserId becomes -1), the bridge state should + // reflect the same by setting cloneProfileApps as empty, without building the entire + // page. + mCloneProfileApps = new ArrayList<>(); } final List allApps = mAppSession.getAllApps(); diff --git a/src/com/android/settings/applications/ClonedAppsPreferenceController.java b/src/com/android/settings/applications/ClonedAppsPreferenceController.java index 332df1837b5..d134ceccd88 100644 --- a/src/com/android/settings/applications/ClonedAppsPreferenceController.java +++ b/src/com/android/settings/applications/ClonedAppsPreferenceController.java @@ -16,8 +16,6 @@ package com.android.settings.applications; -import static android.content.pm.PackageManager.GET_ACTIVITIES; - import static com.android.settings.Utils.PROPERTY_CLONED_APPS_ENABLED; import android.content.Context; @@ -87,8 +85,8 @@ public class ClonedAppsPreferenceController extends BasePreferenceController mContext.getResources().getStringArray( com.android.internal.R.array.cloneable_apps)); List primaryUserApps = mContext.getPackageManager() - .getInstalledPackagesAsUser(GET_ACTIVITIES, - UserHandle.myUserId()).stream().map(x -> x.packageName).toList(); + .getInstalledPackagesAsUser(/* flags*/ 0, UserHandle.myUserId()).stream() + .map(x -> x.packageName).toList(); // Count number of installed apps in system user. int availableAppsCount = (int) cloneableApps.stream() .filter(x -> primaryUserApps.contains(x)).count(); @@ -99,8 +97,8 @@ public class ClonedAppsPreferenceController extends BasePreferenceController } // Get all apps in clone profile if present. List cloneProfileApps = mContext.getPackageManager() - .getInstalledPackagesAsUser(GET_ACTIVITIES, - cloneUserId).stream().map(x -> x.packageName).toList(); + .getInstalledPackagesAsUser(/* flags*/ 0, cloneUserId).stream() + .map(x -> x.packageName).toList(); // Count number of allowlisted app present in clone profile. int clonedAppsCount = (int) cloneableApps.stream() .filter(x -> cloneProfileApps.contains(x)).count(); diff --git a/src/com/android/settings/applications/manageapplications/CloneBackend.java b/src/com/android/settings/applications/manageapplications/CloneBackend.java index 568631ec460..406c9304386 100644 --- a/src/com/android/settings/applications/manageapplications/CloneBackend.java +++ b/src/com/android/settings/applications/manageapplications/CloneBackend.java @@ -165,4 +165,13 @@ public class CloneBackend { public int getCloneUserId() { return mCloneUserId; } + + /** + * Resets {@link #mCloneUserId} to -1. + * Typically called after the cloneUser is removed, so that the obsolete clonedUserId present + * with the CloneBackend instance can be cleared. + */ + public void resetCloneUserId() { + mCloneUserId = -1; + } } diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java index e0f1b5f702d..548ca553b40 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplications.java +++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java @@ -923,10 +923,15 @@ public class ManageApplications extends InstrumentedFragment } IUserManager um = IUserManager.Stub.asInterface( ServiceManager.getService(Context.USER_SERVICE)); + CloneBackend cloneBackend = CloneBackend.getInstance(getContext()); try { // Warning: This removes all the data, media & images present in cloned user. - um.removeUser(clonedUserId); - mApplications.rebuild(); + if (um.removeUser(clonedUserId)) { + cloneBackend.resetCloneUserId(); + mApplications.rebuild(); + } else if (ManageApplications.DEBUG) { + Log.e(TAG, "Failed to remove cloned user"); + } } catch (RemoteException e) { Log.e(TAG, "Failed to remove cloned apps", e); Toast.makeText(getContext(), diff --git a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettings.java b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettings.java index ba0d9afd97f..8169072d868 100644 --- a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettings.java +++ b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettings.java @@ -16,10 +16,8 @@ package com.android.settings.applications.specialaccess.interactacrossprofiles; import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECTED_WORK_AND_PERSONAL_APPS_TITLE; -import static android.content.pm.PackageManager.GET_ACTIVITIES; import android.annotation.Nullable; -import android.app.admin.DevicePolicyManager; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -157,9 +155,9 @@ public class InteractAcrossProfilesSettings extends EmptyTextSettings { private static List getAllInstalledPackages( PackageManager packageManager, UserHandle personalProfile, UserHandle workProfile) { List personalPackages = packageManager.getInstalledPackagesAsUser( - GET_ACTIVITIES, personalProfile.getIdentifier()); + /* flags= */ 0, personalProfile.getIdentifier()); List workPackages = packageManager.getInstalledPackagesAsUser( - GET_ACTIVITIES, workProfile.getIdentifier()); + /* flags= */ 0, workProfile.getIdentifier()); List allPackages = new ArrayList<>(personalPackages); for (PackageInfo workPackage : workPackages) { if (allPackages.stream().noneMatch( diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java index 988a13ca799..724947c2fd6 100644 --- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java +++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java @@ -319,7 +319,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll } /** - * Disable the Le Audio profile, VCP, and CSIP for each of the Le Audio devices. + * Disable the Le Audio profile for each of the Le Audio devices. * * @param profile the LeAudio profile */ @@ -328,20 +328,12 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll Log.e(TAG, "There is no the LE profile or no device in mProfileDeviceMap. Do nothing."); return; } - LocalBluetoothProfile vcp = mProfileManager.getVolumeControlProfile(); - LocalBluetoothProfile csip = mProfileManager.getCsipSetCoordinatorProfile(); for (CachedBluetoothDevice leAudioDevice : mProfileDeviceMap.get(profile.toString())) { Log.d(TAG, "device:" + leAudioDevice.getDevice().getAnonymizedAddress() + "disable LE profile"); profile.setEnabled(leAudioDevice.getDevice(), false); - if (vcp != null) { - vcp.setEnabled(leAudioDevice.getDevice(), false); - } - if (csip != null) { - csip.setEnabled(leAudioDevice.getDevice(), false); - } } if (!SystemProperties.getBoolean(ENABLE_DUAL_MODE_AUDIO, false)) { @@ -352,7 +344,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll } /** - * Enable the Le Audio profile, VCP, and CSIP for each of the Le Audio devices. + * Enable the Le Audio profile for each of the Le Audio devices. * * @param profile the LeAudio profile */ @@ -368,19 +360,11 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll disableProfileBeforeUserEnablesLeAudio(mProfileManager.getHeadsetProfile()); } - LocalBluetoothProfile vcp = mProfileManager.getVolumeControlProfile(); - LocalBluetoothProfile csip = mProfileManager.getCsipSetCoordinatorProfile(); for (CachedBluetoothDevice leAudioDevice : mProfileDeviceMap.get(profile.toString())) { Log.d(TAG, "device:" + leAudioDevice.getDevice().getAnonymizedAddress() + "enable LE profile"); profile.setEnabled(leAudioDevice.getDevice(), true); - if (vcp != null) { - vcp.setEnabled(leAudioDevice.getDevice(), true); - } - if (csip != null) { - csip.setEnabled(leAudioDevice.getDevice(), true); - } } } diff --git a/src/com/android/settings/bluetooth/BluetoothPairingController.java b/src/com/android/settings/bluetooth/BluetoothPairingController.java index 535e04086c0..f75d301ad22 100644 --- a/src/com/android/settings/bluetooth/BluetoothPairingController.java +++ b/src/com/android/settings/bluetooth/BluetoothPairingController.java @@ -72,6 +72,7 @@ public class BluetoothPairingController implements OnCheckedChangeListener, private boolean mIsCoordinatedSetMember; private boolean mIsLeAudio; private boolean mIsLeContactSharingEnabled; + private boolean mIsLateBonding; /** * Creates an instance of a BluetoothPairingController. @@ -98,6 +99,7 @@ public class BluetoothPairingController implements OnCheckedChangeListener, mDeviceName = mBluetoothManager.getCachedDeviceManager().getName(mDevice); mPbapClientProfile = mBluetoothManager.getProfileManager().getPbapClientProfile(); mPasskeyFormatted = formatKey(mPasskey); + mIsLateBonding = mBluetoothManager.getCachedDeviceManager().isLateBonding(mDevice); final CachedBluetoothDevice cachedDevice = mBluetoothManager.getCachedDeviceManager().findDevice(mDevice); @@ -116,7 +118,10 @@ public class BluetoothPairingController implements OnCheckedChangeListener, mIsLeContactSharingEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI, SettingsUIDeviceConfig.BT_LE_AUDIO_CONTACT_SHARING_ENABLED, true); - Log.d(TAG, "BT_LE_AUDIO_CONTACT_SHARING_ENABLED is " + mIsLeContactSharingEnabled); + Log.d(TAG, + "BT_LE_AUDIO_CONTACT_SHARING_ENABLED is " + + mIsLeContactSharingEnabled + " isCooridnatedSetMember " + + mIsCoordinatedSetMember); } } @@ -192,6 +197,15 @@ public class BluetoothPairingController implements OnCheckedChangeListener, return mIsCoordinatedSetMember; } + /** + * A method for querying if the bluetooth device from a coordinated set is bonding late. + * + * @return - A boolean indicating if the device is bonding late. + */ + public boolean isLateBonding() { + return mIsLateBonding; + } + /** * A method for querying if the bluetooth device has a profile already set up on this device. * diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java b/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java index ad38fa1296b..e11c275bded 100644 --- a/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothPairingDialogFragment.java @@ -357,8 +357,14 @@ public class BluetoothPairingDialogFragment extends InstrumentedDialogFragment i pairingViewContent.setText(mPairingController.getPairingContent()); } final TextView messagePairingSet = (TextView) view.findViewById(R.id.pairing_group_message); - messagePairingSet.setVisibility(mPairingController.isCoordinatedSetMemberDevice() - ? View.VISIBLE : View.GONE); + if (mPairingController.isLateBonding()) { + messagePairingSet.setText(getString(R.string.bluetooth_pairing_group_late_bonding)); + } + + boolean setPairingMessage = + mPairingController.isCoordinatedSetMemberDevice() || mPairingController.isLateBonding(); + + messagePairingSet.setVisibility(setPairingMessage ? View.VISIBLE : View.GONE); return view; } } diff --git a/src/com/android/settings/bluetooth/BluetoothPairingRequest.java b/src/com/android/settings/bluetooth/BluetoothPairingRequest.java index ed477c84602..ace0643ee7b 100644 --- a/src/com/android/settings/bluetooth/BluetoothPairingRequest.java +++ b/src/com/android/settings/bluetooth/BluetoothPairingRequest.java @@ -26,6 +26,7 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; /** @@ -53,17 +54,28 @@ public final class BluetoothPairingRequest extends BroadcastReceiver { boolean shouldShowDialog = LocalBluetoothPreferences.shouldShowDialogInForeground( context, device); - // Skips consent pairing dialog if the device was recently associated with CDM + Log.d(TAG, + "Receive ACTION_PAIRING_REQUEST pairingVariant=" + pairingVariant + + " canBondWithoutDialog=" + device.canBondWithoutDialog() + + " isOngoingPairByCsip=" + + mBluetoothManager.getCachedDeviceManager().isOngoingPairByCsip(device) + + " isLateBonding=" + + mBluetoothManager.getCachedDeviceManager().isLateBonding(device)); + + /* Skips consent pairing dialog if the device was recently associated with CDM + * or if the device is a member of the coordinated set and is not bonding late. + */ if (pairingVariant == BluetoothDevice.PAIRING_VARIANT_CONSENT - && (device.canBondWithoutDialog() - || mBluetoothManager.getCachedDeviceManager().isOngoingPairByCsip(device))) { + && (device.canBondWithoutDialog() + || (mBluetoothManager.getCachedDeviceManager().isOngoingPairByCsip(device) + && !mBluetoothManager.getCachedDeviceManager().isLateBonding(device)))) { device.setPairingConfirmation(true); } else if (powerManager.isInteractive() && shouldShowDialog) { // Since the screen is on and the BT-related activity is in the foreground, // just open the dialog // convert broadcast intent into activity intent (same action string) - Intent pairingIntent = BluetoothPairingService.getPairingDialogIntent(context, - intent, BluetoothDevice.EXTRA_PAIRING_INITIATOR_FOREGROUND); + Intent pairingIntent = BluetoothPairingService.getPairingDialogIntent( + context, intent, BluetoothDevice.EXTRA_PAIRING_INITIATOR_FOREGROUND); context.startActivityAsUser(pairingIntent, UserHandle.CURRENT); } else { diff --git a/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java b/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java index e821966f428..ec9f4c6eb51 100644 --- a/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java +++ b/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java @@ -254,16 +254,17 @@ public class StylusDevicesController extends AbstractPreferenceController implem private List getUserAndManagedProfiles() { UserManager um = mContext.getSystemService(UserManager.class); - final ArrayList userManagedProfiles = new ArrayList<>(); + final List userManagedProfiles = new ArrayList<>(); // Add the current user, then add all the associated managed profiles. final UserHandle currentUser = Process.myUserHandle(); userManagedProfiles.add(currentUser); final List userInfos = um.getUsers(); for (UserInfo info : userInfos) { - if (um.isManagedProfile(info.id) - && um.getProfileParent(info.id).id == currentUser.getIdentifier()) { - userManagedProfiles.add(UserHandle.of(info.id)); + int userId = info.id; + if (um.isManagedProfile(userId) + && um.getProfileParent(userId).id == currentUser.getIdentifier()) { + userManagedProfiles.add(UserHandle.of(userId)); } } return userManagedProfiles; diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFeatureProvider.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFeatureProvider.java new file mode 100644 index 00000000000..1c8fd12ec42 --- /dev/null +++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFeatureProvider.java @@ -0,0 +1,35 @@ +/* + * 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.deviceinfo.batteryinfo; + +/** + * Feature Provider used for getting battery information. + */ +public interface BatteryInfoFeatureProvider { + + /** Returns true if Manufacture date should be shown */ + boolean isManufactureDateAvailable(); + + /** Returns true if First use date should be shown */ + boolean isFirstUseDateAvailable(); + + /** Returns the summary of battery manufacture date */ + CharSequence getManufactureDateSummary(); + + /** Returns the summary of battery first use date */ + CharSequence getFirstUseDateSummary(); +} diff --git a/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFeatureProviderImpl.java b/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFeatureProviderImpl.java new file mode 100644 index 00000000000..36ddad7d158 --- /dev/null +++ b/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFeatureProviderImpl.java @@ -0,0 +1,91 @@ +/* + * 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.deviceinfo.batteryinfo; + +import android.content.Context; +import android.os.BatteryManager; + +import java.text.DateFormat; +import java.util.Date; + +/** Implementation of {@code BatteryInfoFeatureProvider} */ +public class BatteryInfoFeatureProviderImpl implements BatteryInfoFeatureProvider { + + private BatteryManager mBatteryManager; + private Context mContext; + private long mManufactureDateInSec; + private long mFirstUseDateInSec; + + public BatteryInfoFeatureProviderImpl(Context context) { + mContext = context; + mBatteryManager = mContext.getSystemService(BatteryManager.class); + } + + @Override + public boolean isManufactureDateAvailable() { + return false; + } + + @Override + public boolean isFirstUseDateAvailable() { + return false; + } + + @Override + public CharSequence getManufactureDateSummary() { + if (!isManufactureDateAvailable()) { + return null; + } + final long manufactureDateInSec = getManufactureDate(); + + return getFormattedDate(manufactureDateInSec * 1000L); + } + + @Override + public CharSequence getFirstUseDateSummary() { + if (!isFirstUseDateAvailable()) { + return null; + } + final long firstUseDateInSec = getFirstUseDate(); + + return getFormattedDate(firstUseDateInSec * 1000L); + } + + protected long getManufactureDate() { + if (mManufactureDateInSec == 0L) { + mManufactureDateInSec = mBatteryManager.getLongProperty( + BatteryManager.BATTERY_PROPERTY_MANUFACTURING_DATE); + } + return mManufactureDateInSec; + } + + protected long getFirstUseDate() { + if (mFirstUseDateInSec == 0L) { + mFirstUseDateInSec = mBatteryManager.getLongProperty( + BatteryManager.BATTERY_PROPERTY_FIRST_USAGE_DATE); + } + return mFirstUseDateInSec; + } + + private CharSequence getFormattedDate(long dateInMs) { + final Date date = new Date(dateInMs); + final CharSequence formattedDate = + DateFormat.getDateInstance(DateFormat.LONG).format(date.getTime()); + + return formattedDate; + } +} diff --git a/src/com/android/settings/inputmethod/KeyboardSettings.java b/src/com/android/settings/inputmethod/KeyboardSettings.java index 2d6ac885cd6..9fddb9328a6 100644 --- a/src/com/android/settings/inputmethod/KeyboardSettings.java +++ b/src/com/android/settings/inputmethod/KeyboardSettings.java @@ -30,9 +30,7 @@ import androidx.annotation.Nullable; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; -import com.android.settings.language.DefaultVoiceInputPreferenceController; import com.android.settings.language.PointerSpeedController; -import com.android.settings.language.TtsPreferenceController; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.widget.PreferenceCategoryController; import com.android.settingslib.core.AbstractPreferenceController; @@ -49,8 +47,6 @@ public class KeyboardSettings extends DashboardFragment { private static final String TAG = "KeyboardSettings"; private static final String KEY_KEYBOARDS_CATEGORY = "keyboards_category"; - private static final String KEY_SPEECH_CATEGORY = "speech_category"; - private static final String KEY_TEXT_TO_SPEECH = "tts_settings_summary"; private static final String KEY_POINTER_CATEGORY = "pointer_category"; @Override @@ -102,17 +98,6 @@ public class KeyboardSettings extends DashboardFragment { Arrays.asList(virtualKeyboardPreferenceController, physicalKeyboardPreferenceController))); - // Speech - final DefaultVoiceInputPreferenceController defaultVoiceInputPreferenceController = - new DefaultVoiceInputPreferenceController(context, lifecycle); - final TtsPreferenceController ttsPreferenceController = - new TtsPreferenceController(context, KEY_TEXT_TO_SPEECH); - controllers.add(defaultVoiceInputPreferenceController); - controllers.add(ttsPreferenceController); - controllers.add(new PreferenceCategoryController(context, - KEY_SPEECH_CATEGORY).setChildren( - Arrays.asList(defaultVoiceInputPreferenceController, ttsPreferenceController))); - // Pointer final PointerSpeedController pointerController = new PointerSpeedController(context); controllers.add(pointerController); diff --git a/src/com/android/settings/language/LanguageSettings.java b/src/com/android/settings/language/LanguageSettings.java index f814e36f275..5da0f2f769a 100644 --- a/src/com/android/settings/language/LanguageSettings.java +++ b/src/com/android/settings/language/LanguageSettings.java @@ -21,14 +21,25 @@ import android.app.settings.SettingsEnums; import android.content.Context; import android.util.FeatureFlagUtils; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.widget.PreferenceCategoryController; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.search.SearchIndexable; +import java.util.ArrayList; +import java.util.List; + @SearchIndexable public class LanguageSettings extends DashboardFragment { + private static final String KEY_SPEECH_CATEGORY = "speech_category"; + private static final String TAG = "LanguageSettings"; @Override @@ -59,8 +70,43 @@ public class LanguageSettings extends DashboardFragment { return R.xml.language_settings; } + protected List createPreferenceControllers(Context context) { + return buildPreferenceControllers(context, getSettingsLifecycle()); + } + + private static List buildPreferenceControllers( + @NonNull Context context, @Nullable Lifecycle lifecycle) { + final List controllers = new ArrayList<>(); + + final DefaultVoiceInputPreferenceController defaultVoiceInputPreferenceController = + new DefaultVoiceInputPreferenceController(context, lifecycle); + final TtsPreferenceController ttsPreferenceController = + new TtsPreferenceController(context); + final OnDeviceRecognitionPreferenceController onDeviceRecognitionPreferenceController = + new OnDeviceRecognitionPreferenceController(context); + + controllers.add(defaultVoiceInputPreferenceController); + controllers.add(ttsPreferenceController); + List speechCategoryChildren = new ArrayList<>( + List.of(defaultVoiceInputPreferenceController, ttsPreferenceController)); + + if (onDeviceRecognitionPreferenceController.isAvailable()) { + controllers.add(onDeviceRecognitionPreferenceController); + speechCategoryChildren.add(onDeviceRecognitionPreferenceController); + } + + controllers.add(new PreferenceCategoryController(context, KEY_SPEECH_CATEGORY) + .setChildren(speechCategoryChildren)); + + return controllers; + } + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider(R.xml.language_settings) { + public List createPreferenceControllers( + Context context) { + return buildPreferenceControllers(context, null); + } @Override protected boolean isPageSearchEnabled(Context context) { return FeatureFlagUtils diff --git a/src/com/android/settings/language/OnDeviceRecognitionPreferenceController.java b/src/com/android/settings/language/OnDeviceRecognitionPreferenceController.java index 318663957c9..35b42187c8f 100644 --- a/src/com/android/settings/language/OnDeviceRecognitionPreferenceController.java +++ b/src/com/android/settings/language/OnDeviceRecognitionPreferenceController.java @@ -33,10 +33,16 @@ import java.util.Optional; /** Controller of the On-device recognition preference. */ public class OnDeviceRecognitionPreferenceController extends BasePreferenceController { + private static final String KEY_ON_DEVICE_RECOGNITION = "on_device_recognition_settings"; + private static final String TAG = "OnDeviceRecognitionPreferenceController"; private Optional mIntent; + public OnDeviceRecognitionPreferenceController(Context context) { + this(context, KEY_ON_DEVICE_RECOGNITION); + } + public OnDeviceRecognitionPreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); } @@ -51,6 +57,10 @@ public class OnDeviceRecognitionPreferenceController extends BasePreferenceContr : CONDITIONALLY_UNAVAILABLE; } + public String getPreferenceKey() { + return KEY_ON_DEVICE_RECOGNITION; + } + @Override public void updateState(Preference preference) { super.updateState(preference); diff --git a/src/com/android/settings/language/TtsPreferenceController.java b/src/com/android/settings/language/TtsPreferenceController.java index 6507785cda3..b38920606cf 100644 --- a/src/com/android/settings/language/TtsPreferenceController.java +++ b/src/com/android/settings/language/TtsPreferenceController.java @@ -26,9 +26,15 @@ import com.android.settings.core.BasePreferenceController; public class TtsPreferenceController extends BasePreferenceController { + private static final String KEY_TEXT_TO_SPEECH = "tts_settings_summary"; + @VisibleForTesting TtsEngines mTtsEngines; + public TtsPreferenceController(Context context) { + this(context, KEY_TEXT_TO_SPEECH); + } + public TtsPreferenceController(Context context, String key) { super(context, key); mTtsEngines = new TtsEngines(context); @@ -40,4 +46,8 @@ public class TtsPreferenceController extends BasePreferenceController { mContext.getResources().getBoolean(R.bool.config_show_tts_settings_summary) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; } + + public String getPreferenceKey() { + return KEY_TEXT_TO_SPEECH; + } } diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java index 9d81baa9bca..d7c2c34ba70 100644 --- a/src/com/android/settings/overlay/FeatureFactory.java +++ b/src/com/android/settings/overlay/FeatureFactory.java @@ -33,6 +33,7 @@ import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider; import com.android.settings.bluetooth.BluetoothFeatureProvider; import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; +import com.android.settings.deviceinfo.batteryinfo.BatteryInfoFeatureProvider; import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider; import com.android.settings.fuelgauge.BatterySettingsFeatureProvider; import com.android.settings.fuelgauge.BatteryStatusFeatureProvider; @@ -204,6 +205,11 @@ public abstract class FeatureFactory { */ public abstract KeyboardSettingsFeatureProvider getKeyboardSettingsFeatureProvider(); + /** + * Retrieves implementation for Battery information feature. + */ + public abstract BatteryInfoFeatureProvider getBatteryInfoFeatureProvider(); + public static final class FactoryNotFoundException extends RuntimeException { public FactoryNotFoundException(Throwable throwable) { super("Unable to create factory. Did you misconfigure Proguard?", throwable); diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java index d4f18782c05..10135f2687f 100644 --- a/src/com/android/settings/overlay/FeatureFactoryImpl.java +++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java @@ -47,6 +47,8 @@ import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.dashboard.DashboardFeatureProviderImpl; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; import com.android.settings.dashboard.suggestions.SuggestionFeatureProviderImpl; +import com.android.settings.deviceinfo.batteryinfo.BatteryInfoFeatureProvider; +import com.android.settings.deviceinfo.batteryinfo.BatteryInfoFeatureProviderImpl; import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider; import com.android.settings.enterprise.EnterprisePrivacyFeatureProviderImpl; import com.android.settings.fuelgauge.BatterySettingsFeatureProvider; @@ -117,6 +119,7 @@ public class FeatureFactoryImpl extends FeatureFactory { private AdvancedVpnFeatureProvider mAdvancedVpnFeatureProvider; private WifiFeatureProvider mWifiFeatureProvider; private KeyboardSettingsFeatureProvider mKeyboardSettingsFeatureProvider; + private BatteryInfoFeatureProvider mBatteryInfoFeatureProvider; @Override public SupportFeatureProvider getSupportFeatureProvider(Context context) { @@ -376,4 +379,12 @@ public class FeatureFactoryImpl extends FeatureFactory { } return mKeyboardSettingsFeatureProvider; } + + @Override + public BatteryInfoFeatureProvider getBatteryInfoFeatureProvider() { + if (mBatteryInfoFeatureProvider == null) { + mBatteryInfoFeatureProvider = new BatteryInfoFeatureProviderImpl(getAppContext()); + } + return mBatteryInfoFeatureProvider; + } } diff --git a/src/com/android/settings/wifi/repository/WifiHotspotRepository.java b/src/com/android/settings/wifi/repository/WifiHotspotRepository.java index c46fb2b0053..676421472a2 100644 --- a/src/com/android/settings/wifi/repository/WifiHotspotRepository.java +++ b/src/com/android/settings/wifi/repository/WifiHotspotRepository.java @@ -102,7 +102,6 @@ public class WifiHotspotRepository { protected Boolean mIs6gBandSupported; protected Boolean mIs6gAvailable; protected MutableLiveData m6gAvailable; - protected String mCurrentCountryCode; protected ActiveCountryCodeChangedCallback mActiveCountryCodeChangedCallback; @VisibleForTesting @@ -568,17 +567,12 @@ public class WifiHotspotRepository { @Override public void onActiveCountryCodeChanged(String country) { log("onActiveCountryCodeChanged(), country:" + country); - mCurrentCountryCode = country; purgeRefreshData(); refresh(); } @Override public void onCountryCodeInactive() { - log("onCountryCodeInactive()"); - mCurrentCountryCode = null; - purgeRefreshData(); - refresh(); } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java index 7fa6236b197..1fcf396f31c 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java @@ -315,7 +315,7 @@ public class StylusDevicesControllerTest { } @Test - public void defaultNotesPreferenceClick_multiUser_showsProfileSelectorDialog() { + public void defaultNotesPreferenceClick_multiUserManagedProfile_showsProfileSelectorDialog() { mContext.setTheme(R.style.Theme_AppCompat); final String permissionPackageName = "permissions.package"; final UserHandle currentUser = Process.myUserHandle(); @@ -337,13 +337,43 @@ public class StylusDevicesControllerTest { assertTrue(mController.mDialog.isShowing()); } + @Test + public void defaultNotesPreferenceClick_noManagedProfile_sendsManageDefaultRoleIntent() { + final ArgumentCaptor captor = ArgumentCaptor.forClass(Intent.class); + mContext.setTheme(R.style.Theme_AppCompat); + final String permissionPackageName = "permissions.package"; + final UserHandle currentUser = Process.myUserHandle(); + List userInfos = Arrays.asList( + new UserInfo(currentUser.getIdentifier(), "current", 0), + new UserInfo(1, "other", UserInfo.FLAG_FULL) + ); + when(mUserManager.getUsers()).thenReturn(userInfos); + when(mUserManager.isManagedProfile(1)).thenReturn(false); + when(mUserManager.getUserInfo(currentUser.getIdentifier())).thenReturn(userInfos.get(0)); + when(mUserManager.getUserInfo(1)).thenReturn(userInfos.get(1)); + when(mUserManager.getProfileParent(any())).thenReturn(null); + when(mPm.getPermissionControllerPackageName()).thenReturn(permissionPackageName); + + showScreen(mController); + Preference defaultNotesPref = mPreferenceContainer.getPreference(0); + mController.onPreferenceClick(defaultNotesPref); + + verify(mContext).startActivity(captor.capture()); + Intent intent = captor.getValue(); + assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MANAGE_DEFAULT_APP); + assertThat(intent.getPackage()).isEqualTo(permissionPackageName); + assertThat(intent.getStringExtra(Intent.EXTRA_ROLE_NAME)).isEqualTo( + RoleManager.ROLE_NOTES); + assertNull(mController.mDialog); + } + @Test public void profileSelectDialogClickCallback_onClick_sendsIntent() { Intent intent = new Intent(); UserHandle user1 = mock(UserHandle.class); UserHandle user2 = mock(UserHandle.class); - List users = Arrays.asList(new UserHandle[] {user1, user2}); - mController.mDialog = mock(Dialog.class); + List users = Arrays.asList(user1, user2); + mController.mDialog = new Dialog(mContext); UserAdapter.OnClickListener callback = mController .createProfileDialogClickCallback(intent, users); diff --git a/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFeatureProviderImplTest.java new file mode 100644 index 00000000000..6c149774e9d --- /dev/null +++ b/tests/robotests/src/com/android/settings/deviceinfo/batteryinfo/BatteryInfoFeatureProviderImplTest.java @@ -0,0 +1,97 @@ +/* + * 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.deviceinfo.batteryinfo; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.os.BatteryManager; + +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; + + +@RunWith(RobolectricTestRunner.class) +public class BatteryInfoFeatureProviderImplTest { + @Mock + private BatteryManager mBatteryManager; + + private Context mContext; + private BatteryInfoFeatureProviderImpl mImpl; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(org.robolectric.RuntimeEnvironment.application); + doReturn(mBatteryManager).when(mContext).getSystemService(BatteryManager.class); + mImpl = spy(new BatteryInfoFeatureProviderImpl(mContext)); + } + + @Test + public void isManufactureDateAvailable_returnFalse() { + assertThat(mImpl.isManufactureDateAvailable()).isFalse(); + } + + @Test + public void isFirstUseDateAvailable_returnFalse() { + assertThat(mImpl.isFirstUseDateAvailable()).isFalse(); + } + + @Test + public void getManufactureDateSummary_available_returnExpectedDate() { + doReturn(true).when(mImpl).isManufactureDateAvailable(); + when(mBatteryManager.getLongProperty(BatteryManager.BATTERY_PROPERTY_MANUFACTURING_DATE)) + .thenReturn(1669680000L); + + final CharSequence result = mImpl.getManufactureDateSummary(); + + assertThat(result).isEqualTo("November 29, 2022"); + } + + @Test + public void getManufactureDateSummary_unavailable_returnNull() { + doReturn(false).when(mImpl).isManufactureDateAvailable(); + + assertThat(mImpl.getManufactureDateSummary()).isNull(); + } + + @Test + public void getFirstUseDateSummary_available_returnExpectedDate() { + doReturn(true).when(mImpl).isFirstUseDateAvailable(); + when(mBatteryManager.getLongProperty(BatteryManager.BATTERY_PROPERTY_FIRST_USAGE_DATE)) + .thenReturn(1669680000L); + + final CharSequence result = mImpl.getFirstUseDateSummary(); + + assertThat(result).isEqualTo("November 29, 2022"); + } + + @Test + public void getFirstUseDateSummary_unavailable_returnNull() { + doReturn(false).when(mImpl).isFirstUseDateAvailable(); + + assertThat(mImpl.getFirstUseDateSummary()).isNull(); + } +} diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java index 47053802e39..e77229f35fa 100644 --- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java +++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java @@ -31,6 +31,7 @@ import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider; import com.android.settings.bluetooth.BluetoothFeatureProvider; import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; +import com.android.settings.deviceinfo.batteryinfo.BatteryInfoFeatureProvider; import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider; import com.android.settings.fuelgauge.BatterySettingsFeatureProvider; import com.android.settings.fuelgauge.BatteryStatusFeatureProvider; @@ -82,6 +83,7 @@ public class FakeFeatureFactory extends FeatureFactory { public final AwareFeatureProvider mAwareFeatureProvider; public final FaceFeatureProvider mFaceFeatureProvider; public final BiometricsRepositoryProvider mBiometricsRepositoryProvider; + public final BatteryInfoFeatureProvider mBatteryInfoFeatureProvider; public PanelFeatureProvider panelFeatureProvider; public SlicesFeatureProvider slicesFeatureProvider; @@ -148,6 +150,7 @@ public class FakeFeatureFactory extends FeatureFactory { mAdvancedVpnFeatureProvider = mock(AdvancedVpnFeatureProvider.class); mWifiFeatureProvider = mock(WifiFeatureProvider.class); mKeyboardSettingsFeatureProvider = mock(KeyboardSettingsFeatureProvider.class); + mBatteryInfoFeatureProvider = mock(BatteryInfoFeatureProvider.class); } @Override @@ -304,4 +307,9 @@ public class FakeFeatureFactory extends FeatureFactory { public KeyboardSettingsFeatureProvider getKeyboardSettingsFeatureProvider() { return mKeyboardSettingsFeatureProvider; } + + @Override + public BatteryInfoFeatureProvider getBatteryInfoFeatureProvider() { + return mBatteryInfoFeatureProvider; + } } diff --git a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt index 50589ac67e0..801b52e0f2e 100644 --- a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt +++ b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt @@ -27,6 +27,7 @@ import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider import com.android.settings.bluetooth.BluetoothFeatureProvider import com.android.settings.dashboard.DashboardFeatureProvider import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider +import com.android.settings.deviceinfo.batteryinfo.BatteryInfoFeatureProvider import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider import com.android.settings.fuelgauge.BatterySettingsFeatureProvider import com.android.settings.fuelgauge.BatteryStatusFeatureProvider @@ -187,4 +188,8 @@ class FakeFeatureFactory : FeatureFactory() { override fun getKeyboardSettingsFeatureProvider(): KeyboardSettingsFeatureProvider { TODO("Not yet implemented") } + + override fun getBatteryInfoFeatureProvider(): BatteryInfoFeatureProvider { + TODO("Not yet implemented") + } } diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java index d7490100990..ae5f5f5272c 100644 --- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java +++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java @@ -29,6 +29,7 @@ import com.android.settings.biometrics2.factory.BiometricsRepositoryProvider; import com.android.settings.bluetooth.BluetoothFeatureProvider; import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; +import com.android.settings.deviceinfo.batteryinfo.BatteryInfoFeatureProvider; import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider; import com.android.settings.fuelgauge.BatterySettingsFeatureProvider; import com.android.settings.fuelgauge.BatteryStatusFeatureProvider; @@ -77,6 +78,7 @@ public class FakeFeatureFactory extends FeatureFactory { public final AwareFeatureProvider mAwareFeatureProvider; public final FaceFeatureProvider mFaceFeatureProvider; public final BiometricsRepositoryProvider mBiometricsRepositoryProvider; + public final BatteryInfoFeatureProvider mBatteryInfoFeatureProvider; public PanelFeatureProvider panelFeatureProvider; public SlicesFeatureProvider slicesFeatureProvider; @@ -134,6 +136,7 @@ public class FakeFeatureFactory extends FeatureFactory { mAdvancedVpnFeatureProvider = mock(AdvancedVpnFeatureProvider.class); mWifiFeatureProvider = mock(WifiFeatureProvider.class); mKeyboardSettingsFeatureProvider = mock(KeyboardSettingsFeatureProvider.class); + mBatteryInfoFeatureProvider = mock(BatteryInfoFeatureProvider.class); } @Override @@ -290,4 +293,9 @@ public class FakeFeatureFactory extends FeatureFactory { public KeyboardSettingsFeatureProvider getKeyboardSettingsFeatureProvider() { return mKeyboardSettingsFeatureProvider; } + + @Override + public BatteryInfoFeatureProvider getBatteryInfoFeatureProvider() { + return mBatteryInfoFeatureProvider; + } } diff --git a/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java b/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java index 7110a1b2a52..af3b8f13ad4 100644 --- a/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java +++ b/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java @@ -78,7 +78,6 @@ import java.util.List; public class WifiHotspotRepositoryTest { static final String WIFI_SSID = "wifi_ssid"; static final String WIFI_PASSWORD = "wifi_password"; - static final String WIFI_CURRENT_COUNTRY_CODE = "US"; static final int WIFI_5GHZ_BAND_PREFERRED = BAND_2GHZ_5GHZ; static final int WIFI_6GHZ_BAND_PREFERRED = BAND_2GHZ_5GHZ_6GHZ; @@ -112,7 +111,6 @@ public class WifiHotspotRepositoryTest { mRepository = new WifiHotspotRepository(mContext, mWifiManager, mTetheringManager); mRepository.mSecurityType = mSecurityType; mRepository.mSpeedType = mSpeedType; - mRepository.mCurrentCountryCode = WIFI_CURRENT_COUNTRY_CODE; mRepository.mIsDualBand = true; mRepository.mIs5gAvailable = true; mRepository.mIs6gAvailable = true;